Browse Source

[Web] Allow ratelimit time frame "day"; Allow to create announcements

andryyy 5 years ago
parent
commit
767ae65946

+ 0 - 7
data/Dockerfiles/postfix/smtp_last_login.sh

@@ -1,7 +0,0 @@
-#!/bin/bash
-echo action=OK
-exit
-while read QUERY; do
-	logger -t last_login -p mail.info "$QUERY"
-done
-echo action=OK

+ 64 - 50
data/web/admin.php

@@ -117,10 +117,14 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
           <span style="font-size:12px" class="arrow rotate glyphicon glyphicon-menu-down"></span> API
           <span style="font-size:12px" class="arrow rotate glyphicon glyphicon-menu-down"></span> API
         </legend>
         </legend>
         <div id="admin_api" class="collapse">
         <div id="admin_api" class="collapse">
+        <div class="row">
         <?php
         <?php
         $api_ro = admin_api('ro', 'get');
         $api_ro = admin_api('ro', 'get');
         $api_rw = admin_api('rw', 'get');
         $api_rw = admin_api('rw', 'get');
         ?>
         ?>
+          <div class="col-lg-12">
+          <p class="help-block"><?=$lang['admin']['api_info'];?></p>
+          </div>
           <div class="col-lg-6">
           <div class="col-lg-6">
             <div class="panel panel-default">
             <div class="panel panel-default">
               <div class="panel-heading">
               <div class="panel-heading">
@@ -156,7 +160,6 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
                     </div>
                     </div>
                     <div class="form-group">
                     <div class="form-group">
                       <div class="col-sm-offset-3 col-sm-9">
                       <div class="col-sm-offset-3 col-sm-9">
-                        <p class="help-block"><?=$lang['admin']['api_info'];?></p>
                         <div class="btn-group">
                         <div class="btn-group">
                           <button class="btn btn-sm btn-success" name="admin_api[ro]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
                           <button class="btn btn-sm btn-success" name="admin_api[ro]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
                           <button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[ro]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
                           <button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[ro]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
@@ -168,52 +171,52 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
             </div>
             </div>
           </div>
           </div>
           <div class="col-lg-6">
           <div class="col-lg-6">
-          <div class="panel panel-default">
-            <div class="panel-heading">
-              <h4 class="panel-title">⇄ Read-Write Access</h4>
-            </div>
-              <div class="panel-body">
-                <form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
-                  <div class="form-group">
-                    <label class="control-label col-sm-3" for="allow_from_rw"><?=$lang['admin']['api_allow_from'];?>:</label>
-                    <div class="col-sm-9">
-                      <textarea class="form-control textarea-code" rows="7" name="allow_from" id="allow_from_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_rw['allow_from']);?></textarea>
+            <div class="panel panel-default">
+              <div class="panel-heading">
+                <h4 class="panel-title">⇄ Read-Write Access</h4>
+              </div>
+                <div class="panel-body">
+                  <form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
+                    <div class="form-group">
+                      <label class="control-label col-sm-3" for="allow_from_rw"><?=$lang['admin']['api_allow_from'];?>:</label>
+                      <div class="col-sm-9">
+                        <textarea class="form-control textarea-code" rows="7" name="allow_from" id="allow_from_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_rw['allow_from']);?></textarea>
+                      </div>
                     </div>
                     </div>
-                  </div>
-                  <div class="form-group">
-                    <div class="col-sm-offset-3 col-sm-9">
-                      <label>
-                        <input type="checkbox" name="skip_ip_check" id="skip_ip_check_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['api_skip_ip_check'];?>
-                      </label>
+                    <div class="form-group">
+                      <div class="col-sm-offset-3 col-sm-9">
+                        <label>
+                          <input type="checkbox" name="skip_ip_check" id="skip_ip_check_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['api_skip_ip_check'];?>
+                        </label>
+                      </div>
                     </div>
                     </div>
-                  </div>
-                  <div class="form-group">
-                    <label class="control-label col-sm-3" for="admin_api_key"><?=$lang['admin']['api_key'];?>:</label>
-                    <div class="col-sm-9">
-                      <pre><?=(empty(htmlspecialchars($api_rw['api_key']))) ? '-' : htmlspecialchars($api_rw['api_key']);?></pre>
+                    <div class="form-group">
+                      <label class="control-label col-sm-3" for="admin_api_key"><?=$lang['admin']['api_key'];?>:</label>
+                      <div class="col-sm-9">
+                        <pre><?=(empty(htmlspecialchars($api_rw['api_key']))) ? '-' : htmlspecialchars($api_rw['api_key']);?></pre>
+                      </div>
                     </div>
                     </div>
-                  </div>
-                  <div class="form-group">
-                    <div class="col-sm-offset-3 col-sm-9">
-                      <label>
-                        <input type="checkbox" name="active" <?=($api_rw['active'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['activate_api'];?>
-                      </label>
+                    <div class="form-group">
+                      <div class="col-sm-offset-3 col-sm-9">
+                        <label>
+                          <input type="checkbox" name="active" <?=($api_rw['active'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['activate_api'];?>
+                        </label>
+                      </div>
                     </div>
                     </div>
-                  </div>
-                  <div class="form-group">
-                    <div class="col-sm-offset-3 col-sm-9">
-                      <p class="help-block"><?=$lang['admin']['api_info'];?></p>
-                      <div class="btn-group">
-                        <button class="btn btn-sm btn-success" name="admin_api[rw]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
-                        <button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[rw]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
+                    <div class="form-group">
+                      <div class="col-sm-offset-3 col-sm-9">
+                        <div class="btn-group">
+                          <button class="btn btn-sm btn-success" name="admin_api[rw]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+                          <button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[rw]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
+                        </div>
                       </div>
                       </div>
                     </div>
                     </div>
-                  </div>
-                </form>
-              </div>
-          </div>
+                  </form>
+                </div>
+            </div>
           </div>
           </div>
         </div>
         </div>
+        </div>
       </div>
       </div>
     </div>
     </div>
 
 
@@ -294,7 +297,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
             </div>
             </div>
             <div class="form-group">
             <div class="form-group">
               <div class="col-sm-offset-3 col-sm-9">
               <div class="col-sm-offset-3 col-sm-9">
-                <button type="submit" class="btn btn-default" id="rspamd_ui" name="rspamd_ui" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+                <button type="submit" class="btn btn-sm btn-success" id="rspamd_ui" name="rspamd_ui" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
               </div>
               </div>
             </div>
             </div>
           </form>
           </form>
@@ -703,7 +706,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
             <textarea class="form-control" name="blacklist" rows="5"><?=$f2b_data['blacklist'];?></textarea>
             <textarea class="form-control" name="blacklist" rows="5"><?=$f2b_data['blacklist'];?></textarea>
           </div>
           </div>
           <div class="btn-group">
           <div class="btn-group">
-            <button class="btn btn-default" data-action="edit_selected" data-item="self" data-id="f2b" data-api-url='edit/fail2ban' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+            <button class="btn btn-success" data-action="edit_selected" data-item="self" data-id="f2b" data-api-url='edit/fail2ban' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
             <a href="#" role="button" class="btn btn-default" data-toggle="modal" data-container="netfilter-mailcow" data-target="#RestartContainer"><span class="glyphicon glyphicon-refresh"></span> <?= $lang['header']['restart_netfilter']; ?></a>
             <a href="#" role="button" class="btn btn-default" data-toggle="modal" data-container="netfilter-mailcow" data-target="#RestartContainer"><span class="glyphicon glyphicon-refresh"></span> <?= $lang['header']['restart_netfilter']; ?></a>
           </div>
           </div>
         </form>
         </form>
@@ -840,7 +843,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
               </div>
               </div>
             </div>
             </div>
           </div>
           </div>
-          <button class="btn btn-default" data-action="edit_selected" data-item="self" data-id="quarantine" data-api-url='edit/quarantine' data-api-attr='{"action":"settings"}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+          <button class="btn btn-sm btn-success" data-action="edit_selected" data-item="self" data-id="quarantine" data-api-url='edit/quarantine' data-api-attr='{"action":"settings"}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
         </form>
         </form>
       </div>
       </div>
     </div>
     </div>
@@ -885,7 +888,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
                 data-item="quota_notification"
                 data-item="quota_notification"
                 data-id="quota_notification"
                 data-id="quota_notification"
                 data-api-url='edit/quota_notification'
                 data-api-url='edit/quota_notification'
-                data-api-attr='{}'><?=$lang['user']['save_changes'];?></a>
+                data-api-attr='{}'><span class="glyphicon glyphicon-check"></span> <?=$lang['user']['save_changes'];?></a>
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
@@ -965,8 +968,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
                         <input type="checkbox" name="active" value="1" <?=($rsetting_details['active_int'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['active'];?>
                         <input type="checkbox" name="active" value="1" <?=($rsetting_details['active_int'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['active'];?>
                       </label>
                       </label>
                     </div>
                     </div>
-                    <button class="btn btn-default" data-action="edit_selected" data-item="<?=$rsetting_details['id'];?>" data-id="rsettings" data-api-url='edit/rsetting' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
-                    <button class="btn btn-danger" data-action="delete_selected" data-item="<?=$rsetting_details['id'];?>" data-id="rsettings" data-api-url="delete/rsetting" data-api-attr='{}' href="#"><?=$lang['admin']['remove'];?></button>
+                    <button class="btn btn-sm btn-success" data-action="edit_selected" data-item="<?=$rsetting_details['id'];?>" data-id="rsettings" data-api-url='edit/rsetting' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+                    <button class="btn btn-sm btn-danger" data-action="delete_selected" data-item="<?=$rsetting_details['id'];?>" data-id="rsettings" data-api-url="delete/rsetting" data-api-attr='{}' href="#"><?=$lang['admin']['remove'];?></button>
                   </form>
                   </form>
                 </div>
                 </div>
                 <?php
                 <?php
@@ -1049,15 +1052,13 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
             ?>
             ?>
           </table>
           </table>
           <p><div class="btn-group">
           <p><div class="btn-group">
-            <button class="btn btn-sm btn-default" data-action="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+            <button class="btn btn-sm btn-success" data-action="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
             <button class="btn btn-sm btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button>
             <button class="btn btn-sm btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button>
           </div></p>
           </div></p>
         </form>
         </form>
         <legend data-target="#ui_texts" style="padding-top:20px" unselectable="on"><?=$lang['admin']['ui_texts'];?></legend>
         <legend data-target="#ui_texts" style="padding-top:20px" unselectable="on"><?=$lang['admin']['ui_texts'];?></legend>
         <div id="ui_texts">
         <div id="ui_texts">
-        <?php
-        $ui_texts = customize('get', 'ui_texts');
-        ?>
+        <?php $ui_texts = customize('get', 'ui_texts'); ?>
           <form class="form" data-id="uitexts" role="form" method="post">
           <form class="form" data-id="uitexts" role="form" method="post">
             <div class="form-group">
             <div class="form-group">
               <label for="title_name"><?=$lang['admin']['title_name'];?>:</label>
               <label for="title_name"><?=$lang['admin']['title_name'];?>:</label>
@@ -1075,11 +1076,24 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
               <label for="help_text"><?=$lang['admin']['help_text'];?>:</label>
               <label for="help_text"><?=$lang['admin']['help_text'];?>:</label>
               <textarea class="form-control" id="help_text" name="help_text" rows="7"><?=$ui_texts['help_text'];?></textarea>
               <textarea class="form-control" id="help_text" name="help_text" rows="7"><?=$ui_texts['help_text'];?></textarea>
             </div>
             </div>
+            <hr>
+            <div class="form-group">
+              <p class="help-block"><?=$lang['admin']['ui_header_announcement_help'];?></p>
+              <label for="ui_announcement_type"><?=$lang['admin']['ui_header_announcement'];?>:</label>
+              <p><select multiple data-width="100%" name="ui_announcement_type" class="selectpicker show-tick" data-max-options="1" title="<?=$lang['admin']['ui_header_announcement_select'];?>">
+                <option <?=($ui_texts['ui_announcement_type'] == 'info') ? 'selected' : null;?> value="info"><?=$lang['admin']['ui_header_announcement_type_info'];?></option>
+                <option <?=($ui_texts['ui_announcement_type'] == 'warning') ? 'selected' : null;?> value="warning"><?=$lang['admin']['ui_header_announcement_type_warning'];?></option>
+                <option <?=($ui_texts['ui_announcement_type'] == 'danger') ? 'selected' : null;?> value="danger"><?=$lang['admin']['ui_header_announcement_type_danger'];?></option>
+              </select></p>
+              <p><textarea class="form-control" id="ui_announcement_text" name="ui_announcement_text" rows="7"><?=$ui_texts['ui_announcement_text'];?></textarea></p>
+              <p><input type="checkbox" name="ui_announcement_active" <?=($ui_texts['ui_announcement_active'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['ui_header_announcement_active'];?></p>
+            </div>
+            <hr>
             <div class="form-group">
             <div class="form-group">
               <label for="ui_footer"><?=$lang['admin']['ui_footer'];?>:</label>
               <label for="ui_footer"><?=$lang['admin']['ui_footer'];?>:</label>
               <textarea class="form-control" id="ui_footer" name="ui_footer" rows="7"><?=$ui_texts['ui_footer'];?></textarea>
               <textarea class="form-control" id="ui_footer" name="ui_footer" rows="7"><?=$ui_texts['ui_footer'];?></textarea>
             </div>
             </div>
-            <button class="btn btn-default" data-action="edit_selected" data-item="ui" data-id="uitexts" data-api-url='edit/ui_texts' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+            <button class="btn btn-sm btn-success" data-action="edit_selected" data-item="ui" data-id="uitexts" data-api-url='edit/ui_texts' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
           </form>
           </form>
         </div>
         </div>
       </div>
       </div>

+ 3 - 0
data/web/edit.php

@@ -382,6 +382,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
             <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
             <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
             <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
             <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
             <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
             <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
+            <option value="d" <?=(isset($rl['frame']) && $rl['frame'] == 'd') ? 'selected' : null;?>>msgs / day</option>
           </select>
           </select>
         </div>
         </div>
         <div class="form-group">
         <div class="form-group">
@@ -537,6 +538,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
                 <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
                 <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
                 <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
                 <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
                 <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
                 <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
+                <option value="d" <?=(isset($rl['frame']) && $rl['frame'] == 'd') ? 'selected' : null;?>>msgs / day</option>
               </select>
               </select>
             </div>
             </div>
             <div class="form-group">
             <div class="form-group">
@@ -833,6 +835,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
                   <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
                   <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
                   <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
                   <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
                   <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
                   <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
+                  <option value="d" <?=(isset($rl['frame']) && $rl['frame'] == 'd') ? 'selected' : null;?>>msgs / day</option>
                 </select>
                 </select>
               </div>
               </div>
               <div class="form-group">
               <div class="form-group">

+ 3 - 7
data/web/inc/footer.inc.php

@@ -182,13 +182,9 @@ $(document).ready(function() {
 </script>
 </script>
 
 
   <div class="container footer">
   <div class="container footer">
-    <?php
-    if (!empty($UI_TEXTS['ui_footer'])):
-    ?>
-     <hr><?=$UI_TEXTS['ui_footer'];?>
-    <?php
-    endif;
-    ?>
+  <?php if (!empty($UI_TEXTS['ui_footer'])) { ?>
+   <hr><?=$UI_TEXTS['ui_footer'];?>
+  <?php } ?>
   </div>
   </div>
 </body>
 </body>
 </html>
 </html>

+ 9 - 0
data/web/inc/functions.customize.inc.php

@@ -113,12 +113,18 @@ function customize($_action, $_item, $_data = null) {
           $apps_name = $_data['apps_name'];
           $apps_name = $_data['apps_name'];
           $help_text = $_data['help_text'];
           $help_text = $_data['help_text'];
           $ui_footer = $_data['ui_footer'];
           $ui_footer = $_data['ui_footer'];
+          $ui_announcement_text = $_data['ui_announcement_text'];
+          $ui_announcement_type = (in_array($_data['ui_announcement_type'], array('info', 'warning', 'danger'))) ? $_data['ui_announcement_type'] : false;
+          $ui_announcement_active = (!empty($_data['ui_announcement_active']) ? 1 : 0);
           try {
           try {
             $redis->set('TITLE_NAME', htmlspecialchars($title_name));
             $redis->set('TITLE_NAME', htmlspecialchars($title_name));
             $redis->set('MAIN_NAME', htmlspecialchars($main_name));
             $redis->set('MAIN_NAME', htmlspecialchars($main_name));
             $redis->set('APPS_NAME', htmlspecialchars($apps_name));
             $redis->set('APPS_NAME', htmlspecialchars($apps_name));
             $redis->set('HELP_TEXT', $help_text);
             $redis->set('HELP_TEXT', $help_text);
             $redis->set('UI_FOOTER', $ui_footer);
             $redis->set('UI_FOOTER', $ui_footer);
+            $redis->set('UI_ANNOUNCEMENT_TEXT', $ui_announcement_text);
+            $redis->set('UI_ANNOUNCEMENT_TYPE', $ui_announcement_type);
+            $redis->set('UI_ANNOUNCEMENT_ACTIVE', $ui_announcement_active);
           }
           }
           catch (RedisException $e) {
           catch (RedisException $e) {
             $_SESSION['return'][] = array(
             $_SESSION['return'][] = array(
@@ -208,6 +214,9 @@ function customize($_action, $_item, $_data = null) {
               $redis->del('UI_IMPRESS');
               $redis->del('UI_IMPRESS');
             }
             }
             $data['ui_footer'] = ($ui_footer = $redis->get('UI_FOOTER')) ? $ui_footer : false;
             $data['ui_footer'] = ($ui_footer = $redis->get('UI_FOOTER')) ? $ui_footer : false;
+            $data['ui_announcement_text'] = ($ui_announcement_text = $redis->get('UI_ANNOUNCEMENT_TEXT')) ? $ui_announcement_text : false;
+            $data['ui_announcement_type'] = ($ui_announcement_type = $redis->get('UI_ANNOUNCEMENT_TYPE')) ? $ui_announcement_type : false;
+            $data['ui_announcement_active'] = ($redis->get('UI_ANNOUNCEMENT_ACTIVE') == 1) ? 1 : 0;
             return $data;
             return $data;
           }
           }
           catch (RedisException $e) {
           catch (RedisException $e) {

+ 2 - 2
data/web/inc/functions.ratelimit.inc.php

@@ -25,7 +25,7 @@ function ratelimit($_action, $_scope, $_data = null) {
           foreach ($objects as $object) {
           foreach ($objects as $object) {
             $rl_value = intval($_data['rl_value']);
             $rl_value = intval($_data['rl_value']);
             $rl_frame = $_data['rl_frame'];
             $rl_frame = $_data['rl_frame'];
-            if (!in_array($rl_frame, array('s', 'm', 'h'))) {
+            if (!in_array($rl_frame, array('s', 'm', 'h', 'd'))) {
               $_SESSION['return'][] = array(
               $_SESSION['return'][] = array(
                 'type' => 'danger',
                 'type' => 'danger',
                 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
                 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
@@ -85,7 +85,7 @@ function ratelimit($_action, $_scope, $_data = null) {
           foreach ($objects as $object) {
           foreach ($objects as $object) {
             $rl_value = intval($_data['rl_value']);
             $rl_value = intval($_data['rl_value']);
             $rl_frame = $_data['rl_frame'];
             $rl_frame = $_data['rl_frame'];
-            if (!in_array($rl_frame, array('s', 'm', 'h'))) {
+            if (!in_array($rl_frame, array('s', 'm', 'h', 'd'))) {
               $_SESSION['return'][] = array(
               $_SESSION['return'][] = array(
                 'type' => 'danger',
                 'type' => 'danger',
                 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
                 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),

+ 8 - 0
data/web/inc/header.inc.php

@@ -138,3 +138,11 @@
     </div><!--/.container-fluid -->
     </div><!--/.container-fluid -->
   </nav>
   </nav>
   <form action="/" method="post" id="logout"><input type="hidden" name="logout"></form>
   <form action="/" method="post" id="logout"><input type="hidden" name="logout"></form>
+  <?php if (!empty($UI_TEXTS['ui_announcement_text']) &&
+    in_array($UI_TEXTS['ui_announcement_type'], array('info', 'warning', 'danger')) &&
+    $UI_TEXTS['ui_announcement_active'] == 1 &&
+    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) != '/') { ?>
+  <div class="container">
+    <div class="alert alert-<?=$UI_TEXTS['ui_announcement_type'];?>"><?=$UI_TEXTS['ui_announcement_text'];?></div>
+  </div>
+  <?php } ?>

+ 3 - 0
data/web/index.php

@@ -32,6 +32,9 @@ $_SESSION['index_query_string'] = $_SERVER['QUERY_STRING'];
         <div class="panel-heading"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?= $lang['login']['login']; ?></div>
         <div class="panel-heading"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?= $lang['login']['login']; ?></div>
         <div class="panel-body">
         <div class="panel-body">
           <div class="text-center mailcow-logo"><img src="<?=($main_logo = customize('get', 'main_logo')) ? $main_logo : '/img/cow_mailcow.svg';?>" alt="mailcow"></div>
           <div class="text-center mailcow-logo"><img src="<?=($main_logo = customize('get', 'main_logo')) ? $main_logo : '/img/cow_mailcow.svg';?>" alt="mailcow"></div>
+          <?php if (!empty($UI_TEXTS['ui_announcement_text']) && in_array($UI_TEXTS['ui_announcement_type'], array('info', 'warning', 'danger')) && $UI_TEXTS['ui_announcement_active'] == 1) { ?>
+          <div class="alert alert-<?=$UI_TEXTS['ui_announcement_type'];?>"><?=$UI_TEXTS['ui_announcement_text'];?></div>
+          <?php } ?>
           <legend><?= isset($_SESSION['oauth2_request']) ? $lang['oauth2']['authorize_app'] : $UI_TEXTS['main_name'];?></legend>
           <legend><?= isset($_SESSION['oauth2_request']) ? $lang['oauth2']['authorize_app'] : $UI_TEXTS['main_name'];?></legend>
             <?php
             <?php
             if (strpos($_SESSION['index_query_string'], 'mobileconfig') !== false) {
             if (strpos($_SESSION['index_query_string'], 'mobileconfig') !== false) {

+ 9 - 0
data/web/lang/lang.de.json

@@ -287,6 +287,15 @@
         "transport_maps": "Transport Maps",
         "transport_maps": "Transport Maps",
         "transports_hint": "→ Transport Maps <b>überwiegen</b> senderabhängige Transport Maps.<br>\r\n→ Transport Maps ignorieren Mailbox-Einstellungen für ausgehende Verschlüsselung. Eine serverweite TLS-Richtlinie wird jedoch angewendet.<br>\r\n→ Der Transport erfolgt immer via \"smtp:\".<br>\r\n→ Adressen, die mit \"/localhost$/\" übereinstimmen, werden immer via \"local:\" transportiert, daher sind sie von einer Zieldefinition \"*\" ausgeschlossen.<br>\r\n→ Die Authentifizierung wird anhand des \"Next hop\" Parameters ermittelt. Hierbei würde bei einem beispielhaften Wert \"[host]:25\" immer zuerst \"host\" abfragt und <b>erst im Anschluss</b> \"[host]:25\". Dieses Verhalten schließt die <b>gleichzeitige Verwendung</b> von Einträgen der Art \"host\" sowie \"[host]:25\" aus.",
         "transports_hint": "→ Transport Maps <b>überwiegen</b> senderabhängige Transport Maps.<br>\r\n→ Transport Maps ignorieren Mailbox-Einstellungen für ausgehende Verschlüsselung. Eine serverweite TLS-Richtlinie wird jedoch angewendet.<br>\r\n→ Der Transport erfolgt immer via \"smtp:\".<br>\r\n→ Adressen, die mit \"/localhost$/\" übereinstimmen, werden immer via \"local:\" transportiert, daher sind sie von einer Zieldefinition \"*\" ausgeschlossen.<br>\r\n→ Die Authentifizierung wird anhand des \"Next hop\" Parameters ermittelt. Hierbei würde bei einem beispielhaften Wert \"[host]:25\" immer zuerst \"host\" abfragt und <b>erst im Anschluss</b> \"[host]:25\". Dieses Verhalten schließt die <b>gleichzeitige Verwendung</b> von Einträgen der Art \"host\" sowie \"[host]:25\" aus.",
         "ui_footer": "Footer (HTML zulässig)",
         "ui_footer": "Footer (HTML zulässig)",
+        "ui_header_announcement": "Ankündigungen",
+        "ui_header_announcement_active": "Ankündigung aktivieren",
+        "ui_header_announcement_content": "Text (HTML zulässig)",
+        "ui_header_announcement_help": "Die Ankündigungsbox erzeugt einen deutlichen Hinweis für alle Benutzer und auf der Login-Seite der UI.",
+        "ui_header_announcement_select": "Ankündigungstyp auswählen",
+        "ui_header_announcement_type": "Typ",
+        "ui_header_announcement_type_info": "Info",
+        "ui_header_announcement_type_warning": "Wichtig",
+        "ui_header_announcement_type_danger": "Sehr wichtig",
         "ui_texts": "UI Label und Texte",
         "ui_texts": "UI Label und Texte",
         "unban_pending": "ausstehend",
         "unban_pending": "ausstehend",
         "unchanged_if_empty": "Unverändert, wenn leer",
         "unchanged_if_empty": "Unverändert, wenn leer",

+ 9 - 0
data/web/lang/lang.en.json

@@ -286,6 +286,15 @@
         "transport_maps": "Transport Maps",
         "transport_maps": "Transport Maps",
         "transports_hint": "→ A transport map entry <b>overrules</b> a sender-dependent transport map</b>.<br>\r\n→ Outbound TLS policy settings per-user are ignored and can only be enforced by TLS policy map entries.<br>\r\n→ The transport service for defined transports is always \"smtp:\".<br>\r\n→ Addresses matching \"/localhost$/\" will always be transported via \"local:\", therefore a \"*\" destination will not apply to those addresses.<br>\r\n→ To determine credentials for an exemplary next hop \"[host]:25\", Postfix <b>always</b> queries for \"host\" before searching for \"[host]:25\". This behavior makes it impossible to use \"host\" and \"[host]:25\" at the same time.",
         "transports_hint": "→ A transport map entry <b>overrules</b> a sender-dependent transport map</b>.<br>\r\n→ Outbound TLS policy settings per-user are ignored and can only be enforced by TLS policy map entries.<br>\r\n→ The transport service for defined transports is always \"smtp:\".<br>\r\n→ Addresses matching \"/localhost$/\" will always be transported via \"local:\", therefore a \"*\" destination will not apply to those addresses.<br>\r\n→ To determine credentials for an exemplary next hop \"[host]:25\", Postfix <b>always</b> queries for \"host\" before searching for \"[host]:25\". This behavior makes it impossible to use \"host\" and \"[host]:25\" at the same time.",
         "ui_footer": "Footer (HTML allowed)",
         "ui_footer": "Footer (HTML allowed)",
+        "ui_header_announcement": "Announcements",
+        "ui_header_announcement_active": "Set announcement active",
+        "ui_header_announcement_content": "Text (HTML allowed)",
+        "ui_header_announcement_help": "The announcement is visible for all logged in users and on the login screen of the UI.",
+        "ui_header_announcement_select": "Select announcement type",
+        "ui_header_announcement_type": "Type",
+        "ui_header_announcement_type_info": "Info",
+        "ui_header_announcement_type_warning": "Important",
+        "ui_header_announcement_type_danger": "Very important",
         "ui_texts": "UI labels and texts",
         "ui_texts": "UI labels and texts",
         "unban_pending": "unban pending",
         "unban_pending": "unban pending",
         "unchanged_if_empty": "If unchanged leave blank",
         "unchanged_if_empty": "If unchanged leave blank",

+ 2 - 0
data/web/modals/mailbox.php

@@ -160,6 +160,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
               <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
               <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
               <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
               <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
               <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
               <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
+              <option value="d" <?=(isset($rl['frame']) && $rl['frame'] == 'd') ? 'selected' : null;?>>msgs / day</option>
             </select>
             </select>
             </div>
             </div>
           </div>
           </div>
@@ -375,6 +376,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
               <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
               <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
               <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
               <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
               <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
               <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
+              <option value="d" <?=(isset($rl['frame']) && $rl['frame'] == 'd') ? 'selected' : null;?>>msgs / day</option>
             </select>
             </select>
             </div>
             </div>
           </div>
           </div>