1: <?php
2:
3: /**
4: * IMAP modules
5: * @package modules
6: * @subpackage imap
7: */
8:
9: if (!defined('DEBUG_MODE')) { die(); }
10:
11: /**
12: * Format a custom list controls section
13: * @subpackage imap/output
14: */
15: class Hm_Output_imap_custom_controls extends Hm_Output_Module {
16: /**
17: * Adds list controls to the IMAP folder page view
18: */
19: protected function output() {
20: if ($this->get('custom_list_controls_type')) {
21: $filter = $this->get('list_filter');
22: $sort = $this->get('list_sort');
23: $keyword = $this->get('list_keyword');
24: $opts = array('all' => $this->trans('All'), 'unseen' => $this->trans('Unread'),
25: 'seen' => $this->trans('Read'), 'flagged' => $this->trans('Flagged'),
26: 'unflagged' => $this->trans('Unflagged'), 'answered' => $this->trans('Answered'),
27: 'unanswered' => $this->trans('Unanswered'));
28:
29: $default_sort_order = $this->get('default_sort_order');
30: if ($default_sort_order == 'arrival') {
31: $sorts = array('arrival' => $this->trans('Arrival Date'), 'from' => $this->trans('From'),
32: 'to' => $this->trans('To'), 'subject' => $this->trans('Subject'), 'date' => $this->trans('Sent Date'));
33: } else {
34: $sorts = array('date' => $this->trans('Sent Date'), 'from' => $this->trans('From'),
35: 'to' => $this->trans('To'), 'subject' => $this->trans('Subject'), 'arrival' => $this->trans('Arrival Date'));
36: }
37:
38: $custom = '<form id="imap_filter_form" method="GET" class="d-flex align-content-center">';
39: $custom .= '<input type="hidden" name="page" value="message_list" />';
40: $custom .= '<input type="hidden" name="list_path" value="'.$this->html_safe($this->get('list_path')).'" />';
41: $custom .= '<input type="hidden" name="list_page" value="'.$this->html_safe($this->get('list_page')).'" />';
42: $custom .= '<input type="search" placeholder="'.$this->trans('Search').
43: '" class="imap_keyword form-control form-control-sm" name="keyword" value="'.$this->html_safe($keyword).'" />';
44: $custom .= '<select name="sort" class="imap_sort form-control form-control-sm">';
45: foreach ($sorts as $name => $val) {
46: $custom .= '<option ';
47: if ($name == $sort) {
48: $custom .= 'selected="selected" ';
49: }
50: $custom .= 'value="'.$name.'">'.$val.' &darr;</option>';
51: $custom .= '<option ';
52: if ('-'.$name == $sort) {
53: $custom .= 'selected="selected" ';
54: }
55: $custom .= 'value="-'.$name.'">'.$val.' &uarr;</option>';
56: }
57: $custom .= '</select>';
58:
59: $custom .= '<select name="filter" class="imap_filter form-control form-control-sm">';
60: foreach ($opts as $name => $val) {
61: $custom .= '<option ';
62: if ($name == $filter) {
63: $custom .= 'selected="selected" ';
64: }
65: $custom .= 'value="'.$name.'">'.$val.'</option>';
66: }
67: $custom .= '</select></form>';
68:
69: if ($this->get('custom_list_controls_type') == 'remove') {
70: $custom .= '<a class="remove_source" title="'.$this->trans('Remove this folder from combined pages').
71: '" href=""><i class="bi bi-x-circle-fill refresh_list"></i></a><a style="display: none;" class="add_source" title="'.
72: $this->trans('Add this folder to combined pages').'" href=""><i class="bi bi-check-circle-fill refresh_list"></i></a>';
73: }
74: else {
75: $custom .= '<a style="display: none;" class="remove_source" title="'.$this->trans('Remove this folder from combined pages').
76: '" href=""><i class="bi bi-x-circle-fill refresh_list"></i></a><a class="add_source" title="'.$this->trans('Add this folder to combined pages').
77: '" href=""><i class="bi bi-check-circle-fill refresh_list"></i></a>';
78: }
79: if ($this->get('folder')) {
80: if ($this->get('screen_emails')) {
81: $custom .= '<span title="Disike" class="screen-email-unlike cursor-pointer"><i class="bi bi-hand-thumbs-down-fill"></i></span><span title="Like" class="screen-email-like cursor-pointer"></i><i class="bi bi-hand-thumbs-up-fill"></i></span>';
82: if ($this->get('move_messages_in_screen_email')) {
83: $custom .= '<input type="hidden" value="1" id="move_messages_in_screen_email"/>';
84: } else {
85: $custom .= '<input type="hidden" value="0" id="move_messages_in_screen_email"/>';
86: }
87: } else {
88: $path = sprintf('?page=message_list&list_path=%s&screen_emails=1', $this->html_safe($this->get('list_path')));
89: $custom .= '<a title="'.sprintf($this->trans('Screen %s first emails'), $this->get('first_time_screen_emails')).
90: '" href="'. $path .'"><i class="bi bi-hand-thumbs-up-fill"></i></a>';
91: }
92: }
93: $this->out('custom_list_controls', $custom);
94: }
95: }
96: }
97:
98: /**
99: * Format a message part body for display
100: * @subpackage imap/output
101: */
102: class Hm_Output_filter_message_body extends Hm_Output_Module {
103: /**
104: * Format html, text, or image content
105: */
106:
107: protected function output() {
108: $txt = '<div class="msg_text_inner">';
109: if ($this->get('msg_text')) {
110: $struct = $this->get('msg_struct_current', array());
111: if (array_key_exists('envelope', $struct) && is_array($struct['envelope']) && count($struct['envelope']) > 0) {
112: $txt .= format_imap_envelope($struct['envelope'], $this);
113: }
114: if (isset($struct['subtype']) && mb_strtolower($struct['subtype']) == 'html') {
115: $allowed = $this->get('header_allow_images');
116: $msgText = $this->get('msg_text');
117: // Everything in the message starting with src="http:// or src="https:// or src='http:// or src='https://
118: $externalResRegexp = '/src="(https?:\/\/[^"]*)"|src=\'(https?:\/\/[^\']*)\'/i';
119:
120: if ($allowed) {
121: $images_whitelist = $this->get('images_whitelist');
122: $sender_email = $this->get('sender_email');
123: if (! in_array($sender_email, $images_whitelist)) {
124: $msgText = preg_replace_callback($externalResRegexp, function ($matches) {
125: return 'data-src="' . $matches[1] . '" ' . 'src="" ' . 'data-message-part="' . $this->html_safe($this->get('imap_msg_part')) . '"';
126: }, $msgText);
127: }
128: }
129:
130: $msgText = sanitize_email_html($msgText);
131: $txt .= format_msg_html($msgText, $allowed);
132: }
133: elseif (isset($struct['type']) && mb_strtolower($struct['type']) == 'image') {
134: $txt .= format_msg_image($this->get('msg_text'), mb_strtolower($struct['subtype']));
135: }
136: else {
137: if ($this->get('imap_msg_part') === "0") {
138: $txt .= format_msg_text($this->get('msg_text'), $this, false);
139: }
140: else {
141: $txt .= format_msg_text($this->get('msg_text'), $this);
142: }
143: }
144: }
145: $txt .= '</div>';
146: $this->out('msg_text', $txt);
147: }
148: }
149:
150: /**
151: * Format the message part section of the message view
152: * @subpackage imap/output
153: */
154: class Hm_Output_filter_message_struct extends Hm_Output_Module {
155: /**
156: * Build message part section HTML
157: */
158: protected function output() {
159: if ($this->get('msg_struct')) {
160: $res = '<table class="msg_parts">';
161: if (!$this->get('is_mobile')) {
162: $res .= '<colgroup><col class="msg_part_mime"><col class="msg_part_size">';
163: $res .= '<col class="msg_part_encoding"><col class="msg_part_charset"><col class="msg_part_desc">';
164: $res .= '<col class="msg_part_download"></colgroup>';
165: }
166: $part = $this->get('imap_msg_part', '1');
167: $args = $this->get('msg_download_args', '');
168: $att_args = $this->get('msg_attachment_remove_args', '');
169: $showMsgArgs = $this->get('msg_show_args', '');
170: $res .= format_msg_part_section($this->get('msg_struct'), $this, $part, $args, $att_args);
171: $res .= '</table>';
172: $res .= format_attached_image_section($this->get('msg_struct'), $this, $showMsgArgs);
173: $this->out('msg_parts', $res);
174: }
175: }
176: }
177:
178: /**
179: * Format the message headers section of the message view
180: * @subpackage imap/output
181: */
182: class Hm_Output_filter_message_headers extends Hm_Output_Module {
183: protected function output() {
184: if ($this->get('msg_headers')) {
185: $txt = '';
186: $small_headers = array('subject', 'x-snoozed', 'date', 'from', 'to', 'reply-to', 'cc', 'flags');
187: $reply_args = sprintf('&amp;list_path=%s&amp;uid=%s',
188: $this->html_safe($this->get('msg_list_path')),
189: $this->html_safe($this->get('msg_text_uid'))
190: );
191: $msg_part = $this->get('imap_msg_part');
192: $headers = $this->get('msg_headers', array());
193: if (!array_key_exists('subject', lc_headers($headers)) || !trim(lc_headers($headers)['subject'])) {
194: $headers['subject'] = $this->trans('[No Subject]');
195: }
196:
197: // Start Bootstrap container
198: $txt .= '<div class="container-fluid p-0 ml-0 border-bottom border-secondary-subtle text-muted">';
199:
200: foreach ($small_headers as $fld) {
201: foreach ($headers as $name => $value) {
202: if ($fld == mb_strtolower($name)) {
203: if ($fld == 'subject') {
204: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
205: $txt .= '<div class="col-12">';
206: if (isset($headers['Flags']) && mb_stristr($headers['Flags'], 'flagged')) {
207: $txt .= '<i class="bi bi-star-half account_icon"></i> ';
208: }
209: $txt .= '<span class="fs-5 fw-normal text-dark js-header_subject">' . $this->html_safe($value) . '</span>';
210: $txt .= '</div></div>';
211: }
212: elseif ($fld == 'x-snoozed') {
213: $snooze_header = parse_delayed_header($value, 'X-Snoozed');
214: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
215: $txt .= '<div class="col-md-2"><span class="text-muted">'.$this->trans('Snoozed').'</span></div>';
216: $txt .= '<div class="col-md-10">'.$this->trans('Until').' '.$this->html_safe($snooze_header['until']).' <a href="#" data-value="unsnooze" class="unsnooze nexter_date_helper">Unsnooze</a></div>';
217: $txt .= '</div>';
218: }
219: elseif ($fld == 'date') {
220: try {
221: $dt = new DateTime($value);
222: $value = sprintf('%s (%s)', $dt->format('c Z'), human_readable_interval($value));
223: } catch (Exception $e) {}
224: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
225: $txt .= '<div class="col-md-2 d-none d-md-block"><span class="text-muted">'.$this->trans($name).'</span></div>';
226: $txt .= '<div class="col-md-10"><small class="text-muted js-header_date">'.$this->html_safe($value).'</small></div>';
227: $txt .= '</div>';
228: }
229: elseif($fld == 'from'){
230: $regexp = '/\s*(.*[^\s])\s*<\s*(.*[^\s])\s*>/';
231: $contact_email = "";
232: $contact_name = "";
233:
234: if(preg_match($regexp, $value, $matches)){
235: $contact_name = $matches[1];
236: $contact_email = $matches[2];
237: }else{
238: $EmailRegexp = "/[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i";
239: if(preg_match($EmailRegexp, $value, $matches)){
240: $contact_email = $matches[0];
241: }
242: }
243:
244: $this->out('sender_email', $contact_email);
245:
246: $contact_store = $this->get('contact_store');
247: $contact = !$contact_store ? null : $contact_store->get(null, false, $contact_email);
248: $contact_exists = !empty($contact);
249:
250: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
251: $txt .= '<div class="col-md-2 d-none d-sm-block"><span class="text-muted">'.$this->trans($name).'</span></div>';
252: $txt .= '<div class="col-md-10">';
253: $txt .= '<div class="dropdown">';
254: $txt .= '<a id="contact_info" data-bs-toggle="dropdown" class="dropdown-toggle text-decoration-none js-header_from" href="#">' . $this->html_safe($value) . '</a>';
255: $txt .= '<div class="dropdown-menu p-4" id="contact_popup" aria-labelledby="dropdownMenuContact">';
256: $txt .= '<div id="contact_popup_body">';
257:
258: if($contact_exists){
259: $txt .= '<div class="d-flex flex-column gap-2">';
260: $txt .= '<div class="d-flex"><span class="fw-bold me-2">Name:</span><span>'.$this->html_safe($contact->value('display_name')).'</span></div>';
261: $txt .= '<div class="d-flex"><span class="fw-bold me-2">Email:</span><span>'.$this->html_safe($contact->value('email_address')).'</span></div>';
262: $txt .= '<div class="d-flex"><span class="fw-bold me-2">Tel:</span><span><a href="tel:'.$this->html_safe($contact->value('phone_number')).'" data-external="true">'.$this->html_safe($contact->value('phone_number')).'</a></span></div>';
263: $txt .= '<div class="d-flex"><span class="fw-bold me-2">Source:</span><span>'.$this->html_safe($contact->value('source')).'</span></div>';
264: $txt .= '</div>';
265: } else {
266: $txt .= '<div class="popup-container_footer">';
267: $txt .= '<button onclick="return add_contact_from_popup(event)" class="add_contact_btn btn btn-primary" type="button" value="">'.$this->trans('Add local contacts').'</button>';
268: $txt .= '</div>';
269: }
270:
271: $txt .= '</div></div></div></div></div>';
272: }
273: elseif ($fld == 'reply-to') {
274: $from = addr_parse($headers['From']);
275: $replyEmails = array_map(function ($addr) {
276: return $addr['email'];
277: }, process_address_fld($headers['Reply-To']));
278:
279: if (count($replyEmails) === 1 && ($replyEmails[0] === $from['email'])) {
280: $txt .= '<div class="row g-0 py-1 long_header">';
281: $txt .= '<div class="col-md-2 col-12"><span class="text-muted">'.$this->html_safe($name).'</span></div>';
282: $txt .= '<div class="col-md-9 col-12">'.$this->html_safe($value).'</div>';
283: $txt .= '</div>';
284: } else {
285: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
286: $txt .= '<div class="col-md-2"><span class="text-muted">'.$this->trans($name).'</span></div>';
287: $txt .= '<div class="col-md-10">'.$this->html_safe(join(',', $replyEmails)).'</div>';
288: $txt .= '</div>';
289: }
290: }
291: else {
292: if (mb_strtolower($name) == 'flags') {
293: $name = $this->trans('Tags');
294: $value = str_replace('\\', '', $value);
295: $new_value = array();
296: foreach (explode(' ', $value) as $v) {
297: $new_value[] = $this->trans(trim($v));
298: }
299: $value = implode(', ', $new_value);
300: }
301: $txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
302: $txt .= '<div class="col-md-2"><span class="text-muted">'.$this->trans($name).'</span></div>';
303: $txt .= '<div class="col-md-10 col-12 js-header_' . strtolower($name) . '">'.$this->html_safe($value).'</div>';
304: $txt .= '</div>';
305: }
306: break;
307: }
308: }
309: }
310: $is_draft = isset($headers['Flags']) && mb_stristr($headers['Flags'], 'draft');
311: if($is_draft) {
312: $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>';
313: }
314: foreach ($headers as $name => $value) {
315: if (!in_array(mb_strtolower($name), $small_headers)) {
316: if (is_array($value)) {
317: foreach ($value as $line) {
318: $txt .= '<div class="row g-0 py-1 long_header">';
319: $txt .= '<div class="col-md-2 col-12"><span class="text-muted">'.$this->html_safe($name).'</span></div>';
320: $txt .= '<div class="col-md-9 col-12">'.$this->html_safe($line).'</div>';
321: $txt .= '</div>';
322: }
323: } else {
324: $txt .= '<div class="row g-0 py-1 long_header">';
325: $txt .= '<div class="col-md-2 col-12"><span class="text-muted">'.$this->html_safe($name).'</span></div>';
326: $txt .= '<div class="col-md-9 col-12">'.$this->html_safe($value).'</div>';
327: $txt .= '</div>';
328: }
329: }
330: }
331:
332: if ($this->get('list_headers')) {
333: $txt .= format_list_headers($this);
334: }
335:
336: $lc_headers = lc_headers($headers);
337: if (array_key_exists('to', $lc_headers)) {
338: $addr_list = process_address_fld($lc_headers['to']);
339: $size = count($addr_list);
340: }
341: if (array_key_exists('cc', $lc_headers)) {
342: $addr_list = process_address_fld($lc_headers['cc']);
343: $size += count($addr_list);
344: }
345: if (array_key_exists('from', $lc_headers)) {
346: $imap_server_id = explode('_', $this->get('msg_list_path'))[1];
347: $server = Hm_IMAP_List::get($imap_server_id, false);
348: if ($server) {
349: $addr_list = process_address_fld($lc_headers['from']);
350: $addr_list = array_filter($addr_list, function ($addr) use($server) {
351: return $addr['email'] != $server['user'];
352: });
353: $size += count($addr_list);
354: }
355: }
356:
357: // Action links section
358: $txt .= '<div class="event_calendar_section"></div>';
359: $txt .= '<div class="row g-0 py-3">';
360: $txt .= '<div class="col-12 msg_actions">';
361: $txt .= '<div class="d-flex flex-wrap gap-2 mb-3">';
362: $txt .= '<a href="#" class="hlink all_headers text-decoration-none btn btn-sm btn-outline-secondary">'.$this->trans('All headers').'</a>';
363: $txt .= '<a class="hlink small_headers text-decoration-none btn btn-sm btn-outline-secondary" href="#">'.$this->trans('Small headers').'</a>';
364: $txt .= '</div>';
365:
366: $txt .= '<div class="d-flex flex-wrap gap-2">';
367: if (!isset($headers['Flags']) || !mb_stristr($headers['Flags'], 'draft')) {
368: $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>';
369: if ($size > 1) {
370: $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>';
371: } else {
372: $txt .= '<a class="reply_all_link hlink disabled_link text-decoration-none">'.$this->trans('Reply-all').'</a>';
373: }
374: $txt .= forward_dropdown($this, $reply_args);
375: }
376: if (isset($headers['Flags']) && mb_stristr($headers['Flags'], 'flagged')) {
377: $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>';
378: $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>';
379: } else {
380: $txt .= '<a id="flag_msg" class="flagged_link hlink text-decoration-none btn btn-sm btn-outline-secondary" data-state="unflagged" href="#">'.$this->trans('Flag').'</a>';
381: $txt .= '<a id="unflag_msg" class="unflagged_link hlink text-decoration-none btn btn-sm btn-outline-secondary hide" data-state="flagged" href="#">'.$this->trans('Unflag').'</a>';
382: }
383:
384: $txt .= '<a class="hlink text-decoration-none btn btn-sm btn-outline-secondary" id="unread_message" href="#">'.$this->trans('Unread').'</a>';
385: $txt .= '<a class="delete_link hlink text-decoration-none btn btn-sm btn-outline-secondary" id="delete_message" href="#">'.$this->trans('Delete').'</a>';
386: $txt .= '<div class="position-relative"><a class="hlink text-decoration-none btn btn-sm btn-outline-secondary dropdown-toggle" id="copy_message" href="#" data-bs-toggle="dropdown">'.$this->trans('Copy').'</a><div class="move_to_location dropdown-menu" data-bs-auto-close="outside"></div></div>';
387: $txt .= '<div class="position-relative"><a class="hlink text-decoration-none btn btn-sm btn-outline-secondary dropdown-toggle" id="move_message" href="#" data-bs-toggle="dropdown">'.$this->trans('Move').'</a><div class="move_to_location dropdown-menu" data-bs-auto-close="outside"></div></div>';
388: if (!$this->get('is_archive_folder')) {
389: $txt .= '<a class="archive_link hlink text-decoration-none btn btn-sm btn-outline-secondary" id="archive_message" href="#">'.$this->trans('Archive').'</a>';
390: }
391:
392: if($this->get('tags')){
393: $txt .= tags_dropdown($this, $headers);
394: }
395: if (isset($headers['X-Schedule'])) {
396: $txt .= schedule_dropdown($this, true);
397: }
398:
399: $settings = $this->get('user_settings', array());
400: if(array_key_exists('enable_snooze_setting', $settings) && $settings['enable_snooze_setting']) {
401: $txt .= snooze_dropdown($this, isset($headers['X-Snoozed']));
402: }
403: if ($this->get('sieve_filters_enabled') && !$is_draft) {
404: $server_id = $this->get('msg_server_id');
405: $imap_server = $this->get('imap_accounts')[$server_id];
406: if ($this->get('sieve_filters_client')) {
407: $user_config = $this->get('user_config');
408: $contact_list = $user_config->get('contacts', []);
409: $existing_emails = array_column($contact_list, 'email_address');
410: $sender = addr_parse($headers['From'])['email'];
411: $domain = '*@'.get_domain($sender);
412: $blocked_senders = get_blocked_senders_array($imap_server, $this->get('site_config'), $this->get('user_config'));
413: $sender_blocked = in_array($sender, $blocked_senders);
414: $domain_blocked = in_array($domain, $blocked_senders);
415: if(!in_array($sender, $existing_emails)){
416: $txt .= '<div class="dropdown d-inline-block"><a class="block_sender_link hlink dropdown-toggle text-decoration-none btn btn-sm btn-outline-danger '.($domain_blocked || $sender_blocked ? '" id="unblock_sender" data-target="'.($domain_blocked? 'domain':'sender').'"' : '"').' href="#" aria-labelledby="dropdownMenuBlockSender" data-bs-toggle="dropdown"><i class="bi bi-lock-fill"></i> <span id="filter_block_txt">'.$this->trans($domain_blocked ? 'Unblock Domain' : ($sender_blocked ? 'Unblock Sender' : 'Block Sender')).'</span></a>';
417: $txt .= block_filter_dropdown($this);
418: }
419: } else {
420: $txt .= '<span class="btn btn-sm" data-bs-toogle="tooltip" title="This functionality requires the email server support &quot;Sieve&quot; technology which is not provided. Contact your email provider to fix it or enable it if supported."><i class="bi bi-lock-fill"></i> <span id="filter_block_txt">'.$this->trans('Block Sender').'</span></span>';
421: }
422: }
423: $txt .= '<a class="hlink text-decoration-none btn btn-sm btn-outline-secondary" id="show_message_source" href="#">' . $this->trans('Show Source') . '</a>';
424:
425: $txt .= '</div><span id="extra-header-buttons"></span>';
426: $txt .= '<input type="hidden" class="move_to_type" value="" />';
427: $txt .= '<input type="hidden" class="move_to_string1" value="'.$this->trans('Move to ...').'" />';
428: $txt .= '<input type="hidden" class="move_to_string2" value="'.$this->trans('Copy to ...').'" />';
429: $txt .= '<input type="hidden" class="move_to_string3" value="'.$this->trans('Removed non-IMAP messages from selection. They cannot be moved or copied').'" />';
430: $txt .= '</div></div>';
431: $txt .= '</div>';
432:
433: $this->out('msg_headers', $txt, false);
434: }
435: }
436: }
437:
438: /**
439: * Format configured IMAP servers for the servers page
440: * @subpackage imap/output
441: */
442: class Hm_Output_display_configured_imap_servers extends Hm_Output_Module {
443: /**
444: * Build HTML for configured IMAP servers
445: */
446: protected function output() {
447: if ($this->get('single_server_mode')) {
448: return '';
449: }
450: $list = array_filter($this->get('imap_servers', array()), fn($s) => ($s['type'] ?? null) !== 'ews');
451:
452: if (empty($list)) {
453: return '';
454: }
455:
456: $res = '<div class="subtitle mt-4 fw-bold">'.$this->trans('IMAP and JMAP Servers').'</div>';
457: foreach ($list as $index => $vals) {
458: $server_id = $vals['id'];
459: $type = 'IMAP';
460:
461: if (array_key_exists('type', $vals) && $vals['type'] == 'jmap') {
462: $type = 'JMAP';
463: }
464:
465: if (array_key_exists('type', $vals) && $vals['type'] == 'ews') {
466: continue;
467: }
468:
469: if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) {
470: $disabled = 'disabled="disabled"';
471: $user_pc = $vals['user'];
472: $pass_pc = $this->trans('[saved]');
473: $pass_value = '*************';
474: }
475: elseif (array_key_exists('nopass', $vals)) {
476: if (array_key_exists('user', $vals)) {
477: $user_pc = $vals['user'];
478: }
479: else {
480: $user_pc = '';
481: }
482: $pass_pc = $this->trans('Password');
483: $disabled = '';
484: $pass_value = '';
485: }
486: else {
487: $user_pc = '';
488: $pass_pc = $this->trans('Password');
489: $disabled = '';
490: $pass_value = '';
491: }
492: $res .= '<div class="' . mb_strtolower($type) . '_server mb-3">';
493: $res .= '<form class="imap_connect" method="POST">';
494: $res .= '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />';
495: $res .= '<input type="hidden" name="imap_server_id" class="imap_server_id" value="'.$this->html_safe($server_id).'" />';
496: $res .= '<div class="row m-0 p-0 credentials-container"><div class="col-xl-2 col-lg-2 col-md-6 overflow-auto" >';
497: $res .= sprintf('
498: <div class="text-muted"><strong>%s</strong></div>
499: <div class="server_subtitle">%s/%d %s</div>',
500: $this->html_safe($vals['name']), $this->html_safe($vals['server']), $this->html_safe($vals['port']),
501: $vals['tls'] ? 'TLS' : '' );
502:
503: $res .= '</div> <div class="col-xl-7 col-lg-7 col-md-9"> <div class="row"> <div class="col-md-6 col-lg-4 mb-2 ">';
504:
505: // IMAP Username
506: $res .= '<div class="form-floating">';
507: $res .= '<input '.$disabled.' id="imap_user_'.$server_id.'" class="form-control credentials" type="text" name="imap_user" value="'.$this->html_safe($user_pc).'" placeholder="'.$this->trans('Username').'">';
508: $res .= '<label for="imap_user_'.$server_id.'">'.$this->trans('IMAP username').'</label></div>';
509: $res .= '</div><div class="col-md-6 col-lg-4">';
510: // IMAP Password
511: $res .= '<div class="form-floating">';
512: $res .= '<input '.$disabled.' id="imap_pass_'.$server_id.'" class="form-control credentials imap_password" type="password" name="imap_pass" value="'.$pass_value.'" placeholder="'.$pass_pc.'">';
513: $res .= '<label for="imap_pass_'.$server_id.'">'.$this->trans('IMAP password').'</label></div>';
514: $res .= '</div><div class="col-md-6 col-lg-4">';
515:
516: // Sieve Host (Conditional)
517:
518: if ($this->get('sieve_filters_enabled') && isset($vals['sieve_config_host'])) {
519: $res .= '<div class="form-floating">';
520: $default_value = $vals['sieve_config_host'];
521: $res .= '<input '.$disabled.' id="imap_sieve_host_'.$server_id.'" class="form-control credentials imap_sieve_host_input" type="text" name="imap_sieve_host" value="'.$default_value.'" placeholder="Sieve Host">';
522: $res .= '<label for="imap_sieve_host_'.$server_id.'">'.$this->trans('Sieve Host').'</label></div>';
523: }
524: $res .= '</div></div></div><div class="col-xl-3 col-lg-4 col-md-6 d-flex justify-content-start align-items-center">';
525:
526: // Buttons
527: $disabled = isset($vals['default']) ? ' disabled': '';
528: if (!isset($vals['user']) || !$vals['user']) {
529: $res .= '<input type="submit" value="'.$this->trans('Delete').'" class="imap_delete btn btn-outline-danger btn-sm me-2 mt-3" />';
530: } else {
531: $keysToRemove = array('object', 'connected', 'default', 'nopass');
532: $serverDetails = array_diff_key($vals, array_flip($keysToRemove));
533:
534: $type = $vals['type'] ?? 'imap';
535: $res .= '<input type="submit" value="'.$this->trans('Edit').'" class="edit_server_connection btn btn-outline-success btn-sm me-2 mt-3"'.$disabled.' data-server-details=\''.$this->html_safe(json_encode($serverDetails)).'\' data-id="'.$this->html_safe($serverDetails['name']).'" data-type="'.$type.'" />';
536: $res .= '<input type="submit" value="'.$this->trans('Test').'" class="test_imap_connect btn btn-outline-primary btn-sm me-2 mt-3" />';
537: $res .= '<input type="submit" value="'.$this->trans('Delete').'" class="imap_delete btn btn-outline-danger btn-sm me-2 mt-3"'.$disabled.' />';
538: }
539:
540: // Hide/Unhide Buttons
541: $hidden = array_key_exists('hide', $vals) && $vals['hide'];
542: $res .= '<input type="submit" '.($hidden ? 'style="display: none;" ' : '').'value="'.$this->trans('Hide').'" class="hide_imap_connection btn btn-outline-secondary btn-sm me-2 mt-3" />';
543: $res .= '<input type="submit" '.(!$hidden ? 'style="display: none;" ' : '').'value="'.$this->trans('Unhide').'" class="unhide_imap_connection btn btn-outline-secondary btn-sm me-2 mt-3" />';
544:
545: $res .= '<input type="hidden" value="ajax_imap_debug" name="hm_ajax_hook" />';
546: $res .= '</div></div></div></form>';
547: }
548: $res .= '';
549: return $res;
550: }
551: }
552:
553: /**
554: * Format the add IMAP server dialog for the servers page
555: * @subpackage imap/output
556: */
557: class Hm_Output_add_imap_server_dialog extends Hm_Output_Module {
558: /**
559: * Build the HTML for the add server dialog
560: */
561: protected function output() {
562: if ($this->get('single_server_mode')) {
563: return '';
564: }
565: $count = count(array_filter($this->get('imap_servers', array()), function($v) { return !array_key_exists('type', $v) || $v['type'] != 'jmap'; }));
566: $count = sprintf($this->trans('%d configured'), $count);
567:
568: $sieve_extra2 = '';
569: $sieve_extra = '';
570: if ($this->get('sieve_filters_enabled')) {
571: $sieve_extra = '<tr class="sieve_config" style="display: none;"><td><div class="subtitle">'.$this->trans('Sieve Configuration').'</div></td></tr>'.
572: '<tr class="sieve_config" style="display: none;"><td colspan="2"><label class="screen_reader" for="new_imap_port">'.$this->trans('Sieve Host').'</label>'.
573: '<input class="form-control" type="text" id="sieve_config_host" name="sieve_config_host" class="txt_fld" placeholder="'.$this->trans('localhost:4190').'"></td></tr>';
574:
575: $sieve_extra2 = '<tr><td colspan="2"><div class="form-check mb-3 mt-2"><input type="checkbox" id="enable_sieve_filter" name="enable_sieve_filter" class="form-check-input" value="0">'.
576: '<label for="enable_sieve_filter" class="form-check-label"> '.$this->trans('Enable Sieve Filters').'</label></div></td></tr>';
577: }
578:
579: return '<div class="imap_server_setup"><div data-target=".imap_section" class="server_section">'.
580: '<i class="bi bi-envelope-fill"></i>'.
581: ' '.$this->trans('IMAP Servers').'<div class="server_count">'.$count.'</div></div><div class="imap_section"></div></div>';
582: }
583: }
584:
585: /**
586: * Format the add IMAP server dialog for the servers page
587: * @subpackage imap/output
588: */
589: class Hm_Output_add_jmap_server_dialog extends Hm_Output_Module {
590: /**
591: * Build the HTML for the add server dialog
592: */
593: protected function output() {
594: if ($this->get('single_server_mode')) {
595: return '';
596: }
597:
598: if(!$this->get('is_jmap_supported')){
599: return '<div class="jmap_server_setup"><div class="jmap_section" style="display: none;">';
600: }
601:
602: $count = count(array_filter($this->get('imap_servers', array()), function($v) { return array_key_exists('type', $v) && $v['type'] == 'jmap';}));
603: $count = sprintf($this->trans('%d configured'), $count);
604:
605: return '
606: <div class="jmap_server_setup">
607: <div data-target=".jmap_section" class="server_section border-bottom cursor-pointer px-1 py-3 pe-auto">
608: <a href="#" class="pe-auto">
609: <i class="bi bi-envelope-fill me-3"></i>
610: <b>'.$this->trans('JMAP Servers').'</b>
611: </a>
612: <div class="server_count">'.$count.'</div>
613: </div>
614: <div class="jmap_section px-4 pt-3">
615: <div class="row">
616: <div class="col-12 col-lg-4">
617: <form class="add_server" method="POST">
618: <input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />
619: <div class="subtitle">'.$this->trans('Add a JMAP Server').'</div>
620:
621: <div class="form-floating mb-3">
622: <input id="new_jmap_name" required type="text" name="new_jmap_name" class="txt_fld form-control" value="" placeholder="'.$this->trans('Account name').'">
623: <label class="" for="new_jmap_name">'.$this->trans('Account name').'</label>
624: </div>
625:
626: <div class="form-floating mb-3">
627: <input required type="url" id="new_jmap_address" name="new_jmap_address" class="txt_fld form-control" placeholder="'.$this->trans('Server URL').'" value="">
628: <label class="" for="new_jmap_address">'.$this->trans('Server URL').'</label>
629: </div>
630:
631: <div class="mb-3">
632: <input type="checkbox" id="new_jmap_hidden" name="new_jmap_hidden" class="form-check-input" value="1">
633: <label for="new_jmap_hidden">'.$this->trans('Hide From Combined Pages').'</label>
634: </div>
635:
636: <input type="submit" class="btn btn-primary px-5" value="'.$this->trans('Add').'" name="submit_jmap_server" />
637: </form>
638: </div>';
639: }
640:
641: }
642:
643: /**
644: * Format configured JMAP servers for the servers page
645: * @subpackage imap/output
646: */
647: class Hm_Output_display_configured_jmap_servers extends Hm_Output_Module {
648: /**
649: * Build HTML for configured JMAP servers
650: */
651: protected function output() {
652: if ($this->get('single_server_mode')) {
653: return '';
654: }
655: $res = '';
656: foreach ($this->get('imap_servers', array()) as $index => $vals) {
657: $server_id = $vals['id'];
658: if (!array_key_exists('type', $vals) || $vals['type'] != 'jmap') {
659: continue;
660: }
661: if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) {
662: $disabled = 'disabled="disabled"';
663: $user_pc = $vals['user'];
664: $pass_pc = $this->trans('[saved]');
665: $pass_value = '*************';
666: }
667: elseif (array_key_exists('nopass', $vals)) {
668: if (array_key_exists('user', $vals)) {
669: $user_pc = $vals['user'];
670: }
671: else {
672: $user_pc = '';
673: }
674: $pass_pc = $this->trans('Password');
675: $disabled = '';
676: $pass_value = '';
677: }
678: else {
679: $user_pc = '';
680: $pass_pc = $this->trans('Password');
681: $disabled = '';
682: $pass_value = '';
683: }
684: $res .= '<div class="configured_server col-12 col-lg-4 mb-2"><div class="card card-body">';
685: $res .= sprintf('<div class="server_title">%s</div><div class="server_subtitle">%s</div>',
686: $this->html_safe($vals['name']), $this->html_safe($vals['server']));
687:
688: $res .= '<form class="imap_connect" method="POST">';
689: $res .= '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />';
690: $res .= '<input type="hidden" name="imap_server_id" class="imap_server_id" value="'.$this->html_safe($server_id).'" />';
691:
692: // JMAP Username
693: $res .= '<div class="form-floating mb-2">';
694: $res .= '<input '.$disabled.' id="imap_user_'.$server_id.'" class="form-control credentials" type="text" name="imap_user" value="'.$this->html_safe($user_pc).'" placeholder="'.$this->trans('Username').'">';
695: $res .= '<label for="imap_user_'.$server_id.'">'.$this->trans('JMAP username').'</label></div>';
696:
697: // JMAP Password
698: $res .= '<div class="form-floating">';
699: $res .= '<input '.$disabled.' id="imap_pass_'.$server_id.'" class="form-control credentials imap_password" type="password" name="imap_pass" value="'.$pass_value.'" placeholder="'.$pass_pc.'">';
700: $res .= '<label for="imap_pass_'.$server_id.'">'.$this->trans('JMAP password').'</label></div>';
701:
702: // Buttons
703: if (!isset($vals['user']) || !$vals['user']) {
704: $res .= '<input type="submit" value="'.$this->trans('Delete').'" class="btn btn-outline-danger btn-sm imap_delete me-2" />';
705: } else {
706: $res .= '<input type="submit" value="'.$this->trans('Test').'" class="btn btn-primary btn-sm test_imap_connect me-2" />';
707: $res .= '<input type="submit" value="'.$this->trans('Delete').'" class="btn btn-danger btn-sm imap_delete me-2" />';
708: }
709:
710: // Hide/Unhide Button Logic
711: $hidden = array_key_exists('hide', $vals) && $vals['hide'];
712: $res .= '<input type="submit" '.($hidden ? 'style="display: none;" ' : '').'value="'.$this->trans('Hide').'" class="btn btn-outline-secondary btn-sm hide_imap_connection me-2" />';
713: $res .= '<input type="submit" '.(!$hidden ? 'style="display: none;" ' : '').'value="'.$this->trans('Unhide').'" class="btn btn-outline-secondary btn-sm unhide_imap_connection me-2" />';
714:
715: $res .= '<input type="hidden" value="ajax_imap_debug" name="hm_ajax_hook" />';
716: $res .= '</form>';
717:
718:
719: $res .= '</div></div>';
720: }
721: $res .= '<br class="clear_float" /></div></div></div>';
722: return $res;
723: }
724: }
725: /**
726: * Format the IMAP status output on the info page
727: * @subpackage imap/output
728: */
729: class Hm_Output_display_imap_status extends Hm_Output_Module {
730: /**
731: * Build the HTML for the status rows. Will be populated by an ajax call per server
732: */
733: protected function output() {
734: $settings = $this->get('user_settings', array());
735: $enable_sieve = $settings['enable_sieve_filter_setting'] ?? DEFAULT_ENABLE_SIEVE_FILTER;
736: $res = '';
737: if(!$this->get('is_mobile')) {
738: foreach ($this->get('imap_servers', array()) as $index => $vals) {
739: $res .= '<tr><td>'.(strtoupper($vals['type'] ?? 'IMAP')).'</td><td>'.$vals['name'].'</td><td class="imap_status_'.$vals['id'].' imap_status" data-id="'.$vals['id'].'"></td>';
740: if ($enable_sieve) {
741: $res .= '<td class="imap_detail_'.$vals['id'].'"></td>';
742: }
743: $res .= '</tr>';
744: }
745: }
746: return $res;
747: }
748: }
749:
750: /**
751: * Format the IMAP status output on the info page
752: * @subpackage imap/output
753: */
754: class Hm_Output_display_imap_capability extends Hm_Output_Module {
755: /**
756: * Build the HTML for the status rows. Will be populated by an ajax call per server
757: */
758: protected function output() {
759: $res = '';
760: if(!$this->get('is_mobile')) {
761: foreach ($this->get('imap_servers', array()) as $index => $vals) {
762: $res .= '<tr><td>IMAP</td><td>'.$vals['name'].'</td>'.
763: '<td class="imap_capabilities_'.$vals['id'].'"></td></tr>';
764: }
765: }
766: return $res;
767: }
768: }
769:
770: /**
771: * Output a hidden field with all the IMAP server ids
772: * @subpackage imap/output
773: */
774: class Hm_Output_imap_server_ids extends Hm_Output_Module {
775: /**
776: * Build HTML for the IMAP server ids
777: */
778: protected function output() {
779: return '<input type="hidden" class="imap_server_ids" value="'.$this->html_safe(implode(',', array_keys($this->get('imap_servers', array ())))).'" />';
780: }
781: }
782:
783: /**
784: * Format a list of subfolders
785: * @subpackage imap/output
786: */
787: class Hm_Output_filter_expanded_folder_data extends Hm_Output_Module {
788: /**
789: * Build the HTML for a list of subfolders. The page cache is used to pass this to the folder list.
790: */
791: protected function output() {
792: $folder_data = $this->get('imap_expanded_folder_data', array());
793: $with_input = $this->get('with_input', false);
794: $can_share_folders = $this->get('can_share_folders', false);
795: if (!empty($folder_data)) {
796: $res = format_imap_folder_section($folder_data, $this->get('imap_expanded_folder_id'), $this, $with_input, $can_share_folders);
797: $this->out('imap_expanded_folder_formatted', $res);
798: }
799: }
800: }
801:
802: /**
803: * @subpackage imap/output
804: */
805: class Hm_Output_get_list_imap_folders_permissions extends Hm_Output_Module {
806: public function output() {
807: $this->out('ajax_imap_folders_permissions', $this->get('imap_folders_permissions', array()));
808: }
809: }
810:
811: /**
812: * Add move/copy dialog to the message list controls
813: * @subpackage imap/output
814: */
815: class Hm_Output_move_copy_controls extends Hm_Output_Module {
816: protected function output() {
817: if ($this->get('move_copy_controls', false)) {
818: $res = '<span class="ctr_divider"></span> <div class="d-flex align-items-start gap-1 dropdown"><a class="imap_move disabled_input btn btn-sm btn-secondary no_mobile" href="#" data-action="copy" data-bs-toggle="dropdown">'.$this->trans('Copy').'</a>';
819: $res .= '<a class="imap_move disabled_input btn btn-sm btn-secondary no_mobile" data-bs-toggle="dropdown" href="#" data-action="move">'.$this->trans('Move').'</a>';
820: $res .= '<a class="imap_move disabled_input text-decoration-none btn btn-outline-secondary btn-sm on_mobile" href="#" data-action="copy">'.$this->trans('Copy').'</a>';
821: $res .= '<a class="imap_move disabled_input text-decoration-none btn btn-outline-secondary btn-sm on_mobile" href="#" data-action="move">'.$this->trans('Move').'</a>';
822: $res .= '<div class="move_to_location dropdown-menu" data-bs-auto-close="outside"></div>';
823: $res .= '<input type="hidden" class="move_to_type" value="" />';
824: $res .= '<input type="hidden" class="move_to_string1" value="'.$this->trans('Move to ...').'" />';
825: $res .= '<input type="hidden" class="move_to_string2" value="'.$this->trans('Copy to ...').'" />';
826: $res .= '<input type="hidden" class="move_to_string3" value="'.$this->trans('Removed non-IMAP messages from selection. They cannot be moved or copied').'" /></div>';
827: $this->concat('msg_controls_extra', $res);
828: }
829: }
830: }
831:
832: /**
833: * Format the status of an IMAP connection used on the info page
834: * @subpackage imap/output
835: */
836: class Hm_Output_filter_imap_status_data extends Hm_Output_Module {
837: /**
838: * Build AJAX response for an IMAP server status
839: */
840: protected function output() {
841: $res = '';
842: if ($this->get('imap_connect_status') != 'disconnected') {
843: $res .= '<span class="online">'.$this->trans(ucwords($this->get('imap_connect_status'))).
844: '</span> in '.round($this->get('imap_connect_time', 0.0), 3);
845: }
846: else {
847: $res .= '<span class="down">'.$this->trans('Down').'</span>';
848: }
849: $this->out('imap_status_display', $res);
850: $res = '';
851: $capabilities = $this->get('sieve_server_capabilities', array());
852: if ($capabilities) {
853: foreach ($capabilities as $key => $val) {
854: $capabilities[$key] = $key . ': ' . (is_array($val) ? implode(', ', $val) : $val);
855: }
856: $res .= '<span class="sieve_extensions">' . implode(', ', $capabilities) . '</span>';
857: }
858: $this->out('sieve_detail_display', $res);
859: $res = '';
860: $extensions = $this->get('imap_capabilities_list', "");
861: if ($extensions) {
862: $res .= '<span class="imap_extensions">'.$extensions.'</span>';
863: }
864: $this->out('imap_extensions_display', $res);
865: }
866: }
867:
868: /**
869: * Format the top level IMAP folders for the folder list
870: * @subpackage imap/output
871: */
872: class Hm_Output_filter_imap_folders extends Hm_Output_Module {
873: /**
874: * Build HTML for the Email section of the folder list
875: */
876: protected function output() {
877: $res = '';
878: if ($this->get('imap_folders')) {
879: foreach ($this->get('imap_folders', array()) as $id => $folder) {
880: $res .= '<li class="imap_'.$id.'_"><a href="#" class="imap_folder_link" data-target="imap_'.$id.'_">';
881: if (!$this->get('hide_folder_icons')) {
882: $res .= '<i class="bi bi-folder fs-5 me-2"></i>';
883: }
884: $res .= $this->html_safe($folder).'</a></li>';
885: }
886: }
887: if ($res) {
888: $this->append('folder_sources', array('email_folders', $res));
889: }
890: return '';
891: }
892: }
893:
894: /**
895: * Format search results row
896: * @subpackage imap/output
897: */
898: class Hm_Output_filter_imap_search extends Hm_Output_Module {
899: /**
900: * Build ajax response from an IMAP server for a search
901: */
902: protected function output() {
903: if ($this->get('imap_search_results')) {
904: prepare_imap_message_list($this->get('imap_search_results'), $this, 'search');
905: }
906: elseif (!$this->get('formatted_message_list')) {
907: $this->out('formatted_message_list', array());
908: }
909: }
910: }
911:
912: /**
913: * Format message headers for the unread and flagged page
914: * @subpackage imap/output
915: */
916: class Hm_Output_filter_by_type extends Hm_Output_Module {
917: /**
918: * Build ajax response for the Flagged message list
919: */
920: protected function output() {
921: if ($this->get('imap_filter_by_type_data')) {
922: prepare_imap_message_list($this->get('imap_filter_by_type_data'), $this, $this->get('type'));
923: }
924: elseif (!$this->get('formatted_message_list')) {
925: $this->out('formatted_message_list', array());
926: }
927: }
928: }
929:
930: /**
931: * Format message headers for the All, Combined Inbox, Sent, Junk, Draft, Unread, Trash E-mail pages
932: * @subpackage imap/output
933: */
934: class Hm_Output_filter_data extends Hm_Output_Module {
935: /**
936: * Build ajax response for the All E-mail message list
937: */
938: protected function output() {
939: if ($this->get('imap_message_list_data')) {
940: prepare_imap_message_list($this->get('imap_message_list_data'), $this, $this->get('list_path'));
941: }
942: else {
943: $this->out('formatted_message_list', array());
944: }
945: }
946: }
947:
948: /**
949: * Normal IMAP folder view
950: * @subpackage imap/output
951: */
952: class Hm_Output_filter_folder_page extends Hm_Output_Module {
953: /**
954: * Build ajax response for a folder page
955: */
956: protected function output() {
957: $res = array();
958: if ($this->get('imap_mailbox_page')) {
959: $details = $this->get('imap_folder_detail');
960: $type = mb_stripos($details['name'], 'Sent') !== false ? 'sent' : false;
961: prepare_imap_message_list($this->get('imap_mailbox_page'), $this, $type);
962: $max_pages = ceil($details['detail']['exists']/$details['limit']);
963: $this->out('pages', $max_pages);
964: }
965: elseif (!$this->get('formatted_message_list')) {
966: $this->out('formatted_message_list', array());
967: $this->out('pages', 0);
968: } else {
969: $this->out('pages', 0);
970: }
971: $this->out('do_not_flag_as_read_on_open', $this->get('do_not_flag_as_read_on_open', false));
972: }
973: }
974:
975: /**
976: * Start the sent section on the settings page.
977: * @subpackage imap/output
978: */
979: class Hm_Output_start_sent_settings extends Hm_Output_Module {
980: /**
981: * Settings in this section control the Sent E-mail view.
982: */
983: protected function output() {
984: return '<tr><td data-target=".sent_setting" colspan="2" class="settings_subtitle cursor-pointer border-bottom p-2">'.
985: '<i class="bi bi-envelope-fill fs-5 me-2"></i>'.
986: $this->trans('Sent').'</td></tr>';
987: }
988: }
989:
990: /**
991: * Option for the "received since" date range for the All E-mail page
992: * @subpackage imap/output
993: */
994: class Hm_Output_sent_since_setting extends Hm_Output_Module {
995: protected function output() {
996: if (!email_is_active($this->get('router_module_list'))) {
997: return '';
998: }
999: $since = DEFAULT_SENT_SINCE;
1000: $settings = $this->get('user_settings', array());
1001: if (array_key_exists('sent_since', $settings) && $settings['sent_since']) {
1002: $since = $settings['sent_since'];
1003: }
1004: return '<tr class="sent_setting"><td><label for="sent_since">'.
1005: $this->trans('Show messages received since').'</label></td>'.
1006: '<td>'.message_since_dropdown($since, 'sent_since', $this, DEFAULT_SENT_SINCE).'</td></tr>';
1007: }
1008: }
1009:
1010: /**
1011: * Option to unflag a message on reply
1012: * @subpackage imap/output
1013: */
1014: class Hm_Output_imap_unflag_on_send_controls extends Hm_Output_Module {
1015: protected function output() {
1016: $flagged = false;
1017: $details = $this->get('reply_details', array());
1018: if (is_array($details) && array_key_exists('msg_headers', $details) && array_key_exists('Flags', $details['msg_headers'])) {
1019: if (mb_stristr($details['msg_headers']['Flags'], 'flagged')) {
1020: $flagged = true;
1021: }
1022: }
1023: if (!$flagged) {
1024: return;
1025: }
1026: if ($this->get('list_path') || $this->get('compose_msg_path')) {
1027: return '<div class="unflag_send_div"><input type="checkbox" value="1" name="compose_unflag_send" id="unflag_send">'.
1028: '<label for="unflag_send">'.$this->trans('Unflag on reply').'</label></div>';
1029: }
1030: }
1031: }
1032:
1033: /**
1034: * Option to enable/disable flagging a message as read when opened (defaults to true)
1035: * @subpackage imap/output
1036: */
1037: class Hm_Output_imap_unread_on_open extends Hm_Output_Module {
1038: protected function output() {
1039: $checked = '';
1040: $settings = $this->get('user_settings', array());
1041: if (array_key_exists('unread_on_open', $settings) && $settings['unread_on_open']) {
1042: $checked = ' checked="checked"';
1043: }
1044: return '<tr class="general_setting"><td><label class="form-check-label" for="unread_on_open">'.
1045: $this->trans('Don\'t flag a message as read on open').'</label></td>'.
1046: '<td><input class="form-check-input" type="checkbox" '.$checked.' id="unread_on_open" name="unread_on_open" data-default-value="false" value="1" /></td></tr>';
1047: }
1048: }
1049:
1050: /**
1051: * Option to enable/disable simple message structure on the message view
1052: * @subpackage imap/output
1053: */
1054: class Hm_Output_imap_simple_msg_parts extends Hm_Output_Module {
1055: protected function output() {
1056: $checked = '';
1057: $settings = $this->get('user_settings', array());
1058: $reset = '';
1059: if (array_key_exists('simple_msg_parts', $settings) && $settings['simple_msg_parts']) {
1060: $checked = ' checked="checked"';
1061: } else {
1062: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>';
1063: }
1064: return '<tr class="general_setting"><td><label class="form-check-label" for="simple_msg_parts">'.
1065: $this->trans('Show simple message part structure when reading a message').'</label></td>'.
1066: '<td><input class="form-check-input" type="checkbox" '.$checked.' id="simple_msg_parts" name="simple_msg_parts" data-default-value="false" value="1" />'.$reset.'</td></tr>';
1067: }
1068: }
1069:
1070: /**
1071: * Option to enable/disable pagination links on the message view
1072: * @subpackage imap/output
1073: */
1074: class Hm_Output_imap_pagination_links extends Hm_Output_Module {
1075: protected function output() {
1076: $checked = '';
1077: $reset = '';
1078: $settings = $this->get('user_settings', array());
1079: if (!array_key_exists('pagination_links', $settings) || (array_key_exists('pagination_links', $settings) && $settings['pagination_links'])) {
1080: $checked = ' checked="checked"';
1081: }
1082: if($settings['pagination_links'] !== DEFAULT_PAGINATION_LINKS) {
1083: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>';
1084: }
1085: $res = '<tr class="general_setting"><td><label class="form-check-label" for="pagination_links">'.
1086: $this->trans('Show next & previous emails links when reading a message').'</label></td>'.
1087: '<td><input class="form-check-input" type="checkbox"'.$checked.' id="pagination_links" name="pagination_links" data-default-value="'.(DEFAULT_PAGINATION_LINKS ? 'true' : 'false') . '" value="1" />'.$reset.'</td></tr>';
1088: return $res;
1089: }
1090: }
1091:
1092: /**
1093: * Option to enable/disable loading the next email instead of returning to your inbox after performing a message action (delete, archive, move, etc.)
1094: * @subpackage imap/output
1095: */
1096: class Hm_Output_imap_auto_advance_email extends Hm_Output_Module {
1097: protected function output() {
1098: $checked = '';
1099: $reset = '';
1100: $settings = $this->get('user_settings', array());
1101: if (!array_key_exists('auto_advance_email', $settings) || (array_key_exists('auto_advance_email', $settings) && $settings['auto_advance_email'])) {
1102: $checked = ' checked="checked"';
1103: } else {
1104: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>';
1105: }
1106: $res = '<tr class="general_setting"><td><label class="form-check-label" for="auto_advance_email">'.
1107: $this->trans('Show next email instead of your inbox after performing action (delete, archive, move, etc)').'</label></td>'.
1108: '<td><input class="form-check-input" type="checkbox"'.$checked.' id="auto_advance_email" name="auto_advance_email" data-default-value="true" value="1" />'.$reset.'</td></tr>';
1109: return $res;
1110: }
1111: }
1112:
1113: /**
1114: * Output imap prefetch ids
1115: * @subpackage imap/output
1116: */
1117: class Hm_Output_prefetch_imap_folder_ids extends Hm_Output_Module {
1118: protected function output() {
1119: $ids = $this->get('prefetch_folder_ids', array());
1120: if (count($ids) == 0) {
1121: return;
1122: }
1123: return '<input type="hidden" id="imap_prefetch_ids" value="'.$this->html_safe(implode(',', $ids)).'" />';
1124: }
1125: }
1126:
1127: /**
1128: * Option to set the per page count for IMAP folder views
1129: * @subpackage imap/output
1130: */
1131: class Hm_Output_imap_per_page_setting extends Hm_Output_Module {
1132: protected function output() {
1133: $settings = $this->get('user_settings', array());
1134: $per_page = DEFAULT_IMAP_PER_PAGE;
1135: $reset = '';
1136: if (array_key_exists('imap_per_page', $settings)) {
1137: $per_page = $settings['imap_per_page'];
1138: }
1139: if ($per_page != DEFAULT_IMAP_PER_PAGE) {
1140: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input"></i></span>';
1141: }
1142: return '<tr class="general_setting"><td><label for="imap_per_page">'.
1143: $this->trans('Messages per page for IMAP folder views').'</label></td><td class="d-flex"><input class="form-control form-control-sm w-auto" type="text" id="imap_per_page" '.
1144: 'name="imap_per_page" value="'.$this->html_safe($per_page).'" data-default-value="'.DEFAULT_IMAP_PER_PAGE.'"/>'.$reset.'</td></tr>';
1145: }
1146: }
1147:
1148: /**
1149: * Option to set number of google contacts
1150: * @subpackage imap/output
1151: */
1152: class Hm_Output_max_google_contacts_number extends Hm_Output_Module {
1153: protected function output() {
1154: $settings = $this->get('user_settings', array());
1155: $max_google_contacts_number = DEFAULT_MAX_GOOGLE_CONTACTS_NUMBER;
1156: $reset = '';
1157: if (array_key_exists('max_google_contacts_number', $settings)) {
1158: $max_google_contacts_number = $settings['max_google_contacts_number'];
1159: }
1160: if ($max_google_contacts_number != DEFAULT_MAX_GOOGLE_CONTACTS_NUMBER) {
1161: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input" default-value="'.DEFAULT_MAX_GOOGLE_CONTACTS_NUMBER.'"></i></span>';
1162: }
1163: return '<tr class="general_setting"><td><label for="max_google_contacts_number">'.
1164: $this->trans('Max google contacts number').'</label></td><td><input class="form-control form-control-sm w-auto" type="number" id="max_google_contacts_number" '.
1165: 'name="max_google_contacts_number" value="'.$this->html_safe($max_google_contacts_number).'" />'.$reset.'</td></tr>';
1166: }
1167: }
1168:
1169: /**
1170: * Option to enable/disable message part icons on the message view
1171: * @subpackage imap/output
1172: */
1173: class Hm_Output_imap_msg_icons_setting extends Hm_Output_Module {
1174: protected function output() {
1175: $checked = '';
1176: $settings = $this->get('user_settings', array());
1177: $reset = '';
1178: if (array_key_exists('msg_part_icons', $settings) && $settings['msg_part_icons']) {
1179: $checked = ' checked="checked"';
1180: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>';
1181: }
1182: return '<tr class="general_setting"><td><label class="form-check-label" for="msg_part_icons">'.
1183: $this->trans('Show message part icons when reading a message').'</label></td>'.
1184: '<td><input class="form-check-input" type="checkbox" '.$checked.' id="msg_part_icons" name="msg_part_icons" data-default-value="false" value="1" />'.$reset.'</td></tr>';
1185: }
1186: }
1187:
1188: /**
1189: * Option to limit mail fromat to text only when possible (not defaulting to HTML)
1190: * @subpackage imap/output
1191: */
1192: class Hm_Output_text_only_setting extends Hm_Output_Module {
1193: protected function output() {
1194: $checked = '';
1195: $settings = $this->get('user_settings', array());
1196: $reset = '';
1197: if (array_key_exists('text_only', $settings) && $settings['text_only']) {
1198: $checked = ' checked="checked"';
1199: }
1200: if($settings['text_only'] !== DEFAULT_TEXT_ONLY) {
1201: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>';
1202: }
1203: return '<tr class="general_setting"><td><label class="form-check-label" for="text_only">'.
1204: $this->trans('Prefer text over HTML when reading messages').'</label></td>'.
1205: '<td><input class="form-check-input" type="checkbox" '.$checked.' id="text_only" name="text_only" data-default-value="'.(DEFAULT_TEXT_ONLY ? 'true' : 'false') . '" value="1" />'.$reset.'</td></tr>';
1206: }
1207: }
1208:
1209: /**
1210: * Option for the maximum number of messages per source for the All E-mail page
1211: * @subpackage imap/output
1212: */
1213: class Hm_Output_sent_source_max_setting extends Hm_Output_Module {
1214: protected function output() {
1215: $sources = DEFAULT_SENT_PER_SOURCE;
1216: $settings = $this->get('user_settings', array());
1217: $reset = '';
1218: if (array_key_exists('sent_per_source', $settings)) {
1219: $sources = $settings['sent_per_source'];
1220: }
1221: if ($sources != DEFAULT_SENT_PER_SOURCE) {
1222: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input"></i></span>';
1223: }
1224: return '<tr class="sent_setting"><td><label for="sent_per_source">'.
1225: $this->trans('Max messages per source').'</label></td>'.
1226: '<td class="d-flex"><input class="form-control form-control-sm w-auto" type="text" size="2" id="sent_per_source" name="sent_per_source" value="'.$this->html_safe($sources).'" data-default-value="'.DEFAULT_SENT_PER_SOURCE.'" />'.$reset.'</td></tr>';
1227: }
1228: }
1229:
1230: /**
1231: * Option for the maximum number of messages per source for the All E-mail page
1232: * @subpackage imap/output
1233: */
1234: class Hm_Output_original_folder_setting extends Hm_Output_Module {
1235: protected function output() {
1236: $checked = '';
1237: $reset = '';
1238: $settings = $this->get('user_settings', array());
1239: if (array_key_exists('original_folder', $settings) && $settings['original_folder']) {
1240: $checked = ' checked="checked"';
1241: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>';
1242: }
1243: return '<tr class="general_setting"><td><label class="form-check-label" for="original_folder">'.
1244: $this->trans('Archive to the original folder').'</label></td>'.
1245: '<td><input class="form-check-input" type="checkbox" '.$checked.' id="original_folder" name="original_folder" data-default-value="false" value="1" />'.$reset.'</td></tr>';
1246: }
1247: }
1248:
1249: /**
1250: * Option to enable/disable snooze functionality
1251: * @subpackage imap/output
1252: */
1253: class Hm_Output_setting_enable_snooze extends Hm_Output_Module {
1254: protected function output() {
1255: $settings = $this->get('user_settings', array());
1256: $checked = '';
1257: $reset = '';
1258: if (array_key_exists('enable_snooze', $settings) && $settings['enable_snooze']) {
1259: $checked = ' checked="checked"';
1260: }
1261: if ($settings['enable_snooze'] !== DEFAULT_ENABLE_SNOOZE) {
1262: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-repeat refresh_list reset_default_value_checkbox"></i></span>';
1263: }
1264: return '<tr class="general_setting"><td><label class="form-check-label" for="enable_snooze">'.
1265: $this->trans('Enable Snooze functionality').'</label></td>'.
1266: '<td><input class="form-check-input" type="checkbox" '.$checked.' id="enable_snooze" name="enable_snooze" data-default-value="'.(DEFAULT_ENABLE_SNOOZE ? 'true' : 'false') . '" value="1" />'.$reset.'</td></tr>';
1267: }
1268: }
1269:
1270: /**
1271: * @subpackage imap/output
1272: */
1273: class Hm_Output_review_sent_email extends Hm_Output_Module {
1274: protected function output() {
1275: $checked = '';
1276: $reset = '';
1277: $settings = $this->get('user_settings', array());
1278: if (!array_key_exists('review_sent_email', $settings) || (array_key_exists('review_sent_email', $settings) && $settings['review_sent_email'])) {
1279: $checked = ' checked="checked"';
1280: }
1281: if($settings['review_sent_email'] !== DEFAULT_REVIEW_SENT_EMAIL) {
1282: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>';
1283: }
1284: return '<tr class="general_setting"><td><label class="form-check-label" for="review_sent_email">'.
1285: $this->trans('Review sent message').'</label></td>'.
1286: '<td><input class="form-check-input" type="checkbox" '.$checked.' id="review_sent_email" name="review_sent_email" data-default-value="'.(DEFAULT_REVIEW_SENT_EMAIL ? 'true' : 'false') . '" value="1" />'.$reset.'</td></tr>';
1287: }
1288: }
1289:
1290: /**
1291: * Add snooze dialog to the message list controls
1292: * @subpackage imap/output
1293: */
1294: class Hm_Output_snooze_msg_control extends Hm_Output_Module {
1295: protected function output() {
1296: $settings = $this->get('user_settings', array());
1297: $enable_snooze = array_key_exists('enable_snooze_setting', $settings) && $settings['enable_snooze_setting'];
1298:
1299: if (!$enable_snooze) {
1300: return;
1301: }
1302:
1303: $parts = explode('_', $this->get('list_path'));
1304: $unsnooze = $parts[0] == 'imap' && hex2bin($parts[2]) == 'Snoozed';
1305: $res = snooze_dropdown($this, $unsnooze);
1306: $this->concat('msg_controls_extra', $res);
1307: }
1308: }
1309:
1310: /**
1311: * Output imap message source
1312: * @subpackage imap/output
1313: */
1314: class Hm_Output_imap_message_source extends Hm_Output_Module {
1315: protected function output() {
1316: $res = '<div class="w-auto mx-auto p-5">';
1317: $res .= '
1318: <div class="d-flex flex-column gap-2 mb-4">
1319: <h1>Message source</h1>
1320: <div class="d-flex justify-content-between mb-3">
1321: <button class="btn btn-success" onclick="handleDownloadMsgSource()">Download</button>
1322: <a href="#" class="hlink" onClick="handleCopyMsgSource(event)">Copy to clipboard</a>
1323: </div>
1324: </div>
1325: ';
1326: if($this->get('msg_source')){
1327: $res .= '<div><pre class="msg_source">'.$this->html_safe($this->get('msg_source')).'</pre></div>';
1328: }
1329: $res .= '</div>';
1330: return $res;
1331: }
1332: }
1333: /**
1334: * @subpackage jmap/output
1335: */
1336: class Hm_Output_stepper_setup_server_jmap extends Hm_Output_Module {
1337: protected function output() {
1338: if(!in_array('jmap', $this->get('router_module_list'), true)) return '';
1339: return '
1340: <div class="form-check" id="srv_setup_stepper_jmap_select_box">
1341: <input class="form-check-input" type="checkbox" role="switch" id="srv_setup_stepper_only_jmap" name="srv_setup_stepper_only_jmap" onchange="handleJmapCheckboxChange(this)">
1342: <label class="form-check-label" style="font-size: 12px;" for="srv_setup_stepper_only_jmap">
1343: '.$this->trans('Setup JMAP Server').'
1344: </label>
1345: </div>
1346: <div class="step_config-jmap_bloc hide" id="step_config-jmap_bloc">
1347: <label><strong>JMAP</strong></label>
1348: <div class="form-floating">
1349: <input required type="text" id="srv_setup_stepper_jmap_address" name="srv_setup_stepper_jmap_address" class="txt_fld form-control" value="" placeholder="'.$this->trans('Address').'">
1350: <label class="" for="srv_setup_stepper_jmap_address">'.$this->trans('Address').'</label>
1351: <span id="srv_setup_stepper_jmap_address-error" class="invalid-feedback"></span>
1352: </div>
1353: </div>
1354: ';
1355: }
1356: }
1357:
1358: /**
1359: * @subpackage imap/output
1360: */
1361: class Hm_Output_stepper_setup_server_imap extends Hm_Output_Module {
1362: protected function output() {
1363: $res = '
1364: <div class="step_config-imap_bloc mb-6" id="step_config-imap_bloc">
1365: <label><strong>IMAP</strong></label>
1366: <div class="form-floating">
1367: <input required type="text" id="srv_setup_stepper_imap_address" name="srv_setup_stepper_imap_address" class="txt_fld form-control" value="" placeholder="'.$this->trans('Address').'">
1368: <label class="" for="srv_setup_stepper_imap_address">'.$this->trans('Address').'</label>
1369: <span id="srv_setup_stepper_imap_address-error" class="invalid-feedback"></span>
1370: </div>
1371: <div class="d-flex">
1372: <div class="flex-fill">
1373: <div class="form-floating">
1374: <input required type="number" id="srv_setup_stepper_imap_port" name="srv_setup_stepper_imap_port" class="txt_fld form-control" value="" placeholder="'.$this->trans('Port').'">
1375: <label class="" for="srv_setup_stepper_imap_port">'.$this->trans('Port').'</label>
1376: <span id="srv_setup_stepper_imap_port-error" class="invalid-feedback"></span>
1377: </div>
1378: <span id="srv_setup_stepper_imap_port-error" class="invalid-feedback"></span>
1379: </div>
1380: <div class="p-2 flex-fill">
1381: <div class="form-check">
1382: <input class="form-check-input" type="radio" id="imap_tls" name="srv_setup_stepper_imap_tls" value="true">
1383: <label class="form-check-label" style="font-size: 12px;" for="imap_tls">
1384: '.$this->trans('Use TLS').'
1385: </label>
1386: </div>
1387: <div class="form-check">
1388: <input class="form-check-input" type="radio" id="imap_start_tls" name="srv_setup_stepper_imap_tls" value="false" checked>
1389: <label class="form-check-label" style="font-size: 12px;" for="imap_start_tls">
1390: '.$this->trans('STARTTLS or unencrypted').'
1391: </label>
1392: </div>
1393: </div>
1394: </div>
1395: ';
1396:
1397: if ($this->get('sieve_filters_enabled')) {
1398: $default_value = '';
1399: $res .= '
1400:
1401: <div class="form-check">
1402: <input class="form-check-input" type="checkbox" id="srv_setup_stepper_enable_sieve" onchange="handleSieveStatusChange(this)">
1403: <label class="form-check-label" for="srv_setup_stepper_enable_sieve">'.$this->trans('Enable Sieve').'</label>
1404: </div>
1405: <div id="srv_setup_stepper_imap_sieve_host_bloc" class="hide">
1406: <div class="form-floating">
1407: <input required type="text" id="srv_setup_stepper_imap_sieve_host" name="srv_setup_stepper_imap_sieve_host" class="txt_fld form-control" value="" placeholder="'.$this->trans('Sieve Host').'">
1408: <label class="" for="srv_setup_stepper_imap_sieve_host">'.$this->trans('Sieve Host').'</label>
1409: <span id="srv_setup_stepper_imap_sieve_host-error" class="invalid-feedback"></span>
1410: </div>
1411: <div class="form-floating">
1412: <div class="form-check" id="srv_setup_stepper_imap_sieve_mode_tls_bloc">
1413: <input class="form-check-input" type="checkbox" role="switch" value="1" id="srv_setup_stepper_imap_sieve_mode_tls" name="srv_setup_stepper_imap_sieve_mode_tls">
1414: <label class="form-check-label" for="srv_setup_stepper_imap_sieve_mode_tls">
1415: '.$this->trans('Sieve TLS Mode').'
1416: </label>
1417: </div>
1418: </div>
1419: </div>';
1420: }
1421: $res .= '</div>';
1422:
1423: return $res;
1424: }
1425: }
1426:
1427: class Hm_Output_stepper_setup_server_jmap_imap_common extends Hm_Output_Module {
1428: protected function output() {
1429: $res = '
1430: <div class="form-check" id="step_config_combined_view">
1431: <input class="form-check-input" type="checkbox" role="switch" id="srv_setup_stepper_imap_hide_from_c_page" name="srv_setup_stepper_imap_hide_from_c_page">
1432: <label class="form-check-label" for="srv_setup_stepper_imap_hide_from_c_page">
1433: '.$this->trans('Hide From Combined Pages').'
1434: </label>
1435: </div>
1436: ';
1437:
1438: return $res;
1439: }
1440: }
1441:
1442: class Hm_Output_server_config_ews extends Hm_Output_Module {
1443: protected function output() {
1444: $hasEWSActivated = in_array('imap', $this->get('router_module_list'), true);
1445:
1446: if(! $hasEWSActivated){
1447: return '';
1448: }
1449:
1450: $ews_servers_count = count(array_filter($this->get('imap_servers', array()), function($v) { return array_key_exists('type', $v) && $v['type'] == 'ews'; }));
1451:
1452: $res = '<div class="ews_server_setup">
1453: <div data-target=".ews_server_config_section" class="server_section border-bottom cursor-pointer px-1 py-3 pe-auto">
1454: <a href="#" class="pe-auto">
1455: <i class="bi bi-envelope-fill me-3"></i>
1456: <b>' . $this->trans('Exchange Servers') . '</b>
1457: </a>
1458: <div class="server_count"><span class="ews_server_count"> ' . $ews_servers_count .'</span> ' . $this->trans('EWS') . '</div>
1459: </div>
1460: <div class="ews_server_config_section px-4 pt-3 me-0">
1461: <div class="d-none col-12 col-xl-7 mb-4" id="ews_form">
1462: <form class="me-0" method="POST">
1463: <div>
1464: <input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />
1465: <input type="hidden" name="ews_server_id" id="ews_server_id" />
1466: <div class="form-floating mb-3">
1467: <input required type="text" id="ews_profile_name" name="ews_profile_name" class="txt_fld form-control" value="" placeholder="'.$this->trans('Name').'">
1468: <label class="" for="ews_profile_name">'.$this->trans('Name').'</label>
1469: </div>
1470: <div class="form-floating mb-3">
1471: <input required type="text" id="ews_server" name="ews_server" class="txt_fld form-control warn_on_paste" value="" placeholder="'.$this->trans('Server Address').'">
1472: <label class="" for="ews_server">'.$this->trans('Server Address').'</label>
1473: </div>
1474: <div class="form-floating mb-3">
1475: <input required type="text" id="ews_email" name="ews_email" class="txt_fld form-control warn_on_paste" value="" placeholder="'.$this->trans('Email or Username').'" autocomplete="username">
1476: <label class="" for="ews_email">'.$this->trans('Email or Username').'</label>
1477: </div>
1478: <div class="form-floating mb-3">
1479: <input type="password" id="ews_password" name="ews_password" class="txt_fld form-control warn_on_paste" value="" placeholder="'.$this->trans('Password').'" autocomplete="new-password">
1480: <label class="" for="ews_password">'.$this->trans('Password').'</label>
1481: </div>
1482: <div class="form-check">
1483: <input type="hidden" name="ews_hide_from_c_page" value="0">
1484: <input class="form-check-input" type="checkbox" role="switch" id="ews_hide_from_c_page" name="ews_hide_from_c_page" value="1">
1485: <label class="form-check-label" for="ews_hide_from_c_page">
1486: '.$this->trans('Hide From Combined Pages').'
1487: </label>
1488: </div>
1489: <div class="form-check form-switch mt-3">
1490: <input type="hidden" name="ews_create_profile" value="0">
1491: <input class="form-check-input" type="checkbox" role="switch" onchange="handleCreateProfileCheckboxChange(this)" id="ews_create_profile" name="ews_create_profile" value="1" checked>
1492: <label class="form-check-label" for="ews_create_profile">'.$this->trans('Create Profile').'</label>
1493: </div>
1494: <div class="ms-3">
1495: <div class="form-floating mb-2">
1496: <input type="text" id="ews_profile_reply_to" name="ews_profile_reply_to" class="txt_fld form-control" value="" placeholder="'.$this->trans('Reply to').'">
1497: <label class="" for="ews_profile_reply_to">'.$this->trans('Reply to').'</label>
1498: </div>
1499: <div class="form-floating mb-2">
1500: <input type="text" id="ews_profile_signature" name="ews_profile_signature" class="txt_fld form-control" value="" placeholder="'.$this->trans('Signature').'">
1501: <label class="" for="ews_profile_signature">'.$this->trans('Signature').'</label>
1502: </div>
1503: <div class="form-check">
1504: <input type="hidden" name="ews_profile_is_default" value="0">
1505: <input class="form-check-input" type="checkbox" role="switch" id="ews_profile_is_default" name="ews_profile_is_default" value="1" checked>
1506: <label class="form-check-label" for="ews_profile_is_default">'.$this->trans('Set this profile default').'</label>
1507: </div>
1508: </div>
1509: </div>
1510: <div class="step_config-actions mt-4 d-flex justify-content-between">
1511: <input type="submit" class="btn btn-primary px-3" value="'.$this->trans('Save').'">
1512: </div>
1513: </form>
1514: </div>
1515: <button class="ews-btn btn btn-primary px-4" id="add_new_ews_button"><i class="bi bi-plus-square-fill me-2"></i> '.$this->trans('Add a new server').'</button>';
1516:
1517: $list = $this->get('imap_servers', array());
1518: foreach ($list as $index => $vals) {
1519: if (! array_key_exists('type', $vals) || $vals['type'] != 'ews') {
1520: continue;
1521: }
1522:
1523: $server_id = $vals['id'];
1524:
1525: if (array_key_exists('user', $vals) && !array_key_exists('nopass', $vals)) {
1526: $disabled = 'disabled="disabled"';
1527: $user_pc = $vals['user'];
1528: $pass_pc = $this->trans('[saved]');
1529: $pass_value = '*************';
1530: }
1531: elseif (array_key_exists('nopass', $vals)) {
1532: if (array_key_exists('user', $vals)) {
1533: $user_pc = $vals['user'];
1534: }
1535: else {
1536: $user_pc = '';
1537: }
1538: $pass_pc = $this->trans('Password');
1539: $disabled = '';
1540: $pass_value = '';
1541: }
1542: else {
1543: $user_pc = '';
1544: $pass_pc = $this->trans('Password');
1545: $disabled = '';
1546: $pass_value = '';
1547: }
1548: $res .= '<div class="ews_server mt-3 mb-3">';
1549: $res .= '<form class="imap_connect" method="POST">';
1550: $res .= '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />';
1551: $res .= '<input type="hidden" name="imap_server_id" class="imap_server_id" value="'.$this->html_safe($server_id).'" />';
1552: $res .= '<div class="row m-0 p-0 credentials-container"><div class="col-xl-2 col-lg-2 col-md-6">';
1553: $res .= sprintf('
1554: <div class="text-muted"><strong>%s</strong></div>
1555: <div class="server_subtitle">%s</div>',
1556: $this->html_safe($vals['name']), $this->html_safe($vals['server']));
1557:
1558: $res .= '</div> <div class="col-xl-7 col-lg-7 col-md-9"> <div class="row"> <div class="col-md-6 col-lg-4 ">';
1559:
1560: $res .= '<div class="form-floating">';
1561: $res .= '<input '.$disabled.' id="imap_user_'.$server_id.'" class="form-control credentials" type="text" name="imap_user" value="'.$this->html_safe($user_pc).'" placeholder="'.$this->trans('Username').'">';
1562: $res .= '<label for="imap_user_'.$server_id.'">'.$this->trans('Username').'</label></div>';
1563: $res .= '</div><div class="col-md-6 col-lg-4">';
1564: $res .= '<div class="form-floating">';
1565: $res .= '<input '.$disabled.' id="imap_pass_'.$server_id.'" class="form-control credentials imap_password" type="password" name="imap_pass" value="'.$pass_value.'" placeholder="'.$pass_pc.'">';
1566: $res .= '<label for="imap_pass_'.$server_id.'">'.$this->trans('Password').'</label></div>';
1567: $res .= '</div><div class="col-md-6 col-lg-4">';
1568:
1569: $res .= '</div></div></div><div class="col-lg-3 d-flex justify-content-start align-items-center">';
1570:
1571: // Buttons
1572: $disabled = isset($vals['default']) ? ' disabled': '';
1573: if (!isset($vals['user']) || !$vals['user']) {
1574: $res .= '<input type="submit" value="'.$this->trans('Delete').'" class="imap_delete btn btn-outline-danger btn-sm me-2" />';
1575: $res .= '<input type="submit" value="'.$this->trans('Save').'" class="save_imap_connection btn btn-primary btn-sm me-2" />';
1576: } else {
1577: $keysToRemove = array('object', 'connected', 'default', 'nopass');
1578: $serverDetails = array_diff_key($vals, array_flip($keysToRemove));
1579:
1580: $res .= '<input type="submit" value="'.$this->trans('Edit').'" class="edit_ews_server_connection btn btn-outline-success btn-sm me-2"'.$disabled.' data-server-details=\''.$this->html_safe(json_encode($serverDetails)).'\' data-id="'.$this->html_safe($serverDetails['name']).'" data-type="ews" />';
1581: $res .= '<input type="submit" value="'.$this->trans('Test').'" class="test_imap_connect btn btn-outline-primary btn-sm me-2" />';
1582: $res .= '<input type="submit" value="'.$this->trans('Delete').'" class="imap_delete btn btn-outline-danger btn-sm me-2"'.$disabled.' />';
1583: }
1584:
1585: // Hide/Unhide Buttons
1586: $hidden = array_key_exists('hide', $vals) && $vals['hide'];
1587: $res .= '<input type="submit" '.($hidden ? 'style="display: none;" ' : '').'value="'.$this->trans('Hide').'" class="hide_imap_connection btn btn-outline-secondary btn-sm me-2" />';
1588: $res .= '<input type="submit" '.(!$hidden ? 'style="display: none;" ' : '').'value="'.$this->trans('Unhide').'" class="unhide_imap_connection btn btn-outline-secondary btn-sm me-2" />';
1589:
1590: $res .= '<input type="hidden" value="ajax_imap_debug" name="hm_ajax_hook" />';
1591: $res .= '</div></div></div></form>';
1592: }
1593:
1594: $res .= '
1595: </div>
1596: </div>';
1597:
1598: return $res;
1599: }
1600: }
1601:
1602: /**
1603: * Option to set the per page count for IMAP folder views
1604: * @subpackage imap/output
1605: */
1606: class Hm_Output_first_time_screen_emails_per_page_setting extends Hm_Output_Module {
1607: protected function output() {
1608: $settings = $this->get('user_settings', array());
1609: $per_page = 20;
1610: $reset = '';
1611: if (array_key_exists('first_time_screen_emails', $settings) && $settings['first_time_screen_emails']) {
1612: $per_page = $settings['first_time_screen_emails'];
1613: }
1614: if ($per_page != 20) {
1615: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input"></i></span>';
1616: }
1617: return '<tr class="general_setting"><td><label for="first_time_screen_emails">'.
1618: $this->trans('First-time screen senders').'</label></td><td><input class="form-control form-control-sm w-auto" type="text" id="first_time_screen_emails" '.
1619: 'name="first_time_screen_emails" value="'.$this->html_safe($per_page).'" />'.$reset.'</td></tr>';
1620: }
1621: }
1622: class Hm_Output_setting_move_messages_in_screen_email extends Hm_Output_Module {
1623: protected function output() {
1624: $settings = $this->get('user_settings', array());
1625: $checked = "";
1626: if (array_key_exists('move_messages_in_screen_email', $settings) && $settings['move_messages_in_screen_email']) {
1627: if ($settings['move_messages_in_screen_email']) {
1628: $checked = "checked";
1629: }
1630: }
1631: $res = '<tr class="general_setting"><td><label for="move_messages_in_screen_email">'.
1632: $this->trans('Move all messages in Screen Email folder if dislike email').'</label></td><td><input class="form-check-input" type="checkbox" role="switch" id="move_messages_in_screen_email" name="move_messages_in_screen_email" '.$checked.' ></td></tr>';
1633:
1634: return $res;
1635: }
1636: }
1637:
1638: class Hm_Output_setting_active_preview_message extends Hm_Output_Module {
1639: protected function output() {
1640: $settings = $this->get('user_settings', array());
1641: $checked = "";
1642: if (array_key_exists('active_preview_message', $settings) && $settings['active_preview_message']) {
1643: if ($settings['active_preview_message']) {
1644: $checked = "checked";
1645: }
1646: }
1647: $res = '<tr class="general_setting"><td><label for="active_preview_message">'.
1648: $this->trans('Active preview message').'</label></td><td><input class="form-check-input" type="checkbox" role="switch" id="active_preview_message" name="active_preview_message" '.$checked.' ></td></tr>';
1649: return $res;
1650: }
1651: }
1652: class Hm_Output_setting_ceo_detection_fraud extends Hm_Output_Module {
1653: protected function output() {
1654: $settings = $this->get('user_settings', array());
1655: $ceo_use_detect_ceo_fraud = "";
1656: $ceo_use_trusted_contact = "checked";
1657: $ceo_suspicious_terms = "wire transfer, urgent, account details, payment instruction";
1658: $ceo_rate_limit = "100";
1659: if (array_key_exists('ceo_use_detect_ceo_fraud', $settings)) {
1660: if ($settings['ceo_use_detect_ceo_fraud']) {
1661: $ceo_use_detect_ceo_fraud = "checked";
1662: } else {
1663: $ceo_use_detect_ceo_fraud = "";
1664: }
1665: }
1666:
1667: if (array_key_exists('ceo_use_trusted_contact', $settings)) {
1668: if ($settings['ceo_use_trusted_contact']) {
1669: $ceo_use_trusted_contact = "checked";
1670: } else {
1671: $ceo_use_trusted_contact = "";
1672: }
1673: }
1674:
1675: if (array_key_exists('ceo_suspicious_terms', $settings) && $settings['ceo_suspicious_terms']) {
1676: if ($settings['ceo_suspicious_terms']) {
1677: $ceo_suspicious_terms = $settings['ceo_suspicious_terms'];
1678: }
1679: }
1680: if (array_key_exists('ceo_rate_limit', $settings) && $settings['ceo_rate_limit']) {
1681: if ($settings['ceo_rate_limit']) {
1682: $ceo_rate_limit = $settings['ceo_rate_limit'];
1683: }
1684: }
1685:
1686: $res = '<tr class="general_setting"><td><label for="ceo_use_detect_ceo_fraud">'.
1687: $this->trans('CEO fraud: Use Detect CEO Fraud').
1688: '</label></td><td><input class="form-check-input" type="checkbox" role="switch" id="ceo_use_detect_ceo_fraud" name="ceo_use_detect_ceo_fraud" '. $ceo_use_detect_ceo_fraud .' ></td></tr>';
1689: $res .= '<tr class="general_setting"><td><label for="ceo_use_trusted_contact">'.
1690: $this->trans('CEO fraud: Use Trusted Contacts as Valid emails ').
1691: '</label></td><td><input class="form-check-input" type="checkbox" role="switch" id="ceo_use_trusted_contact" name="ceo_use_trusted_contact" '. $ceo_use_trusted_contact .' ></td></tr>';
1692: $res .= '<tr class="general_setting"><td><label for="ceo_suspicious_terms">'.
1693: $this->trans('CEO fraud: Suspicious Phrases or Requests(separate by ",")').
1694: '</label></td><td><textarea class="form-control form-control-sm w-auto" role="switch" id="ceo_suspicious_terms" name="ceo_suspicious_terms">'. $ceo_suspicious_terms .'</textarea></td></tr>';
1695: $res .= '<tr class="general_setting"><td><label for="ceo_rate_limit">'.
1696: $this->trans('CEO fraud: Rate-Limit or Monitor Unusual Requests').
1697: '</label></td><td><input class="form-control form-control-sm w-auto" type="number" min="0" role="switch" id="ceo_rate_limit" name="ceo_rate_limit" value="'. $ceo_rate_limit .'" ></td></tr>';
1698: return $res;
1699: }
1700: }
1701: