1: <?php
2:
3: /**
4: * Core modules
5: * @package modules
6: * @subpackage core
7: */
8:
9: /**
10: * Format a value for display
11: * @subpackage core/functions
12: * @param string $name value name to find/format
13: * @param array $haystack details to search for the value name
14: * @param bool $type optional format type
15: * @param mixed $default value to return if the name is not found
16: * @return string
17: */
18: if (!hm_exists('display_value')) {
19: function display_value($name, $haystack, $type=false, $default='') {
20: if (!array_key_exists($name, $haystack)) {
21: return $default;
22: }
23: $value = $haystack[$name];
24: $res = false;
25: if ($type) {
26: $name = $type;
27: }
28: switch($name) {
29: case 'from':
30: $value = preg_replace("/(\<.+\>)/U", '', $value);
31: $res = str_replace('"', '', $value);
32: break;
33: case 'date':
34: $res = human_readable_interval($value);
35: break;
36: case 'time':
37: $res = strtotime($value);
38: break;
39: default:
40: $res = $value;
41: break;
42: }
43: return $res;
44: }}
45:
46: /**
47: * Valid interface langs
48: * @subpackage core/functions
49: * @return array
50: */
51: if (!hm_exists('interface_langs')) {
52: function interface_langs() {
53: return array(
54: 'en' => 'English',
55: 'de' => 'German',
56: 'es' => 'Spanish',
57: 'fa' => 'Farsi',
58: 'fr' => 'French',
59: 'et' => 'Estonian',
60: 'id' => 'Indonesian',
61: 'it' => 'Italian',
62: 'ru' => 'Russian',
63: 'ro' => 'Romanian',
64: 'nl' => 'Dutch',
65: 'ja' => 'Japanese',
66: 'hu' => 'Hungarian',
67: 'pt-BR' => 'Brazilian Portuguese',
68: 'az' => 'Azerbaijani',
69: 'zh-Hans' => 'Chinese Simplified',
70: 'zh-TW' => 'Traditional Chinese',
71: );
72: }}
73:
74: /**
75: * Tranlate a human readable time string
76: * @subpackage core/functions
77: * @param string $str string to translate
78: * @param object $output_mod Hm_Output_Module
79: * @return string
80: */
81: if (!hm_exists('translate_time_str')) {
82: function translate_time_str($str, $output_mod) {
83: $parts = explode(',', $str);
84: $res = array();
85: foreach ($parts as $part) {
86: $part = trim($part);
87: if (preg_match("/(\d+)/", $part, $matches)) {
88: $res[] = sprintf($output_mod->trans(preg_replace("/(\d+)/", '%d', $part)), $matches[1]);
89: }
90: }
91: if (!empty($res)) {
92: return implode(', ', $res);
93: }
94: return $str;
95: }}
96:
97: /**
98: * Format a data source to be a valid JS object
99: * @subpackage core/functions
100: * @param array $array values to format
101: * @param object $output_mod Hm_Output_Module
102: * @return string
103: */
104: if (!hm_exists('format_data_sources')) {
105: function format_data_sources($array, $output_mod) {
106: $result = '';
107: $default = false;
108: $groups = group_data_sources($array);
109: foreach ($groups as $group_name => $sources) {
110: $objects = array();
111: foreach ($sources as $values) {
112: $items = array();
113: foreach ($values as $name => $value) {
114: $items[] = $output_mod->html_safe($name).':"'.$output_mod->html_safe($value).'"';
115: }
116: $objects[] = '{'.implode(',', $items).'}';
117: }
118: $function = 'hm_data_sources';
119: if ($group_name != 'default') {
120: $function .= '_'.$group_name;
121: }
122: else {
123: $default = true;
124: }
125: $result .= 'var '.$function.' = function() { return ['.implode(',', $objects).']; };';
126: }
127: if (!$default) {
128: $result .= 'var hm_data_sources = function() { return []; };';
129: }
130: return $result;
131: }}
132:
133: /**
134: * Group data sources by the "group" attribute if it exists, otherwise use "default"
135: * @subpackage core/functions
136: * @param array $array list of data sources
137: * @return array
138: */
139: if (!hm_exists('group_data_sources')) {
140: function group_data_sources($array) {
141: $groups = array();
142: foreach($array as $vals) {
143: $key = 'default';
144: if (array_key_exists('group', $vals)) {
145: $key = $vals['group'];
146: }
147: $groups[$key][] = $vals;
148: }
149: return $groups;
150: }}
151:
152: /**
153: * Determine if E-mail modules are active
154: * @subpackage core/functions
155: * @param array $mod_list list of active module sets
156: * @return mixed
157: */
158: if (!hm_exists('email_is_active')) {
159: function email_is_active($mod_list) {
160: if (in_array('imap', $mod_list, true)) {
161: return true;
162: }
163: return false;
164: }}
165:
166: /**
167: * Validate an E-mail using RFC 3696
168: * @subpackage core/functions
169: * @param string $val value to check
170: * @param bool $allow_local flag to allow local addresses with no domain
171: * @return bool
172: */
173: if (!hm_exists('is_email_address')) {
174: function is_email_address($val, $allow_local=false) {
175: $val = trim($val, "<>");
176: $domain = false;
177: $local = false;
178: if (!trim($val) || mb_strlen($val) > 320) {
179: return false;
180: }
181: if (mb_strpos($val, '@') !== false) {
182: $local = mb_substr($val, 0, mb_strrpos($val, '@'));
183: $domain = mb_substr($val, (mb_strrpos($val, '@') + 1));
184: }
185: else {
186: $local = $val;
187: }
188: if (!$local || (!$allow_local && !$domain)) {
189: return false;
190: }
191: else {
192: if ($domain && !validate_domain_full($domain)) {
193: return false;
194: }
195: if (!validate_local_full($local)) {
196: return false;
197: }
198: }
199: return true;
200: }}
201:
202: /**
203: * Do email domain part checks per RFC 3696 section 2
204: * @subpackage core/functions
205: * @param string $val value to check
206: * @return bool
207: */
208: if (!hm_exists('validate_domain_full')) {
209: function validate_domain_full($val) {
210: /* check for a dot, max allowed length and standard ASCII characters */
211: if (mb_strpos($val, '.') === false || mb_strlen($val) > 255 || preg_match("/[^A-Z0-9\-\.]/i", $val) ||
212: $val[0] == '-' || $val[(mb_strlen($val) - 1)] == '-') {
213: return false;
214: }
215: return true;
216: }}
217:
218: /**
219: * Do email local part checks per RFC 3696 section 3
220: * @subpackage core/functions
221: * @param string $val value to check
222: * @return bool
223: */
224: if (!hm_exists('validate_local_full')) {
225: function validate_local_full($val) {
226: /* check length, "." rules, and for characters > ASCII 127 */
227: if (mb_strlen($val) > 64 || $val[0] == '.' || $val[(mb_strlen($val) -1)] == '.' || mb_strstr($val, '..') ||
228: preg_match('/[^\x00-\x7F]/',$val)) {
229: return false;
230: }
231: /* remove escaped characters and quoted strings */
232: $local = preg_replace("/\\\\.{1}/", '', $val);
233: $local = preg_replace("/\"[^\"]+\"/", '', $local);
234:
235: /* validate remaining unescaped characters */
236: if (preg_match("/[[:print:]]/", $local) && !preg_match("/[@\\\",\[\]]/", $local)) {
237: return true;
238: }
239: return false;
240: }}
241:
242: /**
243: * Get Oauth2 server info
244: * @subpackage core/functions
245: * @param object $config site config object
246: * @return array
247: */
248: if (!hm_exists('get_oauth2_data')) {
249: function get_oauth2_data($config) {
250: return [
251: 'gmail' => $config->get('gmail',[]),
252: 'outlook' => $config->get('outlook',[]),
253: 'office365' => $config->get('office365',[]),
254: ];
255: }}
256:
257: /**
258: * Process user input for a site setting and prep it to be saved
259: * @subpackage core/functions
260: * @param string $type the name of the setting
261: * @param object $handler hm hanndler module object
262: * @param function $callback a function to sanitize the submitted value
263: * @param mixed $default a default to use if callback is not submitted
264: * @param bool $checkbox true if this is a checkbox setting
265: * @return void
266: */
267: if (!hm_exists('process_site_setting')) {
268: function process_site_setting($type, $handler, $callback=false, $default=false, $checkbox=false) {
269: if ($checkbox) {
270: list($success, $form) = $handler->process_form(array('save_settings'));
271: if (array_key_exists($type, $handler->request->post)) {
272: $form = array($type => $handler->request->post[$type]);
273: }
274: else {
275: $form = array($type => false);
276: }
277: }
278: else {
279: list($success, $form) = $handler->process_form(array('save_settings', $type));
280: }
281: $new_settings = $handler->get('new_user_settings', array());
282: $settings = $handler->get('user_settings', array());
283:
284: if ($success) {
285: if (function_exists($callback)) {
286: $result = $callback($form[$type], $type, $handler);
287: }
288: else {
289: $result = $default;
290: }
291: $new_settings[$type.'_setting'] = $result;
292: }
293: else {
294: $settings[$type] = $handler->user_config->get($type.'_setting', $default);
295: }
296: $handler->out('new_user_settings', $new_settings, false);
297: $handler->out('user_settings', $settings, false);
298: }}
299:
300: /**
301: * Return a date for a "received since" value, or just sanitize it
302: * @subpackage core/functions
303: * @param string $val "received since" value to process
304: * @param bool $validate flag to limit to validation only
305: */
306: if (!hm_exists('process_since_argument')) {
307: function process_since_argument($val, $validate=false) {
308: $date = false;
309: $valid = false;
310: if (in_array($val, array('-1 week', '-2 weeks', '-4 weeks', '-6 weeks', '-6 months', '-1 year', '-5 years'), true)) {
311: $valid = $val;
312: $date = date('j-M-Y', strtotime($val));
313: }
314: else {
315: $val = 'today';
316: $valid = $val;
317: $date = date('j-M-Y');
318: }
319: if ($validate) {
320: return $valid;
321: }
322: return $date;
323: }}
324:
325: /**
326: * Sanitize a "since" setting value for combined pages
327: * @subpackage core/functions
328: * @param string $val value to check
329: * @return sanitized value
330: */
331: if (!hm_exists('since_setting_callback')) {
332: function since_setting_callback($val) {
333: return process_since_argument($val, true);
334: }}
335:
336: /**
337: * Sanitize a max per source value
338: * @subpackage core/functions
339: * @param int $val request max
340: * @return sanitized max
341: */
342: if (!hm_exists('max_source_setting_callback')) {
343: function max_source_setting_callback($val) {
344: if ($val > MAX_PER_SOURCE || $val < 0) {
345: return DEFAULT_PER_SOURCE;
346: }
347: return $val;
348: }}
349:
350: /**
351: * Save user settings from the session to permanent storage
352: * @subpackage core/functions
353: * @param object $handler hm handler module object
354: * @param array $form sanitized user input
355: * @param bool $logout true if this is a save + logout request
356: * @return void
357: */
358: if (!hm_exists('save_user_settings')) {
359: function save_user_settings($handler, $form, $logout) {
360: $user = $handler->session->get('username', false);
361: $path = $handler->config->get('user_settings_dir', false);
362:
363: if ($handler->session->auth($user, $form['password'])) {
364: $pass = $form['password'];
365: }
366: else {
367: Hm_Msgs::add('Incorrect password, could not save settings to the server', 'warning');
368: $pass = false;
369: }
370: if ($user && $path && $pass) {
371: try {
372: $handler->user_config->save($user, $pass);
373: $handler->session->set('changed_settings', array());
374: if ($logout) {
375: $handler->session->destroy($handler->request);
376: Hm_Msgs::add('Saved user data on logout', 'info');
377: Hm_Msgs::add('Session destroyed on logout', 'info');
378: }
379: else {
380: Hm_Msgs::add('Settings saved', 'info');
381: }
382: } catch (Exception $e) {
383: Hm_Msgs::add('Could not save settings: ' . $e->getMessage(), 'warning');
384: }
385: }
386:
387: }}
388:
389: /**
390: * Setup commonly used modules for an ajax request
391: * @subpackage core/functions
392: * @param string $name the page id
393: * @param string $source the module set name
394: * @return void
395: */
396: if (!hm_exists('setup_base_ajax_page')) {
397: function setup_base_ajax_page($name, $source=false) {
398: add_handler($name, 'login', false, $source);
399: add_handler($name, 'default_page_data', true, $source);
400: add_handler($name, 'load_user_data', true, $source);
401: add_handler($name, 'language', true, $source);
402: add_handler($name, 'date', true, $source);
403: add_handler($name, 'http_headers', true, $source);
404: }}
405:
406: /**
407: * Setup commonly used modules for a page
408: * @subpackage core/functions
409: * @param string $name the page id
410: * @param string $source the module set name
411: * @param bool $use_layout true if this page uses the application layout
412: * @return void
413: */
414: if (!hm_exists('setup_base_page')) {
415: function setup_base_page($name, $source=false, $use_layout=true) {
416: add_handler($name, 'stay_logged_in', false, $source);
417: add_handler($name, 'login', false, $source);
418: add_handler($name, 'default_page_data', true, $source);
419: add_handler($name, 'load_user_data', true, $source);
420: add_handler($name, 'message_list_type', true);
421: add_handler($name, 'language', true, $source);
422: add_handler($name, 'process_search_terms', true, $source);
423: add_handler($name, 'title', true, $source);
424: add_handler($name, 'date', true, $source);
425: add_handler($name, 'save_user_data', true, $source);
426: add_handler($name, 'logout', true, $source);
427: add_handler($name, 'http_headers', true, $source);
428:
429: add_output($name, 'header_start', false, $source);
430: add_output($name, 'header_css', false, $source);
431: add_output($name, 'header_content', false, $source);
432: add_output($name, 'js_data', false, $source);
433: add_output($name, 'js_search_data', true, $source);
434: add_output($name, 'header_end', false, $source);
435: add_output($name, 'msgs', false, $source);
436: if($use_layout) {
437: add_output($name, 'content_start', false, $source);
438: add_output($name, 'login_start', false, $source);
439: add_output($name, 'login', false, $source);
440: add_output($name, 'login_end', false, $source);
441: add_output($name, 'date', true, $source);
442: add_output($name, 'folder_list_start', true, $source);
443: add_output($name, 'folder_list_end', true, $source);
444: add_output($name, 'content_section_start', true, $source);
445: add_output($name, 'content_section_end', true, $source);
446: add_output($name, 'modals', true, $source);
447: add_output($name, 'save_reminder', true, $source);
448: add_output($name, 'content_end', false, $source, 'page_js', 'after');
449: }
450: add_output($name, 'page_js', false, $source);
451: }}
452:
453: /**
454: * Merge array details for folder sources
455: * @subpackage core/functions
456: * @param array $folder_sources list of folder list entries
457: * @return array
458: */
459: if (!hm_exists('merge_folder_list_details')) {
460: function merge_folder_list_details($folder_sources) {
461: $res = array();
462: if (!is_array($folder_sources)) {
463: return $res;
464: }
465: foreach ($folder_sources as $vals) {
466: if (array_key_exists($vals[0], $res)) {
467: $res[$vals[0]] .= $vals[1];
468: }
469: else {
470: $res[$vals[0]] = $vals[1];
471: }
472: }
473: ksort($res);
474: return $res;
475: }}
476:
477: /**
478: * Determine the correct TLS connection type to use based
479: * on what this version of PHP supports
480: * @return const
481: */
482: if (!hm_exists('get_tls_stream_type')) {
483: function get_tls_stream_type() {
484: $method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
485: if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
486: $method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
487: $method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
488: }
489: return $method;
490: }}
491:
492: /**
493: * List of valid start page options
494: * @return array
495: */
496: if (!hm_exists('start_page_opts')) {
497: function start_page_opts() {
498: return array(
499: 'None' => 'none',
500: 'Home' => 'page=home',
501: 'Everything' => 'page=message_list&list_path=combined_inbox',
502: 'Unread' => 'page=message_list&list_path=unread',
503: 'Flagged' => 'page=message_list&list_path=flagged',
504: 'Compose' => 'page=compose'
505: );
506: }}
507:
508: /**
509: * List of valid default sort order options
510: * @return array
511: */
512: if (!hm_exists('default_sort_order_opts')) {
513: function default_sort_order_opts() {
514: return array(
515: 'arrival' => 'Arrival Date',
516: 'date' => 'Sent Date',
517: );
518: }}
519:
520: /**
521: * See if a host + username is already in a server list
522: * @param class $list class to check
523: * @param int $id server id to get hostname from
524: * @param string $user username to check for
525: * @return bool
526: */
527: if (!hm_exists('in_server_list')) {
528: function in_server_list($list, $id, $user) {
529: $exists = false;
530: $server = $list::dump($id);
531: $name = false;
532: if (is_array($server) && array_key_exists('server', $server)) {
533: $name = $server['server'];
534: }
535: if (!$name) {
536: return false;
537: }
538: foreach ($list::dump() as $server_id => $vals) {
539: if ($id == $server_id) {
540: continue;
541: }
542: if (array_key_exists('user', $vals) && $vals['user'] == $user && $vals['server'] == $name) {
543: $exists = true;
544: break;
545: }
546: }
547: return $exists;
548: }}
549:
550: /**
551: * Perform a check on last added server
552: * It gets deleted if already configured
553: *
554: * @param string $list class to process on check
555: * @param string $user username to check for
556: * @return bool
557: */
558: if (!hm_exists('can_save_last_added_server')) {
559: function can_save_last_added_server($list, $user) {
560: $servers = $list::dump(false, true);
561: $ids = array_keys($servers);
562: $new_id = array_pop($ids);
563: if (in_server_list($list, $new_id, $user)) {
564: $list::del($new_id);
565: $type = explode('_', $list)[1];
566: Hm_Msgs::add('This ' . $type . ' server and username are already configured', 'warning');
567: return false;
568: }
569: return true;
570: }}
571:
572: /**
573: * @subpackage core/functions
574: */
575: if (!hm_exists('profiles_by_smtp_id')) {
576: function profiles_by_smtp_id($profiles, $id) {
577: $res = array();
578: foreach ($profiles as $vals) {
579: if (!is_array($vals)) {
580: continue;
581: }
582: if ($vals['smtp_id'] == $id) {
583: $res[] = $vals;
584: }
585: }
586: return $res;
587: }}
588:
589: /**
590: * @subpackage cores/functions
591: */
592: function get_special_folders($mod, $id) {
593: $server = Hm_IMAP_List::dump($id);
594: if (!$server) {
595: return array();
596: }
597: $specials = $mod->user_config->get('special_imap_folders', array());
598: foreach ($specials as $vals) {
599: if (array_key_exists('imap_user', $vals) &&
600: array_key_exists('imap_server', $vals) &&
601: $server['server'] == $vals['imap_server'] &&
602: $server['user'] == $vals['imap_user']) {
603:
604: return $vals;
605: }
606: }
607: if (array_key_exists($id, $specials)) {
608: return $specials[$id];
609: }
610: return array();
611: }
612:
613: /**
614: * @subpackage core/functions
615: */
616: if (!hm_exists('check_file_upload')) {
617: function check_file_upload($request, $key) {
618: if (!is_array($request->files) || !array_key_exists($key, $request->files)) {
619: return false;
620: }
621: if (!is_array($request->files[$key]) || !array_key_exists('tmp_name', $request->files[$key])) {
622: return false;
623: }
624: return true;
625: }}
626:
627: function privacy_setting_callback($val, $key, $mod) {
628: $setting = Hm_Output_privacy_settings::$settings[$key];
629: $key .= '_setting';
630: $user_setting = $mod->user_config->get($key);
631: $update = $mod->request->post['update'];
632:
633: if ($update) {
634: $val = implode($setting['separator'], array_filter(array_merge(explode($setting['separator'], $user_setting), [$val])));
635: $mod->user_config->set($key, $val);
636:
637: $user_data = $mod->session->get('user_data', array());
638: $user_data[$key] = $val;
639: $mod->session->set('user_data', $user_data);
640: $mod->session->record_unsaved('Privacy settings updated');
641: }
642: return $val;
643: }
644:
645: if (!hm_exists('get_scheduled_date')) {
646: function get_scheduled_date($format, $only_label = false) {
647: switch ($format) {
648: case 'later_in_day':
649: $date_string = 'today 18:00';
650: $label = 'Later in the day';
651: break;
652: case 'tomorrow':
653: $date_string = '+1 day 08:00';
654: $label = 'Tomorrow';
655: break;
656: case 'next_weekend':
657: $date_string = 'next Saturday 08:00';
658: $label = 'Next weekend';
659: break;
660: case 'next_week':
661: $date_string = 'next week 08:00';
662: $label = 'Next week';
663: break;
664: case 'next_month':
665: $date_string = 'next month 08:00';
666: $label = 'Next month';
667: break;
668: default:
669: $date_string = $format;
670: $label = 'Certain date';
671: break;
672: }
673:
674: $time = strtotime($date_string);
675:
676: if ($only_label) {
677: return [$label, date('D, H:i', $time)];
678: }
679:
680: return date('D, d M Y H:i T', $time);
681: }
682: }
683:
684:
685: /**
686: * @subpackage imap/functions
687: */
688: if (!hm_exists('nexter_formats')) {
689: function nexter_formats() {
690: $values = array(
691: 'tomorrow',
692: 'next_weekend',
693: 'next_week',
694: 'next_month'
695: );
696: if (date('H') <= 16) {
697: array_push($values, 'later_in_day');
698: }
699: return $values;
700: }}
701:
702: if (!hm_exists('schedule_dropdown')) {
703: function schedule_dropdown($output, $send_now = false) {
704: $values = nexter_formats();
705:
706: $txt = '';
707: if ($send_now) {
708: $txt .= '<div class="dropdown d-inline-block">
709: <a class="hlink text-decoration-none dropdown-toggle" id="dropdownMenuNexterDate" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">'.$output->trans('Reschedule').'</a>';
710: }
711: $txt .= '<ul class="dropdown-menu nexter_dropdown schedule_dropdown" aria-labelledby="dropdownMenuNexterDate">';
712: foreach ($values as $format) {
713: $labels = get_scheduled_date($format, true);
714: $txt .= '<li><a href="#" class="nexter_date_helper dropdown-item d-flex justify-content-between gap-5" data-value="'.$format.'"><span>'.$output->trans($labels[0]).'</span> <span class="text-end">'.$labels[1].'</span></a></li>';
715: }
716: $txt .= '<li><hr class="dropdown-divider"></li>';
717: $txt .= '<li><label for="nexter_input_date" class="nexter_date_picker dropdown-item cursor-pointer">'.$output->trans('Pick a date').'</label>';
718: $txt .= '<input id="nexter_input_date" type="datetime-local" min="'.date('Y-m-d\Th:m').'" class="nexter_input_date" style="visibility: hidden; position: absolute; height: 0;">';
719: $txt .= '<input class="nexter_input" style="display:none;"></li>';
720: if ($send_now) {
721: $txt .= '<li><hr class="dropdown-divider"></li>';
722: $txt .= '<li><a href="#" data-value="now" class="nexter_date_helper dropdown-item"">'.$output->trans('Send now').'</a></li>';
723: }
724: $txt .= '</ul>';
725: if ($send_now) {
726: $txt .= '</div>';
727: }
728:
729: return $txt;
730: }}
731:
732: /**
733: * @subpackage imap/functions
734: */
735: if (!hm_exists('parse_delayed_header')) {
736: function parse_delayed_header($header, $name)
737: {
738: $header = str_replace("$name: ", '', $header);
739: $result = [];
740: foreach (explode(';', $header) as $keyValue)
741: {
742: $keyValue = trim($keyValue);
743: $spacePos = strpos($keyValue, ' ');
744: if ($spacePos > 0) {
745: $result[rtrim(substr($keyValue, 0, $spacePos), ':')] = trim(substr($keyValue, $spacePos+1));
746: } else {
747: $result[$keyValue] = true;
748: }
749: }
750: return $result;
751: }
752: }
753:
754: function getSettingsSectionOutput($section, $sectionLabel, $sectionIcon, $settingsOptions, $userSettings) {
755: $res = '<tr><td data-target=".'. $section .'_setting" colspan="2" class="settings_subtitle cursor-pointer border-bottom p-2">'.
756: '<i class="bi bi-'. $sectionIcon . ' fs-5 me-2"></i>'. $sectionLabel .'</td></tr>';
757: foreach ($settingsOptions as $key => $setting) {
758: $value = $userSettings[$key] ?? '';
759: ['type' => $type, 'label' => $label, 'description' => $description] = $setting;
760:
761: if ($type === 'checkbox') {
762: $input = '<input type="checkbox" id="'.$key.'" name="'.$key.'" '.($value ? 'checked' : '').' class="form-check-input">';
763: } else {
764: $input = '<input type="'.$type.'" id="'.$key.'" name="'.$key.'" value="'.$value.'" class="form-control">';
765: }
766:
767: $res .= "<tr class='{$section}_setting'>" .
768: "<td><label for='$key'>$label</label></td>" .
769: "<td>
770: <div>
771: $input
772: </div>
773: <div class='setting_description'>$description</div>
774: </td>" .
775: "</tr>";
776: }
777: return $res;
778: }
779:
780: