1: <?php
2:
3: /**
4: * Contact modules
5: * @package modules
6: * @subpackage contacts
7: */
8:
9: if (!defined('DEBUG_MODE')) { die(); }
10:
11: require APP_PATH.'modules/contacts/hm-contacts.php';
12:
13: /**
14: * @subpackage contacts/handler
15: */
16: class Hm_Handler_process_contact_auto_collect_setting extends Hm_Handler_Module {
17: public function process() {
18: function contact_auto_collect_callback($val) {
19: return $val;
20: }
21: process_site_setting('contact_auto_collect', $this, 'contact_auto_collect_callback', false, true);
22: }
23: }
24:
25: /**
26: * @subpackage contacts/handler
27: */
28: class Hm_Handler_store_contact_message extends Hm_Handler_Module {
29: public function process() {
30: if ($this->get('collect_contacts', false)) {
31: $addresses = process_address_fld($this->request->post['compose_to']);
32: $contacts = $this->get('contact_store');
33: $contact_list = $contacts->getAll();
34: $existingEmails = array_column($contact_list, 'email_address');
35: // Extract email addresses from the new format of $addresses
36: $newEmails = array_column($addresses, 'email');
37: if (!empty($newEmails)) {
38: $newContacts = array_filter($newEmails, function ($email) use ($existingEmails) {
39: return !in_array($email, $existingEmails);
40: });
41:
42: if (!empty($newContacts)) {
43: $newContacts = array_map(function ($email) {
44: return ['source' => 'local', 'email_address' => $email, 'display_name' => $email, 'group' => 'Collected Recipients'];
45: }, $newContacts);
46: $contacts->add_contact($newContacts[0]);
47: $this->session->record_unsaved('Contact Added');
48: Hm_Msgs::add('Contact Added');
49: }
50: }
51: }
52: }
53: }
54:
55: /**
56: * @subpackage contacts/handler
57: */
58: class Hm_Handler_store_contact_allow_images extends Hm_Handler_Module {
59: public function process() {
60: if ($this->get('imap_allow_images', false) && $this->get('collect_contacts', false)) {
61: $email = str_replace(['<', '>'], '', $this->get('collected_contact_email', ''));
62: $name = $this->get('collected_contact_name', '');
63: $contacts = $this->get('contact_store');
64: $contact_list = $contacts->getAll();
65: $existingEmails = array_column($contact_list, 'email_address');
66: if (!in_array($email, $existingEmails)) {
67: $contacts->add_contact(['source' => 'local', 'email_address' => $email, 'display_name' => $name, 'group' => 'Trusted Senders']);
68: $this->session->record_unsaved('Contact Added');
69: Hm_Msgs::add('Contact Added');
70: }
71: }
72: }
73: }
74:
75: /**
76: * @subpackage contacts/handler
77: */
78: class Hm_Handler_autocomplete_contact extends Hm_Handler_Module {
79: public function process() {
80: list($success, $form) = $this->process_form(array('contact_value'));
81: $results = array();
82: if ($success) {
83: $val = trim($form['contact_value']);
84: $contacts = $this->get('contact_store');
85: $contacts->sort('email_address');
86: $results = array_slice($contacts->search(array(
87: 'display_name' => $val,
88: 'email_address' => $val
89: )), 0, 10);
90: }
91: $this->out('contact_suggestions', $results);
92: }
93: }
94:
95: /**
96: * @subpackage contacts/handler
97: */
98: class Hm_Handler_find_message_contacts extends Hm_Handler_Module {
99: public function process() {
100: $existing = $this->get('contact_store');
101: $addr_headers = array('to', 'cc', 'bcc', 'sender', 'reply-to', 'from');
102: $headers = $this->get('msg_headers', array());
103: $addresses = array();
104: foreach ($headers as $name => $value) {
105: if (in_array(mb_strtolower($name), $addr_headers, true)) {
106: $values = is_array($value) ? $value : array($value);
107: foreach ($values as $val) {
108: foreach (Hm_Address_Field::parse($val) as $v) {
109: if (!$existing->search(array('email_address' => $v['email']))) {
110: $addresses[] = $v;
111: }
112: }
113: }
114: }
115: }
116: $this->out('contact_addresses', $addresses);
117: }
118: }
119:
120: /**
121: * @subpackage contacts/handler
122: */
123: class Hm_Handler_process_send_to_contact extends Hm_Handler_Module {
124: public function process() {
125: if (array_key_exists('contact_id', $this->request->get)) {
126: $contacts = $this->get('contact_store');
127: $contact = $contacts->get($this->request->get['contact_id']);
128:
129: if ($contact) {
130: $to = sprintf('%s <%s>', $contact->value('display_name'), $contact->value('email_address'));
131: $this->out('compose_draft', array('draft_to' => $to, 'draft_subject' => '', 'draft_body' => ''));
132: }
133: }
134: }
135: }
136:
137: /**
138: * @subpackage contacts/handler
139: */
140: class Hm_Handler_load_contacts extends Hm_Handler_Module {
141: public function process() {
142: $contacts = new Hm_Contact_Store();
143: $contacts->init($this->user_config, $this->session);
144: $page = 1;
145: if (array_key_exists('contact_page', $this->request->get)) {
146: $page = $this->request->get['contact_page'];
147: }
148: $this->out('contact_page', $page);
149: $this->out('contact_store', $contacts, false);
150: $this->out('enable_warn_contacts_cc_not_exist_in_list_contact', $this->user_config->get('enable_warn_contacts_cc_not_exist_in_list_contact_setting', false));
151:
152: }
153: }
154:
155: /**
156: * @subpackage contacts/handler
157: */
158: class Hm_Handler_process_export_contacts extends Hm_Handler_Module {
159: public function process() {
160: if (array_key_exists('contact_source', $this->request->get)) {
161: $source = $this->request->get['contact_source'];
162: $contacts = $this->get('contact_store');
163: $contact_list = $contacts->getAll();
164: if ($source != 'all') {
165: $contact_list = $contacts->export($source);
166: }
167:
168: Hm_Functions::header('Content-Type: text/csv');
169: Hm_Functions::header('Content-Disposition: attachment; filename="'.$source.'_contacts.csv"');
170: $output = fopen('php://output', 'w');
171: fputcsv($output, array('display_name', 'email_address', 'phone_number'));
172: foreach ($contact_list as $contact) {
173: $contact_data = is_array($contact) ? $contact : $contact->export();
174: fputcsv($output, array($contact_data['display_name'], $contact_data['email_address'], $contact_data['phone_number']));
175: }
176: fclose($output);
177: exit;
178: }
179: }
180: }
181:
182: /**
183: * @subpackage contacts/output
184: */
185: class Hm_Output_contact_auto_collect_setting extends Hm_Output_Module {
186: protected function output() {
187: $checked = '';
188: $settings = $this->get('user_settings', array());
189: $reset = '';
190: if (array_key_exists('contact_auto_collect', $settings) && $settings['contact_auto_collect']) {
191: $checked = ' checked="checked"';
192: } else {
193: $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>';
194: }
195:
196: return '<tr class="general_setting"><td><label for="contact_auto_collect">' .
197: $this->trans('Automatically add outgoing email addresses') . '</label></td>' .
198: '<td><input class="form-check-input" type="checkbox" ' . $checked . ' id="contact_auto_collect" name="contact_auto_collect" data-default-value="true" value="1" />' . $reset . '</td></tr>';
199: }
200: }
201:
202: /**
203: * @subpackage contacts/output
204: */
205: class Hm_Output_contacts_page_link extends Hm_Output_Module {
206: protected function output() {
207: $res = '<li class="menu_contacts"><a class="unread_link" href="?page=contacts">';
208: if (!$this->get('hide_folder_icons')) {
209: $res .= '<i class="bi bi-people-fill menu-icon"></i>';
210: }
211: $res .= '<span class="nav-label">'. $this->trans('Contacts').'</span></a></li>';
212: if ($this->format == 'HTML5') {
213: return $res;
214: }
215: $this->concat('formatted_folder_list', $res);
216: }
217: }
218:
219: /**
220: * @subpackage contacts/output
221: */
222: class Hm_Output_contacts_content_start extends Hm_Output_Module {
223: protected function output() {
224: $contact_source_list = $this->get('contact_sources', array());
225: $actions = '<div class="src_title fs-5 mb-2">'.$this->trans('Export Contacts as CSV').'</div>';
226: $actions .= '<div class="list_src"><a href="?page=export_contact&amp;contact_source=all">'.$this->trans('All Contacts').'</a></div>';
227: foreach ($contact_source_list as $value) {
228: $actions .= '<div class="list_src"><a href="?page=export_contact&amp;contact_source='.$this->html_safe($value).'">'.$this->html_safe($this->html_safe($value).' Contacts').'</a></div>';
229: }
230:
231: return '<div class="contacts_content p-0"><div class="content_title d-flex gap-2 justify-content-between px-3 align-items-center"><div class="d-flex gap-2 align-items-center">'.$this->trans('Contacts'). '</div><div class="list_controls source_link d-flex gap-2 align-items-center"><a href="#" title="' . $this->trans('Export Contacts') . '" class="refresh_list">' .
232: '<i class="bi bi-download" width="16" height="16" onclick="listControlsMenu()"></i></a></div></div>'.
233: '<div class="list_actions">'.$actions.'</div>';
234: }
235: }
236:
237: /**
238: * @subpackage contacts/output
239: */
240: class Hm_Output_contacts_content_end extends Hm_Output_Module {
241: protected function output() {
242: return '</div>';
243: }
244: }
245:
246: /**
247: * @subpackage contacts/output
248: */
249: class Hm_Output_add_message_contacts extends Hm_Output_Module {
250: protected function output() {
251: $addresses = $this->get('contact_addresses');
252: $headers = $this->get('msg_headers');
253: $backends = $this->get('contact_edit', array());
254: if (!empty($addresses) && count($backends) > 0) {
255: $res = '<div class="add_contact_row position-absolute top-0 end-0 z-3 p-2 d-flex align-content-center gap-3"><a href="#" title="'.
256: $this->html_safe('Add Contact').'" onclick="$(\'.add_contact_controls\').toggle(); return false;">'.
257: '<i class="bi bi-person-fill-add fs-3" ></i></a><div class="add_contact_controls"><div class="row g-1 mt-1"><div class="col"><select id="add_contact" class="form-select form-select-sm">';
258: foreach ($addresses as $vals) {
259: $res .= '<option value="'.$this->html_safe($vals['name']).' '.$this->html_safe($vals['email']).
260: '">'.$this->html_safe($vals['name']).' &lt;'.$this->html_safe($vals['email']).'&gt;</option>';
261: }
262: $res .= '</select></div> <div class="col"><select id="contact_source" class="form-select form-select-sm">';
263: foreach ($backends as $val) {
264: $res .= '<option value="'.$this->html_safe($val).'">'.$this->html_safe($val).'</option>';
265: }
266: $res .= '</select></div> <div class="col"><input onclick="return add_contact_from_message_view()" class="add_contact_button w-100 btn btn-primary btn-sm" '.
267: 'type="button" value="'.$this->trans('Add Contact').'"></div></div></div></div>';
268: $headers = $headers.$res;
269: }
270: $this->out('msg_headers', $headers, false);
271: }
272: }
273:
274: /**
275: * @subpackage contacts/handler
276: */
277: class Hm_Handler_check_imported_contacts extends Hm_Handler_Module
278: {
279: public function process()
280: {
281: $imported_contact = $this->session->get('imported_contact', array());
282: $this->session->del('imported_contact');
283: $this->out('imported_contact', $imported_contact);
284: }
285: }
286:
287: /**
288: * Process input from the enable collect address on send setting
289: * @subpackage core/handler
290: */
291: class Hm_Handler_process_enable_collect_address_on_send_setting extends Hm_Handler_Module {
292: /**
293: * Allowed vals are bool true/false
294: */
295: public function process() {
296: function enable_collect_address_on_send_callback($val) {
297: return $val;
298: }
299: process_site_setting('enable_collect_address_on_send', $this, 'enable_collect_address_on_send_callback', DEFAULT_ENABLE_COLLECT_ADDRESS_ON_SEND, true);
300: }
301: }
302:
303: /**
304: * Option for the "allow search in all flagged folders" setting
305: * This setting allows searching flagged messages in all folders, not just the INBOX one.
306: * @subpackage core/output
307: */
308: class Hm_Output_enable_collect_address_on_send_setting extends Hm_Output_Module {
309: /**
310: * Processed by Hm_Handler_process_enable_collect_address_on_send_setting
311: */
312: protected function output() {
313: $settings = $this->get('user_settings', array());
314: if (array_key_exists('enable_collect_address_on_send', $settings) && $settings['enable_collect_address_on_send']) {
315: $checked = ' checked="checked"';
316: if($settings['enable_collect_address_on_send'] !== DEFAULT_ENABLE_COLLECT_ADDRESS_ON_SEND) {
317: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise fs-6 cursor-pointer refresh_list reset_default_value_checkbox"></i></span>';
318: }
319: }
320: else {
321: $checked = '';
322: $reset='';
323: }
324: return '<tr class="general_setting"><td><label class="form-check-label" for="enable_collect_address_on_send">'.
325: $this->trans('Enable collect address on send').'</label></td>'.
326: '<td><input class="form-check-input" type="checkbox" '.$checked.
327: ' value="1" id="enable_collect_address_on_send" name="enable_collect_address_on_send" data-default-value="'.(DEFAULT_ENABLE_COLLECT_ADDRESS_ON_SEND ? 'true' : 'false') . '"/>'.$reset.'</td></tr>';
328: }
329: }
330:
331: /**
332: * @subpackage contacts/output
333: */
334: class Hm_Output_contacts_list extends Hm_Output_Module {
335: protected function output() {
336: $imported_contact = $this->get('imported_contact', array());
337: if (count($this->get('contact_sources', array())) == 0) {
338: return '<div class="no_contact_sources">'.$this->trans('No contact backends are enabled!').
339: '<br />'.$this->trans('At least one backend must be enabled in the config/app.php file to use contacts.').'</div>';
340: }
341: $per_page = 25;
342: $current_page = $this->get('contact_page', 1);
343: $res = '<div class="px-3 mt-3"><table class="contact_list table">';
344: $modal = '';
345: if ($imported_contact) {
346: $res .=
347: '<tr class="contact_import_detail"><td colspan="7"><a href="#" class="show_import_detail text-danger" data-bs-toggle="modal" data-bs-target="#importDetailModal">'.$this->trans('More info about import operation').'</a></td></tr>';
348: $modal .= get_import_detail_modal_content($this, $imported_contact);
349: }
350:
351: $res .= '<tr><td colspan="7" class="contact_list_title"><div class="server_title">'.$this->trans('Contacts').'</div></td></tr>';
352:
353:
354: $contacts = $this->get('contact_store');
355: $editable = $this->get('contact_edit', array());
356:
357: $res = '<div class="contact-group contact-group-effect-scale contact-group-theme-1">';
358: $tabIndex = 1;
359: $contactGroups = [];
360: if ($contacts) {
361: foreach ($contacts->paginate_grouped('group', $current_page, $per_page) as $key => $contact) {
362: if (!array_key_exists($key, $contactGroups)) {
363: $contactGroups[$key] = [];
364: }
365: $contactGroups[$key][] = $contact;
366: }
367: }
368:
369: foreach ($contactGroups as $group => $groupContacts) {
370: $res .= '<input type="radio" name="contact-group" ' . ($tabIndex === 1 ? 'checked ' : '') . 'id="tab' . $tabIndex . '" class="' . ($tabIndex === 1 ? 'tab-content-first' : 'tab-content-' . $tabIndex) . '">';
371: $res .= '<label for="tab' . $tabIndex . '">' . $this->html_safe($group) . '</label>';
372:
373: $tabIndex++;
374: }
375: $tabIndex = 1;
376: $res .= '<ul>';
377:
378: foreach ($contactGroups as $group => $groupContacts) {
379:
380: $res .= '<li class="tab-content '.($tabIndex === 1 ? 'tab-content-first' : 'tab-content-'.$tabIndex).' typography">';
381: $res .= '<table class="contact_list">';
382: $res .= '<tr><td colspan="7" class="contact_list_title"><div class="server_title">'.$this->trans('Contacts').'</div></td></tr>';
383: foreach ($groupContacts as $contact) {
384: foreach ($contact as $c) {
385: $name = $c->value('display_name');
386: if (!trim($name)) {
387: $name = $c->value('fn');
388: }
389:
390: $res .= '<tr class="contact_row_'.$this->html_safe($c->value('id')).'">';
391: $res .= '<td><a data-id="contact_'.$this->html_safe($c->value('id')).'_detail" '.
392: '" class="show_contact" title="'.$this->trans('Details').'">'.
393: '<i class="bi bi-person-fill"></i> '.
394: '</d><td>'.$this->html_safe($c->value('type')).'<td><span class="contact_src">'.
395: ($c->value('source') == 'local' ? '' : $this->html_safe($c->value('source'))).'</span>'.
396: '</td><td>' . $this->html_safe($name) . '</td>' .
397: '<td><div class="contact_fld">'.$this->html_safe($c->value('email_address')).'</div></td>'.
398: '<td class="contact_fld"><a href="tel:'.$this->html_safe($c->value('phone_number')).'">'.
399: $this->html_safe($c->value('phone_number')).'</a></td>'.
400: '<td class="text-end" style="width : 100px">';
401: if (in_array($c->value('type').':'.$c->value('source'), $editable, true)) {
402: $delete_attrs = 'data-id="'.$this->html_safe($c->value('id')).'" data-type="'.$this->html_safe($c->value('type')).'" data-source="'.$this->html_safe($c->value('source')).'"';
403:
404: if (class_exists('Hm_LDAP_Contact')) {
405: $delete_attrs .= Hm_LDAP_Contact::generateDeleteAttributes($c, [$this, 'html_safe']);
406: }
407:
408: $edit_url = '?page=contacts&amp;contact_id='.$this->html_safe($c->value('id')).'&amp;contact_source='.
409: $this->html_safe($c->value('source')).'&amp;contact_type='.
410: $this->html_safe($c->value('type')).'&amp;contact_page='.$current_page;
411:
412: if (class_exists('Hm_LDAP_Contact')) {
413: $edit_url = Hm_LDAP_Contact::addDNToUrl($c, $edit_url);
414: }
415:
416: $res .= '<a '.$delete_attrs.' class="delete_contact cursor-pointer" title="'.$this->trans('Delete').'"><i class="bi bi-trash3 text-danger ms-2"></i></a>'.
417: '<a href="'.$edit_url.'" class="edit_contact cursor-pointer" title="'.$this->trans('Edit').'"><i class="bi bi-pencil-square ms-2"></i></a>';
418: }
419:
420: $send_to_url = '?page=compose&amp;contact_id='.$this->html_safe($c->value('id')).
421: '&amp;contact_source='.$this->html_safe($c->value('source')).
422: '&amp;contact_type='.$this->html_safe($c->value('type'));
423:
424: if (class_exists('Hm_LDAP_Contact')) {
425: $send_to_url = Hm_LDAP_Contact::addDNToUrl($c, $send_to_url);
426: }
427:
428: $res .= '<a href="'.$send_to_url.'" class="send_to_contact cursor-pointer" title="'.$this->trans('Send To').'">'.
429: '<i class="bi bi-envelope-arrow-up ms-2"></i></a>';
430:
431: $res .= '</td></tr>';
432: $res .= '<tr><td id="contact_'.$this->html_safe($c->value('id')).'_detail" class="contact_detail_row" colspan="6">';
433: $res .= build_contact_detail($this, $c, $c->value('id')).'</td>';
434: $res .= '</td></tr>';
435: }
436: }
437: $res .= '<tr><td class="contact_pages" colspan="7">';
438: $contactsPerPage = $per_page;
439: $totalContacts = count($contact);
440: $totalPages = ceil($totalContacts / $contactsPerPage);
441: $currentPage = $current_page;
442: if ($currentPage > 1) {
443: $res .= '<a href="?page=contacts&contact_page='.($currentPage - 1).'">Previous</a>';
444: }
445: if ($currentPage <= $totalPages) {
446: $res .= ' <a href="?page=contacts&contact_page='.($currentPage + 1).'">Next</a>';
447: }
448: $res .= '</td></tr>';
449: $res .= '</table>';
450:
451: $res .= '</li>';
452: $tabIndex++;
453: }
454: $res .= '</table>'.$modal.'</div>';
455: $res .= '</ul>';
456:
457: $res .= '</div>';
458:
459: return $res;
460: }
461: }
462:
463: /**
464: * @subpackage contacts/handler
465: */
466: class Hm_Handler_save_contact extends Hm_Handler_Module
467: {
468: public function process()
469: {
470: list($success, $form ) = $this->process_form(array('email_address'));
471: if ($success) {
472: $contacts = $this->get('contact_store');
473: $contact_list = $contacts->getAll();
474: $emailKeyMap = [];
475: foreach ($contact_list as $key => $contact) {
476: $email = strtolower($contact->value('email_address'));
477: if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
478: $emailKeyMap[$email] = $key;
479: }
480: }
481: $existingEmails = array_keys($emailKeyMap);
482:
483: $list_mails = array_unique(
484: preg_split('/[;,]+/', $form['email_address'], -1, PREG_SPLIT_NO_EMPTY)
485: );
486: $addedCount = 0;
487: $updatedCount = 0;
488: foreach ($list_mails as $addr) {
489: $addresses = process_address_fld($addr);
490: $newEmails = array_column($addresses, 'email');
491: $validEmails = array_filter($newEmails, function($email) {
492: return filter_var($email, FILTER_VALIDATE_EMAIL);
493: });
494: if (empty($validEmails)) {
495: continue;
496: }
497:
498: $newContacts = array_diff($validEmails, $existingEmails);
499: $existingContacts = array_intersect($validEmails, $existingEmails);
500: // add new contacts
501: foreach ($newContacts as $email) {
502: $contacts->add_contact([
503: 'source' => 'local',
504: 'email_address' => $email,
505: 'display_name' => $email,
506: 'group' => 'Trusted Senders'
507: ]);
508: $addedCount++;
509: }
510:
511: // Update existing contacts
512: foreach ($existingContacts as $email) {
513: $contactKey = $emailKeyMap[$email];
514: $contacts->update_contact($contactKey, [
515: 'source' => 'local',
516: 'email_address' => $email,
517: 'group' => 'Trusted Senders'
518: ]);
519: $updatedCount++;
520: }
521: }
522: if ($addedCount > 0 || $updatedCount > 0) {
523: $msgParts = [];
524: if ($addedCount > 0) {
525: $msgParts[] = "$addedCount new contact" . ($addedCount > 1 ? "s" : "") . " added";
526: }
527: if ($updatedCount > 0) {
528: $msgParts[] = "$updatedCount contact" . ($updatedCount > 1 ? "s" : "") . " updated";
529: }
530: $finalMsg = implode(', ', $msgParts);
531:
532: $this->session->record_unsaved($finalMsg);
533: Hm_Msgs::add($finalMsg);
534: }
535: }
536:
537: }
538: }
539:
540: /**
541: * @subpackage contacts/output
542: */
543: class Hm_Output_filter_autocomplete_list extends Hm_Output_Module {
544: protected function output() {
545: $suggestions = array();
546: foreach ($this->get('contact_suggestions', array()) as $item) {
547: if (is_array($item)) {
548: $contact = $item[1];
549: $contact_id = $item[0];
550:
551: if (trim($contact->value('display_name'))) {
552: $suggestions[] = $this->html_safe(sprintf(
553: '{"contact_id": "%s", "contact": "%s %s", "type": "%s", "source": "%s"}',
554: $contact_id,
555: $contact->value('display_name'),
556: $contact->value('email_address'),
557: $contact->value('type'),
558: $contact->value('source')
559: ));
560: } else {
561: $suggestions[] = $this->html_safe(sprintf(
562: '%s',
563: $contact->value('email_address')
564: ));
565: }
566: }
567: }
568: $this->out('contact_suggestions', $suggestions);
569: }
570: }
571:
572: /**
573: * @subpackage contacts/output
574: */
575: class Hm_Output_load_contact_mails extends Hm_Output_Module {
576: protected function output() {
577: if (!$this->get("enable_warn_contacts_cc_not_exist_in_list_contact")) {
578: return "";
579: }
580: $contact_store = $this->get('contact_store');
581: $emails = [];
582: foreach ($contact_store->dump() as $contact) {
583: $email = $contact->value('email_address');
584: if ($email) {
585: $emails[] = $email;
586: }
587: }
588: $emails = json_encode($emails);
589: return "<script>var list_emails = $emails; </script>";
590: }
591: }
592:
593: /**
594: * @subpackage contacts/output
595: */
596: class Hm_Output_enable_warn_contacts_cc_not_exist_in_list_contact extends Hm_Output_Module {
597: protected function output() {
598: $settings = $this->get('user_settings');
599: if (array_key_exists('enable_warn_contacts_cc_not_exist_in_list_contact', $settings) && $settings['enable_warn_contacts_cc_not_exist_in_list_contact']) {
600: $checked = ' checked="checked"';
601: $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise fs-6 cursor-pointer refresh_list reset_default_value_checkbox"></i></span>';
602: }
603: else {
604: $checked = '';
605: $reset='';
606: }
607: return '<tr class="general_setting"><td><label class="form-check-label" for="enable_warn_contacts_cc_not_exist_in_list_contact">'.
608: $this->trans('Enable warn if contacts Cc not exist in list contact').'</label></td>'.
609: '<td><input class="form-check-input" type="checkbox" '.$checked.
610: ' value="1" id="enable_warn_contacts_cc_not_exist_in_list_contact" name="enable_warn_contacts_cc_not_exist_in_list_contact" />'.$reset.'</td></tr>';
611: }
612: }
613:
614: class Hm_Handler_process_enable_warn_contacts_cc_not_exist_in_list_contact extends Hm_Handler_Module {
615: public function process() {
616: function enable_warn_contacts_cc_not_exist_in_list_contact_callback($val) { return $val; }
617: process_site_setting('enable_warn_contacts_cc_not_exist_in_list_contact', $this, 'enable_warn_contacts_cc_not_exist_in_list_contact_callback', false, true);
618: }
619: }
620:
621:
622: /**
623: * @subpackage contacts/functions
624: */
625: if (!hm_exists('build_contact_detail')) {
626: function build_contact_detail($output_mod, $contact, $id) {
627: $res = '<div class="contact_detail m-3" /><table class="w-auto"><thead></thead><tbody>';
628: $all_fields = false;
629: $contacts = $contact->export();
630: ksort($contacts);
631:
632: foreach ($contacts as $name => $val) {
633: if ($name == 'all_fields') {
634: $all_fields = $val;
635: continue;
636: }
637: if (mb_substr($name, 0, 8) == 'carddav_') {
638: continue;
639: }
640: if (!trim($val)) {
641: continue;
642: }
643: $res .= '<tr><th>'.$output_mod->trans(name_map($name)).'</th>';
644: $res .= '<td class="'.$output_mod->html_safe($name).'">'.$output_mod->html_safe($val).'</td></tr>';
645: }
646: if ($all_fields) {
647: ksort($all_fields);
648: foreach ($all_fields as $name => $val) {
649: if (in_array($name, array(0, 'raw', 'objectclass', 'ID', 'APP:EDITED', 'UPDATED'), true)) {
650: continue;
651: }
652: $res .= '<tr><th>'.$output_mod->trans(name_map($name)).'</th>';
653: $res .= '<td>'.$output_mod->html_safe($val).'</td></tr>';
654: }
655: }
656: $res .= '</tbody></table></div>';
657: return $res;
658: }}
659:
660:
661: /**
662: * @subpackage contacts/functions
663: */
664: if (!hm_exists('name_map')) {
665: function name_map($val) {
666: $names = array(
667: 'display_name' => 'Display Name',
668: 'displayname' => 'Display Name',
669: 'givenname' => 'Given Name',
670: 'GD:GIVENNAME' => 'Given Name',
671: 'GD:FAMILYNAME' => 'Surname',
672: 'sn' => 'Surname',
673: 'mail' => 'E-mail Address',
674: 'source' => 'Source',
675: 'email_address' => 'E-mail Address',
676: 'l' => 'Locality',
677: 'st' => 'State',
678: 'street' => 'Street',
679: 'postalcode' => 'Postal Code',
680: 'title' => 'Title',
681: 'TITLE' => 'Title',
682: 'phone_number' => 'Telephone Number',
683: 'telephonenumber' => 'Telephone Number',
684: 'facsimiletelephonenumber' => 'Fax Number',
685: 'mobile' => 'Mobile Number',
686: 'roomnumber' => 'Room Number',
687: 'carlicense' => 'Vehicle License',
688: 'o' => 'Organization',
689: 'ou' => 'Organizational Unit',
690: 'departmentnumber' => 'Department Number',
691: 'employeenumber' => 'Employee Number',
692: 'employeetype' => 'Employee Type',
693: 'preferredlanguage' => 'Preferred Language',
694: 'labeleduri' => 'Homepage URL',
695: 'home_address' => 'Home Address',
696: 'work_address' => 'Work Address',
697: 'nickname' => 'Nickname',
698: 'pager' => 'Pager',
699: 'homephone' => 'Home Phone',
700: 'type' => 'Type',
701: 'url' => 'Website',
702: 'org' => 'Company',
703: 'fn' => 'Full Name',
704: 'uid' => 'Uid',
705: 'src_url' => 'URL',
706: 'adr' => 'Address',
707: 'dn' => 'Distinguished Name'
708: );
709: if (array_key_exists($val, $names)) {
710: return $names[$val];
711: }
712: return $val;
713: }}
714:
715:
716: /**
717: * @subpackage contacts/functions
718: */
719: if (!hm_exists('get_import_detail_modal_content')) {
720: function get_import_detail_modal_content($output_mod, $imported_contacts) {
721: $per_page = 10;
722: $page = 1;
723: $total_contacts = count($imported_contacts);
724: $total_pages = ceil($total_contacts / $per_page);
725: $res = '<table class="table">
726: <thead>
727: <tr>
728: <th scope="col">#</th>
729: <th scope="col">Display Name</th>
730: <th scope="col">E-mail Address</th>
731: <th scope="col">Telephone Number</th>
732: <th scope="col">Status</th>
733: </tr>
734: </thead>
735: <tbody class="import_body">';
736:
737: for ($i = 0; $i < $total_contacts; $i++) {
738: $contact = $imported_contacts[$i];
739: $status = $contact['status'] == "invalid email" ? "danger" : "success";
740: $res .= '<tr class="page_'.ceil(($i + 1) / $per_page).'">
741: <td>'.($i + 1).'</td>
742: <td>'.$output_mod->html_safe($contact['display_name']).'</td>
743: <td>'.$output_mod->html_safe($contact['email_address']).'</td>
744: <td>'.$output_mod->html_safe($contact['phone_number']).'</td>
745: <td class="text-'.$status.'">'.$output_mod->html_safe($contact['status']).'</td>
746: </tr>';
747: }
748:
749: $res .= '</tbody></table>';
750:
751: if ($total_pages > 1) {
752: $res .= '<nav aria-label="Pagination">
753: <ul class="pagination justify-content-center">
754: <li class="prev_page '.($page == 1 ? "disabled" : "").'">
755: <span role="button" class="page-link" tabindex="-1" aria-disabled="true">Previous</span>
756: </li>';
757:
758: for ($i = 1; $i <= $total_pages; $i++) {
759: $res .= '<li class="page_item_'.$i.' '.($page == $i ? "active" : "").' page_link_selector" data-page="'. $i .'"><span role="button" class="page-link">'.$i.'</span></li>';
760: }
761:
762: $res .= '<li class="next_page '.($page == $total_pages ? "disabled" : "").'">
763: <span role="button" class="page-link">Next</span>
764: </li>
765: </ul>
766: </nav>';
767: }
768:
769: $res .= '<input type="hidden" id="totalPages" value="'.$total_pages.'">';
770:
771: return '<div class="modal fade" id="importDetailModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="importDetailModalLabel" aria-hidden="true">
772: <div class="modal-dialog modal-lg">
773: <div class="modal-content">
774: <div class="modal-header">
775: <h5 class="modal-title" id="importDetailModalLabel">'.$output_mod->trans('Import details').'</h5>
776: <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
777: </div>
778: <div class="modal-body">
779: <div>
780: '.$res.'
781: </div>
782: </div>
783: <div class="modal-footer">
784: <input class="btn btn-secondary" data-bs-dismiss="modal" type="button" value="Cancel" />
785: </div>
786: </div>
787: </div>
788: </div>';
789: }}
790: