| @@ -103,6 +103,7 @@ $template_data = [ | ||||
|   'rsettings' => $rsettings, | ||||
|   'rspamd_regex_maps' => $rspamd_regex_maps, | ||||
|   'logo_specs' => customize('get', 'main_logo_specs'), | ||||
|   'ip_check' => customize('get', 'ip_check'), | ||||
|   'password_complexity' => password_complexity('get'), | ||||
|   'show_rspamd_global_filters' => @$_SESSION['show_rspamd_global_filters'], | ||||
|   'lang_admin' => json_encode($lang['admin']), | ||||
|   | ||||
| @@ -370,3 +370,14 @@ button[aria-expanded='true'] > .caret { | ||||
| .btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { | ||||
|     background-color: #f0f0f0 !important; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| div.dataTables_wrapper div.dataTables_filter { | ||||
|   text-align: left; | ||||
| } | ||||
| div.dataTables_wrapper div.dataTables_length { | ||||
|   text-align: right; | ||||
| } | ||||
| .dataTables_paginate, .dataTables_length, .dataTables_filter { | ||||
|   margin: 10px 0!important; | ||||
| } | ||||
| @@ -199,6 +199,10 @@ | ||||
|     display: none !important; | ||||
|   } | ||||
| 
 | ||||
|   div.dataTables_wrapper div.dataTables_length { | ||||
|     text-align: left; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 350px) { | ||||
| @@ -11,7 +11,86 @@ | ||||
|  * Copyright 2011-2021 Twitter, Inc. | ||||
|  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) | ||||
|  */ | ||||
| @import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;1,400&display=swap"); | ||||
|  | ||||
| /* source-sans-pro-300 - latin */ | ||||
| @font-face { | ||||
|   font-family: 'Source Sans Pro'; | ||||
|   font-style: normal; | ||||
|   font-weight: 300; | ||||
|   src: url('/fonts/source-sans-pro-v21-latin-300.eot'); /* IE9 Compat Modes */ | ||||
|   src: local(''), | ||||
|        url('/fonts/source-sans-pro-v21-latin-300.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300.woff2') format('woff2'), /* Super Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300.woff') format('woff'), /* Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300.ttf') format('truetype'), /* Safari, Android, iOS */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300.svg#SourceSansPro') format('svg'); /* Legacy iOS */ | ||||
| } | ||||
| /* source-sans-pro-300italic - latin */ | ||||
| @font-face { | ||||
|   font-family: 'Source Sans Pro'; | ||||
|   font-style: italic; | ||||
|   font-weight: 300; | ||||
|   src: url('/fonts/source-sans-pro-v21-latin-300italic.eot'); /* IE9 Compat Modes */ | ||||
|   src: local(''), | ||||
|        url('/fonts/source-sans-pro-v21-latin-300italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300italic.woff') format('woff'), /* Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300italic.ttf') format('truetype'), /* Safari, Android, iOS */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-300italic.svg#SourceSansPro') format('svg'); /* Legacy iOS */ | ||||
| } | ||||
| /* source-sans-pro-regular - latin */ | ||||
| @font-face { | ||||
|   font-family: 'Source Sans Pro'; | ||||
|   font-style: normal; | ||||
|   font-weight: 400; | ||||
|   src: url('/fonts/source-sans-pro-v21-latin-regular.eot'); /* IE9 Compat Modes */ | ||||
|   src: local(''), | ||||
|        url('/fonts/source-sans-pro-v21-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-regular.woff') format('woff'), /* Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-regular.svg#SourceSansPro') format('svg'); /* Legacy iOS */ | ||||
| } | ||||
| /* source-sans-pro-italic - latin */ | ||||
| @font-face { | ||||
|   font-family: 'Source Sans Pro'; | ||||
|   font-style: italic; | ||||
|   font-weight: 400; | ||||
|   src: url('/fonts/source-sans-pro-v21-latin-italic.eot'); /* IE9 Compat Modes */ | ||||
|   src: local(''), | ||||
|        url('/fonts/source-sans-pro-v21-latin-italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-italic.woff2') format('woff2'), /* Super Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-italic.woff') format('woff'), /* Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-italic.ttf') format('truetype'), /* Safari, Android, iOS */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-italic.svg#SourceSansPro') format('svg'); /* Legacy iOS */ | ||||
| } | ||||
| /* source-sans-pro-700 - latin */ | ||||
| @font-face { | ||||
|   font-family: 'Source Sans Pro'; | ||||
|   font-style: normal; | ||||
|   font-weight: 700; | ||||
|   src: url('/fonts/source-sans-pro-v21-latin-700.eot'); /* IE9 Compat Modes */ | ||||
|   src: local(''), | ||||
|        url('/fonts/source-sans-pro-v21-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700.woff') format('woff'), /* Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700.svg#SourceSansPro') format('svg'); /* Legacy iOS */ | ||||
| } | ||||
| /* source-sans-pro-700italic - latin */ | ||||
| @font-face { | ||||
|   font-family: 'Source Sans Pro'; | ||||
|   font-style: italic; | ||||
|   font-weight: 700; | ||||
|   src: url('/fonts/source-sans-pro-v21-latin-700italic.eot'); /* IE9 Compat Modes */ | ||||
|   src: local(''), | ||||
|        url('/fonts/source-sans-pro-v21-latin-700italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700italic.woff') format('woff'), /* Modern Browsers */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700italic.ttf') format('truetype'), /* Safari, Android, iOS */ | ||||
|        url('/fonts/source-sans-pro-v21-latin-700italic.svg#SourceSansPro') format('svg'); /* Legacy iOS */ | ||||
| } | ||||
|  | ||||
| :root { | ||||
|   --bs-blue: #158cba; | ||||
|   --bs-indigo: #6610f2; | ||||
|   | ||||
| @@ -358,3 +358,11 @@ table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty { | ||||
|     background: #333; | ||||
| } | ||||
|  | ||||
| span.mail-address-item { | ||||
|     background-color: #333; | ||||
|     border-radius: 4px; | ||||
|     border: 1px solid #555; | ||||
|     padding: 2px 7px; | ||||
|     display: inline-block; | ||||
|     margin: 2px 6px 2px 0; | ||||
| } | ||||
|   | ||||
| @@ -65,6 +65,7 @@ $template_data = [ | ||||
|   'solr_uptime' => round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60), | ||||
|   'clamd_status' => $clamd_status, | ||||
|   'containers' => $containers, | ||||
|   'ip_check' => customize('get', 'ip_check'), | ||||
|   'lang_admin' => json_encode($lang['admin']), | ||||
|   'lang_debug' => json_encode($lang['debug']), | ||||
|   'lang_datatables' => json_encode($lang['datatables']), | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300italic.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300italic.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300italic.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-300italic.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700italic.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700italic.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700italic.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-700italic.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-italic.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-italic.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-italic.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-italic.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-regular.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-regular.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-regular.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/fonts/source-sans-pro-v21-latin-regular.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -160,6 +160,25 @@ function customize($_action, $_item, $_data = null) { | ||||
|             'msg' => 'ui_texts' | ||||
|           ); | ||||
|         break; | ||||
|         case 'ip_check': | ||||
|           $ip_check = ($_data['ip_check_opt_in'] == "1") ? 1 : 0; | ||||
|           try { | ||||
|             $redis->set('IP_CHECK', $ip_check); | ||||
|           } | ||||
|           catch (RedisException $e) { | ||||
|             $_SESSION['return'][] = array( | ||||
|               'type' => 'danger', | ||||
|               'log' => array(__FUNCTION__, $_action, $_item, $_data), | ||||
|               'msg' => array('redis_error', $e) | ||||
|             ); | ||||
|             return false; | ||||
|           } | ||||
|           $_SESSION['return'][] = array( | ||||
|             'type' => 'success', | ||||
|             'log' => array(__FUNCTION__, $_action, $_item, $_data), | ||||
|             'msg' => 'ip_check_opt_in_modified' | ||||
|           ); | ||||
|         break; | ||||
|       } | ||||
|     break; | ||||
|     case 'delete': | ||||
| @@ -276,6 +295,20 @@ function customize($_action, $_item, $_data = null) { | ||||
|             return false; | ||||
|           } | ||||
|         break; | ||||
|         case 'ip_check': | ||||
|           try { | ||||
|             $ip_check = ($ip_check = $redis->get('IP_CHECK')) ? $ip_check : 0; | ||||
|             return $ip_check; | ||||
|           } | ||||
|           catch (RedisException $e) { | ||||
|             $_SESSION['return'][] = array( | ||||
|               'type' => 'danger', | ||||
|               'log' => array(__FUNCTION__, $_action, $_item, $_data), | ||||
|               'msg' => array('redis_error', $e) | ||||
|             ); | ||||
|             return false; | ||||
|           } | ||||
|         break; | ||||
|       } | ||||
|     break; | ||||
|   } | ||||
|   | ||||
| @@ -2879,67 +2879,68 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { | ||||
|                 $_SESSION['return'][] = array( | ||||
|                   'type' => 'danger', | ||||
|                   'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||
|                   'msg' => 'access_denied' | ||||
|                   'msg' => 'extended_sender_acl_denied' | ||||
|                 ); | ||||
|                 return false; | ||||
|               } | ||||
|               $extra_acls = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['extended_sender_acl'])); | ||||
|               foreach ($extra_acls as $i => &$extra_acl) { | ||||
|                 if (empty($extra_acl)) { | ||||
|                   continue; | ||||
|                 } | ||||
|                 if (substr($extra_acl, 0, 1) === "@") { | ||||
|                   $extra_acl = ltrim($extra_acl, '@'); | ||||
|                 } | ||||
|                 if (!filter_var($extra_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name($extra_acl)) { | ||||
|                   $_SESSION['return'][] = array( | ||||
|                     'type' => 'danger', | ||||
|                     'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||
|                     'msg' => array('extra_acl_invalid', htmlspecialchars($extra_acl)) | ||||
|                   ); | ||||
|                   unset($extra_acls[$i]); | ||||
|                   continue; | ||||
|                 } | ||||
|                 $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')); | ||||
|                 if (filter_var($extra_acl, FILTER_VALIDATE_EMAIL)) { | ||||
|                   $extra_acl_domain = idn_to_ascii(substr(strstr($extra_acl, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46); | ||||
|                   if (in_array($extra_acl_domain, $domains)) { | ||||
|               else { | ||||
|                 $extra_acls = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['extended_sender_acl'])); | ||||
|                 foreach ($extra_acls as $i => &$extra_acl) { | ||||
|                   if (empty($extra_acl)) { | ||||
|                     continue; | ||||
|                   } | ||||
|                   if (substr($extra_acl, 0, 1) === "@") { | ||||
|                     $extra_acl = ltrim($extra_acl, '@'); | ||||
|                   } | ||||
|                   if (!filter_var($extra_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name($extra_acl)) { | ||||
|                     $_SESSION['return'][] = array( | ||||
|                       'type' => 'danger', | ||||
|                       'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||
|                       'msg' => array('extra_acl_invalid_domain', $extra_acl_domain) | ||||
|                       'msg' => array('extra_acl_invalid', htmlspecialchars($extra_acl)) | ||||
|                     ); | ||||
|                     unset($extra_acls[$i]); | ||||
|                     continue; | ||||
|                   } | ||||
|                 } | ||||
|                 else { | ||||
|                   if (in_array($extra_acl, $domains)) { | ||||
|                     $_SESSION['return'][] = array( | ||||
|                       'type' => 'danger', | ||||
|                       'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||
|                       'msg' => array('extra_acl_invalid_domain', $extra_acl_domain) | ||||
|                     ); | ||||
|                     unset($extra_acls[$i]); | ||||
|                     continue; | ||||
|                   $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')); | ||||
|                   if (filter_var($extra_acl, FILTER_VALIDATE_EMAIL)) { | ||||
|                     $extra_acl_domain = idn_to_ascii(substr(strstr($extra_acl, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46); | ||||
|                     if (in_array($extra_acl_domain, $domains)) { | ||||
|                       $_SESSION['return'][] = array( | ||||
|                         'type' => 'danger', | ||||
|                         'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||
|                         'msg' => array('extra_acl_invalid_domain', $extra_acl_domain) | ||||
|                       ); | ||||
|                       unset($extra_acls[$i]); | ||||
|                       continue; | ||||
|                     } | ||||
|                   } | ||||
|                   else { | ||||
|                     if (in_array($extra_acl, $domains)) { | ||||
|                       $_SESSION['return'][] = array( | ||||
|                         'type' => 'danger', | ||||
|                         'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), | ||||
|                         'msg' => array('extra_acl_invalid_domain', $extra_acl_domain) | ||||
|                       ); | ||||
|                       unset($extra_acls[$i]); | ||||
|                       continue; | ||||
|                     } | ||||
|                     $extra_acl = '@' . $extra_acl; | ||||
|                   } | ||||
|                   $extra_acl = '@' . $extra_acl; | ||||
|                 } | ||||
|               } | ||||
|               $extra_acls = array_filter($extra_acls); | ||||
|               $extra_acls = array_values($extra_acls); | ||||
|               $extra_acls = array_unique($extra_acls); | ||||
|               $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `logged_in_as` = :username"); | ||||
|               $stmt->execute(array( | ||||
|                 ':username' => $username | ||||
|               )); | ||||
|               foreach ($extra_acls as $sender_acl_external) { | ||||
|                 $stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`, `external`) | ||||
|                   VALUES (:sender_acl, :username, 1)"); | ||||
|                 $extra_acls = array_filter($extra_acls); | ||||
|                 $extra_acls = array_values($extra_acls); | ||||
|                 $extra_acls = array_unique($extra_acls); | ||||
|                 $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `logged_in_as` = :username"); | ||||
|                 $stmt->execute(array( | ||||
|                   ':sender_acl' => $sender_acl_external, | ||||
|                   ':username' => $username | ||||
|                 )); | ||||
|                 foreach ($extra_acls as $sender_acl_external) { | ||||
|                   $stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`, `external`) | ||||
|                     VALUES (:sender_acl, :username, 1)"); | ||||
|                   $stmt->execute(array( | ||||
|                     ':sender_acl' => $sender_acl_external, | ||||
|                     ':username' => $username | ||||
|                   )); | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             if (isset($_data['sender_acl'])) { | ||||
|   | ||||
| @@ -12,14 +12,22 @@ $(document).ready(function() { | ||||
|     $.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}}); | ||||
|   } | ||||
|  | ||||
|   $(".generate_password").click(function( event ) { | ||||
|   $(".generate_password").click(async function( event ) {    | ||||
|     try {  | ||||
|       var password_policy = await window.fetch("/api/v1/get/passwordpolicy", { method:'GET', cache:'no-cache' }); | ||||
|       var password_policy = await password_policy.json(); | ||||
|       random_passwd_length = password_policy.length; | ||||
|     } catch(err) { | ||||
|       var random_passwd_length = 8; | ||||
|     } | ||||
|  | ||||
|     event.preventDefault(); | ||||
|     $('[data-hibp]').trigger('input'); | ||||
|     if (typeof($(this).closest("form").data('pwgen-length')) == "number") { | ||||
|       var random_passwd = GPW.pronounceable($(this).closest("form").data('pwgen-length')) | ||||
|     } | ||||
|     else { | ||||
|       var random_passwd = GPW.pronounceable(8) | ||||
|       var random_passwd = GPW.pronounceable(random_passwd_length) | ||||
|     } | ||||
|     $(this).closest("form").find('[data-pwgen-field]').attr('type', 'text'); | ||||
|     $(this).closest("form").find('[data-pwgen-field]').val(random_passwd); | ||||
|   | ||||
| @@ -70,8 +70,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#domainadminstable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -143,8 +148,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#oauth2clientstable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -206,8 +216,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#adminstable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -272,8 +287,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#forwardinghoststable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -330,8 +350,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#relayhoststable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -402,8 +427,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#transportstable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
|   | ||||
| @@ -51,7 +51,40 @@ $(document).ready(function() { | ||||
|     showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag); | ||||
|   }) | ||||
|   // get public ips | ||||
|   get_public_ips(); | ||||
|   $("#host_show_ip").click(function(){   | ||||
|     $("#host_show_ip").find(".text").addClass("d-none"); | ||||
|     $("#host_show_ip").find(".spinner-border").removeClass("d-none"); | ||||
|  | ||||
|     window.fetch("/api/v1/get/status/host/ip", { method:'GET', cache:'no-cache' }).then(function(response) { | ||||
|       return response.json(); | ||||
|     }).then(function(data) { | ||||
|       console.log(data); | ||||
|  | ||||
|       // display host ips | ||||
|       if (data.ipv4) | ||||
|         $("#host_ipv4").text(data.ipv4); | ||||
|       if (data.ipv6) | ||||
|         $("#host_ipv6").text(data.ipv6); | ||||
|  | ||||
|       $("#host_show_ip").addClass("d-none"); | ||||
|       $("#host_show_ip").find(".text").removeClass("d-none"); | ||||
|       $("#host_show_ip").find(".spinner-border").addClass("d-none"); | ||||
|       $("#host_ipv4").removeClass("d-none"); | ||||
|       $("#host_ipv6").removeClass("d-none"); | ||||
|       $("#host_ipv6").removeClass("text-danger"); | ||||
|       $("#host_ipv4").addClass("d-block"); | ||||
|       $("#host_ipv6").addClass("d-block"); | ||||
|     }).catch(function(error){ | ||||
|       console.log(error); | ||||
|        | ||||
|       $("#host_ipv6").removeClass("d-none"); | ||||
|       $("#host_ipv6").addClass("d-block"); | ||||
|       $("#host_ipv6").addClass("text-danger"); | ||||
|       $("#host_ipv6").text(lang_debug.error_show_ip); | ||||
|       $("#host_show_ip").find(".text").removeClass("d-none"); | ||||
|       $("#host_show_ip").find(".spinner-border").addClass("d-none"); | ||||
|     }); | ||||
|   }); | ||||
|   update_container_stats(); | ||||
| }); | ||||
| jQuery(function($){ | ||||
| @@ -86,8 +119,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#autodiscover_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -143,8 +181,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#postfix_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -185,8 +228,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#watchdog_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -231,8 +279,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#api_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -284,8 +337,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#rl_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -375,8 +433,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#ui_logs').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -446,8 +509,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#sasl_logs').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -494,8 +562,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#acme_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -531,8 +604,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#netfilter_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -573,8 +651,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#sogo_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -615,8 +698,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#dovecot_log').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -718,8 +806,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#rspamd_history').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order: [[0, 'desc']], | ||||
|       ajax: { | ||||
| @@ -1224,20 +1317,6 @@ function update_container_stats(timeout=5){ | ||||
|   // run again in n seconds | ||||
|   setTimeout(update_container_stats, timeout * 1000); | ||||
| } | ||||
| // get public ips | ||||
| function get_public_ips(){ | ||||
|   window.fetch("/api/v1/get/status/host/ip", {method:'GET',cache:'no-cache'}).then(function(response) { | ||||
|     return response.json(); | ||||
|   }).then(function(data) { | ||||
|     console.log(data); | ||||
|  | ||||
|     // display host ips | ||||
|     if (data.ipv4) | ||||
|       $("#host_ipv4").text(data.ipv4); | ||||
|     if (data.ipv6) | ||||
|       $("#host_ipv6").text(data.ipv6); | ||||
|   }); | ||||
| } | ||||
| // format hosts uptime seconds to readable string | ||||
| function formatUptime(seconds){ | ||||
|   seconds = Number(seconds); | ||||
|   | ||||
| @@ -78,8 +78,13 @@ jQuery(function($){ | ||||
|   } | ||||
|   function draw_wl_policy_domain_table() { | ||||
|     $('#wl_policy_domain_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -133,8 +138,13 @@ jQuery(function($){ | ||||
|   } | ||||
|   function draw_bl_policy_domain_table() { | ||||
|     $('#bl_policy_domain_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
|   | ||||
| @@ -433,8 +433,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     var table = $('#domain_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -619,9 +624,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#templates_domain_table').DataTable({ | ||||
| 			responsive : true, | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
| @@ -817,16 +826,26 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#mailbox_table').DataTable({ | ||||
| 			responsive : true, | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
|         url: "/api/v1/get/mailbox/reduced", | ||||
|         dataSrc: function(json){ | ||||
|           $.each(json, function (i, item) { | ||||
|             item.quota = item.quota_used + "/" + item.quota; | ||||
|             item.quota = { | ||||
|               sortBy: item.quota_used, | ||||
|               value: item.quota | ||||
|             } | ||||
|             item.quota.value = (item.quota.value == 0 ? "∞" : humanFileSize(item.quota.value)); | ||||
|             item.quota.value = humanFileSize(item.quota_used) + "/" + item.quota.value; | ||||
|  | ||||
|             item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); | ||||
|             item.last_mail_login = item.last_imap_login + '/' + item.last_pop3_login + '/' + item.last_smtp_login; | ||||
|             /* | ||||
| @@ -931,14 +950,10 @@ jQuery(function($){ | ||||
|           }, | ||||
|           { | ||||
|             title: lang.domain_quota, | ||||
|             data: 'quota', | ||||
|             data: 'quota.value', | ||||
|             responsivePriority: 8, | ||||
|             defaultContent: '', | ||||
|             render: function (data, type) { | ||||
|               data = data.split("/"); | ||||
|               var of_q = (data[1] == 0 ? "∞" : humanFileSize(data[1])); | ||||
|               return humanFileSize(data[0]) + " / " + of_q; | ||||
|             } | ||||
|             defaultContent: '',   | ||||
|             orderData: 23 | ||||
|           }, | ||||
|           { | ||||
|             title: lang.last_mail_login, | ||||
| @@ -1064,6 +1079,13 @@ jQuery(function($){ | ||||
|             responsivePriority: 6, | ||||
|             defaultContent: '' | ||||
|           }, | ||||
|           { | ||||
|             title: "", | ||||
|             data: 'quota.sortBy', | ||||
|             responsivePriority: 8, | ||||
|             defaultContent: '', | ||||
|             className: "d-none" | ||||
|           }, | ||||
|       ] | ||||
|     }); | ||||
|   } | ||||
| @@ -1075,9 +1097,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#templates_mbox_table').DataTable({ | ||||
| 			responsive : true, | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
| @@ -1287,8 +1313,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#resource_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -1413,8 +1444,13 @@ jQuery(function($){ | ||||
|     } | ||||
|      | ||||
|     $('#bcc_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
| @@ -1510,8 +1546,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#recipient_map_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
| @@ -1594,8 +1635,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#tls_policy_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
| @@ -1688,8 +1734,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#alias_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
| @@ -1829,8 +1880,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#aliasdomain_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -1911,8 +1967,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#sync_job_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
| @@ -2051,9 +2112,14 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     var table = $('#filter_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       autoWidth: false, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       order:[[2, 'desc']], | ||||
|       ajax: { | ||||
|   | ||||
| @@ -14,8 +14,13 @@ jQuery(function($){ | ||||
|   }); | ||||
|   function draw_quarantine_table() { | ||||
|     $('#quarantinetable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -129,9 +134,15 @@ jQuery(function($){ | ||||
|             title: lang.received, | ||||
|             data: 'created', | ||||
|             defaultContent: '', | ||||
|             render: function (data,type) { | ||||
|               var date = new Date(data ? data * 1000 : 0);  | ||||
|               return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"}); | ||||
|             createdCell: function(td, cellData) {     | ||||
|               $(td).attr({ | ||||
|                 "data-order": cellData, | ||||
|                 "data-sort": cellData | ||||
|               }); | ||||
|                | ||||
|               var date = new Date(cellData ? cellData * 1000 : 0);  | ||||
|               var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"}); | ||||
|               $(td).html(dateString); | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|   | ||||
| @@ -35,8 +35,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#queuetable').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
|   | ||||
| @@ -135,8 +135,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#tla_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -216,8 +221,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#sync_job_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -366,8 +376,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#app_passwd_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -456,8 +471,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#wl_policy_mailbox_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
| @@ -521,8 +541,13 @@ jQuery(function($){ | ||||
|     } | ||||
|  | ||||
|     $('#bl_policy_mailbox_table').DataTable({ | ||||
| 			responsive: true, | ||||
|       processing: true, | ||||
|       serverSide: false, | ||||
|       stateSave: true, | ||||
|       dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + | ||||
|            "tr" + | ||||
|            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", | ||||
|       language: lang_datatables, | ||||
|       ajax: { | ||||
|         type: "GET", | ||||
|   | ||||
| @@ -561,6 +561,15 @@ if (isset($_GET['query'])) { | ||||
|                   echo '{}'; | ||||
|                 } | ||||
|               break; | ||||
|               default: | ||||
|                 $password_complexity_rules = password_complexity('get'); | ||||
|                 if ($password_complexity_rules !== false) { | ||||
|                   process_get_return($password_complexity_rules); | ||||
|                 } | ||||
|                 else { | ||||
|                   echo '{}'; | ||||
|                 } | ||||
|               break; | ||||
|             } | ||||
|           break; | ||||
|  | ||||
| @@ -1544,14 +1553,15 @@ if (isset($_GET['query'])) { | ||||
|                   }  | ||||
|                   else if ($extra == "ip") { | ||||
|                     // get public ips | ||||
|                      | ||||
|                     $curl = curl_init(); | ||||
|                     curl_setopt($curl, CURLOPT_URL, 'http://ipv4.mailcow.email'); | ||||
|                     curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); | ||||
|                     curl_setopt($curl, CURLOPT_POST, 0); | ||||
|                     curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); | ||||
|                     curl_setopt($curl, CURLOPT_TIMEOUT, 15); | ||||
|                     curl_setopt($curl, CURLOPT_URL, 'http://ipv4.mailcow.email'); | ||||
|                     $ipv4 = curl_exec($curl); | ||||
|                     curl_setopt($curl, CURLOPT_URL, 'http://ipv6.mailcow.email'); | ||||
|                     curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); | ||||
|                     curl_setopt($curl, CURLOPT_POST, 0); | ||||
|                     $ipv6 = curl_exec($curl); | ||||
|                     $ips = array( | ||||
|                       "ipv4" => $ipv4, | ||||
| @@ -1913,6 +1923,9 @@ if (isset($_GET['query'])) { | ||||
|         case "ui_texts": | ||||
|           process_edit_return(customize('edit', 'ui_texts', $attr)); | ||||
|         break; | ||||
|         case "ip_check": | ||||
|           process_edit_return(customize('edit', 'ip_check', $attr)); | ||||
|         break; | ||||
|         case "self": | ||||
|           if ($_SESSION['mailcow_cc_role'] == "domainadmin") { | ||||
|             process_edit_return(domain_admin('edit', $attr)); | ||||
|   | ||||
| @@ -204,6 +204,9 @@ | ||||
|         "include_exclude": "Ein- und Ausschlüsse", | ||||
|         "include_exclude_info": "Ohne Auswahl werden <b>alle Mailboxen</b> adressiert.", | ||||
|         "includes": "Diese Empfänger einschließen", | ||||
|         "ip_check": "IP Check", | ||||
|         "ip_check_disabled": "IP check ist deaktiviert. Unter dem angegebenen Pfad kann es aktiviert werden<br> <strong>System > Konfiguration > Einstellungen > UI-Anpassung</strong>", | ||||
|         "ip_check_opt_in": "Opt-In für die Nutzung der Drittanbieter-Dienste <strong>ipv4.mailcow.email</strong> und <strong>ipv6.mailcow.email</strong> zur Auflösung externer IP-Adressen.", | ||||
|         "is_mx_based": "MX-basiert", | ||||
|         "last_applied": "Zuletzt angewendet", | ||||
|         "license_info": "Eine Lizenz ist nicht erforderlich, hilft jedoch der Entwicklung mailcows.<br><a href=\"https://www.servercow.de/mailcow#sal\" target=\"_blank\" alt=\"SAL Bestellung\">Hier kann die mailcow-GUID registriert werden.</a> Alternativ ist <a href=\"https://www.servercow.de/mailcow#support\" target=\"_blank\" alt=\"SAL Bestellung\">die Bestellung von Support-Paketen möglich</a>.", | ||||
| @@ -363,6 +366,7 @@ | ||||
|         "domain_not_empty": "Domain %s ist nicht leer", | ||||
|         "domain_not_found": "Domain %s nicht gefunden", | ||||
|         "domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein", | ||||
|         "extended_sender_acl_denied": "Keine Rechte zum setzen von externen Absenderadressen", | ||||
|         "extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig", | ||||
|         "extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain", | ||||
|         "fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s", | ||||
| @@ -494,6 +498,7 @@ | ||||
|         "current_time": "Systemzeit", | ||||
|         "disk_usage": "Festplattennutzung", | ||||
|         "docs": "Dokumente", | ||||
|         "error_show_ip": "konnte die öffentlichen IP Adressen nicht auflösen", | ||||
|         "external_logs": "Externe Logs", | ||||
|         "history_all_servers": "History (alle Server)", | ||||
|         "in_memory_logs": "In-memory Logs", | ||||
| @@ -506,6 +511,7 @@ | ||||
|         "online_users": "Benutzer online", | ||||
|         "restart_container": "Neustart", | ||||
|         "service": "Dienst", | ||||
|         "show_ip": "Zeige öffentliche IP", | ||||
|         "size": "Größe", | ||||
|         "solr_dead": "Solr startet, ist deaktiviert oder temporär nicht erreichbar.", | ||||
|         "solr_status": "Solr Status", | ||||
| @@ -1001,6 +1007,7 @@ | ||||
|         "forwarding_host_removed": "Weiterleitungs-Host %s wurde entfernt", | ||||
|         "global_filter_written": "Filterdatei wurde erfolgreich geschrieben", | ||||
|         "hash_deleted": "Hash wurde gelöscht", | ||||
|         "ip_check_opt_in_modified": "IP Check wurde erfolgreich gespeichert", | ||||
|         "item_deleted": "Objekt %s wurde entfernt", | ||||
|         "item_released": "Objekt %s freigegeben", | ||||
|         "items_deleted": "Objekt(e) %s wurde(n) erfolgreich entfernt", | ||||
|   | ||||
| @@ -206,6 +206,9 @@ | ||||
|         "include_exclude": "Include/Exclude", | ||||
|         "include_exclude_info": "By default - with no selection - <b>all mailboxes</b> are addressed", | ||||
|         "includes": "Include these recipients", | ||||
|         "ip_check": "IP Check", | ||||
|         "ip_check_disabled": "IP check is disabled. You can enable it under<br> <strong>System > Configuration > Options > Customize</strong>", | ||||
|         "ip_check_opt_in": "Opt-In for using third party service <strong>ipv4.mailcow.email</strong> and <strong>ipv6.mailcow.email</strong> to resolve external IP addresses.", | ||||
|         "is_mx_based": "MX based", | ||||
|         "last_applied": "Last applied", | ||||
|         "license_info": "A license is not required but helps further development.<br><a href=\"https://www.servercow.de/mailcow?lang=en#sal\" target=\"_blank\" alt=\"SAL order\">Register your GUID here</a> or <a href=\"https://www.servercow.de/mailcow?lang=en#support\" target=\"_blank\" alt=\"Support order\">buy support for your mailcow installation.</a>", | ||||
| @@ -363,6 +366,7 @@ | ||||
|         "domain_not_empty": "Cannot remove non-empty domain %s", | ||||
|         "domain_not_found": "Domain %s not found", | ||||
|         "domain_quota_m_in_use": "Domain quota must be greater or equal to %s MiB", | ||||
|         "extended_sender_acl_denied": "missing ACL to set external sender addresses", | ||||
|         "extra_acl_invalid": "External sender address \"%s\" is invalid", | ||||
|         "extra_acl_invalid_domain": "External sender \"%s\" uses an invalid domain", | ||||
|         "fido2_verification_failed": "FIDO2 verification failed: %s", | ||||
| @@ -497,6 +501,7 @@ | ||||
|         "current_time": "System Time", | ||||
|         "disk_usage": "Disk usage", | ||||
|         "docs": "Docs", | ||||
|         "error_show_ip": "Could not resolve the public IP addresses", | ||||
|         "external_logs": "External logs", | ||||
|         "history_all_servers": "History (all servers)", | ||||
|         "in_memory_logs": "In-memory logs", | ||||
| @@ -509,6 +514,7 @@ | ||||
|         "online_users": "Users online", | ||||
|         "restart_container": "Restart", | ||||
|         "service": "Service", | ||||
|         "show_ip": "Show public IP", | ||||
|         "size": "Size", | ||||
|         "solr_dead": "Solr is starting, disabled or died.", | ||||
|         "solr_status": "Solr status", | ||||
| @@ -1013,6 +1019,7 @@ | ||||
|         "forwarding_host_removed": "Forwarding host %s has been removed", | ||||
|         "global_filter_written": "Filter was successfully written to file", | ||||
|         "hash_deleted": "Hash deleted", | ||||
|         "ip_check_opt_in_modified": "IP check was saved successfully", | ||||
|         "item_deleted": "Item %s successfully deleted", | ||||
|         "item_released": "Item %s released", | ||||
|         "items_deleted": "Item %s successfully deleted", | ||||
|   | ||||
| @@ -33,6 +33,20 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|       {% endif %} | ||||
|       <legend style="padding-top:20px" unselectable="on">{{ lang.admin.ip_check }}</legend><hr /> | ||||
|       <div id="ip_check"> | ||||
|         <form class="form" data-id="ip_check" role="form" method="post"> | ||||
|           <div class="mb-4"> | ||||
|             <input class="form-check-input" type="checkbox" value="1" name="ip_check_opt_in" id="ip_check_opt_in" {% if ip_check == 1 %}checked{% endif %}> | ||||
|             <label class="form-check-label" for="ip_check_opt_in"> | ||||
|               {{ lang.admin.ip_check_opt_in|raw }} | ||||
|             </label> | ||||
|           </div> | ||||
|           <p><div class="btn-group"> | ||||
|             <button class="btn btn-sm btn-xs-half d-block d-sm-inline btn-success" data-action="edit_selected" data-item="admin" data-id="ip_check" data-reload="no" data-api-url='edit/ip_check' data-api-attr='{}' href="#"><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button> | ||||
|           </div></p> | ||||
|         </form> | ||||
|       </div> | ||||
|       <legend>{{ lang.admin.app_links }}</legend><hr /> | ||||
|       <p class="text-muted">{{ lang.admin.merged_vars_hint|raw }}</p> | ||||
|       <form class="form-inline" data-id="app_links" role="form" method="post"> | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
|             </div> | ||||
|             <div class="mb-4"> | ||||
|               <label for="rlyhost_password">{{ lang.admin.password }}</label> | ||||
|               <input class="form-control" id="rlyhost_password" name="password"> | ||||
|               <input class="form-control" id="rlyhost_password" name="password" type="password"> | ||||
|             </div> | ||||
|             <button class="btn btn-sm d-block d-sm-inline btn-success" data-action="add_item" data-id="rlyhost" data-api-url='add/relayhost' data-api-attr='{}' href="#"><i class="bi bi-plus-lg"></i> {{ lang.admin.add }}</button> | ||||
|           </form> | ||||
| @@ -86,7 +86,7 @@ | ||||
|             </div> | ||||
|             <div class="mb-4"> | ||||
|               <label for="transport_password">{{ lang.admin.password }}</label> | ||||
|               <input class="form-control" id="transport_password" name="password"> | ||||
|               <input class="form-control" id="transport_password" name="password" type="password"> | ||||
|             </div> | ||||
|             <div class="mb-2"> | ||||
|               <label> | ||||
|   | ||||
| @@ -52,8 +52,18 @@ | ||||
|                       <tr> | ||||
|                         <td>IPs</td> | ||||
|                         <td class="text-break"> | ||||
|                           <span class="d-block" id="host_ipv4">-</span> | ||||
|                           <span class="d-block" id="host_ipv6">-</span> | ||||
|                           {% if ip_check == 1 %} | ||||
|                             <span class="d-none" id="host_ipv4">-</span> | ||||
|                             <span class="d-none mb-2" id="host_ipv6">-</span> | ||||
|                             <button class="d-block btn btn-primary btn-sm" id="host_show_ip"> | ||||
|                               <span class="text">{{ lang.debug.show_ip }}</span> | ||||
|                               <div class="spinner-border spinner-border-sm d-none" role="status"> | ||||
|                                 <span class="visually-hidden">Loading...</span> | ||||
|                               </div>   | ||||
|                             </button> | ||||
|                           {% else %} | ||||
|                             <span class="d-block">{{ lang.admin.ip_check_disabled|raw }}</span> | ||||
|                           {% endif %} | ||||
|                         </td> | ||||
|                       </tr> | ||||
|                       <tr> | ||||
|   | ||||
| @@ -200,8 +200,10 @@ | ||||
|           {% if sender_acl_handles.external_sender_aliases %} | ||||
|             {% set ext_sender_acl = sender_acl_handles.external_sender_aliases|join(', ') %} | ||||
|           {% endif %} | ||||
|           <input type="text" class="form-control" name="extended_sender_acl" value="{{ ext_sender_acl }}" placeholder="user1@example.com, user2@example.org, @example.com, ..."> | ||||
|           <small class="text-muted">{{ lang.edit.extended_sender_acl_info|raw }}</small> | ||||
|           {% if acl.extend_sender_acl and acl.extend_sender_acl == 1 %} | ||||
|             <input type="text" class="form-control" name="extended_sender_acl" value="{{ ext_sender_acl }}" placeholder="user1@example.com, user2@example.org, @example.com, ..."> | ||||
|             <small class="text-muted">{{ lang.edit.extended_sender_acl_info|raw }}</small> | ||||
|           {% endif %} | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="row"> | ||||
|   | ||||
| @@ -4,18 +4,18 @@ | ||||
| <div id="mail-content" class="responsive-tabs"> | ||||
|   <ul class="nav nav-tabs" role="tablist"> | ||||
|     <li class="nav-item dropdown" role="presentation"> | ||||
|     <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.domains }}</a> | ||||
|     <ul class="dropdown-menu"> | ||||
|       <li><button class="dropdown-item" aria-selected="false" aria-controls="tab-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domains">{{ lang.mailbox.domains }}</button></li> | ||||
|       <li><button class="dropdown-item {% if mailcow_cc_role != 'admin' %} d-none{% endif %}" aria-selected="false" aria-controls="tab-templates-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-templates-domains">{{ lang.mailbox.templates }}</button></li> | ||||
|     </ul> | ||||
|       <a class="nav-link dropdown-toggle active" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.domains }}</a> | ||||
|       <ul class="dropdown-menu"> | ||||
|         <li><button class="dropdown-item" aria-selected="false" aria-controls="tab-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domains">{{ lang.mailbox.domains }}</button></li> | ||||
|         <li><button class="dropdown-item {% if mailcow_cc_role != 'admin' %} d-none{% endif %}" aria-selected="false" aria-controls="tab-templates-domains" role="tab" data-bs-toggle="tab" data-bs-target="#tab-templates-domains">{{ lang.mailbox.templates }}</button></li> | ||||
|       </ul> | ||||
|     </li> | ||||
|     <li class="nav-item dropdown" role="presentation"> | ||||
|     <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.mailboxes }}</a> | ||||
|     <ul class="dropdown-menu"> | ||||
|       <li><button class="dropdown-item" aria-selected="false" aria-controls="tab-mailboxes" role="tab" data-bs-toggle="tab" data-bs-target="#tab-mailboxes">{{ lang.mailbox.mailboxes }}</button></li> | ||||
|       <li><button class="dropdown-item {% if mailcow_cc_role != 'admin' %} d-none{% endif %}" aria-selected="false" aria-controls="tab-templates-mbox" role="tab" data-bs-toggle="tab" data-bs-target="#tab-templates-mbox">{{ lang.mailbox.templates }}</button></li> | ||||
|     </ul> | ||||
|       <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.mailboxes }}</a> | ||||
|       <ul class="dropdown-menu"> | ||||
|         <li><button class="dropdown-item" aria-selected="false" aria-controls="tab-mailboxes" role="tab" data-bs-toggle="tab" data-bs-target="#tab-mailboxes">{{ lang.mailbox.mailboxes }}</button></li> | ||||
|         <li><button class="dropdown-item {% if mailcow_cc_role != 'admin' %} d-none{% endif %}" aria-selected="false" aria-controls="tab-templates-mbox" role="tab" data-bs-toggle="tab" data-bs-target="#tab-templates-mbox">{{ lang.mailbox.templates }}</button></li> | ||||
|       </ul> | ||||
|     </li> | ||||
|     <li class="nav-item" role="presentation"><button class="nav-link" aria-controls="tab-resources" role="tab" data-bs-toggle="tab" data-bs-target="#tab-resources">{{ lang.mailbox.resources }}</button></li> | ||||
|     <li class="nav-item dropdown"> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user