]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Add console messages log envelope
authorMarcin Haba <marcin.haba@bacula.pl>
Sun, 24 Jan 2021 19:57:56 +0000 (20:57 +0100)
committerEric Bollengier <eric@baculasystems.com>
Thu, 24 Mar 2022 08:03:24 +0000 (09:03 +0100)
20 files changed:
gui/baculum/protected/Common/Class/BClientScript.php
gui/baculum/protected/Web/Class/MessagesLog.php [new file with mode: 0644]
gui/baculum/protected/Web/JavaScript/misc.js
gui/baculum/protected/Web/Lang/en/messages.mo
gui/baculum/protected/Web/Lang/en/messages.po
gui/baculum/protected/Web/Lang/ja/messages.mo
gui/baculum/protected/Web/Lang/ja/messages.po
gui/baculum/protected/Web/Lang/pl/messages.mo
gui/baculum/protected/Web/Lang/pl/messages.po
gui/baculum/protected/Web/Lang/pt/messages.mo
gui/baculum/protected/Web/Lang/pt/messages.po
gui/baculum/protected/Web/Lang/ru/messages.mo
gui/baculum/protected/Web/Lang/ru/messages.po
gui/baculum/protected/Web/Layouts/Main.php
gui/baculum/protected/Web/Layouts/Main.tpl
gui/baculum/protected/Web/Pages/Monitor.php
gui/baculum/protected/Web/Pages/config.xml
gui/baculum/protected/Web/Portlets/MsgEnvelope.php [new file with mode: 0644]
gui/baculum/protected/Web/Portlets/MsgEnvelope.tpl [new file with mode: 0644]
gui/baculum/themes/Baculum-v2/css/baculum.css

index 79ce1d737773c1639afdd127ae3e78b25fc62521..5de8b14d596e8edf11818d105f5983d8712fb478 100644 (file)
@@ -31,7 +31,7 @@ Prado::using('System.Web.UI.WebControls.TClientScript');
  */
 class BClientScript extends TClientScript {
 
-       const SCRIPTS_VERSION = 15;
+       const SCRIPTS_VERSION = 16;
 
        public function getScriptUrl()
        {
diff --git a/gui/baculum/protected/Web/Class/MessagesLog.php b/gui/baculum/protected/Web/Class/MessagesLog.php
new file mode 100644 (file)
index 0000000..24facd6
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2021 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('Application.:Web.Class.WebModule');
+
+/**
+ * Module responsible for managing messages log.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Module
+ * @package Baculum Web
+ */
+class MessagesLog extends WebModule {
+
+       /**
+        * Messages log file path.
+        */
+       const LOG_FILE_PATH = 'Application.Web.Logs.messages';
+
+       /**
+        * Messages log file extension.
+        */
+       const LOG_FILE_EXT = '.log';
+
+       /**
+        * Maximum number of lines to keep.
+        */
+       const MAX_LINES = 1000;
+
+       /**
+        * Append messages to messages log.
+        * NOTE: Max. lines limit is taken into acocunt.
+        *
+        * @param array $logs log messages
+        * @return array logs stored in log file
+        */
+       public function append(array $logs) {
+               $logs_all = [];
+               $f = Prado::getPathOfNamespace(self::LOG_FILE_PATH, self::LOG_FILE_EXT);
+               $fp = fopen($f, 'c+');
+               if (flock($fp, LOCK_EX)) {
+                       $fsize = filesize($f);
+                       $messages_file = $fsize > 0 ? fread($fp, $fsize) : '';
+                       $logs_file = explode(PHP_EOL, $messages_file);
+                       $logs_all = array_merge($logs_file, $logs);
+                       $all_len = count($logs_all);
+                       if ($all_len > self::MAX_LINES) {
+                               $len = $all_len - self::MAX_LINES;
+                               array_splice($logs_all, 0, $len);
+                       }
+                       $messages = implode(PHP_EOL, $logs_all);
+                       rewind($fp);
+                       ftruncate($fp, 0);
+                       fwrite($fp, $messages);
+                       fflush($fp);
+                       flock($fp, LOCK_UN);
+               } else {
+                       $emsg = 'Could not get the exclusive lock: ' . $f;
+                       $this->Application->getModule('logging')->log(
+                               __FUNCTION__,
+                               $emsg,
+                               Logging::CATEGORY_APPLICATION,
+                               __FILE__,
+                               __LINE__
+                       );
+               }
+               fclose($fp);
+               return $logs_all;
+       }
+
+       /**
+        * Truncate messages log.
+        *
+        * @return none
+        */
+       public function truncate() {
+               $f = Prado::getPathOfNamespace(self::LOG_FILE_PATH, self::LOG_FILE_EXT);
+               $fp = fopen($f, 'w');
+               if (flock($fp, LOCK_EX)) {
+                       fflush($fp);
+                       flock($fp, LOCK_UN);
+               } else {
+                       $emsg = 'Could not get the exclusive lock: ' . $f;
+                       $this->Application->getModule('logging')->log(
+                               __FUNCTION__,
+                               $emsg,
+                               Logging::CATEGORY_APPLICATION,
+                               __FILE__,
+                               __LINE__
+                       );
+               }
+               fclose($fp);
+       }
+
+       /**
+        * Save logs to file.
+        *
+        * @param array $logs log messages
+        * @return none
+        */
+       public function save(array $logs) {
+               $f = Prado::getPathOfNamespace(self::LOG_FILE_PATH, self::LOG_FILE_EXT);
+               $fp = fopen($f, 'a');
+               if (flock($fp, LOCK_EX)) {
+                       $messages = implode(PHP_EOL, $logs);
+                       fwrite($fp, $messages);
+                       fflush($fp);
+                       flock($fp, LOCK_UN);
+               } else {
+                       $emsg = 'Could not get the exclusive lock: ' . $f;
+                       $this->Application->getModule('logging')->log(
+                               __FUNCTION__,
+                               $emsg,
+                               Logging::CATEGORY_APPLICATION,
+                               __FILE__,
+                               __LINE__
+                       );
+               }
+               fclose($fp);
+       }
+
+       /**
+        * Read logs from file.
+        *
+        * @return array log messages
+        */
+       public function read() {
+               $logs = [];
+               $f = Prado::getPathOfNamespace(self::LOG_FILE_PATH, self::LOG_FILE_EXT);
+               if (!file_exists($f)) {
+                       return $logs;
+               }
+               $fp = fopen($f, 'r');
+               if (flock($fp, LOCK_SH)) {
+                       $fsize = filesize($f);
+                       $messages = $fsize > 0 ? fread($fp, $fsize) : '';
+                       $logs = explode(PHP_EOL, $messages);
+                       flock($fp, LOCK_UN);
+               } else {
+                       $emsg = 'Could not get the shared lock: ' . $f;
+                       $this->Application->getModule('logging')->log(
+                               __FUNCTION__,
+                               $emsg,
+                               Logging::CATEGORY_APPLICATION,
+                               __FILE__,
+                               __LINE__
+                       );
+               }
+               fclose($fp);
+               return $logs;
+       }
+}
+?>
index 2f429f1fff47c77b244f91bd1c0d71fe32f605ce..59bea3c9bdb1fcba6a5603ca30d7e6f980f81df0 100644 (file)
@@ -802,7 +802,118 @@ var Dashboard = {
                        container_id: this.ids.pie_summary
                });
        }
-}
+};
+
+var MsgEnvelope = {
+       ids: {
+               envelope: 'msg_envelope',
+               modal: 'msg_envelope_modal',
+               container: 'msg_envelope_container',
+               content: 'msg_envelope_content'
+       },
+       issue_regex: { // @TODO: add more regexes
+               warning: [
+                       /Cannot find any appendable volumes/g
+               ],
+               error: [
+                       /ERR=/g
+               ]
+       },
+       init: function() {
+               this.set_events();
+               this.set_actions();
+       },
+       set_events: function() {
+               document.getElementById(this.ids.envelope).addEventListener('click', function(e) {
+                       this.open();
+                       var container = document.getElementById(this.ids.container);
+                       // set scroll to the bottom
+                       container.scrollTop = container.scrollHeight;
+               }.bind(this));
+       },
+       set_actions: function() {
+               var monitor_func = function() {
+                       var is_bottom = false;
+                       var container = document.getElementById(this.ids.container);
+
+                       // detect if before adding content, scroll is at the bottom
+                       if (container.scrollTop === (container.scrollHeight - container.offsetHeight)) {
+                               is_bottom = true
+                       }
+
+                       // add logs
+                       var logs = oData.messages;
+                       MsgEnvelope.set_logs(logs);
+
+                       // set scroll to the bottom
+                       if (is_bottom) {
+                               container.scrollTop = container.scrollHeight;
+                       }
+               }.bind(this);
+               MonitorCallsInterval.push(monitor_func);
+       },
+       open: function() {
+               document.getElementById(this.ids.modal).style.display = 'block';
+       },
+       close: function() {
+               document.getElementById(this.ids.modal).style.display = 'none';
+       },
+       set_logs: function(logs) {
+               this.find_issues(logs);
+               document.getElementById(this.ids.content).innerHTML = logs.join("\n");
+       },
+       mark_envelope_error: function() {
+               var envelope = document.getElementById(this.ids.envelope);
+               if (envelope.classList.contains('w3-green')) {
+                       envelope.classList.replace('w3-green', 'w3-red');
+               }
+               if (envelope.classList.contains('w3-orange')) {
+                       envelope.classList.replace('w3-orange', 'w3-red');
+               }
+               envelope.querySelector('I').classList.add('blink');
+       },
+       mark_envelope_warning: function() {
+               var envelope = document.getElementById(this.ids.envelope);
+               if (envelope.classList.contains('w3-green')) {
+                       envelope.classList.replace('w3-green', 'w3-orange');
+               }
+               envelope.querySelector('I').classList.add('blink');
+       },
+       mark_envelope_ok: function() {
+               var envelope = document.getElementById(this.ids.envelope);
+               if (envelope.classList.contains('w3-red')) {
+                       envelope.classList.replace('w3-red', 'w3-green');
+               }
+               if (envelope.classList.contains('w3-orange')) {
+                       envelope.classList.replace('w3-orange', 'w3-green');
+               }
+               envelope.querySelector('I').classList.remove('blink');
+       },
+       find_issues: function(logs) {
+               var error = warning = false;
+               var logs_len = logs.length;
+               for (var i = 0; i < logs_len; i++) {
+                       for (var j = 0; j < this.issue_regex.warning.length; j++) {
+                               if (this.issue_regex.warning[j].test(logs[i])) {
+                                       logs[i] = '<span class="w3-orange">' + logs[i] + '</span>';
+                                       warning = true;
+                               }
+                       }
+                       for (var j = 0; j < this.issue_regex.error.length; j++) {
+                               if (this.issue_regex.error[j].test(logs[i])) {
+                                       logs[i] = '<span class="w3-red">' + logs[i] + '</span>';
+                                       error = true;
+                               }
+                       }
+               }
+
+               if (error) {
+                       this.mark_envelope_error();
+               } else if (warning) {
+                       this.mark_envelope_warning();
+               }
+       }
+};
 
 var W3SideBar = {
        ids: {
index 5c5c6d8b0f205581a5531237b9ffc92e774147d0..8bcba4f1bbec9155f561289e03d910e76436f31b 100644 (file)
Binary files a/gui/baculum/protected/Web/Lang/en/messages.mo and b/gui/baculum/protected/Web/Lang/en/messages.mo differ
index 2873ea5f4f6bdbf808ca1d37d035b15a0a8c0785..e8a00749473ac115a0e79e1d3bd0b5e1d15034a3 100644 (file)
@@ -3194,3 +3194,11 @@ msgstr "Graphical storage status is supported for Bacula storages version 9.0 an
 msgid "Status request timed out. The most probably the Bacula storage is not available or it is not running."
 msgstr "Status request timed out. The most probably the Bacula storage is not available or it is not running."
 
+msgid "Display messages log window"
+msgstr "Display messages log window"
+
+msgid "Truncate log"
+msgstr "Truncate log"
+
+msgid "Messages"
+msgstr "Messages"
index 0f09865cd04bf3b651810bc30f7592a0b26b1247..2f9f65b36039b652fcfaa1fcaad3bba920c3cd66 100644 (file)
Binary files a/gui/baculum/protected/Web/Lang/ja/messages.mo and b/gui/baculum/protected/Web/Lang/ja/messages.mo differ
index 168466d68d430a384cca872558b0999d6e96cbd5..3f40e3251ea8b22ece6ef025ef679e6cd7bdfd85 100644 (file)
@@ -3280,3 +3280,11 @@ msgstr "Graphical storage status is supported for Bacula storages version 9.0 an
 msgid "Status request timed out. The most probably the Bacula storage is not available or it is not running."
 msgstr "Status request timed out. The most probably the Bacula storage is not available or it is not running."
 
+msgid "Display messages log window"
+msgstr "Display messages log window"
+
+msgid "Truncate log"
+msgstr "Truncate log"
+
+msgid "Messages"
+msgstr "Messages"
index df7d08979cc5844a17c4a8080cd658cda4f6d519..781cd396bc72d9e9e4452a63e447766a6288d92c 100644 (file)
Binary files a/gui/baculum/protected/Web/Lang/pl/messages.mo and b/gui/baculum/protected/Web/Lang/pl/messages.mo differ
index 6eb1629e87a5e43b02c4363d52fbf6cbd32be930..132a3c4736b83c8586805eaab670437ebc30a58e 100644 (file)
@@ -3205,3 +3205,11 @@ msgstr "Graficzny status magazynu jest wspierany dla magazynów Bacula w wersji
 msgid "Status request timed out. The most probably the Bacula storage is not available or it is not running."
 msgstr "Przekroczono limit czasu żądania statusu. Najprawdopodobniej magazyn Bacula jest niedostępny lub nie jest uruchomiony."
 
+msgid "Display messages log window"
+msgstr "Wyświetl okno z wiadomościami"
+
+msgid "Truncate log"
+msgstr "Przytnij dziennik"
+
+msgid "Messages"
+msgstr "Wiadomości"
index 741104abea995272c7e5f3126393ab668183bea5..c06cc1ad901764129d5c69307ce999f2f3882728 100644 (file)
Binary files a/gui/baculum/protected/Web/Lang/pt/messages.mo and b/gui/baculum/protected/Web/Lang/pt/messages.mo differ
index 525dc3fe595d885bb298bd399304a0314ce52251..e5f77bb314ea833f6805e964a79653d18586fec9 100644 (file)
@@ -3204,3 +3204,11 @@ msgstr "O status gráfico de armazenamento é suportado para armazenamento Bacul
 msgid "Status request timed out. The most probably the Bacula storage is not available or it is not running."
 msgstr "A solicitação de status expirou. O mais provavelmente o armazenamento Bacula não está disponível ou não está em execução."
 
+msgid "Display messages log window"
+msgstr "Display messages log window"
+
+msgid "Truncate log"
+msgstr "Truncate log"
+
+msgid "Messages"
+msgstr "Messages"
index 8120e564208b59d1bf21d85d59a25b2cebc7123a..3fb88d8f1923be4f33b6e1940afa1e21e6ff2e75 100644 (file)
Binary files a/gui/baculum/protected/Web/Lang/ru/messages.mo and b/gui/baculum/protected/Web/Lang/ru/messages.mo differ
index bd4c92a75d1ef84763f8b4113054bef3a60d58b8..3f0e3276239c1c17fa21af6c380fdddb4136107e 100644 (file)
@@ -3204,3 +3204,11 @@ msgstr "Графическое отображение состояния хра
 msgid "Status request timed out. The most probably the Bacula storage is not available or it is not running."
 msgstr "Истекло время ожидания. Скорее всего, хранилище Bacula недоступно или не работает."
 
+msgid "Display messages log window"
+msgstr "Display messages log window"
+
+msgid "Truncate log"
+msgstr "Truncate log"
+
+msgid "Messages"
+msgstr "Messages"
index 2f9c118fc46fa61dadb43f6356d1e2f0c155a8e8..217b677a8f6cba1aeaee3e813d8997a905b360d6 100644 (file)
@@ -21,6 +21,7 @@
  */
  
 Prado::using('Application.Common.Class.Params');
+Prado::using('Application.Web.Class.WebUserRoles');
 
 /**
  * Main layout class.
index 7bf70b74e1400fee7d9462a9727cb5cdda0a4891..2c3ace0a4dda6aac1be3fe52609c01193d5b2743 100644 (file)
@@ -36,6 +36,9 @@
                                <span class="w3-tag w3-large w3-purple w3-right w3-padding-small w3-margin-top w3-margin-right">
                                        <i class="fa fa-cogs w3-large"></i> <%[ Running jobs: ]%> <span id="running_jobs"></span>
                                </span>
+                               <span id="msg_envelope" class="w3-tag w3-large w3-green w3-text-white w3-right w3-padding-small w3-margin-top w3-margin-right" style="cursor: pointer;<%=$this->User->isInRole(WebUserRoles::ADMIN) === false ? 'display: none' : ''%>" title="<%[ Display messages log window ]%>">
+                                       <i class="fas fa-envelope w3-large"></i>
+                               </span>
                                <script type="text/javascript">
                                        var SIZE_VALUES_UNIT = '<%=(count($this->web_config) > 0 && key_exists('size_values_unit', $this->web_config['baculum'])) ? $this->web_config['baculum']['size_values_unit'] : WebConfig::DEF_SIZE_VAL_UNIT%>';
                                        var DATE_TIME_FORMAT = '<%=(count($this->web_config) > 0 && key_exists('date_time_format', $this->web_config['baculum'])) ? $this->web_config['baculum']['date_time_format'] : WebConfig::DEF_DATE_TIME_FORMAT%>';
@@ -139,5 +142,6 @@ function show_error(output, error) {
        err_box.style.display = 'block';
 }
        </script>
+       <com:Application.Web.Portlets.MsgEnvelope Visible="<%=$this->User->isInRole(WebUserRoles::ADMIN)%>" />
        </body>
 </html>
index 835e33f02b8ec6f0c82c12ddd4079b096aa7d542..cb59083170cea813aa5c15fe8ea2723b91bd2446 100644 (file)
@@ -20,6 +20,7 @@
  * Bacula(R) is a registered trademark of Kern Sibbald.
  */
 Prado::using('Application.Web.Class.BaculumWebPage');
+Prado::using('Application.Web.Class.WebUserRoles');
 
 /**
  * Monitor class.
@@ -37,16 +38,17 @@ class Monitor extends BaculumPage {
 
        public function onInit($param) {
                parent::onInit($param);
-               $monitor_data = array(
-                       'jobs' => array(),
-                       'running_jobs' => array(),
-                       'terminated_jobs' => array(),
-                       'pools' => array(),
-                       'clients' => array(),
-                       'jobtotals' => array(),
-                       'dbsize' => array(),
-                       'error' => array('error' => 0, 'output' => '')
-               );
+               $monitor_data = [
+                       'jobs' => [],
+                       'running_jobs' => [],
+                       'terminated_jobs' => [],
+                       'pools' => [],
+                       'clients' => [],
+                       'jobtotals' => [],
+                       'dbsize' => [],
+                       'messages' => [],
+                       'error' => ['error' => 0, 'output' => '']
+               ];
 
                // Initialize session cache to have clear session for Monitor
                $this->getModule('api')->initSessionCache(true);
@@ -58,10 +60,10 @@ class Monitor extends BaculumPage {
                }
 
                $error = null;
-               $params = $this->Request->contains('params') ? $this->Request['params'] : array();
+               $params = $this->Request->contains('params') ? $this->Request['params'] : [];
                if (is_array($params) && key_exists('jobs', $params)) {
-                       $job_params = array('jobs');
-                       $job_query = array();
+                       $job_params = ['jobs'];
+                       $job_query = [];
                        if (is_array($params['jobs'])) {
                                if (key_exists('name', $params['jobs']) && is_array($params['jobs']['name'])) {
                                        for ($i = 0; $i < count($params['jobs']['name']); $i++) {
@@ -88,7 +90,7 @@ class Monitor extends BaculumPage {
                        }
                }
                if (!$error) {
-                       $result = $this->getModule('api')->get(array('jobs', '?jobstatus=CR'));
+                       $result = $this->getModule('api')->get(['jobs', '?jobstatus=CR']);
                        if ($result->error === 0) {
                                $monitor_data['running_jobs'] = $result->output;
                        } else {
@@ -96,7 +98,7 @@ class Monitor extends BaculumPage {
                        }
                }
                if (!$error && key_exists('clients', $params)) {
-                       $result = $this->getModule('api')->get(array('clients'));
+                       $result = $this->getModule('api')->get(['clients']);
                        if ($result->error === 0) {
                                $monitor_data['clients'] = $result->output;
                        } else {
@@ -104,7 +106,7 @@ class Monitor extends BaculumPage {
                        }
                }
                if (!$error && key_exists('pools', $params)) {
-                       $result = $this->getModule('api')->get(array('pools'));
+                       $result = $this->getModule('api')->get(['pools']);
                        if ($result->error === 0) {
                                $monitor_data['pools'] = $result->output;
                        } else {
@@ -112,7 +114,7 @@ class Monitor extends BaculumPage {
                        }
                }
                if (!$error && key_exists('job_totals', $params)) {
-                       $result = $this->getModule('api')->get(array('jobs', 'totals'));
+                       $result = $this->getModule('api')->get(['jobs', 'totals']);
                        if ($result->error === 0) {
                                $monitor_data['jobtotals'] = $result->output;
                        } else {
@@ -120,13 +122,27 @@ class Monitor extends BaculumPage {
                        }
                }
                if (!$error && key_exists('dbsize', $params)) {
-                       $result = $this->getModule('api')->get(array('dbsize'));
+                       $result = $this->getModule('api')->get(['dbsize']);
                        if ($result->error === 0) {
                                $monitor_data['dbsize'] = $result->output;
                        } else {
                                $error = $result;
                        }
                }
+               if (!$error && $this->User->isInRole(WebUserRoles::ADMIN)) {
+                       $result = $this->getModule('api')->get(['joblog', 'messages']);
+                       if ($result->error === 0) {
+                               $ml = [];
+                               if (count($result->output) > 0 && $result->output[0] != 'You have no messages.') {
+                                       $ml = $this->getModule('messages_log')->append($result->output);
+                               } else {
+                                       $ml = $this->getModule('messages_log')->read();
+                               }
+                               $monitor_data['messages'] = $this->getModule('log_parser')->parse($ml);
+                       } else {
+                               $error = $result;
+                       }
+               }
 
                $running_job_states = $this->Application->getModule('misc')->getRunningJobStates();
 
index 242e853348dd8cd7e5f584d7308d842a049ec482..9f0ff33c8f64eec421ce7851f4bd1f28c7f1b8ac 100644 (file)
@@ -27,5 +27,6 @@
                <module id="users" class="Application.Web.Class.WebUserManager" UserClass="Application.Web.Class.WebUser" />
                <!-- data modules -->
                <module id="job_info" class="Application.Web.Class.JobInfo" />
+               <module id="messages_log" class="Application.Web.Class.MessagesLog" />
        </modules>
 </configuration>
diff --git a/gui/baculum/protected/Web/Portlets/MsgEnvelope.php b/gui/baculum/protected/Web/Portlets/MsgEnvelope.php
new file mode 100644 (file)
index 0000000..eaf26bb
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2021 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+
+Prado::using('System.Web.UI.ActiveControls.TCallback');
+Prado::using('Application.Web.Class.WebUserRoles');
+Prado::using('Application.Web.Portlets.Portlets');
+
+/**
+ * Message envelope control.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Control
+ * @package Baculum Web
+ */
+class MsgEnvelope extends Portlets {
+
+       public function truncate() {
+               if (!$this->User->isInRole(WebUserRoles::ADMIN)) {
+                       return;
+               }
+               $this->getModule('messages_log')->truncate();
+       }
+}
+?>
diff --git a/gui/baculum/protected/Web/Portlets/MsgEnvelope.tpl b/gui/baculum/protected/Web/Portlets/MsgEnvelope.tpl
new file mode 100644 (file)
index 0000000..22b6210
--- /dev/null
@@ -0,0 +1,29 @@
+<div id="msg_envelope_modal" class="w3-modal">
+       <div class="w3-modal-content w3-animate-top w3-card-4">
+               <header class="w3-container w3-green">
+                       <span onclick="MsgEnvelope.close();" class="w3-button w3-display-topright">&times;</span>
+                       <h2><%[ Messages ]%></h2>
+               </header>
+               <div class="w3-container w3-margin-left w3-margin-right">
+                       <div id="msg_envelope_container" class="w3-code" style="font-size: 12px; min-height: 50px; max-height: 610px; overflow-y: scroll; overflow-x: auto;">
+                               <pre id="msg_envelope_content"></pre>
+                       </div>
+               </div>
+               <footer class="w3-container w3-center">
+                       <button class="w3-button w3-red w3-section w3-margin-right" onclick="msg_envelope_truncate();"><i class="fas fa-cut"></i> &nbsp;<%[ Truncate log ]%></button>
+                       <button class="w3-button w3-red w3-section" onclick="MsgEnvelope.close();"><i class="fas fa-times"></i> &nbsp;<%[ Close ]%></button>
+               </footer>
+       </div>
+</div>
+<com:TCallback
+       ID="MsgEnvelopeTruncate"
+       OnCallback="truncate"
+       ClientSide.OnComplete="MsgEnvelope.set_logs([]); MsgEnvelope.mark_envelope_ok();"
+/>
+<script>
+function msg_envelope_truncate() {
+       var cb = <%=$this->MsgEnvelopeTruncate->ActiveControl->Javascript%>;
+       cb.dispatch();
+}
+MsgEnvelope.init();
+</script>
index 14dd9635ee76f7ac738810f84fc8f24be56cbd08..38ad6a4dd8926eb409166b543eb258ecea3eeab4 100644 (file)
@@ -516,3 +516,13 @@ table.component td:nth-of-type(1) {
                display: inline;
        }
 }
+
+@keyframes blinker {
+       50% {
+               opacity: 0;
+       }
+}
+
+.blink {
+  animation: blinker 4s linear infinite;
+}