diff --git a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql
index c9784987fd..6140297ef8 100644
--- a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql
+++ b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql
@@ -1858,6 +1858,19 @@ ALTER TABLE `tevento` ADD COLUMN `data` double(22,5) default NULL;
 
 ALTER TABLE `tevento` ADD COLUMN `module_status` int(4) NOT NULL default '0';
 
+-- ---------------------------------------------------------------------
+-- Table `tevent_extended`
+-- ---------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `tevent_extended` (
+	`id` serial PRIMARY KEY,
+	`id_evento` bigint(20) unsigned NOT NULL,
+	`external_id` bigint(20) unsigned,
+	`utimestamp` bigint(20) NOT NULL default '0',
+	`description` text,
+	FOREIGN KEY `tevent_ext_fk`(`id_evento`) REFERENCES `tevento`(`id_evento`)
+    ON UPDATE CASCADE ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 -- -----------------------------------------------------
 -- Table `tgis_map_layer_groups`
 -- -----------------------------------------------------
@@ -1977,4 +1990,9 @@ CREATE TABLE `tnotification_source_group_user` (
         ON UPDATE CASCADE ON DELETE CASCADE,
     FOREIGN KEY (`id_group`) REFERENCES `tnotification_source_group`(`id_group`)
         ON UPDATE CASCADE ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
\ No newline at end of file
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- ----------------------------------------------------------------------
+-- Add alert command 'Generate notification'
+-- ----------------------------------------------------------------------
+INSERT INTO `talert_commands` (`name`, `command`, `description`, `internal`, `fields_descriptions`, `fields_values`) VALUES ('Generate Notification','Internal type','This command allows you to send an internal notification to any user or group.',1,'[\"Destination user\",\"Destination group\",\"Title\",\"Message\",\"Link\",\"Criticity\",\"\",\"\",\"\",\"\",\"\"]','[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]');
diff --git a/pandora_console/general/header.php b/pandora_console/general/header.php
index 34976f5839..e35d401502 100644
--- a/pandora_console/general/header.php
+++ b/pandora_console/general/header.php
@@ -1,5 +1,4 @@
 <?php
-
 // Pandora FMS - http://pandorafms.com
 // ==================================================
 // Copyright (c) 2005-2011 Artica Soluciones Tecnologicas
@@ -13,6 +12,7 @@
 // GNU General Public License for more details.
 require_once 'include/functions_messages.php';
 require_once 'include/functions_servers.php';
+require_once 'include/functions_notifications.php';
 
 // Check permissions
 // Global errors/warnings checking.
@@ -57,7 +57,7 @@ config_check();
                 $table->cellspacing = 0;
                 $table->head = [];
                 $table->data = [];
-                $table->style[0] = $table->style['clippy'] = $table->style[1] = $table->style[3] = $table->style[4] = $table->style[5] = $table->style[6] = $table->style[8] = $table->style[9] = $table->style['qr'] = $table->style['notifications'] = 'width: 22px; text-align:center; height: 22px; padding-right: 9px;padding-left: 9px;';
+                $table->style['clippy'] = $table->style[1] = $table->style[4] = $table->style[5] = $table->style[6] = $table->style[8] = $table->style[9] = $table->style['qr'] = $table->style['notifications'] = 'width: 22px; text-align:center; height: 22px; padding-right: 9px;padding-left: 9px;';
                 $table->style[7] = 'width: 20px; padding-right: 9px;';
                 $table->style['searchbar'] = 'width: 180px; min-width: 180px;';
                 $table->style[11] = 'padding-left: 10px; padding-right: 5px;width: 16px;';
@@ -110,27 +110,6 @@ config_check();
                 $table->data[0]['searchbar'] = $search_bar;
             }
 
-                // Servers check
-                $servers = [];
-                $servers['all'] = (int) db_get_value('COUNT(id_server)', 'tserver');
-                $servers['up'] = (int) servers_check_status();
-                $servers['down'] = ($servers['all'] - $servers['up']);
-            if ($servers['up'] == 0) {
-                // All Servers down or no servers at all
-                $servers_check_img = html_print_image('images/header_down.png', true, ['alt' => 'cross', 'class' => 'bot', 'title' => __('All systems').': '.__('Down')]);
-            } else if ($servers['down'] != 0) {
-                // Some servers down
-                $servers_check_img = html_print_image('images/header_warning.png', true, ['alt' => 'error', 'class' => 'bot', 'title' => $servers['down'].' '.__('servers down')]);
-            } else {
-                // All servers up
-                $servers_check_img = html_print_image('images/header_ready.png', true, ['alt' => 'ok', 'class' => 'bot', 'title' => __('All systems').': '.__('Ready')]);
-            }
-
-                unset($servers);
-            // Since this is the header, we don't like to trickle down variables.
-                $servers_link_open = '<a class="white" href="index.php?sec=gservers&amp;sec2=godmode/servers/modificar_server&amp;refr=60">';
-                $servers_link_close = '</a>';
-
             if ($config['show_qr_code_header'] == 0) {
                 $show_qr_code_header = 'display: none;';
             } else {
@@ -175,12 +154,6 @@ config_check();
                     ).'</a>';
                 }
 
-
-                $table->data[0][0] = $servers_link_open.$servers_check_img.$servers_link_close;
-
-
-
-
                 // ======= Autorefresh code =============================
                 $autorefresh_txt = '';
                 $autorefresh_additional = '';
@@ -256,42 +229,6 @@ config_check();
                 // ======================================================
                 $pandora_management = check_acl($config['id_user'], 0, 'PM');
 
-                echo '<div id="alert_messages" style="display: none"></div>';
-
-                if ($config['alert_cnt'] > 0) {
-                    $maintenance_link = 'javascript:';
-                    $maintenance_title = __('System alerts detected - Please fix as soon as possible');
-                    $maintenance_class = $maintenance_id = 'show_systemalert_dialog white';
-
-                    $maintenance_link_open_txt = '<a href="'.$maintenance_link.'" title="'.$maintenance_title.'" class="'.$maintenance_class.'" id="show_systemalert_dialog">';
-                    $maintenance_link_open_img = '<a href="'.$maintenance_link.'" title="'.$maintenance_title.'" class="'.$maintenance_class.'">';
-                    $maintenance_link_close = '</a>';
-                    if (!$pandora_management) {
-                        $maintenance_img = '';
-                    } else {
-                        $maintenance_img = $maintenance_link_open_img.html_print_image(
-                            'images/header_yellow.png',
-                            true,
-                            [
-                                'title' => __(
-                                    'You have %d warning(s)',
-                                    $config['alert_cnt']
-                                ),
-                                'id'    => 'yougotalert',
-                                'class' => 'bot',
-                            ]
-                        ).$maintenance_link_close;
-                    }
-                } else {
-                    if (!$pandora_management) {
-                        $maintenance_img = '';
-                    } else {
-                        $maintenance_img = html_print_image('images/header_ready.png', true, ['title' => __('There are not warnings'), 'id' => 'yougotalert', 'class' => 'bot']);
-                    }
-                }
-
-                $table->data[0][3] = $maintenance_img;
-
                 // Main help icon
                 if (!$config['disable_help']) {
                     $table->data[0][4] = '<a href="#" class="modalpopup" id="helpmodal">'.html_print_image(
@@ -305,6 +242,12 @@ config_check();
                     ).'</a>';
                 }
 
+                $notifications_numbers = notifications_get_counters();
+                $table->data[0]['notifications'] = notifications_print_ball(
+                    $notifications_numbers['notifications'],
+                    $notifications_numbers['last_id']
+                );
+
                 // Logout
                 $table->data[0][5] = '<a class="white" href="'.ui_get_full_url('index.php?bye=bye').'">';
                 $table->data[0][5] .= html_print_image('images/header_logout.png', true, ['alt' => __('Logout'), 'class' => 'bot', 'title' => __('Logout')]);
@@ -328,18 +271,6 @@ config_check();
                 $table->data[0][8] .= '</a>';
                 $table->data[0][8] .= '</span>';
 
-                // Messages
-                $msg_cnt = messages_get_count($config['id_user']);
-                if ($msg_cnt > 0) {
-                    echo '<div id="dialog_messages" style="display: none"></div>';
-
-                    $table->data[0][9] = '<a href="ajax.php?page=operation/messages/message_list" title="'.__('Message overview').'" id="show_messages_dialog">';
-                    $table->data[0][9] .= html_print_image('images/header_email.png', true, ['title' => __('You have %d unread message(s)', $msg_cnt), 'id' => 'yougotmail', 'class' => 'bot', 'style' => 'width:24px;']);
-                    $table->data[0][9] .= '</a>';
-                }
-
-                $table->data[0]['notifications'] = notifications_print_ball();
-
                 html_print_table($table);
 
                 unset($table);
@@ -360,6 +291,9 @@ config_check();
     </tr>
 </table>
 
+<!-- Notifications content wrapper-->
+<div id='notification-content' style='display:none;' /></div>
+
 <script type="text/javascript">
     /* <![CDATA[ */
     
@@ -368,12 +302,223 @@ config_check();
     if (isset($config['fixed_header'])) {
         $config_fixed_header = $config['fixed_header'];
     }
+
     ?>
-    
+
+    function addNotifications(event) {
+        var element = document.getElementById("notification-content");
+        if (!element) {
+            console.error('Cannot locate the notification content element.');
+            return;
+        }
+        // If notification-content is empty, retrieve the notifications.
+        if (!element.firstChild) {
+            jQuery.post ("ajax.php",
+                {
+                    "page" : "godmode/setup/setup_notifications",
+                    "get_notifications_dropdown" : 1,
+                },
+                function (data, status) {
+                    // Apppend data
+                    element.innerHTML = data;
+                    // Show the content
+                    element.style.display = "block";
+                    attatch_to_image();
+                },
+                "html"
+            );
+        } else {
+            // If there is some notifications retrieved, only show it.
+            element.style.display = "block";
+            attatch_to_image();
+        }
+    }
+
+    function attatch_to_image() {
+        var notification_elem = document.getElementById("notification-wrapper");
+        if (!notification_elem) return;
+        var image_attached =
+            document.getElementById("notification-ball-header")
+                .getBoundingClientRect()
+                .left
+        ;
+        notification_elem.style.left = image_attached - 300 + "px";
+    }
+
+    function notifications_clean_ui(action, self_id) {
+        switch(action) {
+            case 'item':
+                // Recalculate the notification ball.
+                check_new_notifications();
+                break;
+            case 'toast':
+                // Only remove the toast element.
+                document.getElementById(self_id).remove();
+                break;
+        }
+    }
+
+    function notifications_hide() {
+        var element = document.getElementById("notification-content");
+        element.style.display = "none"
+    }
+
+    function click_on_notification_toast(event) {
+        var match = /notification-(.*)-id-([0-9]+)/.exec(event.target.id);
+        if (!match) {
+            console.error(
+                "Cannot handle toast click event. Id not valid: ",
+                event.target.id
+            );
+            return;
+        }
+        jQuery.post ("ajax.php",
+            {
+                "page" : "godmode/setup/setup_notifications",
+                "mark_notification_as_read" : 1,
+                "message": match[2]
+            },
+            function (data, status) {
+                if (!data.result) {
+                    console.error("Cannot redirect to URL.");
+                    return;
+                }
+                notifications_clean_ui(match[1], event.target.id);
+            },
+            "json"
+        )
+        .fail(function(xhr, textStatus, errorThrown){
+            console.error(
+                "Failed onclik event on toast. Error: ",
+                xhr.responseText
+            );
+        });
+    }
+
+    function print_toast(title, subtitle, severity, url, id, onclick) {
+        // TODO severity.
+        severity = '';
+
+        // Start the toast.
+        var toast = document.createElement('a');
+        toast.setAttribute('onclick', onclick);
+        toast.setAttribute('href', url);
+        toast.setAttribute('target', '_blank');
+
+        // Fill toast.
+        var toast_div = document.createElement('div');
+        toast_div.className = 'snackbar ' + severity;
+        toast_div.id = id;
+        var toast_title = document.createElement('h3');
+        var toast_text = document.createElement('p');
+        toast_title.innerHTML = title;
+        toast_text.innerHTML = subtitle;
+        toast_div.appendChild(toast_title);
+        toast_div.appendChild(toast_text);
+        toast.appendChild(toast_div);
+
+        // Show and program the hide event.
+        toast_div.className = toast_div.className + ' show';
+        setTimeout(function(){
+            toast_div.className = toast_div.className.replace("show", "");
+        }, 8000);
+
+        return toast;
+    }
+
+    function check_new_notifications() {
+        var last_id = document.getElementById('notification-ball-header')
+            .getAttribute('last_id');
+        if (last_id === null) {
+            console.error('Cannot retrieve notifications ball last_id.');
+            return;
+        }
+
+        jQuery.post ("ajax.php",
+            {
+                "page" : "godmode/setup/setup_notifications",
+                "check_new_notifications" : 1,
+                "last_id": last_id
+            },
+            function (data, status) {
+                // Clean the toasts wrapper at first.
+                var toast_wrapper = document.getElementById(
+                    'notifications-toasts-wrapper'
+                );
+                if (toast_wrapper === null) {
+                    console.error('Cannot place toast notifications.');
+                    return;
+                }
+                while (toast_wrapper.firstChild) {
+                    toast_wrapper.removeChild(toast_wrapper.firstChild);
+                }
+
+                // Return if no new notification.
+                if(!data.has_new_notifications) return;
+
+                // Substitute the ball
+                var new_ball = atob(data.new_ball);
+                var ball_wrapper = document
+                    .getElementById('notification-ball-header')
+                    .parentElement;
+                if (ball_wrapper === null) {
+                    console.error('Cannot update notification ball');
+                    return;
+                }
+                // Print the new ball and clean old notifications
+                ball_wrapper.innerHTML = new_ball;
+                var not_drop = document.getElementById('notification-content');
+                while (not_drop.firstChild && not_drop) {
+                    not_drop.removeChild(not_drop.firstChild);
+                }
+
+                // Add the new toasts.
+                if (Array.isArray(data.new_notifications)) {
+                    data.new_notifications.forEach(function(ele) {
+                        toast_wrapper.appendChild(
+                            print_toast(
+                                ele.subject,
+                                ele.mensaje,
+                                ele.criticity,
+                                ele.full_url,
+                                'notification-toast-id-' + ele.id_mensaje,
+                                'click_on_notification_toast(event)'
+                            )
+                        );
+                    });
+                }
+            },
+            "json"
+        )
+        .fail(function(xhr, textStatus, errorThrown){
+            console.error(
+                "Cannot get new notifications. Error: ",
+                xhr.responseText
+            );
+        });
+    }
+
+    // Resize event
+    window.addEventListener("resize", function() {
+        attatch_to_image();
+    });
+
     var fixed_header = <?php echo json_encode((bool) $config_fixed_header); ?>;
     
     var new_chat = <?php echo (int) $_SESSION['new_chat']; ?>;
     $(document).ready (function () {
+
+        // Check new notifications on a periodic way
+        setInterval(check_new_notifications, 10000);
+
+        // Print the wrapper for notifications
+        var notifications_toasts_wrapper = document.createElement('div');
+        notifications_toasts_wrapper.id = 'notifications-toasts-wrapper';
+        document.body.insertBefore(
+            notifications_toasts_wrapper,
+            document.body.firstChild
+        );
+
         <?php
         if (($autorefresh_list !== null) && (array_search($_GET['sec2'], $autorefresh_list) !== false) && (!isset($_GET['refr']))) {
             $do_refresh = true;
@@ -425,33 +570,12 @@ config_check();
         $("#ui_close_dialog_titlebar").click(function () {
             $("#agent_access").css("display","");
         });
-        
-        function blinkmail(){
-            $("#yougotmail").delay(100).fadeTo(300,0.2).delay(100).fadeTo(300,1, blinkmail);
-        }
-        function blinkalert(){
-            $("#yougotalert").delay(100).fadeTo(300,0.2).delay(100).fadeTo(300,1, blinkalert);
-        }
+
         function blinkpubli(){
             $(".publienterprise").delay(100).fadeTo(300,0.2).delay(100).fadeTo(300,1, blinkpubli);
         }
-        <?php
-        if ($msg_cnt > 0) {
-            ?>
-            blinkmail();
-            <?php
-        }
-        ?>
-        
-        
-        <?php
-        if ($config['alert_cnt'] > 0) {
-            ?>
-            blinkalert();
-            <?php
-        }
-        ?>
-            blinkpubli();
+
+        blinkpubli();
 
         <?php
         if ($_GET['refr']) {
@@ -486,4 +610,4 @@ config_check();
         });
     });
 /* ]]> */
-</script>
+</script>
\ No newline at end of file
diff --git a/pandora_console/godmode/menu.php b/pandora_console/godmode/menu.php
index 7722463c65..1c369db688 100644
--- a/pandora_console/godmode/menu.php
+++ b/pandora_console/godmode/menu.php
@@ -276,6 +276,9 @@ if (check_acl($config['id_user'], 0, 'PM')) {
     $sub2['godmode/setup/setup&amp;section=ehorus']['text'] = __('eHorus');
     $sub2['godmode/setup/setup&amp;section=ehorus']['refr'] = 0;
 
+    $sub2['godmode/setup/setup&amp;section=notifications']['text'] = __('Notifications');
+    $sub2['godmode/setup/setup&amp;section=notifications']['refr'] = 0;
+
     if ($config['activate_gis']) {
         $sub2['godmode/setup/gis']['text'] = __('Map conections GIS');
     }
diff --git a/pandora_console/godmode/setup/performance.php b/pandora_console/godmode/setup/performance.php
index b8a397531e..a426c44cc2 100644
--- a/pandora_console/godmode/setup/performance.php
+++ b/pandora_console/godmode/setup/performance.php
@@ -82,6 +82,18 @@ if (enterprise_installed()) {
     $table->data[12][1] = html_print_input_text('inventory_purge', $config['inventory_purge'], '', 5, 5, true);
 }
 
+$table->data[] = [
+    __('Max. days before delete old messages'),
+    html_print_input_text(
+        'delete_old_messages',
+        $config['delete_old_messages'],
+        '',
+        5,
+        5,
+        true
+    ),
+];
+
 $table_other = new stdClass();
 $table_other->width = '100%';
 $table_other->class = 'databox filters';
diff --git a/pandora_console/godmode/setup/setup.php b/pandora_console/godmode/setup/setup.php
index f5f229ddd5..ebfc11493f 100644
--- a/pandora_console/godmode/setup/setup.php
+++ b/pandora_console/godmode/setup/setup.php
@@ -107,6 +107,12 @@ $buttons['ehorus'] = [
     'text'   => '<a href="index.php?sec=gsetup&sec2=godmode/setup/setup&section=ehorus">'.html_print_image('images/ehorus/ehorus.png', true, ['title' => __('eHorus')]).'</a>',
 ];
 
+// FIXME: Not definitive icon
+$buttons['notifications'] = [
+    'active' => false,
+    'text'   => '<a href="index.php?sec=gsetup&sec2=godmode/setup/setup&section=notifications">'.html_print_image('images/alerts_template.png', true, ['title' => __('Notifications')]).'</a>',
+];
+
 $help_header = '';
 if (enterprise_installed()) {
     $subpage = setup_enterprise_add_subsection_main($section, $buttons, $help_header);
@@ -143,6 +149,11 @@ switch ($section) {
         $buttons['ehorus']['active'] = true;
         $subpage = ' &raquo '.__('eHorus');
     break;
+
+    case 'notifications':
+        $buttons['notifications']['active'] = true;
+        $subpage = ' &raquo '.__('Notifications');
+    break;
 }
 
 // Header
@@ -183,6 +194,10 @@ switch ($section) {
         include_once $config['homedir'].'/godmode/setup/setup_ehorus.php';
     break;
 
+    case 'notifications':
+        include_once $config['homedir'].'/godmode/setup/setup_notifications.php';
+    break;
+
     default:
         enterprise_hook('setup_enterprise_select_tab', [$section]);
     break;
diff --git a/pandora_console/godmode/setup/setup_notifications.php b/pandora_console/godmode/setup/setup_notifications.php
index d315f14711..14a29c4bb2 100644
--- a/pandora_console/godmode/setup/setup_notifications.php
+++ b/pandora_console/godmode/setup/setup_notifications.php
@@ -1,18 +1,31 @@
 <?php
+/**
+ * Library. Notification system auxiliary functions.
+ *
+ * @category   UI file
+ * @package    Pandora FMS
+ * @subpackage Community
+ * @version    1.0.0
+ * @license    See below
+ *
+ *    ______                 ___                    _______ _______ ________
+ *   |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
+ *  |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
+ * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
+ *
+ * ============================================================================
+ * Copyright (c) 2005-2019 Artica Soluciones Tecnologicas
+ * Please see http://pandorafms.org for full contribution list
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation for version 2.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * ============================================================================
+ */
 
-// Pandora FMS - http://pandorafms.com
-// ==================================================
-// Copyright (c) 2005-2019 Artica Soluciones Tecnologicas
-// Please see http://pandorafms.org for full contribution list
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation for version 2.
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-// Warning: This file may be required into the metaconsole's setup
-// Load global vars
 global $config;
 
 require_once $config['homedir'].'/include/functions_notifications.php';
@@ -26,66 +39,127 @@ if (! check_acl($config['id_user'], 0, 'PM') && ! is_user_admin($config['id_user
 }
 
 // AJAX actions.
-$source_id = get_parameter('source_id', '');
+$source = get_parameter('source', '');
 $users = get_parameter('users', '');
 $elements = get_parameter('elements', []);
-$id = empty($source_id) ? 0 : get_notification_source_id($source_id);
 $is_users = $users === 'users';
 if (get_parameter('get_selection_two_ways_form', 0)) {
-    $info_selec = $is_users ? notifications_get_user_source_not_configured($id) : notifications_get_group_source_not_configured($id);
+    $info_selec = ($is_users === true) ? notifications_get_user_source_not_configured($source) : notifications_get_group_source_not_configured($source);
 
     echo notifications_print_two_ways_select(
         $info_selec,
         $users,
-        $source_id
+        $source
     );
     return;
 }
 
 if (get_parameter('add_source_to_database', 0)) {
-    $res = $is_users ? notifications_add_users_to_source($id, $elements) : notifications_add_group_to_source($id, $elements);
+    $res = ($is_users) ? notifications_add_users_to_source($source, $elements) : notifications_add_group_to_source($source, $elements);
     $result = ['result' => $res];
     echo json_encode($result);
     return;
 }
 
 if (get_parameter('remove_source_on_database', 0)) {
-    $res = $is_users ? notifications_remove_users_from_source($id, $elements) : notifications_remove_group_from_source($id, $elements);
+    $res = ($is_users) ? notifications_remove_users_from_source($source, $elements) : notifications_remove_group_from_source($source, $elements);
     $result = ['result' => $res];
     echo json_encode($result);
     return;
 }
 
-// Form actions.
 if (get_parameter('update_config', 0)) {
-    $res_global = array_reduce(
-        notifications_get_all_sources(),
-        function ($carry, $source) {
-            $id = notifications_desc_to_id($source['description']);
-            if (empty($id)) {
-                return false;
-            }
+    $element = (string) get_parameter('element', '');
+    $value = (int) get_parameter('value', 0);
 
-            $enable_value = switch_to_int(get_parameter("enable-$id"));
-            $mail_value = (int) get_parameter("mail-{$id}", 0);
-            $user_value = (int) get_parameter("user-{$id}", 0);
-            $postpone_value = (int) get_parameter("postpone-{$id}", 0);
-            $all_users = (int) get_parameter("all-{$id}", 0);
-            $res = db_process_sql_update(
+    // Update the label value.
+    ob_clean();
+    $res = false;
+    switch ($element) {
+        // All users has other action.
+        case 'all_users':
+            $res = ($value) ? notifications_add_group_to_source($source, [0]) : notifications_remove_group_from_source($source, [0]);
+        break;
+
+        default:
+            $res = (bool) db_process_sql_update(
                 'tnotification_source',
-                [
-                    'enabled'           => $enable_value,
-                    'user_editable'     => $user_value,
-                    'also_mail'         => $mail_value,
-                    'max_postpone_time' => $postpone_value,
-                ],
-                ['id' => $source['id']]
+                [$element => $value],
+                ['id' => $source]
             );
-            $all_users_res = $all_users ? notifications_add_group_to_source($source['id'], [0]) : notifications_remove_group_from_source($source['id'], [0]);
-            return $all_users_res && $res && $carry;
-        },
-        true
+        break;
+    }
+
+    echo json_encode(['result' => $res]);
+    return;
+}
+
+if (get_parameter('check_new_notifications', 0)) {
+    $last_id_ui = (int) get_parameter('last_id', 0);
+    $counters = notifications_get_counters();
+    if ((int) $last_id_ui === (int) $counters['last_id']) {
+        echo json_encode(['has_new_notifications' => false]);
+        return;
+    }
+
+    $messages = messages_get_overview(
+        'timestamp',
+        'ASC',
+        false,
+        true,
+        0,
+        ['id_mensaje' => '>'.$last_id_ui]
     );
+    if ($messages === false) {
+        $messages = [];
+    }
+
+    // If there is new messages, get the info.
+    echo json_encode(
+        [
+            'has_new_notifications' => true,
+            'new_ball'              => base64_encode(
+                notifications_print_ball(
+                    $counters['notifications'],
+                    $counters['last_id']
+                )
+            ),
+            'new_notifications'     => array_map(
+                function ($elem) {
+                    $elem['full_url'] = messages_get_url($elem['id_mensaje']);
+                    return $elem;
+                },
+                $messages
+            ),
+        ]
+    );
+    return;
+}
+
+if (get_parameter('mark_notification_as_read', 0)) {
+    $message = (int) get_parameter('message', 0);
+    messages_process_read($message);
+    // TODO check read.
+    $url = messages_get_url($message);
+    // Return false if cannot get the URL.
+    if ($url === false) {
+        echo json_encode(['result' => false]);
+        return;
+    }
+
+    // If there is new messages, get the info.
+    echo json_encode(
+        [
+            'result' => true,
+            'url'    => $url,
+        ]
+    );
+    return;
+}
+
+if (get_parameter('get_notifications_dropdown', 0)) {
+    echo notifications_print_dropdown();
+    return;
 }
 
 // Notification table. It is just a wrapper.
@@ -96,36 +170,18 @@ $table_content->id = 'notifications-wrapper';
 $table_content->class = 'databox filters';
 $table_content->size['name'] = '30%';
 
-// Print each source configuration
+// Print each source configuration.
 $table_content->data = array_map(
     function ($source) {
         return notifications_print_global_source_configuration($source);
     },
     notifications_get_all_sources()
 );
-$table_content->data[] = html_print_submit_button(
-    __('Update'),
-    'update_button',
-    false,
-    'class="sub upd" style="display: flex; "',
-    true
-);
 
-echo '<form id="form_enable" method="post">';
-html_print_input_hidden('update_config', 1);
 html_print_table($table_content);
-echo '</form>';
 
 ?>
 <script>
-
-// Get the source id
-function notifications_get_source_id(id) {
-    var matched = id.match(/.*-(.*)/);
-    if (matched == null) return '';
-    return matched[1];
-}
-
 // Get index of two ways element dialog.
 function notifications_two_ways_element_get_dialog (id, source_id) {
     return 'global_config_notifications_dialog_add-' + id + '-' + source_id;
@@ -136,16 +192,6 @@ function notifications_two_ways_element_get_sufix (id, source_id) {
     return 'multi-' + id + '-' + source_id;
 }
 
-// Disable or enable the select seeing the checked value of notify all users
-function notifications_disable_source(event) {
-    var id = notifications_get_source_id(event.target.id);
-    var is_checked = document.getElementById(event.target.id).checked;
-    var selectors = ['groups', 'users'];
-    selectors.map(function (select) {
-        document.getElementById(notifications_two_ways_element_get_sufix(select, id)).disabled = is_checked;
-    });
-}
-
 // Open a dialog with selector of source elements.
 function add_source_dialog(users, source_id) {
     // Display the dialog
@@ -155,7 +201,10 @@ function add_source_dialog(users, source_id) {
     if (previous_dialog !== null) previous_dialog.remove();
     // Create or recreate the content.
     var not_dialog = document.createElement('div');
-    not_dialog.setAttribute('class', 'global_config_notifications_dialog_add_wrapper');
+    not_dialog.setAttribute(
+        'class',
+        'global_config_notifications_dialog_add_wrapper'
+    );
     not_dialog.setAttribute('id', dialog_id);
     document.body.appendChild(not_dialog);
     $("#" + dialog_id).dialog({
@@ -175,7 +224,7 @@ function add_source_dialog(users, source_id) {
         {"page" : "godmode/setup/setup_notifications",
             "get_selection_two_ways_form" : 1,
             "users" : users,
-            "source_id" : source_id
+            "source" : source_id
         },
         function (data, status) {
             not_dialog.innerHTML = data
@@ -204,7 +253,8 @@ function notifications_modify_two_ways_element (id, source_id, operation) {
 
 // Add elements to database and close dialog
 function notifications_add_source_element_to_database(id, source_id) {
-    var index = 'selected-' + notifications_two_ways_element_get_sufix (id, source_id);
+    var index = 'selected-' +
+        notifications_two_ways_element_get_sufix (id, source_id);
     var select = document.getElementById(index);
     var selected = [];
     for (var i = select.options.length - 1; i >= 0; i--) {
@@ -214,7 +264,7 @@ function notifications_add_source_element_to_database(id, source_id) {
         {"page" : "godmode/setup/setup_notifications",
             "add_source_to_database" : 1,
             "users" : id,
-            "source_id" : source_id,
+            "source" : source_id,
             "elements": selected
         },
         function (data, status) {
@@ -227,7 +277,11 @@ function notifications_add_source_element_to_database(id, source_id) {
                     out_select.appendChild(select.options[i]);
                 }
                 // Close the dialog
-                $("#" + notifications_two_ways_element_get_dialog(id, source_id)).dialog("close");
+                $("#" + notifications_two_ways_element_get_dialog(
+                    id,
+                    source_id
+                ))
+                .dialog("close");
             } else {
                 console.log("Cannot update element.");
             }
@@ -252,13 +306,13 @@ function remove_source_elements(id, source_id) {
         {"page" : "godmode/setup/setup_notifications",
             "remove_source_on_database" : 1,
             "users" : id,
-            "source_id" : source_id,
+            "source" : source_id,
             "elements": selected
         },
         function (data, status) {
             if (data.result) {
                 // Append to other element
-                for (var i = selected_index.length - 1; i >= 0; i--) {
+                for (var i = 0; i < selected_index.length; i++) {
                     select.remove(selected_index[i]);
                 }
             } else {
@@ -268,4 +322,94 @@ function remove_source_elements(id, source_id) {
         "json"
     );
 }
+
+function notifications_handle_change_element(event) {
+    event.preventDefault();
+    var match = /nt-([0-9]+)-(.*)/.exec(event.target.id);
+    if (!match) {
+        console.error(
+            "Cannot handle change element. Id not valid: ", event.target.id
+        );
+        return;
+    }
+    var action = {source: match[1], bit: match[2]};
+    var element = document.getElementById(event.target.id);
+    if (element === null) {
+        console.error(
+            "Cannot get element. Id: ", event.target.id
+        );
+        return;
+    }
+
+    var value;
+    switch (action.bit) {
+        case 'enabled':
+        case 'also_mail':
+        case 'user_editable':
+        case 'all_users':
+            value = element.checked ? 1 : 0;
+            break;
+        case 'max_postpone_time':
+            value = element.value;
+            break;
+        default:
+            console.error("Unregonized action", action.bit, '.');
+            return;
+
+    }
+    jQuery.post ("ajax.php",
+        {
+            "page" : "godmode/setup/setup_notifications",
+            "update_config" : 1,
+            "source" : match[1],
+            "element" : match[2],
+            "value": value
+        },
+        function (data, status) {
+            if (!data.result) {
+                console.error("Error changing configuration in database.");
+            } else {
+                switch (action.bit) {
+                    case 'enabled':
+                    case 'also_mail':
+                    case 'user_editable':
+                    case 'all_users':
+                        element.checked = !element.checked;
+                        break;
+                    case 'max_postpone_time':
+                        value = element.value;
+                        break;
+                    default:
+                        console.error(
+                            "Unregonized action (insert on db)", action.bit, '.'
+                        );
+                        return;
+                }
+            }
+        },
+        "json"
+    )
+    .done(function(m){})
+    .fail(function(xhr, textStatus, errorThrown){
+        console.error(
+            "Cannot change configuration in database. Server error.",
+            xhr.responseText
+        );
+    });
+}
+(function(){
+    // Add listener to all componentes marked
+    var all_clickables = document.getElementsByClassName('elem-clickable');
+    for (var i = 0; i < all_clickables.length; i++) {
+        all_clickables[i].addEventListener(
+            'click', notifications_handle_change_element, false
+        );
+    }
+    var all_changes = document.getElementsByClassName('elem-changeable');
+    for (var i = 0; i < all_changes.length; i++) {
+        all_changes[i].addEventListener(
+            'change', notifications_handle_change_element, false
+        );
+    }
+})();
 </script>
diff --git a/pandora_console/include/ajax/events.php b/pandora_console/include/ajax/events.php
index 5f6f62568b..711743d735 100644
--- a/pandora_console/include/ajax/events.php
+++ b/pandora_console/include/ajax/events.php
@@ -1,16 +1,31 @@
 <?php
+/**
+ * Manage AJAX response for event pages.
+ *
+ * @category   Ajax
+ * @package    Pandora FMS
+ * @subpackage Events
+ * @version    1.0.0
+ * @license    See below
+ *
+ *    ______                 ___                    _______ _______ ________
+ *   |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
+ *  |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
+ * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
+ *
+ * ============================================================================
+ * Copyright (c) 2005-2019 Artica Soluciones Tecnologicas
+ * Please see http://pandorafms.org for full contribution list
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation for version 2.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * ============================================================================
+ */
 
-// Pandora FMS - http://pandorafms.com
-// ==================================================
-// Copyright (c) 2005-2010 Artica Soluciones Tecnologicas
-// Please see http://pandorafms.org for full contribution list
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation for version 2.
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
 require_once 'include/functions_events.php';
 require_once 'include/functions_agents.php';
 require_once 'include/functions_ui.php';
@@ -210,11 +225,14 @@ if ($dialogue_event_response) {
     $prompt = '<br>> ';
     switch ($event_response['type']) {
         case 'command':
-
-
             if ($massive) {
                 echo "<div style='text-align:left'>";
-                echo $prompt.sprintf("(Event #$event_id) ".__('Executing command: %s', $command));
+                echo $prompt.sprintf(
+                    '(Event #'.$event_id.') '.__(
+                        'Executing command: %s',
+                        $command
+                    )
+                );
                 echo '</div><br>';
 
                 echo "<div id='response_loading_command_".$out_iterator."' style='display:none'>".html_print_image('images/spinner.gif', true).'</div>';
@@ -242,7 +260,11 @@ if ($dialogue_event_response) {
 
         case 'url':
             $command = str_replace('localhost', $_SERVER['SERVER_NAME'], $command);
-            echo "<iframe src='$command' id='divframe' style='width:100%;height:90%;'></iframe>";
+            echo "<iframe src='".$command."' id='divframe' style='width:100%;height:90%;'></iframe>";
+        break;
+
+        default:
+            // Ignore.
         break;
     }
 }
@@ -319,10 +341,10 @@ if ($get_extended_event) {
             $readonly = true;
     }
 
-    // Clean url from events and store in array
+    // Clean url from events and store in array.
     $event['clean_tags'] = events_clean_tags($event['tags']);
 
-    // If the event is not found, we abort
+    // If the event is not found, we abort.
     if (empty($event)) {
         ui_print_error_message('Event not found');
         return false;
@@ -341,42 +363,62 @@ if ($get_extended_event) {
     $event['timestamp_last'] = $timestamp_last;
     $event['event_rep'] = $event_rep;
 
-    // Check ACLs
+    // Check ACLs.
     if (is_user_admin($config['id_user'])) {
-        // Do nothing if you're admin, you get full access
+        // Do nothing if you're admin, you get full access.
+        $__ignored_line = 0;
     } else if ($config['id_user'] == $event['owner_user']) {
-        // Do nothing if you're the owner user, you get access
+        // Do nothing if you're the owner user, you get access.
+        $__ignored_line = 0;
     } else if ($event['id_grupo'] == 0) {
-        // If the event has access to all groups, you get access
+        // If the event has access to all groups, you get access.
+        $__ignored_line = 0;
     } else {
-        // Get your groups
+        // Get your groups.
         $groups = users_get_groups($config['id_user'], 'ER');
 
         if (in_array($event['id_grupo'], array_keys($groups))) {
-            // If the event group is among the groups of the user, you get access
+            // If event group is among the groups of the user, you get access.
+            $__ignored_line = 0;
         } else {
-            // If all the access types fail, abort
+            // If all the access types fail, abort.
             echo 'Access denied';
             return false;
         }
     }
 
-    // Print group_rep in a hidden field to recover it from javascript
+    // Print group_rep in a hidden field to recover it from javascript.
     html_print_input_hidden('group_rep', (int) $group_rep);
 
     if ($event === false) {
         return;
     }
 
-    // Tabs
+    // Tabs.
     $tabs = "<ul style='background:#ffffff !important; border-top: 0px; border-left: 0px; border-right: 0px; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; border-color: #D3D3D3;'>";
     $tabs .= "<li><a href='#extended_event_general_page' id='link_general'>".html_print_image('images/lightning_go.png', true)."<span style='position:relative;top:-6px;left:5px;margin-right:10px;'>".__('General').'</span></a></li>';
+    if (events_has_extended_info($event['id_evento']) === true) {
+        $tabs .= "<li><a href='#extended_event_related_page' id='link_related'>".html_print_image('images/zoom.png', true)."<span style='position:relative;top:-6px;left:5px;margin-right:10px;'>".__('Related').'</span></a></li>';
+    }
+
     $tabs .= "<li><a href='#extended_event_details_page' id='link_details'>".html_print_image('images/zoom.png', true)."<span style='position:relative;top:-6px;left:5px;margin-right:10px;'>".__('Details').'</span></a></li>';
     $tabs .= "<li><a href='#extended_event_custom_fields_page' id='link_custom_fields'>".html_print_image('images/custom_field_col.png', true)."<span style='position:relative;top:-6px;left:5px;margin-right:10px;'>".__('Agent fields').'</span></a></li>';
     $tabs .= "<li><a href='#extended_event_comments_page' id='link_comments'>".html_print_image('images/pencil.png', true)."<span style='position:relative;top:-6px;left:5px;margin-right:10px;'>".__('Comments').'</span></a></li>';
 
     if (!$readonly
-        && ((tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids)) || (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EW', $event['clean_tags'], $childrens_ids)))
+        && ((tags_checks_event_acl(
+            $config['id_user'],
+            $event['id_grupo'],
+            'EM',
+            $event['clean_tags'],
+            $childrens_ids
+        )) || (tags_checks_event_acl(
+            $config['id_user'],
+            $event['id_grupo'],
+            'EW',
+            $event['clean_tags'],
+            $childrens_ids
+        )))
     ) {
         $tabs .= "<li><a href='#extended_event_responses_page' id='link_responses'>".html_print_image('images/event_responses_col.png', true)."<span style='position:relative;top:-6px;left:3px;margin-right:10px;'>".__('Responses').'</span></a></li>';
     }
@@ -387,7 +429,7 @@ if ($get_extended_event) {
 
     $tabs .= '</ul>';
 
-    // Get criticity image
+    // Get criticity image.
     switch ($event['criticity']) {
         default:
         case 0:
@@ -419,7 +461,19 @@ if ($get_extended_event) {
     }
 
     if (!$readonly
-        && ((tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids)) || (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EW', $event['clean_tags'], $childrens_ids)))
+        && ((tags_checks_event_acl(
+            $config['id_user'],
+            $event['id_grupo'],
+            'EM',
+            $event['clean_tags'],
+            $childrens_ids
+        )) || (tags_checks_event_acl(
+            $config['id_user'],
+            $event['id_grupo'],
+            'EW',
+            $event['clean_tags'],
+            $childrens_ids
+        )))
     ) {
         $responses = events_page_responses($event, $childrens_ids);
     } else {
@@ -427,7 +481,7 @@ if ($get_extended_event) {
     }
 
     $console_url = '';
-    // If metaconsole switch to node to get details and custom fields
+    // If metaconsole switch to node to get details and custom fields.
     if ($meta) {
         $server = metaconsole_get_connection_by_id($server_id);
         metaconsole_connect($server);
@@ -437,7 +491,12 @@ if ($get_extended_event) {
 
     $details = events_page_details($event, $server);
 
-    // Juanma (09/05/2014) Fix: Needs to reconnect to node, in previous funct node connection was lost
+    if (events_has_extended_info($event['id_evento']) === true) {
+        $related = events_page_related($event, $server);
+    }
+
+    // Juanma (09/05/2014) Fix: Needs to reconnect to node, in previous funct
+    // node connection was lost.
     if ($meta) {
         $server = metaconsole_get_connection_by_id($server_id);
             metaconsole_connect($server);
@@ -464,7 +523,7 @@ if ($get_extended_event) {
 
     $loading = '<div id="response_loading" style="display:none">'.html_print_image('images/spinner.gif', true).'</div>';
 
-    $out = '<div id="tabs" style="height:95%; overflow: auto">'.$tabs.$notifications.$loading.$general.$details.$custom_fields.$comments.$responses.$custom_data.html_print_input_hidden('id_event', $event['id_evento']).'</div>';
+    $out = '<div id="tabs" style="height:95%; overflow: auto">'.$tabs.$notifications.$loading.$general.$details.$related.$custom_fields.$comments.$responses.$custom_data.html_print_input_hidden('id_event', $event['id_evento']).'</div>';
 
     $js = '<script>
 	$(function() {
@@ -472,7 +531,7 @@ if ($get_extended_event) {
 		});
 		';
 
-    // Load the required tab
+    // Load the required tab.
     switch ($dialog_page) {
         case 'general':
             $js .= '$tabs.tabs( "option", "active", 0);';
@@ -482,26 +541,53 @@ if ($get_extended_event) {
             $js .= '$tabs.tabs( "option", "active", 1);';
         break;
 
-        case 'custom_fields':
+        case 'related':
             $js .= '$tabs.tabs( "option", "active", 2);';
         break;
 
-        case 'comments':
+        case 'custom_fields':
             $js .= '$tabs.tabs( "option", "active", 3);';
         break;
 
-        case 'responses':
+        case 'comments':
             $js .= '$tabs.tabs( "option", "active", 4);';
         break;
 
-        case 'custom_data':
+        case 'responses':
             $js .= '$tabs.tabs( "option", "active", 5);';
         break;
+
+        case 'custom_data':
+            $js .= '$tabs.tabs( "option", "active", 6);';
+        break;
+
+        default:
+            // Ignore.
+        break;
     }
 
-    $js .= '
-	});
-	</script>';
+    $js .= '});';
+
+    if (events_has_extended_info($event['id_evento']) === true) {
+        $js .= '
+        $("#link_related").click(function (){
+          $.post ({
+                url : "ajax.php",
+                data : {
+                    page: "include/ajax/events_extended",
+                    get_extended_info: 1,
+                    id_event: '.$event['id_evento'].'
+                },
+                dataType : "html",
+                success: function (data) {
+                    $("#related_data").html(data);
+                    console.log("vamos puta");
+                }
+            });
+        });';
+    }
+
+    $js .= '</script>';
 
     echo $out.$js;
 }
@@ -543,6 +629,10 @@ if ($get_events_details) {
                 $img = ui_get_full_url('images/hourglass.png', false, false, false);
                 $title = __('Event in process');
             break;
+
+            default:
+                // Ignore.
+            break;
         }
 
         $out .= '<tr class="'.get_priority_class($event['criticity']).'" style="height: 25px;">';
@@ -594,7 +684,8 @@ if ($table_events) {
     $id_agente = (int) get_parameter('id_agente', 0);
     $all_events_24h = (int) get_parameter('all_events_24h', 0);
 
-    // Fix: for tag functionality groups have to be all user_groups (propagate ACL funct!)
+    // Fix: for tag functionality groups have to be all user_groups
+    // (propagate ACL funct!).
     $groups = users_get_groups($config['id_user']);
 
     $tags_condition = tags_get_acl_tags(
@@ -612,7 +703,7 @@ if ($table_events) {
 
     if ($all_events_24h) {
         events_print_event_table(
-            "utimestamp > $date_subtract_day",
+            'utimestamp > '.$date_subtract_day,
             200,
             '100%',
             false,
@@ -621,7 +712,7 @@ if ($table_events) {
         );
     } else {
         events_print_event_table(
-            "estado <> 1 $tags_condition",
+            'estado <> 1 '.$tags_condition,
             200,
             '100%',
             false,
diff --git a/pandora_console/include/ajax/events_extended.php b/pandora_console/include/ajax/events_extended.php
new file mode 100644
index 0000000000..4993014ddf
--- /dev/null
+++ b/pandora_console/include/ajax/events_extended.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ * Manage AJAX response for event pages.
+ *
+ * @category   Ajax
+ * @package    Pandora FMS
+ * @subpackage Events extended
+ * @version    1.0.0
+ * @license    See below
+ *
+ *    ______                 ___                    _______ _______ ________
+ *   |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
+ *  |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
+ * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
+ *
+ * ============================================================================
+ * Copyright (c) 2005-2019 Artica Soluciones Tecnologicas
+ * Please see http://pandorafms.org for full contribution list
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation for version 2.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * ============================================================================
+ */
+
+require_once 'include/functions_events.php';
+
+
+enterprise_include_once('meta/include/functions_events_meta.php');
+enterprise_include_once('include/functions_metaconsole.php');
+
+
+global $config;
+
+// Check ACLs.
+if (is_user_admin($config['id_user']) === true) {
+    // Do nothing if you're admin, you get full access.
+    $allowed = true;
+} else if ($config['id_user'] == $event['owner_user']) {
+    // Do nothing if you're the owner user, you get access.
+    $allowed = true;
+} else if ($event['id_grupo'] == 0) {
+    // If the event has access to all groups, you get access.
+    $allowed = true;
+} else {
+    // Get your groups.
+    $groups = users_get_groups($config['id_user'], 'ER');
+
+    if (in_array($event['id_grupo'], array_keys($groups))) {
+        // If event group is among the groups of the user, you get access.
+        $__ignored_line = true;
+    } else {
+        // If all the access types fail, abort.
+        $allowed = false;
+    }
+}
+
+if ($allowed === false) {
+    echo 'Access denied';
+    exit;
+}
+
+$id_event = get_parameter('id_event', null);
+$get_extended_info = get_parameter('get_extended_info', 0);
+
+
+if ($get_extended_info == 1) {
+    if (isset($id_event) === false) {
+        echo 'Internal error. Invalid event.';
+        exit;
+    }
+
+    $extended_info = events_get_extended_events($id_event);
+
+    $table = new StdClass();
+    //
+    // Details.
+    //
+    $table->width = '100%';
+    $table->data = [];
+    $table->head = [];
+    $table->style[0] = 'min-width:120px; text-align: left;';
+    $table->style[1] = 'text-align: left;';
+    $table->cellspacing = 2;
+    $table->cellpadding = 2;
+    $table->class = 'alternate rounded_cells';
+
+    $output = [];
+    $output[] = '<b>'.__('Timestamp').'</b>';
+    $output[] = '<b>'.__('Description').'</b>';
+    $table->data[] = $output;
+
+    foreach ($extended_info as $data) {
+        $output = [];
+        $output[] = date('Y/m/d H:i:s', $data['utimestamp']);
+        $output[] = io_safe_output($data['description']);
+        $table->data[] = $output;
+    }
+
+    html_print_table($table);
+}
diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php
index e532deea15..d55eda62e5 100644
--- a/pandora_console/include/functions_config.php
+++ b/pandora_console/include/functions_config.php
@@ -748,6 +748,10 @@ function config_update_config()
                         }
                     }
 
+                    if (!config_update_value('delete_old_messages', get_parameter('delete_old_messages'))) {
+                        $error_update[] = __('Max. days before delete old messages');
+                    }
+
                     if (!config_update_value('max_graph_container', get_parameter('max_graph_container'))) {
                         $error_update[] = __('Graph container - Max. Items');
                     }
@@ -1535,6 +1539,10 @@ function config_process_config()
         }
     }
 
+    if (!isset($config['delete_old_messages'])) {
+        config_update_value('delete_old_messages', 21);
+    }
+
     if (!isset($config['max_graph_container'])) {
         config_update_value('max_graph_container', 10);
     }
diff --git a/pandora_console/include/functions_events.php b/pandora_console/include/functions_events.php
index a2ca914b37..d03c797138 100644
--- a/pandora_console/include/functions_events.php
+++ b/pandora_console/include/functions_events.php
@@ -1,17 +1,32 @@
 <?php
+/**
+ * Events library.
+ *
+ * @category   Library
+ * @package    Pandora FMS
+ * @subpackage Events
+ * @version    1.0.0
+ * @license    See below
+ *
+ *    ______                 ___                    _______ _______ ________
+ *   |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
+ *  |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
+ * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
+ *
+ * ============================================================================
+ * Copyright (c) 2005-2019 Artica Soluciones Tecnologicas
+ * Please see http://pandorafms.org for full contribution list
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation for version 2.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * ============================================================================
+ */
 
-// Pandora FMS - http://pandorafms.com
-// ==================================================
-// Copyright (c) 2005-2011 Artica Soluciones Tecnologicas
-// Please see http://pandorafms.org for full contribution list
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the  GNU Lesser General Public License
-// as published by the Free Software Foundation; version 2
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-require_once $config['homedir'].'/include/functions_ui.php';
+ require_once $config['homedir'].'/include/functions_ui.php';
 require_once $config['homedir'].'/include/functions_tags.php';
 require_once $config['homedir'].'/include/functions.php';
 enterprise_include_once('meta/include/functions_events_meta.php');
@@ -20,11 +35,10 @@ enterprise_include_once('meta/include/functions_modules_meta.php');
 
 
 /**
- * @package    Include
- * @subpackage Events
+ * Return all header string for each event field.
+ *
+ * @return array
  */
-
-
 function events_get_all_fields()
 {
     $columns = [];
@@ -59,23 +73,36 @@ function events_get_all_fields()
  * Get all rows of events from the database, that
  * pass the filter, and can get only some fields.
  *
- * @param mixed Filters elements. It can be an indexed array
- * (keys would be the field name and value the expected value, and would be
- * joined with an AND operator) or a string, including any SQL clause (without
- * the WHERE keyword). Example:
- * <code>
- * Both are similars:
- * db_get_all_rows_filter ('table', array ('disabled', 0));
- * db_get_all_rows_filter ('table', 'disabled = 0');
+ * @param mixed $filter Filters elements. It can be an indexed array
+ *                      (keys would be the field name and value the expected
+ *                      value, and would be joined with an AND operator) or a
+ *                      string, including any SQL clause (without the WHERE
+ *                      keyword). Example:
+ *                <code>
+ *                Both are similars:
+ *                db_get_all_rows_filter ('table', ['disabled', 0]);
+ *                db_get_all_rows_filter ('table', 'disabled = 0');
+ *                Both are similars:
+ *                db_get_all_rows_filter (
+ *                    'table',
+ *                    [
+ *                         'disabled' => 0,
+ *                         'history_data' => 0
+ *                    ],
+ *                    'name',
+ *                    'OR'
+ *                );
+ *                db_get_all_rows_filter (
+ *                    'table',
+ *                    'disabled = 0 OR history_data = 0', 'name'
+ *                );
+ *                </code>.
+ * @param mixed $fields Fields of the table to retrieve. Can be an array or a
+ *                      coma separated string. All fields are retrieved by
+ *                      default.
  *
- * Both are similars:
- * db_get_all_rows_filter ('table', array ('disabled' => 0, 'history_data' => 0), 'name', 'OR');
- * db_get_all_rows_filter ('table', 'disabled = 0 OR history_data = 0', 'name');
- * </code>
- * @param mixed Fields of the table to retrieve. Can be an array or a coma
- * separated string. All fields are retrieved by default
- *
- * @return mixed False in case of error or invalid values passed. Affected rows otherwise
+ * @return mixed False in case of error or invalid values passed.
+ *               Affected rows otherwise
  */
 function events_get_events($filter=false, $fields=false)
 {
@@ -93,10 +120,13 @@ function events_get_events($filter=false, $fields=false)
 /**
  * Get the event with the id pass as parameter.
  *
- * @param integer $id     Event id
- * @param mixed   $fields The fields to show or by default all with false.
+ * @param integer $id      Event id.
+ * @param mixed   $fields  The fields to show or by default all with false.
+ * @param boolean $meta    Metaconsole environment or not.
+ * @param boolean $history Retrieve also historical data.
  *
- * @return mixed False in case of error or invalid values passed. Event row otherwise
+ * @return mixed False in case of error or invalid values passed.
+ *               Event row otherwise.
  */
 function events_get_event($id, $fields=false, $meta=false, $history=false)
 {
@@ -123,6 +153,20 @@ function events_get_event($id, $fields=false, $meta=false, $history=false)
 }
 
 
+/**
+ * Retrieve all events ungrouped.
+ *
+ * @param string  $sql_post   Sql_post.
+ * @param integer $offset     Offset.
+ * @param integer $pagination Pagination.
+ * @param boolean $meta       Meta.
+ * @param boolean $history    History.
+ * @param boolean $total      Total.
+ * @param boolean $history_db History_db.
+ * @param string  $order      Order.
+ *
+ * @return mixed Array of events or false.
+ */
 function events_get_events_no_grouped(
     $sql_post,
     $offset=0,
@@ -137,7 +181,7 @@ function events_get_events_no_grouped(
 
     $table = events_get_events_table($meta, $history);
 
-    $sql = "SELECT * FROM $table te WHERE 1=1 ".$sql_post;
+    $sql = 'SELECT * FROM '.$table.' te WHERE 1=1 '.$sql_post;
 
     $events = db_get_all_rows_sql($sql, $history_db);
 
@@ -145,6 +189,21 @@ function events_get_events_no_grouped(
 }
 
 
+/**
+ * Return all events matching sql_post grouped.
+ *
+ * @param [type]  $sql_post   Sql_post.
+ * @param integer $offset     Offset.
+ * @param integer $pagination Pagination.
+ * @param boolean $meta       Meta.
+ * @param boolean $history    History.
+ * @param boolean $total      Total.
+ * @param boolean $history_db History_db.
+ * @param string  $order      Order.
+ * @param string  $sort_field Sort_field.
+ *
+ * @return mixed Array of events or false.
+ */
 function events_get_events_grouped(
     $sql_post,
     $offset=0,
@@ -166,101 +225,39 @@ function events_get_events_grouped(
         $groupby_extra = '';
     }
 
-    switch ($config['dbtype']) {
-        case 'mysql':
-            db_process_sql('SET group_concat_max_len = 9999999');
-            $event_lj = events_get_secondary_groups_left_join($table);
-            if ($total) {
-                $sql = "SELECT COUNT(*) FROM (SELECT *
-					FROM $table te $event_lj
-					WHERE 1=1 ".$sql_post.'
-					GROUP BY estado, evento, id_agente, id_agentmodule'.$groupby_extra.') AS t';
-            } else {
-                $sql = "SELECT *, MAX(id_evento) AS id_evento,
-					GROUP_CONCAT(DISTINCT user_comment SEPARATOR '<br>') AS user_comment,
-					GROUP_CONCAT(DISTINCT id_evento SEPARATOR ',') AS similar_ids,
-					COUNT(*) AS event_rep, MAX(utimestamp) AS timestamp_rep, 
-					MIN(utimestamp) AS timestamp_rep_min,
-					(SELECT owner_user FROM $table WHERE id_evento = MAX(te.id_evento)) owner_user,
-					(SELECT id_usuario FROM $table WHERE id_evento = MAX(te.id_evento)) id_usuario,
-					(SELECT id_agente FROM $table WHERE id_evento = MAX(te.id_evento)) id_agente,
-					(SELECT criticity FROM $table WHERE id_evento = MAX(te.id_evento)) AS criticity,
-					(SELECT ack_utimestamp FROM $table WHERE id_evento = MAX(te.id_evento)) AS ack_utimestamp,
-					(SELECT nombre FROM tagente_modulo WHERE id_agente_modulo = te.id_agentmodule) AS module_name
-				FROM $table te $event_lj
-				WHERE 1=1 ".$sql_post.'
-				GROUP BY estado, evento, id_agente, id_agentmodule'.$groupby_extra;
-                $sql .= ' '.events_get_sql_order($sort_field, $order, 2);
-                $sql .= ' LIMIT '.$offset.','.$pagination;
-            }
-        break;
-
-        case 'postgresql':
-            if ($total) {
-                $sql = "SELECT COUNT(*)
-					FROM $table te
-					WHERE 1=1 ".$sql_post.'
-					GROUP BY estado, evento, id_agentmodule, id_evento, id_agente, id_usuario, id_grupo, estado, timestamp, utimestamp, event_type, id_alert_am, criticity, user_comment, tags, source, id_extra'.$groupby_extra;
-            } else {
-                $sql = "SELECT *, MAX(id_evento) AS id_evento, array_to_string(array_agg(DISTINCT user_comment), '<br>') AS user_comment,
-						array_to_string(array_agg(DISTINCT id_evento), ',') AS similar_ids,
-						COUNT(*) AS event_rep, MAX(utimestamp) AS timestamp_rep, 
-						MIN(utimestamp) AS timestamp_rep_min,
-						(SELECT owner_user FROM $table WHERE id_evento = MAX(te.id_evento)) owner_user,
-						(SELECT id_usuario FROM $table WHERE id_evento = MAX(te.id_evento)) id_usuario,
-						(SELECT id_agente FROM $table WHERE id_evento = MAX(te.id_evento)) id_agente,
-						(SELECT criticity FROM $table WHERE id_evento = MAX(te.id_evento)) AS criticity,
-						(SELECT ack_utimestamp FROM $table WHERE id_evento = MAX(te.id_evento)) AS ack_utimestamp
-					FROM $table te
-					WHERE 1=1 ".$sql_post.'
-					GROUP BY estado, evento, id_agentmodule, id_evento,
-						id_agente, id_usuario, id_grupo, estado,
-						timestamp, utimestamp, event_type, id_alert_am,
-						criticity, user_comment, tags, source, id_extra,
-						te.critical_instructions,
-						te.warning_instructions,
-						te.unknown_instructions,
-						te.owner_user,
-						te.ack_utimestamp,
-						te.custom_data '.$groupby_extra.'
-					ORDER BY timestamp_rep ASC LIMIT '.$pagination.' OFFSET '.$offset;
-            }
-        break;
-
-        case 'oracle':
-            if ($total) {
-                $sql = "SELECT COUNT(*)
-						FROM $table te
-						WHERE 1=1 $sql_post
-						GROUP BY estado, to_char(evento), id_agentmodule".$groupby_extra.') b ';
-            } else {
-                $set = [];
-                $set['limit'] = $pagination;
-                $set['offset'] = $offset;
-
-                $sql = "SELECT ta.*, tb.event_rep, tb.timestamp_rep, tb.timestamp_rep_min, tb.user_comments, tb.similar_ids
-						FROM $table ta
-						INNER JOIN (SELECT MAX(id_evento) AS id_evento, COUNT(id_evento) AS event_rep,
-										MAX(utimestamp) AS timestamp_rep, MIN(utimestamp) AS timestamp_rep_min,
-										TAB_TO_STRING(CAST(COLLECT(TO_CHAR(user_comment) ORDER BY id_evento ASC) AS t_varchar2_tab), '<br>') AS user_comments,
-										TAB_TO_STRING(CAST(COLLECT(CAST(id_evento AS VARCHAR2(4000)) ORDER BY id_evento ASC) AS t_varchar2_tab)) AS similar_ids
-									FROM $table te
-									WHERE 1=1 $sql_post
-									GROUP BY estado, to_char(evento), id_agentmodule$groupby_extra) tb
-							ON ta.id_evento = tb.id_evento
-						ORDER BY tb.timestamp_rep ASC";
-                $sql = oracle_recode_query($sql, $set);
-            }
-        break;
+    db_process_sql('SET group_concat_max_len = 9999999');
+    $event_lj = events_get_secondary_groups_left_join($table);
+    if ($total) {
+        $sql = 'SELECT COUNT(*) FROM (SELECT *
+            FROM '.$table.' te '.$event_lj.'
+            WHERE 1=1 '.$sql_post.'
+            GROUP BY estado, evento, id_agente, id_agentmodule'.$groupby_extra.') AS t';
+    } else {
+        $sql = "SELECT *, MAX(id_evento) AS id_evento,
+            GROUP_CONCAT(DISTINCT user_comment SEPARATOR '<br>') AS user_comment,
+            GROUP_CONCAT(DISTINCT id_evento SEPARATOR ',') AS similar_ids,
+            COUNT(*) AS event_rep, MAX(utimestamp) AS timestamp_rep, 
+            MIN(utimestamp) AS timestamp_rep_min,
+            (SELECT owner_user FROM ".$table.' WHERE id_evento = MAX(te.id_evento)) owner_user,
+            (SELECT id_usuario FROM '.$table.' WHERE id_evento = MAX(te.id_evento)) id_usuario,
+            (SELECT id_agente FROM '.$table.' WHERE id_evento = MAX(te.id_evento)) id_agente,
+            (SELECT criticity FROM '.$table.' WHERE id_evento = MAX(te.id_evento)) AS criticity,
+            (SELECT ack_utimestamp FROM '.$table.' WHERE id_evento = MAX(te.id_evento)) AS ack_utimestamp,
+            (SELECT nombre FROM tagente_modulo WHERE id_agente_modulo = te.id_agentmodule) AS module_name
+        FROM '.$table.' te '.$event_lj.'
+        WHERE 1=1 '.$sql_post.'
+        GROUP BY estado, evento, id_agente, id_agentmodule'.$groupby_extra;
+        $sql .= ' '.events_get_sql_order($sort_field, $order, 2);
+        $sql .= ' LIMIT '.$offset.','.$pagination;
     }
 
-    // Extract the events by filter (or not) from db
+    // Extract the events by filter (or not) from db.
     $events = db_get_all_rows_sql($sql, $history_db);
 
     if ($total) {
         return reset($events[0]);
     } else {
-        // Override the column 'user_comment' with the column 'user_comments' when oracle
+        // Override the column 'user_comment' with the column 'user_comments' when oracle.
         if (!empty($events) && $config['dbtype'] == 'oracle') {
             array_walk(
                 $events,
@@ -275,6 +272,15 @@ function events_get_events_grouped(
 }
 
 
+/**
+ * Return count of events grouped.
+ *
+ * @param string  $sql_post Sql_post.
+ * @param boolean $meta     Meta.
+ * @param boolean $history  History.
+ *
+ * @return integer Number of events or false if failed.
+ */
 function events_get_total_events_grouped($sql_post, $meta=false, $history=false)
 {
     return events_get_events_grouped($sql_post, 0, 0, $meta, $history, true);
@@ -287,9 +293,9 @@ function events_get_total_events_grouped($sql_post, $meta=false, $history=false)
  * An event is similar then the event text (evento) and the id_agentmodule are
  * the same.
  *
- * @param int Event id to get similar events.
- * @param bool Metaconsole mode flag
- * @param bool History mode flag
+ * @param integer $id      Event id to get similar events.
+ * @param boolean $meta    Metaconsole mode flag.
+ * @param boolean $history History mode flag.
  *
  * @return array A list of events ids.
  */
@@ -299,7 +305,14 @@ function events_get_similar_ids($id, $meta=false, $history=false)
 
     $ids = [];
     if ($meta) {
-        $event = events_meta_get_event($id, ['evento', 'id_agentmodule'], $history);
+        $event = events_meta_get_event(
+            $id,
+            [
+                'evento',
+                'id_agentmodule',
+            ],
+            $history
+        );
     } else {
         $event = events_get_event($id, ['evento', 'id_agentmodule']);
     }
@@ -331,26 +344,33 @@ function events_get_similar_ids($id, $meta=false, $history=false)
 /**
  * Delete events in a transresponse
  *
- * @param mixed Event ID or array of events
- * @param bool Whether to delete similar events too.
- * @param bool Metaconsole mode flag
- * @param bool History mode flag
+ * @param mixed   $id_event Event ID or array of events.
+ * @param boolean $similar  Whether to delete similar events too.
+ * @param boolean $meta     Metaconsole mode flag.
+ * @param boolean $history  History mode flag.
  *
  * @return boolean Whether or not it was successful
  */
-function events_delete_event($id_event, $similar=true, $meta=false, $history=false)
-{
+function events_delete_event(
+    $id_event,
+    $similar=true,
+    $meta=false,
+    $history=false
+) {
     global $config;
 
     $table_event = events_get_events_table($meta, $history);
 
-    // Cleans up the selection for all unwanted values also casts any single values as an array
+    // Cleans up the selection for all unwanted values also casts any single values as an array.
     $id_event = (array) safe_int($id_event, 1);
 
-    // We must delete all events like the selected
+    // We must delete all events like the selected.
     if ($similar) {
         foreach ($id_event as $id) {
-            $id_event = array_merge($id_event, events_get_similar_ids($id, $meta, $history));
+            $id_event = array_merge(
+                $id_event,
+                events_get_similar_ids($id, $meta, $history)
+            );
         }
 
         $id_event = array_unique($id_event);
@@ -366,7 +386,7 @@ function events_delete_event($id_event, $similar=true, $meta=false, $history=fal
         }
 
         if (check_acl($config['id_user'], $event_group, 'EM') == 0) {
-            // Check ACL
+            // Check ACL.
             db_pandora_audit('ACL Violation', 'Attempted deleting event #'.$event);
             $errors++;
         } else {
@@ -376,7 +396,7 @@ function events_delete_event($id_event, $similar=true, $meta=false, $history=fal
                 $errors++;
             } else {
                 db_pandora_audit('Event deleted', 'Deleted event #'.$event);
-                // ACL didn't fail nor did return
+                // ACL didn't fail nor did return.
                 continue;
             }
         }
@@ -393,25 +413,29 @@ function events_delete_event($id_event, $similar=true, $meta=false, $history=fal
 
 
 /**
- * Change the status of one or various events
+ * Change the status of one or multiple events.
  *
- * @param mixed Event ID or array of events
- * @param int new status of the event
- * @param bool metaconsole mode flag
- * @param bool history mode flag
+ * @param mixed   $id_event   Event ID or array of events.
+ * @param integer $new_status New status of the event.
+ * @param boolean $meta       Metaconsole mode flag.
+ * @param boolean $history    History mode flag.
  *
  * @return boolean Whether or not it was successful
  */
-function events_change_status($id_event, $new_status, $meta=false, $history=false)
-{
+function events_change_status(
+    $id_event,
+    $new_status,
+    $meta=false,
+    $history=false
+) {
     global $config;
 
     $event_table = events_get_events_table($meta, $history);
 
-    // Cleans up the selection for all unwanted values also casts any single values as an array
+    // Cleans up the selection for all unwanted values also casts any single values as an array.
     $id_event = (array) safe_int($id_event, 1);
 
-    // Update ack info if the new status is validated
+    // Update ack info if the new status is validated.
     if ($new_status == EVENT_STATUS_VALIDATED) {
         $ack_utimestamp = time();
         $ack_user = $config['id_user'];
@@ -481,14 +505,20 @@ function events_change_status($id_event, $new_status, $meta=false, $history=fals
         return false;
     }
 
-    events_comment($id_event, '', "Change status to $status_string", $meta, $history);
+    events_comment(
+        $id_event,
+        '',
+        'Change status to '.$status_string,
+        $meta,
+        $history
+    );
 
     if ($meta && !empty($alerts)) {
         $server = metaconsole_get_connection_by_id($server_id);
         metaconsole_connect($server);
     }
 
-    // Put the alerts in standby or not depends the new status
+    // Put the alerts in standby or not depends the new status.
     foreach ($alerts as $alert) {
         switch ($new_status) {
             case EVENT_NEW:
@@ -499,6 +529,10 @@ function events_change_status($id_event, $new_status, $meta=false, $history=fals
             case EVENT_PROCESS:
                 alerts_agent_module_standby($alert, 1);
             break;
+
+            default:
+                // Ignore.
+            break;
         }
     }
 
@@ -511,23 +545,31 @@ function events_change_status($id_event, $new_status, $meta=false, $history=fals
 
 
 /**
- * Change the owner of an event if the event hasn't owner
+ * Change the owner of an event if the event hasn't owner.
  *
- * @param mixed Event ID or array of events
- * @param string id_user of the new owner. If is false, the current owner will be setted
- * @param bool flag to force the change or not (not force is change only when it hasn't owner)
- * @param bool metaconsole mode flag
- * @param bool history mode flag
+ * @param mixed   $id_event  Event ID or array of events.
+ * @param string  $new_owner Id_user of the new owner. If is false, the current
+ *                           owner will be setted.
+ * @param boolean $force     Flag to force the change or not (not force is
+ *                           change only when it hasn't owner).
+ * @param boolean $meta      Metaconsole mode flag.
+ * @param boolean $history   History mode flag.
  *
- * @return boolean Whether or not it was successful
+ * @return boolean Whether or not it was successful.
  */
-function events_change_owner($id_event, $new_owner=false, $force=false, $meta=false, $history=false)
-{
+function events_change_owner(
+    $id_event,
+    $new_owner=false,
+    $force=false,
+    $meta=false,
+    $history=false
+) {
     global $config;
 
     $event_table = events_get_events_table($meta, $history);
 
-    // Cleans up the selection for all unwanted values also casts any single values as an array
+    // Cleans up the selection for all unwanted values also casts any single
+    // values as an array.
     $id_event = (array) safe_int($id_event, 1);
 
     foreach ($id_event as $k => $id) {
@@ -548,20 +590,27 @@ function events_change_owner($id_event, $new_owner=false, $force=false, $meta=fa
     }
 
     // If no new_owner is provided, the current user will be the owner
-    // ** Comment this lines because if possible selected None owner in owner event. TIQUET: #2250***
+    // * #2250: Comment this lines because if possible selected None owner.
     // if (empty($new_owner)) {
     // $new_owner = $config['id_user'];
     // }
-    // Only generate comment when is forced (sometimes is changed the owner when comment)
+    // Only generate comment when is forced (sometimes is owner changes when
+    // comment).
     if ($force) {
-        events_comment($id_event, '', "Change owner to $new_owner", $meta, $history);
+        events_comment(
+            $id_event,
+            '',
+            'Change owner to '.$new_owner,
+            $meta,
+            $history
+        );
     }
 
     $values = ['owner_user' => $new_owner];
 
     $where = ['id_evento' => $id_event];
 
-    // If not force, add to where if owner_user = ''
+    // If not force, add to where if owner_user = ''.
     if (!$force) {
         $where['owner_user'] = '';
     }
@@ -582,6 +631,14 @@ function events_change_owner($id_event, $new_owner=false, $force=false, $meta=fa
 }
 
 
+/**
+ * Returns proper event table based on environment.
+ *
+ * @param boolean $meta    Metaconsole environment or not.
+ * @param boolean $history Historical data or not.
+ *
+ * @return string Table name.
+ */
 function events_get_events_table($meta, $history)
 {
     if ($meta) {
@@ -601,21 +658,30 @@ function events_get_events_table($meta, $history)
 /**
  * Comment events in a transresponse
  *
- * @param mixed Event ID or array of events
- * @param string comment to be registered
- * @param string action performed with the comment. Bu default just Added comment
- * @param bool Flag of metaconsole mode
- * @param bool Flag of history mode
+ * @param mixed   $id_event Event ID or array of events.
+ * @param string  $comment  Comment to be registered.
+ * @param string  $action   Action performed with comment. By default just add
+ *                          a comment.
+ * @param boolean $meta     Flag of metaconsole mode.
+ * @param boolean $history  Flag of history mode.
+ * @param boolean $similars Similars.
  *
  * @return boolean Whether or not it was successful
  */
-function events_comment($id_event, $comment='', $action='Added comment', $meta=false, $history=false, $similars=true)
-{
+function events_comment(
+    $id_event,
+    $comment='',
+    $action='Added comment',
+    $meta=false,
+    $history=false,
+    $similars=true
+) {
     global $config;
 
     $event_table = events_get_events_table($meta, $history);
 
-    // Cleans up the selection for all unwanted values also casts any single values as an array
+    // Cleans up the selection for all unwanted values also casts any single
+    // values as an array.
     $id_event = (array) safe_int($id_event, 1);
 
     foreach ($id_event as $k => $id) {
@@ -636,25 +702,30 @@ function events_comment($id_event, $comment='', $action='Added comment', $meta=f
         return false;
     }
 
-    // If the event hasn't owner, assign the user as owner
+    // If the event hasn't owner, assign the user as owner.
     events_change_owner($id_event);
 
-    // Get the current event comments
+    // Get the current event comments.
     $first_event = $id_event;
     if (is_array($id_event)) {
         $first_event = reset($id_event);
     }
 
-    $event_comments = db_get_value('user_comment', $event_table, 'id_evento', $first_event);
+    $event_comments = db_get_value(
+        'user_comment',
+        $event_table,
+        'id_evento',
+        $first_event
+    );
     $event_comments_array = [];
 
     if ($event_comments == '') {
         $comments_format = 'new';
     } else {
-        // If comments are not stored in json, the format is old
+        // If comments are not stored in json, the format is old.
         $event_comments_array = json_decode($event_comments);
 
-        if (is_null($event_comments_array)) {
+        if (empty($event_comments_array)) {
             $comments_format = 'old';
         } else {
             $comments_format = 'new';
@@ -672,12 +743,17 @@ function events_comment($id_event, $comment='', $action='Added comment', $meta=f
 
             $event_comments = io_json_mb_encode($event_comments_array);
 
-            // Update comment
-            $ret = db_process_sql_update($event_table, ['user_comment' => $event_comments], ['id_evento' => implode(',', $id_event)]);
+            // Update comment.
+            $ret = db_process_sql_update(
+                $event_table,
+                ['user_comment' => $event_comments],
+                ['id_evento' => implode(',', $id_event)]
+            );
         break;
 
         case 'old':
-            // Give old ugly format to comment. TODO: Change this method for aux table or json
+            // Give old ugly format to comment. TODO: Change this method for
+            // aux table or json.
             $comment = str_replace(["\r\n", "\r", "\n"], '<br>', $comment);
 
             if ($comment != '') {
@@ -686,30 +762,26 @@ function events_comment($id_event, $comment='', $action='Added comment', $meta=f
                 $commentbox = '';
             }
 
-            // Don't translate 'by' word because if various users with different languages
-            // make comments in the same console will be a mess
-            $comment = '<b>-- '.$action.' by '.$config['id_user'].' '.'['.date($config['date_format']).'] --</b><br>'.$commentbox.'<br>';
+            // Don't translate 'by' word because if multiple users with
+            // different languages make comments in the same console
+            // will be a mess.
+            $comment = '<b>-- '.$action.' by '.$config['id_user'].' ['.date($config['date_format']).'] --</b><br>'.$commentbox.'<br>';
 
-            // Update comment
-            switch ($config['dbtype']) {
-                // Oldstyle SQL to avoid innecesary PHP foreach
-                case 'mysql':
-                    $sql_validation = "UPDATE $event_table 
-						SET user_comment = concat('".$comment."', user_comment) 
-						WHERE id_evento in (".implode(',', $id_event).')';
+            // Update comment.
+            $sql_validation = sprintf(
+                'UPDATE %s
+                SET user_comment = concat("%s", user_comment)
+                WHERE id_evento in (%s)',
+                $event_table,
+                $comment,
+                implode(',', $id_event)
+            );
 
-                    $ret = db_process_sql($sql_validation);
-                break;
+            $ret = db_process_sql($sql_validation);
+        break;
 
-                case 'postgresql':
-                case 'oracle':
-                    $sql_validation = "UPDATE $event_table 
-						SET user_comment='".$comment."' || user_comment) 
-						WHERE id_evento in (".implode(',', $id_event).')';
-
-                    $ret = db_process_sql($sql_validation);
-                break;
-            }
+        default:
+            // Ignore.
         break;
     }
 
@@ -724,13 +796,18 @@ function events_comment($id_event, $comment='', $action='Added comment', $meta=f
 /**
  * Get group id of an event.
  *
- * @param integer $id_event Event id
+ * @param integer $id_event Event id.
  *
  * @return integer Group id of the given event.
  */
 function events_get_group($id_event)
 {
-    return (int) db_get_value('id_grupo', 'tevento', 'id_evento', (int) $id_event);
+    return (int) db_get_value(
+        'id_grupo',
+        'tevento',
+        'id_evento',
+        (int) $id_event
+    );
 }
 
 
@@ -743,24 +820,37 @@ function events_get_group($id_event)
  */
 function events_get_description($id_event)
 {
-    return (string) db_get_value('evento', 'tevento', 'id_evento', (int) $id_event);
+    return (string) db_get_value(
+        'evento',
+        'tevento',
+        'id_evento',
+        (int) $id_event
+    );
 }
 
 
 /**
  * Insert a event in the event log system.
  *
- * @param integer $event
- * @param integer $id_group
- * @param integer $id_agent
- * @param integer $status
- * @param string  $id_user
- * @param string  $event_type
- * @param integer $priority
- * @param integer $id_agent_module
- * @param integer $id_aam
+ * @param integer $event                 Event.
+ * @param integer $id_group              Id_group.
+ * @param integer $id_agent              Id_agent.
+ * @param integer $status                Status.
+ * @param string  $id_user               Id_user.
+ * @param string  $event_type            Event_type.
+ * @param integer $priority              Priority.
+ * @param integer $id_agent_module       Id_agent_module.
+ * @param integer $id_aam                Id_aam.
+ * @param string  $critical_instructions Critical_instructions.
+ * @param string  $warning_instructions  Warning_instructions.
+ * @param string  $unknown_instructions  Unknown_instructions.
+ * @param boolean $source                Source.
+ * @param string  $tags                  Tags.
+ * @param string  $custom_data           Custom_data.
+ * @param integer $server_id             Server_id.
+ * @param string  $id_extra              Id_extra.
  *
- * @return integer event id
+ * @return integer Event id.
  */
 function events_create_event(
     $event,
@@ -791,206 +881,64 @@ function events_create_event(
     if (defined('METACONSOLE')) {
         $table_events = 'tmetaconsole_event';
 
-        switch ($config['dbtype']) {
-            case 'mysql':
-                $sql = sprintf(
-                    '
-					INSERT INTO '.$table_events.' (id_agente, id_grupo, evento,
-						timestamp, estado, utimestamp, id_usuario,
-						event_type, criticity, id_agentmodule, id_alert_am,
-						critical_instructions, warning_instructions,
-						unknown_instructions, source, tags, custom_data,
-						server_id, id_extra, data, module_status) 
-					VALUES (%d, %d, "%s", NOW(), %d, UNIX_TIMESTAMP(NOW()),
-						"%s", "%s", %d, %d, %d, "%s", "%s", "%s", "%s",
-						"%s", "%s", %d, "%s", %d, %d)',
-                    $id_agent,
-                    $id_group,
-                    $event,
-                    $status,
-                    $id_user,
-                    $event_type,
-                    $priority,
-                    $id_agent_module,
-                    $id_aam,
-                    $critical_instructions,
-                    $warning_instructions,
-                    $unknown_instructions,
-                    $source,
-                    $tags,
-                    $custom_data,
-                    $server_id,
-                    $id_extra,
-                    $data,
-                    $module_status
-                );
-            break;
-
-            case 'postgresql':
-                $sql = sprintf(
-                    '
-					INSERT INTO '.$table_events.' (id_agente, id_grupo, evento,
-						timestamp, estado, utimestamp, id_usuario,
-						event_type, criticity, id_agentmodule, id_alert_am,
-						critical_instructions, warning_instructions,
-						unknown_instructions, source, tags, custom_data,
-						server_id, id_extra, data, module_status) 
-					VALUES (%d, %d, "%s", NOW(), %d,
-						ceil(date_part(\'epoch\', CURRENT_TIMESTAMP)), "%s",
-						"%s", %d, %d, %d, "%s", "%s", "%s", "%s", "%s",
-						"%s", %d, "%s", %d, %d)',
-                    $id_agent,
-                    $id_group,
-                    $event,
-                    $status,
-                    $id_user,
-                    $event_type,
-                    $priority,
-                    $id_agent_module,
-                    $id_aam,
-                    $critical_instructions,
-                    $warning_instructions,
-                    $unknown_instructions,
-                    $source,
-                    $tags,
-                    $custom_data,
-                    $server_id,
-                    $id_extra,
-                    $data,
-                    $module_status
-                );
-            break;
-
-            case 'oracle':
-                $sql = sprintf(
-                    '
-					INSERT INTO '.$table_events.' (id_agente, id_grupo, evento,
-						timestamp, estado, utimestamp, id_usuario,
-						event_type, criticity, id_agentmodule, id_alert_am,
-						critical_instructions, warning_instructions,
-						unknown_instructions, source, tags, custom_data,
-						server_id, id_extra, data, module_status) 
-					VALUES (%d, %d, "%s", CURRENT_TIMESTAMP, %d, UNIX_TIMESTAMP,
-						"%s", "%s", %d, %d, %d, "%s", "%s", "%s", "%s",
-						"%s", "%s", %d, "%s", %d, %d)',
-                    $id_agent,
-                    $id_group,
-                    $event,
-                    $status,
-                    $id_user,
-                    $event_type,
-                    $priority,
-                    $id_agent_module,
-                    $id_aam,
-                    $critical_instructions,
-                    $warning_instructions,
-                    $unknown_instructions,
-                    $source,
-                    $tags,
-                    $custom_data,
-                    $server_id,
-                    $id_extra,
-                    $data,
-                    $module_status
-                );
-            break;
-        }
+        $sql = sprintf(
+            'INSERT INTO '.$table_events.' (id_agente, id_grupo, evento,
+                timestamp, estado, utimestamp, id_usuario,
+                event_type, criticity, id_agentmodule, id_alert_am,
+                critical_instructions, warning_instructions,
+                unknown_instructions, source, tags, custom_data,
+                server_id, id_extra, data, module_status) 
+            VALUES (%d, %d, "%s", NOW(), %d, UNIX_TIMESTAMP(NOW()),
+                "%s", "%s", %d, %d, %d, "%s", "%s", "%s", "%s",
+                "%s", "%s", %d, "%s", %d, %d)',
+            $id_agent,
+            $id_group,
+            $event,
+            $status,
+            $id_user,
+            $event_type,
+            $priority,
+            $id_agent_module,
+            $id_aam,
+            $critical_instructions,
+            $warning_instructions,
+            $unknown_instructions,
+            $source,
+            $tags,
+            $custom_data,
+            $server_id,
+            $id_extra,
+            $data,
+            $module_status
+        );
     } else {
-        switch ($config['dbtype']) {
-            case 'mysql':
-                $sql = sprintf(
-                    '
-					INSERT INTO '.$table_events.' (id_agente, id_grupo, evento,
-						timestamp, estado, utimestamp, id_usuario,
-						event_type, criticity, id_agentmodule, id_alert_am,
-						critical_instructions, warning_instructions,
-						unknown_instructions, source, tags, custom_data, id_extra, data, module_status) 
-					VALUES (%d, %d, "%s", NOW(), %d, UNIX_TIMESTAMP(NOW()),
-						"%s", "%s", %d, %d, %d, "%s", "%s", "%s", "%s", "%s", "%s", "%s", %d, %d)',
-                    $id_agent,
-                    $id_group,
-                    $event,
-                    $status,
-                    $id_user,
-                    $event_type,
-                    $priority,
-                    $id_agent_module,
-                    $id_aam,
-                    $critical_instructions,
-                    $warning_instructions,
-                    $unknown_instructions,
-                    $source,
-                    $tags,
-                    $custom_data,
-                    $id_extra,
-                    $data,
-                    $module_status
-                );
-            break;
-
-            case 'postgresql':
-                $sql = sprintf(
-                    '
-					INSERT INTO '.$table_events.' (id_agente, id_grupo, evento,
-						timestamp, estado, utimestamp, id_usuario,
-						event_type, criticity, id_agentmodule, id_alert_am,
-						critical_instructions, warning_instructions,
-						unknown_instructions, source, tags, custom_data, id_extra, data, module_status) 
-					VALUES (%d, %d, "%s", NOW(), %d,
-						ceil(date_part(\'epoch\', CURRENT_TIMESTAMP)), "%s",
-						"%s", %d, %d, %d, "%s", "%s", "%s", "%s", "%s", "%s", "%s", %d, %d)',
-                    $id_agent,
-                    $id_group,
-                    $event,
-                    $status,
-                    $id_user,
-                    $event_type,
-                    $priority,
-                    $id_agent_module,
-                    $id_aam,
-                    $critical_instructions,
-                    $warning_instructions,
-                    $unknown_instructions,
-                    $source,
-                    $tags,
-                    $custom_data,
-                    $id_extra,
-                    $data,
-                    $module_status
-                );
-            break;
-
-            case 'oracle':
-                $sql = sprintf(
-                    '
-					INSERT INTO '.$table_events." (id_agente, id_grupo, evento,
-						timestamp, estado, utimestamp, id_usuario,
-						event_type, criticity, id_agentmodule, id_alert_am,
-						critical_instructions, warning_instructions,
-						unknown_instructions, source, tags, custom_data, id_extra, data, module_status) 
-					VALUES (%d, %d, '%s', CURRENT_TIMESTAMP, %d, UNIX_TIMESTAMP,
-						'%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d)",
-                    $id_agent,
-                    $id_group,
-                    $event,
-                    $status,
-                    $id_user,
-                    $event_type,
-                    $priority,
-                    $id_agent_module,
-                    $id_aam,
-                    $critical_instructions,
-                    $warning_instructions,
-                    $unknown_instructions,
-                    $source,
-                    $tags,
-                    $custom_data,
-                    $id_extra,
-                    $data,
-                    $module_status
-                );
-            break;
-        }
+        $sql = sprintf(
+            'INSERT INTO '.$table_events.' (id_agente, id_grupo, evento,
+                timestamp, estado, utimestamp, id_usuario,
+                event_type, criticity, id_agentmodule, id_alert_am,
+                critical_instructions, warning_instructions,
+                unknown_instructions, source, tags, custom_data, id_extra, data, module_status) 
+            VALUES (%d, %d, "%s", NOW(), %d, UNIX_TIMESTAMP(NOW()),
+                "%s", "%s", %d, %d, %d, "%s", "%s", "%s", "%s", "%s", "%s", "%s", %d, %d)',
+            $id_agent,
+            $id_group,
+            $event,
+            $status,
+            $id_user,
+            $event_type,
+            $priority,
+            $id_agent_module,
+            $id_aam,
+            $critical_instructions,
+            $warning_instructions,
+            $unknown_instructions,
+            $source,
+            $tags,
+            $custom_data,
+            $id_extra,
+            $data,
+            $module_status
+        );
     }
 
     return (int) db_process_sql($sql, 'insert_id');
@@ -998,24 +946,32 @@ function events_create_event(
 
 
 /**
- * Prints a small event table
+ * Prints a small event table.
  *
- * @param string                                    $filter SQL WHERE clause
- * @param integer                                   $limit  How many events to show
- * @param integer                                   $width  How wide the table should be
- * @param boolean                                   $return Prints out HTML if false
- * @param int agent id if is the table of one agent. 0 otherwise
+ * @param string  $filter        SQL WHERE clause.
+ * @param integer $limit         How many events to show.
+ * @param integer $width         How wide the table should be.
+ * @param boolean $return        Prints out HTML if false.
+ * @param integer $agent_id      Agent id if is the table of one agent.
+ *                               0 otherwise.
+ * @param boolean $tactical_view Be shown in tactical view or not.
  *
- * @return string HTML with table element
+ * @return string HTML with table element.
  */
-function events_print_event_table($filter='', $limit=10, $width=440, $return=false, $agent_id=0, $tactical_view=false)
-{
+function events_print_event_table(
+    $filter='',
+    $limit=10,
+    $width=440,
+    $return=false,
+    $agent_id=0,
+    $tactical_view=false
+) {
     global $config;
 
     if ($agent_id == 0) {
         $agent_condition = '';
     } else {
-        $agent_condition = " id_agente = $agent_id AND ";
+        $agent_condition = ' id_agente = '.$agent_id.' AND ';
     }
 
     if ($filter == '') {
@@ -1063,7 +1019,9 @@ function events_print_event_table($filter='', $limit=10, $width=440, $return=fal
         $table->cellclass = [];
         $table->data = [];
         $table->align = [];
-        $table->style[0] = $table->style[1] = $table->style[2] = 'width:25px;';
+        $table->style[0] = 'width:25px;';
+        $table->style[1] = 'width:25px;';
+        $table->style[2] = 'width:25px;';
         if ($agent_id == 0) {
             $table->style[3] = 'word-break: break-all;';
         }
@@ -1098,7 +1056,7 @@ function events_print_event_table($filter='', $limit=10, $width=440, $return=fal
         }
 
         foreach ($result as $event) {
-            // Copy all groups of the agent and append the event group
+            // Copy all groups of the agent and append the event group.
             $check_events = $all_groups;
             $check_events[] = $event['id_grupo'];
             if (! check_acl_one_of_groups($config['id_user'], $check_events, 'ER')) {
@@ -1107,7 +1065,7 @@ function events_print_event_table($filter='', $limit=10, $width=440, $return=fal
 
             $data = [];
 
-            // Colored box
+            // Colored box.
             switch ($event['estado']) {
                 case 0:
                     $img = 'images/star.png';
@@ -1123,6 +1081,10 @@ function events_print_event_table($filter='', $limit=10, $width=440, $return=fal
                     $img = 'images/hourglass.png';
                     $title = __('Event in process');
                 break;
+
+                default:
+                    // Ignore.
+                break;
             }
 
             $data[0] = html_print_image(
@@ -1167,20 +1129,25 @@ function events_print_event_table($filter='', $limit=10, $width=440, $return=fal
                 ]
             );
 
-            // Event type
+            // Event type.
             $data[2] = events_print_type_img($event['event_type'], true);
 
-            // Event text
-            $data[3] = ui_print_string_substr(strip_tags(io_safe_output($event['evento'])), 75, true, '7.5');
+            // Event text.
+            $data[3] = ui_print_string_substr(
+                strip_tags(io_safe_output($event['evento'])),
+                75,
+                true,
+                '7.5'
+            );
 
             if ($agent_id == 0) {
                 if ($event['id_agente'] > 0) {
-                    // Agent name
-                    // Get class name, for the link color...
+                    // Agent name.
+                    // Get class name, for the link color, etc.
                     $myclass = get_priority_class($event['criticity']);
 
-                    $data[4] = "<a class='$myclass' href='index.php?sec=estado&sec2=operation/agentes/ver_agente&id_agente=".$event['id_agente']."'>".agents_get_alias($event['id_agente']).'</A>';
-                    // for System or SNMP generated alerts
+                    $data[4] = "<a class='".$myclass."' href='index.php?sec=estado&sec2=operation/agentes/ver_agente&id_agente=".$event['id_agente']."'>".agents_get_alias($event['id_agente']).'</A>';
+                    // For System or SNMP generated alerts.
                 } else if ($event['event_type'] == 'system') {
                     $data[4] = __('System');
                 } else {
@@ -1188,13 +1155,21 @@ function events_print_event_table($filter='', $limit=10, $width=440, $return=fal
                 }
             }
 
-            // Timestamp
+            // Timestamp.
             $data[5] = ui_print_timestamp($event['timestamp'], true, ['style' => 'font-size: 7.5pt; letter-spacing: 0.3pt;']);
 
             $class = get_priority_class($event['criticity']);
-            $cell_classes[3] = $cell_classes[4] = $cell_classes[5] = $class;
+            $cell_classes[3] = $class;
+            $cell_classes[4] = $class;
+            $cell_classes[5] = $class;
+
             array_push($table->cellclass, $cell_classes);
-            // array_push ($table->rowclass, get_priority_class ($event["criticity"]));
+
+            /*
+                Commented out (old).
+                // array_push ($table->rowclass, get_priority_class ($event["criticity"]));
+            */
+
             array_push($table->data, $data);
         }
 
@@ -1234,16 +1209,19 @@ function events_print_event_table($filter='', $limit=10, $width=440, $return=fal
 
 
 /**
- * Prints the event type image
+ * Prints the event type image.
  *
- * @param string  $type     Event type from SQL
- * @param boolean $return   Whether to return or print
+ * @param string  $type     Event type from SQL.
+ * @param boolean $return   Whether to return or print.
  * @param boolean $only_url Flag to return only url of image, by default false.
  *
- * @return string HTML with img
+ * @return string HTML with img.
  */
-function events_print_type_img($type, $return=false, $only_url=false)
-{
+function events_print_type_img(
+    $type,
+    $return=false,
+    $only_url=false
+) {
     global $config;
 
     $output = '';
@@ -1261,13 +1239,13 @@ function events_print_type_img($type, $return=false, $only_url=false)
 
         case 'going_down_critical':
         case 'going_up_critical':
-            // This is to be backwards compatible
+            // This is to be backwards compatible.
             $icon = 'module_critical.png';
         break;
 
         case 'going_up_normal':
         case 'going_down_normal':
-            // This is to be backwards compatible
+            // This is to be backwards compatible.
             $icon = 'module_ok.png';
         break;
 
@@ -1307,7 +1285,7 @@ function events_print_type_img($type, $return=false, $only_url=false)
     }
 
     if ($only_url) {
-        $output = $urlImage.'/'.'images/'.$icon;
+        $output = $urlImage.'/images/'.$icon;
     } else {
         $output .= html_print_image(
             'images/'.$icon,
@@ -1327,8 +1305,8 @@ function events_print_type_img($type, $return=false, $only_url=false)
 /**
  * Prints the event type description
  *
- * @param string  $type   Event type from SQL
- * @param boolean $return Whether to return or print
+ * @param string  $type   Event type from SQL.
+ * @param boolean $return Whether to return or print.
  *
  * @return string HTML with img
  */
@@ -1355,13 +1333,13 @@ function events_print_type_description($type, $return=false)
 
         case 'going_down_critical':
         case 'going_up_critical':
-            // This is to be backwards compatible
+            // This is to be backwards compatible.
             $output .= __('Going up to critical state');
         break;
 
         case 'going_up_normal':
         case 'going_down_normal':
-            // This is to be backwards compatible
+            // This is to be backwards compatible.
             $output .= __('Going up to normal state');
         break;
 
@@ -1416,9 +1394,15 @@ function events_print_type_description($type, $return=false)
  *
  * The returned events will be in the time interval ($date - $period, $date]
  *
- * @param mixed   $id_group Group id to get events for.
- * @param integer $period   Period of time in seconds to get events.
- * @param integer $date     Beginning date to get events.
+ * @param mixed   $begin                     Begin.
+ * @param mixed   $result                    Result.
+ * @param mixed   $id_group                  Group id to get events for.
+ * @param integer $period                    Period in seconds to get events.
+ * @param integer $date                      Beginning date to get events.
+ * @param boolean $filter_event_validated    Filter_event_validated.
+ * @param boolean $filter_event_critical     Filter_event_critical.
+ * @param boolean $filter_event_warning      Filter_event_warning.
+ * @param boolean $filter_event_no_validated Filter_event_no_validated.
  *
  * @return array An array with all the events happened.
  */
@@ -1438,7 +1422,7 @@ function events_get_group_events_steps(
     $id_group = groups_safe_acl($config['id_user'], $id_group, 'ER');
 
     if (empty($id_group)) {
-        // An empty array means the user doesn't have access
+        // An empty array means the user doesn't have access.
         return false;
     }
 
@@ -1492,9 +1476,20 @@ function events_get_group_events_steps(
  *
  * The returned events will be in the time interval ($date - $period, $date]
  *
- * @param integer $id_agent Agent id to get events.
- * @param integer $period   Period of time in seconds to get events.
- * @param integer $date     Beginning date to get events.
+ * @param integer $id_agent                   Agent id to get events.
+ * @param integer $period                     Period in seconds to get events.
+ * @param integer $date                       Beginning date to get events.
+ * @param boolean $history                    History.
+ * @param boolean $show_summary_group         Show_summary_group.
+ * @param boolean $filter_event_severity      Filter_event_severity.
+ * @param boolean $filter_event_type          Filter_event_type.
+ * @param boolean $filter_event_status        Filter_event_status.
+ * @param boolean $filter_event_filter_search Filter_event_filter_search.
+ * @param boolean $id_group                   Id_group.
+ * @param boolean $events_group               Events_group.
+ * @param boolean $id_agent_module            Id_agent_module.
+ * @param boolean $events_module              Events_module.
+ * @param boolean $id_server                  Id_server.
  *
  * @return array An array with all the events happened.
  */
@@ -1528,7 +1523,7 @@ function events_get_agent(
         $id_group = groups_safe_acl($config['id_user'], $id_group, 'ER');
 
         if (empty($id_group)) {
-            // An empty array means the user doesn't have access
+            // An empty array means the user doesn't have access.
             return false;
         }
     }
@@ -1557,6 +1552,7 @@ function events_get_agent(
                 break;
 
                 default:
+                    // Ignore.
                 break;
             }
         }
@@ -1577,12 +1573,16 @@ function events_get_agent(
                 case 3:
                     $filter_event_status[$key] = ('0, 2');
                 default:
+                    // Ignore.
                 break;
             }
         }
 
         if (!$status_all) {
-            $sql_where .= ' AND estado IN ('.implode(', ', $filter_event_status).')';
+            $sql_where .= ' AND estado IN ('.implode(
+                ', ',
+                $filter_event_status
+            ).')';
         }
     }
 
@@ -1591,10 +1591,10 @@ function events_get_agent(
         $type = [];
         foreach ($filter_event_type as $event_type) {
             if ($event_type != '') {
-                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex
-                // for the user so for him is presented only "warning, critical and normal"
+                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex.
+                // Shown to user only "warning, critical and normal".
                 if ($event_type == 'warning' || $event_type == 'critical' || $event_type == 'normal') {
-                    $type[] = " event_type LIKE '%$event_type%' ";
+                    $type[] = " event_type LIKE '%".$event_type."%' ";
                 } else if ($event_type == 'not_normal') {
                     $type[] = " (event_type LIKE '%warning%' OR event_type LIKE '%critical%' OR event_type LIKE '%unknown%') ";
                 } else if ($event_type != 'all') {
@@ -1607,7 +1607,7 @@ function events_get_agent(
     }
 
     if (!empty($filter_event_filter_search)) {
-        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%"'.' OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
+        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%" OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
     }
 
     if ($events_group) {
@@ -1801,6 +1801,8 @@ function events_get_severity_types($severity_id)
 /**
  * Return all descriptions of event status.
  *
+ * @param boolean $report Show in report or not.
+ *
  * @return array Status description array.
  */
 function events_get_all_status($report=false)
@@ -1853,6 +1855,10 @@ function events_get_status($status_id)
         case 3:
             $status_desc = __('Only not validated');
         break;
+
+        default:
+            // Ignore.
+        break;
     }
 
     return $status_desc;
@@ -1872,10 +1878,10 @@ function events_check_event_filter_group($id_filter)
 
     $id_group = db_get_value('id_group_filter', 'tevent_filter', 'id_filter', $id_filter);
     $own_info = get_user_info($config['id_user']);
-    // Get group list that user has access
+    // Get group list that user has access.
     $groups_user = users_get_groups($config['id_user'], 'EW', $own_info['is_admin'], true);
 
-    // Permissions in any group allow to edit "All group" filters
+    // Permissions in any group allow to edit "All group" filters.
     if ($id_group == 0 && !empty($groups_user)) {
         return true;
     }
@@ -1896,9 +1902,9 @@ function events_check_event_filter_group($id_filter)
 /**
  *  Get a event filter.
  *
- * @param int Filter id to be fetched.
- * @param array Extra filter.
- * @param array Fields to be fetched.
+ * @param integer $id_filter Filter id to be fetched.
+ * @param array   $filter    Extra filter.
+ * @param array   $fields    Fields to be fetched.
  *
  * @return array A event filter matching id and filter or false.
  */
@@ -1920,7 +1926,9 @@ function events_get_event_filter($id_filter, $filter=false, $fields=false)
 /**
  *  Get a event filters in select format.
  *
- * @param  boolean If event filters are used for manage/view operations (non admin users can see group ALL for manage) # Fix
+ * @param boolean $manage If event filters are used for manage/view operations
+ *                        (non admin users can see group ALL for manage) # Fix.
+ *
  * @return array A event filter matching id and filter or false.
  */
 function events_get_event_filter_select($manage=true)
@@ -1967,13 +1975,20 @@ function events_get_event_filter_select($manage=true)
 }
 
 
-// Events pages functions to load modal window with advanced view of an event.
-// Called from include/ajax/events.php
+/**
+ * Events pages functions to load modal window with advanced view of an event.
+ * Called from include/ajax/events.php.
+ *
+ * @param mixed $event         Event.
+ * @param array $childrens_ids Children_ids.
+ *
+ * @return string HTML.
+ */
 function events_page_responses($event, $childrens_ids=[])
 {
     global $config;
     //
-    // Responses
+    // Responses.
     //
     $table_responses->cellspacing = 2;
     $table_responses->cellpadding = 2;
@@ -1986,26 +2001,43 @@ function events_page_responses($event, $childrens_ids=[])
     $table_responses->class = 'alternate rounded_cells';
 
     if (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids)) {
-        // Owner
+        // Owner.
         $data = [];
         $data[0] = __('Change owner');
-        // Owner change can be done to users that belong to the event group with ER permission
+        // Owner change can be done to users that belong to the event group
+        // with ER permission.
         $profiles_view_events = db_get_all_rows_filter('tperfil', ['event_view' => '1'], 'id_perfil');
         foreach ($profiles_view_events as $k => $v) {
             $profiles_view_events[$k] = reset($v);
         }
 
-        // Juanma (05/05/2014) Fix : Propagate ACL hell!
-        $_user_groups = array_keys(users_get_groups($config['id_user'], 'ER', users_can_manage_group_all()));
-        $strict_user = db_get_value('strict_acl', 'tusuario', 'id_user', $config['id_user']);
+        // Juanma (05/05/2014) Fix : Propagate ACL.
+        $_user_groups = array_keys(
+            users_get_groups($config['id_user'], 'ER', users_can_manage_group_all())
+        );
+        $strict_user = db_get_value(
+            'strict_acl',
+            'tusuario',
+            'id_user',
+            $config['id_user']
+        );
         if ($strict_user) {
-            $user_name = db_get_value('fullname', 'tusuario', 'id_user', $config['id_user']);
+            $user_name = db_get_value(
+                'fullname',
+                'tusuario',
+                'id_user',
+                $config['id_user']
+            );
 
             $users = [];
             $users[0]['id_user'] = $config['id_user'];
             $users[0]['fullname'] = $user_name;
         } else {
-            $users = groups_get_users($_user_groups, ['id_perfil' => $profiles_view_events], true);
+            $users = groups_get_users(
+                $_user_groups,
+                ['id_perfil' => $profiles_view_events],
+                true
+            );
         }
 
         foreach ($users as $u) {
@@ -2015,24 +2047,52 @@ function events_page_responses($event, $childrens_ids=[])
         if ($event['owner_user'] == '') {
             $owner_name = __('None');
         } else {
-            $owner_name = db_get_value('fullname', 'tusuario', 'id_user', $event['owner_user']);
+            $owner_name = db_get_value(
+                'fullname',
+                'tusuario',
+                'id_user',
+                $event['owner_user']
+            );
             $owners[$event['owner_user']] = $owner_name;
         }
 
-        $data[1] = html_print_select($owners, 'id_owner', $event['owner_user'], '', __('None'), -1, true);
-        $data[1] .= html_print_button(__('Update'), 'owner_button', false, 'event_change_owner();', 'class="sub next"', true);
+        $data[1] = html_print_select(
+            $owners,
+            'id_owner',
+            $event['owner_user'],
+            '',
+            __('None'),
+            -1,
+            true
+        );
+        $data[1] .= html_print_button(
+            __('Update'),
+            'owner_button',
+            false,
+            'event_change_owner();',
+            'class="sub next"',
+            true
+        );
 
         $table_responses->data[] = $data;
     }
 
-    // Status
+    // Status.
     $data = [];
     $data[0] = __('Change status');
 
     $status_blocked = false;
 
-    if (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids)) {
-        // If the user has manager acls, the status can be changed to all possibilities always
+    if (tags_checks_event_acl(
+        $config['id_user'],
+        $event['id_grupo'],
+        'EM',
+        $event['clean_tags'],
+        $childrens_ids
+    )
+    ) {
+        // If the user has manager acls, the status can be changed to all
+        // possibilities always.
         $status = [
             0 => __('New'),
             2 => __('In process'),
@@ -2041,7 +2101,8 @@ function events_page_responses($event, $childrens_ids=[])
     } else {
         switch ($event['estado']) {
             case 0:
-                // If the user hasnt manager acls and the event is new. The status can be changed
+                // If the user hasnt manager acls and the event is new.
+                // The status can be changed.
                 $status = [
                     2 => __('In process'),
                     1 => __('Validated'),
@@ -2049,48 +2110,99 @@ function events_page_responses($event, $childrens_ids=[])
             break;
 
             case 1:
-                // If the user hasnt manager acls and the event is validated. The status cannot be changed
+                // If the user hasnt manager acls and the event is validated.
+                // The status cannot be changed.
                 $status = [1 => __('Validated')];
                 $status_blocked = true;
             break;
 
             case 2:
-                // If the user hasnt manager acls and the event is in process. The status only can be changed to validated
+                // If the user hasnt manager acls and the event is in process.
+                // The status only can be changed to validated.
                 $status = [1 => __('Validated')];
             break;
+
+            default:
+                // Ignored.
+            break;
         }
     }
 
-    // The change status option will be enabled only when is possible change the status
-    $data[1] = html_print_select($status, 'estado', $event['estado'], '', '', 0, true, false, false, '', $status_blocked);
+    // The change status option will be enabled only when is possible change
+    // the status.
+    $data[1] = html_print_select(
+        $status,
+        'estado',
+        $event['estado'],
+        '',
+        '',
+        0,
+        true,
+        false,
+        false,
+        '',
+        $status_blocked
+    );
 
     if (!$status_blocked) {
-        $data[1] .= html_print_button(__('Update'), 'status_button', false, 'event_change_status(\''.$event['similar_ids'].'\');', 'class="sub next"', true);
+        $data[1] .= html_print_button(
+            __('Update'),
+            'status_button',
+            false,
+            'event_change_status(\''.$event['similar_ids'].'\');',
+            'class="sub next"',
+            true
+        );
     }
 
     $table_responses->data[] = $data;
 
-    // Comments
+    // Comments.
     $data = [];
     $data[0] = __('Comment');
-    $data[1] = html_print_button(__('Add comment'), 'comment_button', false, '$(\'#link_comments\').trigger(\'click\');', 'class="sub next"', true);
+    $data[1] = html_print_button(
+        __('Add comment'),
+        'comment_button',
+        false,
+        '$(\'#link_comments\').trigger(\'click\');',
+        'class="sub next"',
+        true
+    );
 
     $table_responses->data[] = $data;
 
-    if (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids)) {
-        // Delete
+    if (tags_checks_event_acl(
+        $config['id_user'],
+        $event['id_grupo'],
+        'EM',
+        $event['clean_tags'],
+        $childrens_ids
+    )
+    ) {
+        // Delete.
         $data = [];
         $data[0] = __('Delete event');
         $data[1] = '<form method="post">';
-        $data[1] .= html_print_button(__('Delete event'), 'delete_button', false, 'if(!confirm(\''.__('Are you sure?').'\')) { return false; } this.form.submit();', 'class="sub cancel"', true);
+        $data[1] .= html_print_button(
+            __('Delete event'),
+            'delete_button',
+            false,
+            'if(!confirm(\''.__('Are you sure?').'\')) { return false; } this.form.submit();',
+            'class="sub cancel"',
+            true
+        );
         $data[1] .= html_print_input_hidden('delete', 1, true);
-        $data[1] .= html_print_input_hidden('validate_ids', $event['id_evento'], true);
+        $data[1] .= html_print_input_hidden(
+            'validate_ids',
+            $event['id_evento'],
+            true
+        );
         $data[1] .= '</form>';
 
         $table_responses->data[] = $data;
     }
 
-    // Custom responses
+    // Custom responses.
     $data = [];
     $data[0] = __('Custom responses');
 
@@ -2126,7 +2238,14 @@ function events_page_responses($event, $childrens_ids=[])
             $server_id = 0;
         }
 
-        $data[1] .= html_print_button(__('Execute'), 'custom_response_button', false, 'execute_response('.$event['id_evento'].','.$server_id.')', "class='sub next'", true);
+        $data[1] .= html_print_button(
+            __('Execute'),
+            'custom_response_button',
+            false,
+            'execute_response('.$event['id_evento'].','.$server_id.')',
+            "class='sub next'",
+            true
+        );
     }
 
     $table_responses->data[] = $data;
@@ -2161,10 +2280,23 @@ function events_page_responses($event, $childrens_ids=[])
 }
 
 
-// Replace macros in the target of a response and return it
-// If server_id > 0, is a metaconsole query
-function events_get_response_target($event_id, $response_id, $server_id, $history=false)
-{
+/**
+ * Replace macros in the target of a response and return it.
+ * If server_id > 0, is a metaconsole query.
+ *
+ * @param integer $event_id    Event_id.
+ * @param integer $response_id Response_id.
+ * @param integer $server_id   Server_id.
+ * @param boolean $history     History.
+ *
+ * @return string Target.
+ */
+function events_get_response_target(
+    $event_id,
+    $response_id,
+    $server_id,
+    $history=false
+) {
     global $config;
 
     $event_response = db_get_row('tevent_response', 'id', $response_id);
@@ -2181,7 +2313,7 @@ function events_get_response_target($event_id, $response_id, $server_id, $histor
 
     $target = io_safe_output($event_response['target']);
 
-    // Substitute each macro
+    // Substitute each macro.
     if (strpos($target, '_agent_address_') !== false) {
         if ($meta) {
             $agente_table_name = 'tmetaconsole_agent';
@@ -2195,7 +2327,7 @@ function events_get_response_target($event_id, $response_id, $server_id, $histor
         }
 
         $ip = db_get_value_filter('direccion', $agente_table_name, $filter);
-        // If agent has not an ip, display N/A
+        // If agent has not an ip, display N/A.
         if ($ip === false) {
             $ip = __('N/A');
         }
@@ -2266,7 +2398,11 @@ function events_get_response_target($event_id, $response_id, $server_id, $histor
     }
 
     if (strpos($target, '_event_utimestamp_') !== false) {
-        $target = str_replace('_event_utimestamp_', $event['utimestamp'], $target);
+        $target = str_replace(
+            '_event_utimestamp_',
+            $event['utimestamp'],
+            $target
+        );
     }
 
     if (strpos($target, '_event_date_') !== false) {
@@ -2355,12 +2491,17 @@ function events_get_response_target($event_id, $response_id, $server_id, $histor
     }
 
     if (strpos($target, '_group_custom_id_') !== false) {
-        $group_custom_id = db_get_value($dbh, 'SELECT custom_id FROM tgrupo WHERE id_grupo=?', $event['id_grupo']);
+        $group_custom_id = db_get_value_sql(
+            sprintf(
+                'SELECT custom_id FROM tgrupo WHERE id_grupo=%s',
+                $event['id_grupo']
+            )
+        );
         $event_st = events_display_status($event['estado']);
         $target = str_replace('_group_custom_id_', $group_custom_id, $target);
     }
 
-    // Parse the event custom data
+    // Parse the event custom data.
     if (!empty($event['custom_data'])) {
         $custom_data = json_decode(base64_decode($event['custom_data']));
         foreach ($custom_data as $key => $value) {
@@ -2372,12 +2513,19 @@ function events_get_response_target($event_id, $response_id, $server_id, $histor
 }
 
 
+/**
+ * Generates 'custom field' page for event viewer.
+ *
+ * @param array $event Event to be displayed.
+ *
+ * @return string HTML.
+ */
 function events_page_custom_fields($event)
 {
     global $config;
 
     //
-    // Custom fields
+    // Custom fields.
     //
     $table->cellspacing = 2;
     $table->cellpadding = 2;
@@ -2418,7 +2566,7 @@ function events_page_custom_fields($event)
     }
 
     foreach ($fields as $field) {
-        // Owner
+        // Owner.
         $data = [];
         $data[0] = $field['name'];
 
@@ -2443,15 +2591,82 @@ function events_page_custom_fields($event)
 }
 
 
+/**
+ * Retrieves extended information of given event.
+ *
+ * @param integer $id_evento Target event.
+ *
+ * @return mixed array Of extended events or false if error.
+ */
+function events_get_extended_events(int $id_evento)
+{
+    return db_get_all_rows_sql(
+        sprintf(
+            'SELECT * FROM tevent_extended
+            WHERE id_evento=%d ORDER BY utimestamp DESC',
+            $id_evento
+        )
+    );
+
+}
+
+
+/**
+ * Return if event has extended info or not.
+ *
+ * @param integer $id_event Target event.
+ *
+ * @return boolean Has extended info or not
+ */
+function events_has_extended_info(int $id_event)
+{
+    return (bool) db_get_value_sql(
+        sprintf(
+            'SELECT count(*) as "n" FROM (
+                SELECT *
+                FROM tevent_extended WHERE id_evento=%d LIMIT 1) t',
+            $id_event
+        )
+    );
+}
+
+
+/**
+ * Generates the 'related' page in event view.
+ *
+ * @param array  $event  To be displayed.
+ * @param string $server Server (if in metaconsole environment).
+ *
+ * @return string HTML to be displayed.
+ */
+function events_page_related($event, $server='')
+{
+    $html = '<div id="extended_event_related_page" class="extended_event_pages">';
+    $html .= '<h4>'.__('Extended information').'</h4>';
+    $html .= '<div id="related_data"><p>'.__('Loading').'...</p></div>';
+    $html .= '</div>';
+
+    return $html;
+}
+
+
+/**
+ * Generates the 'details' page in event view.
+ *
+ * @param array  $event  To be displayed.
+ * @param string $server Server (if in metaconsole environment).
+ *
+ * @return string HTML to be displayed.
+ */
 function events_page_details($event, $server='')
 {
     global $img_sev;
     global $config;
 
-    // If server is provided, get the hash parameters
+    // If server is provided, get the hash parameters.
     if (!empty($server) && defined('METACONSOLE')) {
         $hashdata = metaconsole_get_server_hashdata($server);
-        $hashstring = '&amp;'.'loginhash=auto&'.'loginhash_data='.$hashdata.'&'.'loginhash_user='.str_rot13($config['id_user']);
+        $hashstring = '&amp;loginhash=auto&loginhash_data='.$hashdata.'&loginhash_user='.str_rot13($config['id_user']);
         $serverstring = $server['server_url'].'/';
 
         if (metaconsole_connect($server) !== NOERR) {
@@ -2463,7 +2678,7 @@ function events_page_details($event, $server='')
     }
 
     //
-    // Details
+    // Details.
     //
     $table_details->width = '100%';
     $table_details->data = [];
@@ -2474,15 +2689,20 @@ function events_page_details($event, $server='')
     $table_details->style[1] = 'text-align: left; height: 23px;';
     $table_details->class = 'alternate rounded_cells';
 
-    switch ($event['event_type']) {
+    /*
+     * Useless switch.
+
+        switch ($event['event_type']) {
         case 'going_unknown':
         case 'going_up_warning':
         case 'going_down_warning':
         case 'going_up_critical':
         case 'going_down_critical':
-
+        default:
+            // Ignore.
         break;
-    }
+        }
+     */
 
     if ($event['id_agente'] != 0) {
         $agent = db_get_row('tagente', 'id_agente', $event['id_agente']);
@@ -2490,7 +2710,6 @@ function events_page_details($event, $server='')
         $agent = [];
     }
 
-    $data = [];
     $data[0] = __('Agent details');
     $data[1] = empty($agent) ? '<i>'.__('N/A').'</i>' : '';
     $table_details->data[] = $data;
@@ -2499,9 +2718,24 @@ function events_page_details($event, $server='')
         $data = [];
         $data[0] = '<div style="font-weight:normal; margin-left: 20px;">'.__('Name').'</div>';
         if (can_user_access_node()) {
-            $data[1] = ui_print_agent_name($event['id_agente'], true, 'agent_medium', '', false, $serverstring, $hashstring, $agent['alias']);
+            $data[1] = ui_print_agent_name(
+                $event['id_agente'],
+                true,
+                'agent_medium',
+                '',
+                false,
+                $serverstring,
+                $hashstring,
+                $agent['alias']
+            );
         } else {
-            $data[1] = ui_print_truncate_text($agent['alias'], 'agent_medium', true, true, true);
+            $data[1] = ui_print_truncate_text(
+                $agent['alias'],
+                'agent_medium',
+                true,
+                true,
+                true
+            );
         }
 
         $table_details->data[] = $data;
@@ -2522,22 +2756,35 @@ function events_page_details($event, $server='')
 
         $data = [];
         $data[0] = '<div style="font-weight:normal; margin-left: 20px;">'.__('Last contact').'</div>';
-        $data[1] = $agent['ultimo_contacto'] == '1970-01-01 00:00:00' ? '<i>'.__('N/A').'</i>' : date_w_fixed_tz($agent['ultimo_contacto']);
+        $data[1] = ($agent['ultimo_contacto'] == '1970-01-01 00:00:00') ? '<i>'.__('N/A').'</i>' : date_w_fixed_tz($agent['ultimo_contacto']);
         $table_details->data[] = $data;
 
         $data = [];
         $data[0] = '<div style="font-weight:normal; margin-left: 20px;">'.__('Last remote contact').'</div>';
-        $data[1] = $agent['ultimo_contacto_remoto'] == '1970-01-01 00:00:00' ? '<i>'.__('N/A').'</i>' : date_w_fixed_tz($agent['ultimo_contacto_remoto']);
+        $data[1] = ($agent['ultimo_contacto_remoto'] == '1970-01-01 00:00:00') ? '<i>'.__('N/A').'</i>' : date_w_fixed_tz($agent['ultimo_contacto_remoto']);
         $table_details->data[] = $data;
 
         $data = [];
         $data[0] = '<div style="font-weight:normal; margin-left: 20px;">'.__('Custom fields').'</div>';
-        $data[1] = html_print_button(__('View custom fields'), 'custom_button', false, '$(\'#link_custom_fields\').trigger(\'click\');', 'class="sub next"', true);
+        $data[1] = html_print_button(
+            __('View custom fields'),
+            'custom_button',
+            false,
+            '$(\'#link_custom_fields\').trigger(\'click\');',
+            'class="sub next"',
+            true
+        );
         $table_details->data[] = $data;
     }
 
     if ($event['id_agentmodule'] != 0) {
-        $module = db_get_row_filter('tagente_modulo', ['id_agente_modulo' => $event['id_agentmodule'], 'delete_pending' => 0]);
+        $module = db_get_row_filter(
+            'tagente_modulo',
+            [
+                'id_agente_modulo' => $event['id_agentmodule'],
+                'delete_pending'   => 0,
+            ]
+        );
     } else {
         $module = [];
     }
@@ -2548,20 +2795,25 @@ function events_page_details($event, $server='')
     $table_details->data[] = $data;
 
     if (!empty($module)) {
-        // Module name
+        // Module name.
         $data = [];
         $data[0] = '<div style="font-weight:normal; margin-left: 20px;">'.__('Name').'</div>';
         $data[1] = $module['nombre'];
         $table_details->data[] = $data;
 
-        // Module group
+        // Module group.
         $data = [];
         $data[0] = '<div style="font-weight:normal; margin-left: 20px;">'.__('Module group').'</div>';
         $id_module_group = $module['id_module_group'];
         if ($id_module_group == 0) {
             $data[1] = __('No assigned');
         } else {
-            $module_group = db_get_value('name', 'tmodule_group', 'id_mg', $id_module_group);
+            $module_group = db_get_value(
+                'name',
+                'tmodule_group',
+                'id_mg',
+                $id_module_group
+            );
             $data[1] = '<a href="'.$serverstring.'index.php?sec=view&amp;sec2=operation/agentes/status_monitor&amp;status=-1&amp;modulegroup='.$id_module_group.$hashstring.'">';
             $data[1] .= $module_group;
             $data[1] .= '</a>';
@@ -2569,12 +2821,21 @@ function events_page_details($event, $server='')
 
         $table_details->data[] = $data;
 
-        // ACL
+        // ACL.
         $acl_graph = false;
-        $strict_user = (bool) db_get_value('strict_acl', 'tusuario', 'id_user', $config['id_user']);
+        $strict_user = (bool) db_get_value(
+            'strict_acl',
+            'tusuario',
+            'id_user',
+            $config['id_user']
+        );
 
         if (!empty($agent['id_grupo'])) {
-            $acl_graph = check_acl($config['id_user'], $agent['id_grupo'], 'RR');
+            $acl_graph = check_acl(
+                $config['id_user'],
+                $agent['id_grupo'],
+                'RR'
+            );
         }
 
         if ($acl_graph) {
@@ -2587,9 +2848,16 @@ function events_page_details($event, $server='')
             }
 
             $graph_type = return_graphtype($module_type);
-            $url = ui_get_full_url('operation/agentes/stat_win.php', false, false, false);
-            $handle = dechex(crc32($module['id_agente_modulo'].$module['nombre']));
-            $win_handle = "day_$handle";
+            $url = ui_get_full_url(
+                'operation/agentes/stat_win.php',
+                false,
+                false,
+                false
+            );
+            $handle = dechex(
+                crc32($module['id_agente_modulo'].$module['nombre'])
+            );
+            $win_handle = 'day_'.$handle;
 
             $graph_params = [
                 'type'    => $graph_type,
@@ -2600,13 +2868,13 @@ function events_page_details($event, $server='')
             ];
 
             if (defined('METACONSOLE')) {
-                // Set the server id
+                // Set the server id.
                 $graph_params['server'] = $server['id'];
             }
 
             $graph_params_str = http_build_query($graph_params);
 
-            $link = "winopeng('$url?$graph_params_str','$win_handle')";
+            $link = "winopeng('".$url.'?'.$graph_params_str."','".$win_handle."')";
 
             $data[1] = '<a href="javascript:'.$link.'">';
             $data[1] .= html_print_image('images/chart_curve.png', true);
@@ -2617,7 +2885,7 @@ function events_page_details($event, $server='')
 
     $data = [];
     $data[0] = __('Alert details');
-    $data[1] = $event['id_alert_am'] == 0 ? '<i>'.__('N/A').'</i>' : '';
+    $data[1] = ($event['id_alert_am'] == 0) ? '<i>'.__('N/A').'</i>' : '';
     $table_details->data[] = $data;
 
     if ($event['id_alert_am'] != 0) {
@@ -2708,12 +2976,19 @@ function events_page_details($event, $server='')
 }
 
 
+/**
+ * Generates content for 'custom data' page in event viewer.
+ *
+ * @param array $event Event.
+ *
+ * @return string HTML.
+ */
 function events_page_custom_data($event)
 {
     global $config;
 
     //
-    // Custom data
+    // Custom data.
     //
     if ($event['custom_data'] == '') {
         return '';
@@ -2745,14 +3020,26 @@ function events_page_custom_data($event)
 }
 
 
-// Get the event name from tevento and display it in console
+/**
+ * Get the event name from tevento and display it in console.
+ *
+ * @param string $db_name Target event name.
+ *
+ * @return string Event name.
+ */
 function events_display_name($db_name='')
 {
     return io_safe_output(str_replace('&#x0a;', '<br>', $db_name));
 }
 
 
-// Get the image and status value of event
+/**
+ * Get the image and status value of event.
+ *
+ * @param integer $status Status.
+ *
+ * @return string Image path.
+ */
 function events_display_status($status)
 {
     switch ($status) {
@@ -2773,15 +3060,26 @@ function events_display_status($status)
             'img'   => 'images/hourglass.png',
             'title' => __('Event in process'),
         ];
+
+        default:
+            // Ignore.
+        break;
     }
 }
 
 
-// Get the instruction of an event
-// $event_type: Type of event
-// $inst: Array with unknown warning and critical instructions
-// $italic: Display N/A between italic html marks if instruction is not found
-function events_display_instructions($event_type='', $inst, $italic=true)
+/**
+ * Get the instruction of an event.
+ *
+ * @param string  $event_type Type of event.
+ * @param array   $inst       Array with unknown warning and critical
+ *                            instructions.
+ * @param boolean $italic     Display N/A between italic html marks if
+ *                            instruction is not found.
+ *
+ * @return string Safe output.
+ */
+function events_display_instructions($event_type='', $inst=[], $italic=true)
 {
     switch ($event_type) {
         case 'going_unknown':
@@ -2818,23 +3116,39 @@ function events_display_instructions($event_type='', $inst, $italic=true)
                 return str_replace("\n", '<br>', io_safe_output($inst['unknown_instructions']));
             }
         break;
+
+        default:
+            // Ignore.
+        break;
     }
 
-    $na_return = $italic ? '<i>'.__('N/A').'</i>' : __('N/A');
+    $na_return = ($italic === true) ? '<i>'.__('N/A').'</i>' : __('N/A');
+
     return $na_return;
 }
 
 
+/**
+ * Generates 'general' page for events viewer.
+ *
+ * @param array $event Event.
+ *
+ * @return string HTML.
+ */
 function events_page_general($event)
 {
     global $img_sev;
     global $config;
 
-    // $group_rep = $event['similar_ids'] == -1 ? 1 : count(explode(',',$event['similar_ids']));
+    /*
+        Commented out (old)
+        // $group_rep = $event['similar_ids'] == -1 ? 1 : count(explode(',',$event['similar_ids']));
+    */
+
     global $group_rep;
 
     //
-    // General
+    // General.
     //
     $table_general->cellspacing = 2;
     $table_general->cellpadding = 2;
@@ -2885,7 +3199,10 @@ function events_page_general($event)
 
     $data = [];
     $data[0] = __('Type');
-    $data[1] = events_print_type_img($event['event_type'], true).' '.events_print_type_description($event['event_type'], true);
+    $data[1] = events_print_type_img(
+        $event['event_type'],
+        true
+    ).' '.events_print_type_description($event['event_type'], true);
     $table_general->data[] = $data;
 
     $data = [];
@@ -2919,7 +3236,7 @@ function events_page_general($event)
     $data[1] .= ' '.$event_criticity;
     $table_general->data[] = $data;
 
-    // Get Status
+    // Get Status.
     $event_st = events_display_status($event['estado']);
 
     $data = [];
@@ -2927,7 +3244,7 @@ function events_page_general($event)
     $data[1] = html_print_image($event_st['img'], true).' '.$event_st['title'];
     $table_general->data[] = $data;
 
-    // If event is validated, show who and when acknowleded it
+    // If event is validated, show who and when acknowleded it.
     $data = [];
     $data[0] = __('Acknowledged by');
 
@@ -2984,10 +3301,18 @@ function events_page_general($event)
 }
 
 
+/**
+ * Generate 'comments' page for event viewer.
+ *
+ * @param array $event         Event.
+ * @param array $childrens_ids Children ids.
+ *
+ * @return string HTML.
+ */
 function events_page_comments($event, $childrens_ids=[])
 {
     //
-    // Comments
+    // Comments.
     //
     global $config;
 
@@ -3001,13 +3326,13 @@ function events_page_comments($event, $childrens_ids=[])
     $event_comments = $event['user_comment'];
     $event_comments = str_replace(["\n", '&#x0a;'], '<br>', $event_comments);
 
-    // If comments are not stored in json, the format is old
+    // If comments are not stored in json, the format is old.
     $event_comments_array = json_decode($event_comments, true);
 
-    // Show the comments more recent first
+    // Show the comments more recent first.
     $event_comments_array = array_reverse($event_comments_array);
 
-    if (is_null($event_comments_array)) {
+    if (empty($event_comments_array)) {
         $comments_format = 'old';
     } else {
         $comments_format = 'new';
@@ -3034,7 +3359,7 @@ function events_page_comments($event, $childrens_ids=[])
         case 'old':
             $comments_array = explode('<br>', $event_comments);
 
-            // Split comments and put in table
+            // Split comments and put in table.
             $col = 0;
             $data = [];
 
@@ -3050,6 +3375,10 @@ function events_page_comments($event, $childrens_ids=[])
                     case 1:
                         $row_text = preg_replace("/[\r\n|\r|\n]/", '<br>', io_safe_output(strip_tags($c)));
                     break;
+
+                    default:
+                        // Ignore.
+                    break;
                 }
 
                 $data[$col] = $row_text;
@@ -3071,14 +3400,33 @@ function events_page_comments($event, $childrens_ids=[])
                 $table_comments->data[] = $data;
             }
         break;
+
+        default:
+            // Ignore.
+        break;
     }
 
-    if (((tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids)) || (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EW', $event['clean_tags'], $childrens_ids))) && $config['show_events_in_local'] == false || $config['event_replication'] == false) {
+    if (((tags_checks_event_acl(
+        $config['id_user'],
+        $event['id_grupo'],
+        'EM',
+        $event['clean_tags'],
+        $childrens_ids
+    )) || (tags_checks_event_acl(
+        $config['id_user'],
+        $event['id_grupo'],
+        'EW',
+        $event['clean_tags'],
+        $childrens_ids
+    ))) && $config['show_events_in_local'] == false || $config['event_replication'] == false
+    ) {
         $comments_form = '<br><div id="comments_form" style="width:98%;">'.html_print_textarea('comment', 3, 10, '', 'style="min-height: 15px; width: 100%; disabled"', true);
 
         $comments_form .= '<br><div style="text-align:right;">'.html_print_button(__('Add comment'), 'comment_button', false, 'event_comment();', 'class="sub next"', true).'</div><br></div>';
     } else {
-        $comments_form = ui_print_message(__('If event replication is ongoing, it won\'t be possible to enter comments here. This option is only to allow local pandora users to see comments, but not to operate with them. The operation, when event replication is enabled, must be done only in the Metaconsole.'));
+        $comments_form = ui_print_message(
+            __('If event replication is ongoing, it won\'t be possible to enter comments here. This option is only to allow local pandora users to see comments, but not to operate with them. The operation, when event replication is enabled, must be done only in the Metaconsole.')
+        );
     }
 
     $comments = '<div id="extended_event_comments_page" class="extended_event_pages">'.$comments_form.html_print_table($table_comments, true).'</div>';
@@ -3087,6 +3435,13 @@ function events_page_comments($event, $childrens_ids=[])
 }
 
 
+/**
+ * Retrieve event tags (cleaned).
+ *
+ * @param string $tags Tags.
+ *
+ * @return array of Tags.
+ */
 function events_clean_tags($tags)
 {
     if (empty($tags)) {
@@ -3103,9 +3458,14 @@ function events_clean_tags($tags)
  *
  * The returned events will be in the time interval ($date - $period, $date]
  *
- * @param mixed   $id_group Group id to get events for.
- * @param integer $period   Period of time in seconds to get events.
- * @param integer $date     Beginning date to get events.
+ * @param mixed   $id_group                   Group id to get events for.
+ * @param integer $period                     Period  in seconds to get events.
+ * @param integer $date                       Beginning date to get events.
+ * @param boolean $filter_event_severity      Filter_event_severity.
+ * @param boolean $filter_event_type          Filter_event_type.
+ * @param boolean $filter_event_status        Filter_event_status.
+ * @param boolean $filter_event_filter_search Filter_event_filter_search.
+ * @param boolean $dbmeta                     Dbmeta.
  *
  * @return array An array with all the events happened.
  */
@@ -3121,7 +3481,7 @@ function events_get_count_events_by_agent(
 ) {
     global $config;
 
-    // date
+    // Date.
     if (!is_numeric($date)) {
         $date = time_w_fixed_tz($date);
     }
@@ -3130,11 +3490,11 @@ function events_get_count_events_by_agent(
         $date = get_system_time();
     }
 
-    // group
+    // Group.
     $id_group = groups_safe_acl($config['id_user'], $id_group, 'AR');
 
     if (empty($id_group)) {
-        // An empty array means the user doesn't have access
+        // An empty array means the user doesn't have access.
         return false;
     }
 
@@ -3162,6 +3522,7 @@ function events_get_count_events_by_agent(
                 break;
 
                 default:
+                    // Ignore.
                 break;
             }
         }
@@ -3182,6 +3543,7 @@ function events_get_count_events_by_agent(
                 case 3:
                     $filter_event_status[$key] = ('0, 2');
                 default:
+                    // Ignore.
                 break;
             }
         }
@@ -3196,10 +3558,10 @@ function events_get_count_events_by_agent(
         $type = [];
         foreach ($filter_event_type as $event_type) {
             if ($event_type != '') {
-                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex
-                // for the user so for him is presented only "warning, critical and normal"
+                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex.
+                // Shown to user only "warning, critical and normal".
                 if ($event_type == 'warning' || $event_type == 'critical' || $event_type == 'normal') {
-                    $type[] = " event_type LIKE '%$event_type%' ";
+                    $type[] = " event_type LIKE '%".$event_type."%' ";
                 } else if ($event_type == 'not_normal') {
                     $type[] = " (event_type LIKE '%warning%' OR event_type LIKE '%critical%' OR event_type LIKE '%unknown%') ";
                 } else if ($event_type != 'all') {
@@ -3212,7 +3574,7 @@ function events_get_count_events_by_agent(
     }
 
     if (!empty($filter_event_filter_search)) {
-        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%"'.' OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
+        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%" OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
     }
 
     $tagente = 'tagente';
@@ -3265,9 +3627,14 @@ function events_get_count_events_by_agent(
  *
  * The returned events will be in the time interval ($date - $period, $date]
  *
- * @param mixed   $id_group Group id to get events for.
- * @param integer $period   Period of time in seconds to get events.
- * @param integer $date     Beginning date to get events.
+ * @param array   $filter                     Use target filter.
+ * @param integer $period                     Period in seconds to get events.
+ * @param integer $date                       Beginning date to get events.
+ * @param boolean $filter_event_severity      Filter_event_severity.
+ * @param boolean $filter_event_type          Filter_event_type.
+ * @param boolean $filter_event_status        Filter_event_status.
+ * @param boolean $filter_event_filter_search Filter_event_filter_search.
+ * @param boolean $dbmeta                     Dbmeta.
  *
  * @return array An array with all the events happened.
  */
@@ -3282,13 +3649,13 @@ function events_get_count_events_validated_by_user(
     $dbmeta=false
 ) {
     global $config;
-    // group
+    // Group.
     $sql_filter = ' AND 1=1 ';
     if (isset($filter['id_group'])) {
         $id_group = groups_safe_acl($config['id_user'], $filter['id_group'], 'AR');
 
         if (empty($id_group)) {
-            // An empty array means the user doesn't have access
+            // An empty array means the user doesn't have access.
             return false;
         }
 
@@ -3303,7 +3670,7 @@ function events_get_count_events_validated_by_user(
         $sql_filter .= sprintf(' AND id_agentmodule = %d ', $filter['id_agentmodule']);
     }
 
-    // date
+    // Date.
     if (!is_numeric($date)) {
         $date = time_w_fixed_tz($date);
     }
@@ -3336,6 +3703,7 @@ function events_get_count_events_validated_by_user(
                 break;
 
                 default:
+                    // Ignore.
                 break;
             }
         }
@@ -3356,6 +3724,7 @@ function events_get_count_events_validated_by_user(
                 case 3:
                     $filter_event_status[$key] = ('0, 2');
                 default:
+                    // Ignore.
                 break;
             }
         }
@@ -3370,10 +3739,10 @@ function events_get_count_events_validated_by_user(
         $type = [];
         foreach ($filter_event_type as $event_type) {
             if ($event_type != '') {
-                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex
-                // for the user so for him is presented only "warning, critical and normal"
+                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex.
+                // Shown to user only "warning, critical and normal".
                 if ($event_type == 'warning' || $event_type == 'critical' || $event_type == 'normal') {
-                    $type[] = " event_type LIKE '%$event_type%' ";
+                    $type[] = " event_type LIKE '%".$event_type."%' ";
                 } else if ($event_type == 'not_normal') {
                     $type[] = " (event_type LIKE '%warning%' OR event_type LIKE '%critical%' OR event_type LIKE '%unknown%') ";
                 } else if ($event_type != 'all') {
@@ -3386,7 +3755,7 @@ function events_get_count_events_validated_by_user(
     }
 
     if (!empty($filter_event_filter_search)) {
-        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%"'.' OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
+        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%" OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
     }
 
     $tevento = 'tevento';
@@ -3435,9 +3804,14 @@ function events_get_count_events_validated_by_user(
  *
  * The returned events will be in the time interval ($date - $period, $date]
  *
- * @param mixed   $id_group Group id to get events for.
- * @param integer $period   Period of time in seconds to get events.
- * @param integer $date     Beginning date to get events.
+ * @param mixed   $filter                     Target filter.
+ * @param integer $period                     Period in seconds to get events.
+ * @param integer $date                       Beginning date to get events.
+ * @param boolean $filter_event_severity      Filter_event_severity.
+ * @param boolean $filter_event_type          Filter_event_type.
+ * @param boolean $filter_event_status        Filter_event_status.
+ * @param boolean $filter_event_filter_search Filter_event_filter_search.
+ * @param boolean $dbmeta                     Dbmeta.
  *
  * @return array An array with all the events happened.
  */
@@ -3458,7 +3832,7 @@ function events_get_count_events_by_criticity(
         $id_group = groups_safe_acl($config['id_user'], $filter['id_group'], 'AR');
 
         if (empty($id_group)) {
-            // An empty array means the user doesn't have access
+            // An empty array means the user doesn't have access.
             return false;
         }
 
@@ -3505,6 +3879,7 @@ function events_get_count_events_by_criticity(
                 break;
 
                 default:
+                    // Ignore.
                 break;
             }
         }
@@ -3524,7 +3899,10 @@ function events_get_count_events_by_criticity(
 
                 case 3:
                     $filter_event_status[$key] = ('0, 2');
+                break;
+
                 default:
+                    // Ignored.
                 break;
             }
         }
@@ -3539,10 +3917,10 @@ function events_get_count_events_by_criticity(
         $type = [];
         foreach ($filter_event_type as $event_type) {
             if ($event_type != '') {
-                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex
-                // for the user so for him is presented only "warning, critical and normal"
+                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex.
+                // Shown to user only "warning, critical and normal".
                 if ($event_type == 'warning' || $event_type == 'critical' || $event_type == 'normal') {
-                    $type[] = " event_type LIKE '%$event_type%' ";
+                    $type[] = " event_type LIKE '%".$event_type."%' ";
                 } else if ($event_type == 'not_normal') {
                     $type[] = " (event_type LIKE '%warning%' OR event_type LIKE '%critical%' OR event_type LIKE '%unknown%') ";
                 } else if ($event_type != 'all') {
@@ -3555,7 +3933,7 @@ function events_get_count_events_by_criticity(
     }
 
     if (!empty($filter_event_filter_search)) {
-        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%"'.' OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
+        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%" OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
     }
 
     $tevento = 'tevento';
@@ -3597,9 +3975,14 @@ function events_get_count_events_by_criticity(
  *
  * The returned events will be in the time interval ($date - $period, $date]
  *
- * @param mixed   $id_group Group id to get events for.
- * @param integer $period   Period of time in seconds to get events.
- * @param integer $date     Beginning date to get events.
+ * @param mixed   $filter                     Target filter.
+ * @param integer $period                     Period in seconds to get events.
+ * @param integer $date                       Beginning date to get events.
+ * @param boolean $filter_event_severity      Filter_event_severity.
+ * @param boolean $filter_event_type          Filter_event_type.
+ * @param boolean $filter_event_status        Filter_event_status.
+ * @param boolean $filter_event_filter_search Filter_event_filter_search.
+ * @param boolean $dbmeta                     Dbmeta.
  *
  * @return array An array with all the events happened.
  */
@@ -3615,30 +3998,43 @@ function events_get_count_events_validated(
 ) {
     global $config;
 
-    // group
+    // Group.
     $sql_filter = ' 1=1 ';
     if (isset($filter['id_group'])) {
-        $id_group = groups_safe_acl($config['id_user'], $filter['id_group'], 'AR');
+        $id_group = groups_safe_acl(
+            $config['id_user'],
+            $filter['id_group'],
+            'AR'
+        );
 
         if (empty($id_group)) {
-            // An empty array means the user doesn't have access
+            // An empty array means the user doesn't have access.
             return false;
         }
 
-        $sql_filter .= sprintf(' AND id_grupo IN (%s) ', implode(',', $id_group));
+        $sql_filter .= sprintf(
+            ' AND id_grupo IN (%s) ',
+            implode(',', $id_group)
+        );
     }
 
-    // agent
+    // Agent.
     if (!empty($filter['id_agent'])) {
-        $sql_filter .= sprintf(' AND id_agente = %d ', $filter['id_agent']);
+        $sql_filter .= sprintf(
+            ' AND id_agente = %d ',
+            $filter['id_agent']
+        );
     }
 
-    // module
+    // Module.
     if (!empty($filter['id_agentmodule'])) {
-        $sql_filter .= sprintf(' AND id_agentmodule = %d ', $filter['id_agentmodule']);
+        $sql_filter .= sprintf(
+            ' AND id_agentmodule = %d ',
+            $filter['id_agentmodule']
+        );
     }
 
-    // date
+    // Date.
     if (!is_numeric($date)) {
         $date = time_w_fixed_tz($date);
     }
@@ -3691,6 +4087,7 @@ function events_get_count_events_validated(
                 break;
 
                 default:
+                    // Ingore.
                 break;
             }
         }
@@ -3710,7 +4107,10 @@ function events_get_count_events_validated(
 
                 case 3:
                     $filter_event_status[$key] = ('0, 2');
+                break;
+
                 default:
+                    // Ignore.
                 break;
             }
         }
@@ -3725,10 +4125,10 @@ function events_get_count_events_validated(
         $type = [];
         foreach ($filter_event_type as $event_type) {
             if ($event_type != '') {
-                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex
-                // for the user so for him is presented only "warning, critical and normal"
+                // If normal, warning, could be several (going_up_warning, going_down_warning... too complex.
+                // Shown to user only "warning, critical and normal".
                 if ($event_type == 'warning' || $event_type == 'critical' || $event_type == 'normal') {
-                    $type[] = " event_type LIKE '%$event_type%' ";
+                    $type[] = " event_type LIKE '%".$event_type."%' ";
                 } else if ($event_type == 'not_normal') {
                     $type[] = " (event_type LIKE '%warning%' OR event_type LIKE '%critical%' OR event_type LIKE '%unknown%') ";
                 } else if ($event_type != 'all') {
@@ -3741,7 +4141,7 @@ function events_get_count_events_validated(
     }
 
     if (!empty($filter_event_filter_search)) {
-        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%"'.' OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
+        $sql_where .= ' AND (evento LIKE "%'.io_safe_input($filter_event_filter_search).'%" OR id_evento LIKE "%'.io_safe_input($filter_event_filter_search).'%")';
     }
 
     $tevento = 'tevento';
@@ -3781,6 +4181,14 @@ function events_get_count_events_validated(
 }
 
 
+/**
+ * Check event tags.
+ *
+ * @param array $event_data Event.
+ * @param array $acltags    Acl tags.
+ *
+ * @return boolean True or false.
+ */
 function events_checks_event_tags($event_data, $acltags)
 {
     global $config;
@@ -3809,6 +4217,18 @@ function events_checks_event_tags($event_data, $acltags)
 }
 
 
+/**
+ * Retrieves events grouped by agent.
+ *
+ * @param string  $sql_post   Sql_post.
+ * @param integer $offset     Offset.
+ * @param integer $pagination Pagination.
+ * @param boolean $meta       Meta.
+ * @param boolean $history    History.
+ * @param boolean $total      Total.
+ *
+ * @return array Data.
+ */
 function events_get_events_grouped_by_agent(
     $sql_post,
     $offset=0,
@@ -3831,23 +4251,23 @@ function events_get_events_grouped_by_agent(
 
     $event_lj = events_get_secondary_groups_left_join($table);
     if ($total) {
-        $sql = "SELECT COUNT(*) FROM (select id_agente from $table $event_lj WHERE 1=1 
-				$sql_post GROUP BY id_agente, event_type$groupby_extra ORDER BY id_agente ) AS t";
+        $sql = 'SELECT COUNT(*) FROM (select id_agente from '.$table.' '.$event_lj.' WHERE 1=1 
+		    '.$sql_post.' GROUP BY id_agente, event_type'.$groupby_extra.' ORDER BY id_agente ) AS t';
     } else {
-        $sql = "select id_agente, count(*) as total$fields_extra from $table te $event_lj
-			WHERE id_agente > 0 $sql_post GROUP BY id_agente$groupby_extra ORDER BY id_agente LIMIT $offset,$pagination";
+        $sql = 'select id_agente, count(*) as total'.$fields_extra.' from '.$table.' te '.$event_lj.'
+			WHERE id_agente > 0 '.$sql_post.' GROUP BY id_agente'.$groupby_extra.' ORDER BY id_agente LIMIT '.$offset.','.$pagination;
     }
 
     $result = [];
-    // Extract the events by filter (or not) from db
+    // Extract the events by filter (or not) from db.
     $events = db_get_all_rows_sql($sql);
     $result = [];
 
     if ($events) {
         foreach ($events as $event) {
             if ($meta) {
-                $sql = "select event_type from $table te $event_lj
-								WHERE agent_name = '".$event['agent_name']."' $sql_post ORDER BY utimestamp DESC ";
+                $sql = 'SELECT event_type FROM '.$table.' te '.$event_lj."
+								WHERE agent_name = '".$event['agent_name']."' ".$sql_post.' ORDER BY utimestamp DESC ';
                 $resultado = db_get_row_sql($sql);
 
                 $id_agente = $event['agent_name'];
@@ -3858,8 +4278,8 @@ function events_get_events_grouped_by_agent(
                     'event_type' => $resultado['event_type'],
                 ];
             } else {
-                $sql = "SELECT event_type FROM $table te $event_lj
-					WHERE id_agente = ".$event['id_agente']." $sql_post ORDER BY utimestamp DESC ";
+                $sql = 'SELECT event_type FROM '.$table.' te '.$event_lj.'
+					WHERE id_agente = '.$event['id_agente'].' '.$sql_post.' ORDER BY utimestamp DESC ';
                 $resultado = db_get_row_sql($sql);
 
                 $id_agente = $event['id_agente'];
@@ -3876,6 +4296,28 @@ function events_get_events_grouped_by_agent(
 }
 
 
+/**
+ * Return SQL query to group events by agents.
+ *
+ * @param mixed   $id_agent          Id_agent.
+ * @param integer $server_id         Server_id.
+ * @param string  $event_type        Event_type.
+ * @param integer $severity          Severity.
+ * @param integer $status            Status.
+ * @param string  $search            Search.
+ * @param integer $id_agent_module   Id_agent_module.
+ * @param integer $event_view_hr     Event_view_hr.
+ * @param boolean $id_user_ack       Id_user_ack.
+ * @param array   $tag_with          Tag_with.
+ * @param array   $tag_without       Tag_without.
+ * @param boolean $filter_only_alert Filter_only_alert.
+ * @param string  $date_from         Date_from.
+ * @param string  $date_to           Date_to.
+ * @param boolean $id_user           Id_user.
+ * @param boolean $server_id_search  Server_id_search.
+ *
+ * @return string SQL.
+ */
 function events_sql_events_grouped_agents(
     $id_agent,
     $server_id=-1,
@@ -3913,17 +4355,21 @@ function events_sql_events_grouped_agents(
         case 3:
             $sql_post .= ' AND (estado = 0 OR estado = 2)';
         break;
+
+        default:
+            // Ignore.
+        break;
     }
 
     if ($search != '') {
-        $sql_post .= " AND (evento LIKE '%".io_safe_input($search)."%' OR id_evento LIKE '%$search%')";
+        $sql_post .= " AND (evento LIKE '%".io_safe_input($search)."%' OR id_evento LIKE '%".$search."%')";
     }
 
     if ($event_type != '') {
         // If normal, warning, could be several (going_up_warning, going_down_warning... too complex
-        // for the user so for him is presented only "warning, critical and normal"
+        // Shown to user only "warning, critical and normal".
         if ($event_type == 'warning' || $event_type == 'critical' || $event_type == 'normal') {
-            $sql_post .= " AND event_type LIKE '%$event_type%' ";
+            $sql_post .= " AND event_type LIKE '%".$event_type."%' ";
         } else if ($event_type == 'not_normal') {
             $sql_post .= " AND (event_type LIKE '%warning%' OR event_type LIKE '%critical%' OR event_type LIKE '%unknown%') ";
         } else if ($event_type != 'all') {
@@ -3950,23 +4396,25 @@ function events_sql_events_grouped_agents(
             break;
 
             default:
-                $sql_post .= " AND criticity = $severity";
+                $sql_post .= ' AND criticity = '.$severity;
             break;
         }
     }
 
-    // In metaconsole mode the agent search is performed by name
+    // In metaconsole mode the agent search is performed by name.
     if ($meta) {
         if ($id_agent != __('All')) {
-            $sql_post .= " AND agent_name LIKE '%$id_agent%'";
+            $sql_post .= " AND agent_name LIKE '%".$id_agent."%'";
         }
     } else {
         switch ($id_agent) {
             case 0:
+                // Ignore.
+                $__invalid_value = 1;
             break;
 
             case -1:
-                // Agent doesnt exist. No results will returned
+                // Agent doesnt exist. No results will returned.
                 $sql_post .= ' AND 1 = 0';
             break;
 
@@ -3976,14 +4424,13 @@ function events_sql_events_grouped_agents(
         }
     }
 
-    if ($meta) {
-        // There is another filter.
-    } else {
+    // There is another filter for if ($meta).
+    if (!$meta) {
         if (!empty($text_module)) {
             $sql_post .= " AND id_agentmodule IN (
 					SELECT id_agente_modulo
 					FROM tagente_modulo
-					WHERE nombre = '$text_module'
+					WHERE nombre = '".$text_module."'
 				)";
         }
     }
@@ -4017,7 +4464,7 @@ function events_sql_events_grouped_agents(
         }
     }
 
-    // Search by tag
+    // Search by tag.
     if (!empty($tag_with) && (io_safe_output($tag_with) != '[]') && (io_safe_output($tag_with) != '["0"]')) {
         $sql_post .= ' AND ( ';
         $first = true;
@@ -4050,7 +4497,7 @@ function events_sql_events_grouped_agents(
         $sql_post .= ' ) ';
     }
 
-    // Filter/Only alerts
+    // Filter/Only alerts.
     if (isset($filter_only_alert)) {
         if ($filter_only_alert == 0) {
             $sql_post .= " AND event_type NOT LIKE '%alert%'";
@@ -4059,7 +4506,7 @@ function events_sql_events_grouped_agents(
         }
     }
 
-    // Tags ACLS
+    // Tags ACLS.
     if ($id_group > 0 && in_array($id_group, array_keys($groups))) {
         $group_array = (array) $id_group;
     } else {
@@ -4077,12 +4524,12 @@ function events_sql_events_grouped_agents(
         [],
         true
     );
-    // FORCE CHECK SQL "(TAG = tag1 AND id_grupo = 1)"
+    // FORCE CHECK SQL "(TAG = tag1 AND id_grupo = 1)".
     if (($tags_acls_condition != ERR_WRONG_PARAMETERS) && ($tags_acls_condition != ERR_ACL) && ($tags_acls_condition != -110000)) {
         $sql_post .= $tags_acls_condition;
     }
 
-    // Metaconsole fitlers
+    // Metaconsole filters.
     if ($meta) {
         if ($server_id_search) {
             $sql_post .= ' AND server_id = '.$server_id_search;
@@ -4116,29 +4563,40 @@ function events_sql_events_grouped_agents(
 }
 
 
+/**
+ * Retrieve list of events grouped by agents.
+ *
+ * @param string $sql SQL.
+ *
+ * @return string HTML.
+ */
 function events_list_events_grouped_agents($sql)
 {
     global $config;
 
     $table = events_get_events_table(is_metaconsole(), $history);
 
-    $sql = "select * from $table 
-				LEFT JOIN tagent_secondary_group 
-				ON tagent_secondary_group.id_agent = tevento.id_agente
-				WHERE $sql";
+    $sql = sprintf(
+        'SELECT * FROM %s 
+	    LEFT JOIN tagent_secondary_group 
+	       ON tagent_secondary_group.id_agent = tevento.id_agente
+        WHERE %s',
+        $table,
+        $sql
+    );
 
     $result = db_get_all_rows_sql($sql);
     $group_rep = 0;
     $meta = is_metaconsole();
 
-    // fields that the user has selected to show
+    // Fields that the user has selected to show.
     if ($meta) {
         $show_fields = events_meta_get_custom_fields_user();
     } else {
         $show_fields = explode(',', $config['event_fields']);
     }
 
-    // headers
+    // Headers.
     $i = 0;
     $table = new stdClass();
     if (!isset($table->width)) {
@@ -4299,7 +4757,7 @@ function events_list_events_grouped_agents($sql)
     }
 
     if ($meta) {
-        // Get info of the all servers to use it on hash auth
+        // Get info of the all servers to use it on hash auth.
         $servers_url_hash = metaconsole_get_servers_url_hash();
         $servers = metaconsole_get_servers();
     }
@@ -4308,7 +4766,7 @@ function events_list_events_grouped_agents($sql)
     $show_validate_button = false;
 
     $idx = 0;
-    // Arrange data. We already did ACL's in the query
+    // Arrange data. We already did ACL's in the query.
     foreach ($result as $event) {
         $data = [];
 
@@ -4318,16 +4776,16 @@ function events_list_events_grouped_agents($sql)
             $event['server_name'] = $servers[$event['server_id']]['server_name'];
         }
 
-        // Clean url from events and store in array
+        // Clean url from events and store in array.
         $event['clean_tags'] = events_clean_tags($event['tags']);
 
-        // First pass along the class of this row
+        // First pass along the class of this row.
         $myclass = get_priority_class($event['criticity']);
 
-        // print status
+        // Print status.
         $estado = $event['estado'];
 
-        // Colored box
+        // Colored box.
         switch ($estado) {
             case EVENT_NEW:
                 $img_st = 'images/star.png';
@@ -4343,6 +4801,10 @@ function events_list_events_grouped_agents($sql)
                 $img_st = 'images/hourglass.png';
                 $title_st = __('Event in process');
             break;
+
+            default:
+                // Ignore.
+            break;
         }
 
         $i = 0;
@@ -4350,7 +4812,7 @@ function events_list_events_grouped_agents($sql)
         $data[$i] = '#'.$event['id_evento'];
         $table->cellstyle[count($table->data)][$i] = 'background: #F3F3F3; color: #111 !important;';
 
-        // Pass grouped values in hidden fields to use it from modal window
+        // Pass grouped values in hidden fields to use it from modal window.
         if ($group_rep) {
             $similar_ids = $event['similar_ids'];
             $timestamp_first = $event['timestamp_rep_min'];
@@ -4361,17 +4823,17 @@ function events_list_events_grouped_agents($sql)
             $timestamp_last = $event['utimestamp'];
         }
 
-        // Store group data to show in extended view
+        // Store group data to show in extended view.
         $data[$i] .= html_print_input_hidden('similar_ids_'.$event['id_evento'], $similar_ids, true);
         $data[$i] .= html_print_input_hidden('timestamp_first_'.$event['id_evento'], $timestamp_first, true);
         $data[$i] .= html_print_input_hidden('timestamp_last_'.$event['id_evento'], $timestamp_last, true);
         $data[$i] .= html_print_input_hidden('childrens_ids', json_encode($childrens_ids), true);
 
-        // Store server id if is metaconsole. 0 otherwise
+        // Store server id if is metaconsole. 0 otherwise.
         if ($meta) {
             $server_id = $event['server_id'];
 
-            // If meta activated, propagate the id of the event on node (source id)
+            // If meta activated, propagate the id of the event on node (source id).
             $data[$i] .= html_print_input_hidden('source_id_'.$event['id_evento'], $event['id_source_event'], true);
             $table->cellclass[count($table->data)][$i] = $myclass;
         } else {
@@ -4385,7 +4847,7 @@ function events_list_events_grouped_agents($sql)
         }
 
         $data[$i] .= html_print_input_hidden('event_rep_'.$event['id_evento'], $event['event_rep'], true);
-        // Store concat comments to show in extended view
+        // Store concat comments to show in extended view.
         $data[$i] .= html_print_input_hidden('user_comment_'.$event['id_evento'], base64_encode($event['user_comment']), true);
 
         $i++;
@@ -4456,7 +4918,7 @@ function events_list_events_grouped_agents($sql)
         }
 
         if (in_array('evento', $show_fields)) {
-            // Event description
+            // Event description.
             $data[$i] = '<span title="'.$event['evento'].'" class="f9">';
             if ($allow_action) {
                 $data[$i] .= '<a href="javascript:" onclick="show_event_dialog('.$event['id_evento'].', '.$group_rep.');">';
@@ -4476,7 +4938,7 @@ function events_list_events_grouped_agents($sql)
             $data[$i] = '<span class="'.$myclass.'">';
 
             if ($event['id_agente'] > 0) {
-                // Agent name
+                // Agent name.
                 if ($meta) {
                     $agent_link = '<a href="'.$event['server_url'].'/index.php?sec=estado&amp;sec2=operation/agentes/ver_agente&amp;id_agente='.$event['id_agente'].$event['server_url_hash'].'">';
                     if (can_user_access_node()) {
@@ -4497,7 +4959,7 @@ function events_list_events_grouped_agents($sql)
         }
 
         if (in_array('timestamp', $show_fields)) {
-            // Time
+            // Time.
             $data[$i] = '<span class="'.$myclass.'">';
             if ($group_rep == 1) {
                 $data[$i] .= ui_print_timestamp($event['timestamp_rep'], true);
@@ -4700,6 +5162,10 @@ function events_list_events_grouped_agents($sql)
                         $data[$i] = html_print_image('images/page_white_text.png', true, ['title' => str_replace("\n", '<br>', io_safe_output($event['unknown_instructions']))]);
                     }
                 break;
+
+                default:
+                    // Ignore.
+                break;
             }
 
             if (!isset($data[$i])) {
@@ -4729,11 +5195,11 @@ function events_list_events_grouped_agents($sql)
         }
 
         if ($i != 0 && $allow_action) {
-            // Actions
+            // Actions.
             $data[$i] = '';
 
             if (!$readonly) {
-                // Validate event
+                // Validate event.
                 if (($event['estado'] != 1) && (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EW', $event['clean_tags'], $childrens_ids))) {
                     $show_validate_button = true;
                     $data[$i] .= '<a href="javascript:validate_event_advanced('.$event['id_evento'].', 1)" id="validate-'.$event['id_evento'].'">';
@@ -4745,7 +5211,7 @@ function events_list_events_grouped_agents($sql)
                     $data[$i] .= '</a>';
                 }
 
-                // Delete event
+                // Delete event.
                 if ((tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids) == 1)) {
                     if ($event['estado'] != 2) {
                         $show_delete_button = true;
@@ -4784,11 +5250,11 @@ function events_list_events_grouped_agents($sql)
 
             if (!$readonly) {
                 if (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids) == 1) {
-                    // Checkbox
-                    // Class 'candeleted' must be the fist class to be parsed from javascript. Dont change
+                    // Checkbox.
+                    // Class 'candeleted' must be the fist class to be parsed from javascript. Dont change.
                     $data[$i] = html_print_checkbox_extended('validate_ids[]', $event['id_evento'], false, false, false, 'class="candeleted chk_val"', true);
                 } else if (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EW', $event['clean_tags'], $childrens_ids) == 1) {
-                    // Checkbox
+                    // Checkbox.
                     $data[$i] = html_print_checkbox_extended('validate_ids[]', $event['id_evento'], false, false, false, 'class="chk_val"', true);
                 } else if (isset($table->header[$i]) || true) {
                     $data[$i] = '';
@@ -4807,6 +5273,15 @@ function events_list_events_grouped_agents($sql)
 }
 
 
+/**
+ * Retrieves SQL for custom order.
+ *
+ * @param string  $sort_field Field.
+ * @param string  $sort       Order.
+ * @param integer $group_rep  Group field.
+ *
+ * @return string SQL.
+ */
 function events_get_sql_order($sort_field='timestamp', $sort='DESC', $group_rep=0)
 {
     $sort_field_translated = $sort_field;
@@ -4854,18 +5329,22 @@ function events_get_sql_order($sort_field='timestamp', $sort='DESC', $group_rep=
         case 'extra_id':
             $sort_field_translated = 'id_extra';
         break;
+
+        default:
+            // Ignore.
+        break;
     }
 
     $dir = ($sort == 'up') ? 'ASC' : 'DESC';
 
-    return "ORDER BY $sort_field_translated $dir";
+    return 'ORDER BY '.$sort_field_translated.' '.$dir;
 }
 
 
 /**
- * SQL left join of event queries to handle secondary groups
+ * SQL left join of event queries to handle secondary groups.
  *
- * @param string Table to see if is metaconsole or not
+ * @param string $table Table to use based on environment.
  *
  * @return string With the query.
  */
diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php
index 508b79e5b9..8985655745 100644
--- a/pandora_console/include/functions_html.php
+++ b/pandora_console/include/functions_html.php
@@ -2998,14 +2998,35 @@ function html_print_csrf_error()
  * @param  array $atributes. Valid params:
  *         name: Usefull to handle in forms
  *         value: If is checked or not
+ *         disabled: Disabled. Cannot be pressed.
+ *         id: Optional id for the switch.
+ *         class: Additional classes (string).
  * @return string with HTML of button
  */
 function html_print_switch($attributes=[])
 {
-    $name_html = isset($attributes['name']) ? "name = {$attributes['name']}" : '';
-    $checked_html = (bool) $attributes['value'] ? 'checked' : '';
+    $html_expand = '';
+
+    // Check the load values on status.
+    $html_expand .= (bool) $attributes['value'] ? ' checked' : '';
+    $html_expand .= (bool) $attributes['disabled'] ? ' disabled' : '';
+
+    // Only load the valid attributes.
+    $valid_attrs = [
+        'id',
+        'class',
+        'name',
+    ];
+    foreach ($valid_attrs as $va) {
+        if (!isset($attributes[$va])) {
+            continue;
+        }
+
+        $html_expand .= ' '.$va.'="'.$attributes[$va].'"';
+    }
+
     return "<label class='p-switch'>
-			<input type='checkbox' $name_html $checked_html>
+			<input type='checkbox' $html_expand>
 			<span class='p-slider'></span>
 		</label>";
 }
diff --git a/pandora_console/include/functions_messages.php b/pandora_console/include/functions_messages.php
index 7552dc74f2..f633866f65 100644
--- a/pandora_console/include/functions_messages.php
+++ b/pandora_console/include/functions_messages.php
@@ -215,7 +215,6 @@ function messages_process_read(
     bool $read=true
 ) {
     global $config;
-
     // Check if user has grants to read the message.
     if (check_notification_readable($message_id) === false) {
         return false;
@@ -339,14 +338,16 @@ function messages_get_message_sent(int $message_id)
 /**
  * Counts private messages
  *
- * @param string  $user      Target user.
- * @param boolean $incl_read Whether or not to include read messages.
+ * @param string  $user          Target user.
+ * @param boolean $incl_read     Whether or not to include read messages.
+ * @param boolean $ignore_source Ignore source.
  *
  * @return integer The number of messages this user has
  */
 function messages_get_count(
     string $user='',
-    bool $incl_read=false
+    bool $incl_read=false,
+    bool $ignore_source=false
 ) {
     if (empty($user)) {
         global $config;
@@ -361,20 +362,33 @@ function messages_get_count(
         $read = 'where t.read is null';
     }
 
+    if ($ignore_source === true) {
+        $source_sql = '';
+    } else {
+        $source_sql = 'INNER JOIN tnotification_source ns
+            ON tm.id_source = ns.id
+            AND ns.enabled = 1';
+    }
+
     $sql = sprintf(
         'SELECT count(*) FROM (
-            SELECT tm.*, utimestamp_read > 0 as "read" FROM tmensajes tm 
+            SELECT DISTINCT tm.*, utimestamp_read > 0 as "read"
+            FROM tmensajes tm 
+            %s
             LEFT JOIN tnotification_user nu
                 ON tm.id_mensaje=nu.id_mensaje 
+                AND nu.id_user="%s"
             LEFT JOIN (tnotification_group ng
                 INNER JOIN tusuario_perfil up
                     ON ng.id_group=up.id_grupo
                     AND up.id_grupo=ng.id_group
             ) ON tm.id_mensaje=ng.id_mensaje 
             WHERE utimestamp_erased is null
-                AND (up.id_usuario="%s" OR nu.id_user="%s" OR ng.id_group=0)
+                AND (nu.id_user="%s" OR (up.id_usuario="%s" AND ng.id_group=0))
         ) t 
         %s',
+        $source_sql,
+        $user,
         $user,
         $user,
         $read
@@ -411,18 +425,24 @@ function messages_get_count_sent(string $user='')
 /**
  * Get message overview in array
  *
- * @param string  $order     How to order them valid:
- *                           (status (default), subject, timestamp, sender).
- * @param string  $order_dir Direction of order
- *                           (ASC = Ascending, DESC = Descending).
- * @param boolean $incl_read Include read messages in return.
+ * @param string  $order            How to order them valid:
+ *                                  (status (default), subject, timestamp, sender).
+ * @param string  $order_dir        Direction of order
+ *                                  (ASC = Ascending, DESC = Descending).
+ * @param boolean $incl_read        Include read messages in return.
+ * @param boolean $incl_source_info Include source info.
+ * @param integer $limit            Maximum number of result in the query.
+ * @param array   $other_filter     Add a filter on main query.
  *
  * @return integer The number of messages this user has
  */
 function messages_get_overview(
     string $order='status',
     string $order_dir='ASC',
-    bool $incl_read=true
+    bool $incl_read=true,
+    bool $incl_source_info=false,
+    int $limit=0,
+    array $other_filter=[]
 ) {
     global $config;
 
@@ -453,25 +473,44 @@ function messages_get_overview(
         $read = 'where t.read is null';
     }
 
+    $source_fields = '';
+    $source_join = '';
+    if ($incl_source_info) {
+        $source_fields = ', tns.*';
+        $source_join = 'INNER JOIN tnotification_source tns
+            ON tns.id=tm.id_source';
+    }
+
+    // Using distinct because could be double assignment due group/user.
     $sql = sprintf(
         'SELECT * FROM (
-            SELECT tm.*, utimestamp_read > 0 as "read" FROM tmensajes tm 
+            SELECT DISTINCT tm.*, utimestamp_read > 0 as "read" %s
+            FROM tmensajes tm 
             LEFT JOIN tnotification_user nu
                 ON tm.id_mensaje=nu.id_mensaje 
+                AND nu.id_user="%s" 
             LEFT JOIN (tnotification_group ng
                 INNER JOIN tusuario_perfil up
                     ON ng.id_group=up.id_grupo
                     AND up.id_grupo=ng.id_group
-            ) ON tm.id_mensaje=ng.id_mensaje 
+            ) ON tm.id_mensaje=ng.id_mensaje
+            %s
             WHERE utimestamp_erased is null
-                AND (up.id_usuario="%s" OR nu.id_user="%s" OR ng.id_group=0)
+                AND (nu.id_user="%s" OR (up.id_usuario="%s" AND ng.id_group=0))
         ) t 
         %s
-        ORDER BY %s',
+        %s
+        ORDER BY %s
+        %s',
+        $source_fields,
+        $config['id_user'],
+        $source_join,
         $config['id_user'],
         $config['id_user'],
         $read,
-        $order
+        db_format_array_where_clause_sql($other_filter, 'AND', ' AND '),
+        $order,
+        ($limit !== 0) ? ' LIMIT '.$limit : ''
     );
 
     return db_get_all_rows_sql($sql);
@@ -520,3 +559,28 @@ function messages_get_overview_sent(
         $order
     );
 }
+
+
+/**
+ * Get the URL of a message. If field in db is null, it returs a link to
+ *      messages view.
+ *
+ * @param integer $message_id Message id to get URL.
+ *
+ * @return mixed False if fails. A string with URL otherwise.
+ */
+function messages_get_url($message_id)
+{
+    $messages = messages_get_message($message_id);
+    if ($messages === false) {
+        return false;
+    }
+
+    // Return URL stored if is set in database.
+    if (isset($messages['url'])) {
+        return $messages['url'];
+    }
+
+    // Return the message direction.
+    return ui_get_full_url('index.php?sec=message_list&sec2=operation/messages/message_edit&read_message=1&id_message='.$message_id);
+}
diff --git a/pandora_console/include/functions_notifications.php b/pandora_console/include/functions_notifications.php
index 2ac5eb1227..0e35b7ea37 100644
--- a/pandora_console/include/functions_notifications.php
+++ b/pandora_console/include/functions_notifications.php
@@ -26,6 +26,8 @@
  * ============================================================================
  */
 
+require_once $config['homedir'].'/include/functions_messages.php';
+
 define('NOTIFICATIONS_POSTPONE_FOREVER', -1);
 
 
@@ -43,28 +45,16 @@ function get_notification_source_id(string $source)
     }
 
     return db_get_value_sql(
-        "SELECT id
-            FROM `tnotification_source`
-            WHERE `description` LIKE '".$source."%'"
+        sprintf(
+            'SELECT id
+                FROM `tnotification_source`
+                WHERE lower(`description`) = lower("%s")',
+            $source
+        )
     );
 }
 
 
-/**
- * Converts description into a handable identifier
- *
- * @param string $desc Full description.
- *
- * @return string First word in lowercase. Empty string if no word detected.
- */
-function notifications_desc_to_id(string $desc)
-{
-    preg_match('/^[a-zA-Z]*/', $desc, $matches);
-    $match = $matches[0];
-    return isset($match) ? $match : '';
-}
-
-
 /**
  * Retrieve all targets for given message.
  *
@@ -141,10 +131,13 @@ function check_notification_readable(int $id_message)
         return false;
     }
 
+    // Using distinct to avoid double response on group messages read by user.
     $sql = sprintf(
-        'SELECT tm.*, utimestamp_read > 0 as "read" FROM tmensajes tm 
+        'SELECT DISTINCT tm.*, utimestamp_read > 0 as "read"
+            FROM tmensajes tm 
             LEFT JOIN tnotification_user nu
                 ON tm.id_mensaje=nu.id_mensaje 
+                AND nu.id_user="%s"
                 AND tm.id_mensaje=%d
             LEFT JOIN (tnotification_group ng
                 INNER JOIN tusuario_perfil up
@@ -152,7 +145,8 @@ function check_notification_readable(int $id_message)
                     AND up.id_grupo=ng.id_group
             ) ON tm.id_mensaje=ng.id_mensaje 
             WHERE utimestamp_erased is null
-                AND (up.id_usuario="%s" OR nu.id_user="%s" OR ng.id_group=0)',
+                AND (nu.id_user="%s" OR (up.id_usuario="%s" AND ng.id_group=0))',
+        $config['id_user'],
         $id_message,
         $config['id_user'],
         $config['id_user']
@@ -166,7 +160,7 @@ function check_notification_readable(int $id_message)
  * Returns the target users and groups assigned to be notified on
  * desired source.
  *
- * @param integer $id_source
+ * @param integer $id_source Source identificator.
  *
  * @return array [users] and [groups] with the targets.
  */
@@ -224,41 +218,62 @@ function get_notification_source_targets(int $id_source)
 /**
  * Return all info from tnotification_source
  *
+ * @param array $filter Filter to table tnotification_source.
+ *
  * @return array with sources info
  */
-function notifications_get_all_sources()
+function notifications_get_all_sources($filter=[])
 {
-    return db_get_all_rows_in_table('tnotification_source');
+    return db_get_all_rows_filter('tnotification_source', $filter);
 }
 
 
 /**
  * Return the user sources to be inserted into a select
  *
- * @param integer $source_id Source database identificator
+ * @param integer $source_id Source database identificator.
  *
  * @return array with the user id in keys and user id in value too
  */
 function notifications_get_user_sources_for_select($source_id)
 {
-    $users = db_get_all_rows_filter(
-        'tnotification_source_user',
+    $users = notifications_get_user_sources(
         ['id_source' => $source_id],
-        'id_user'
+        ['id_user']
     );
-    // If fails or no one is selected, return empty array
-    if ($users === false) {
-        return [];
-    }
 
     return index_array($users, 'id_user', 'id_user');
 }
 
 
+/**
+ * Get the user sources
+ *
+ * @param array $filter Filter of sql query.
+ * @param array $fields Fields to get of query.
+ *
+ * @return array Array with user sources data.
+ */
+function notifications_get_user_sources($filter=[], $fields=[])
+{
+    $users = db_get_all_rows_filter(
+        'tnotification_source_user',
+        $filter,
+        $fields
+    );
+    // If fails or no one is selected, return empty array.
+    if ($users === false) {
+        return [];
+    }
+
+    return $users;
+}
+
+
 /**
  * Return the groups sources to be inserted into a select
  *
- * @param integer $source_id Source database identificator
+ * @param integer $source_id Source database identificator.
  *
  * @return array with the group id in keys and group name in value
  */
@@ -276,6 +291,9 @@ function notifications_get_group_sources_for_select($source_id)
  * Get the group sources
  *
  * @param array $filter Filter of sql query.
+ * @param array $fields Fields retrieved.
+ *
+ * @return array With the group info
  */
 function notifications_get_group_sources($filter=[], $fields=[])
 {
@@ -287,7 +305,7 @@ function notifications_get_group_sources($filter=[], $fields=[])
     $fields = array_map(
         function ($field) {
             if (!preg_match('/^tnsg./', $field)) {
-                $field = "tnsg.{$field}";
+                $field = 'tnsg.'.$field;
             }
 
             return $field;
@@ -303,7 +321,7 @@ function notifications_get_group_sources($filter=[], $fields=[])
         array_merge($fields, ['IFNULL(tg.nombre, "All") AS name'])
     );
 
-    // If fails or no one is selected, return empty array
+    // If fails or no one is selected, return empty array.
     if ($groups === false) {
         return [];
     }
@@ -315,19 +333,19 @@ function notifications_get_group_sources($filter=[], $fields=[])
 /**
  * Delete a set of groups from notification source
  *
- * @param int Source id
- * @param array Id of groups to be deleted
+ * @param integer $source_id Source id.
+ * @param array   $groups    Id of groups to be deleted.
  *
  * @return boolean True if success. False otherwise.
  */
 function notifications_remove_group_from_source($source_id, $groups)
 {
-    // Source id is mandatory
+    // Source id is mandatory.
     if (!isset($source_id)) {
         return false;
     }
 
-    // Delete from database
+    // Delete from database.
     return db_process_sql_delete(
         'tnotification_source_group',
         [
@@ -341,19 +359,19 @@ function notifications_remove_group_from_source($source_id, $groups)
 /**
  * Delete a set of users from notification source
  *
- * @param int Source id
- * @param array Id of users to be deleted
+ * @param integer $source_id Source id.
+ * @param array   $users     Id of users to be deleted.
  *
  * @return boolean True if success. False otherwise.
  */
 function notifications_remove_users_from_source($source_id, $users)
 {
-    // Source id is mandatory
+    // Source id is mandatory.
     if (!isset($source_id)) {
         return false;
     }
 
-    // Delete from database
+    // Delete from database.
     return db_process_sql_delete(
         'tnotification_source_user',
         [
@@ -367,22 +385,22 @@ function notifications_remove_users_from_source($source_id, $users)
 /**
  * Insert a set of groups to notification source
  *
- * @param int Source id
- * @param array Id of groups to be deleted
+ * @param integer $source_id Source id.
+ * @param array   $groups    Id of groups to be deleted.
  *
  * @return boolean True if success. False otherwise.
  */
 function notifications_add_group_to_source($source_id, $groups)
 {
-    // Source id is mandatory
+    // Source id is mandatory.
     if (!isset($source_id)) {
         return false;
     }
 
-    // Insert into database all groups passed
+    // Insert into database all groups passed.
     $res = true;
     foreach ($groups as $group) {
-        if (empty($group)) {
+        if (!isset($group)) {
             continue;
         }
 
@@ -402,20 +420,26 @@ function notifications_add_group_to_source($source_id, $groups)
 /**
  * Insert a set of users to notification source
  *
- * @param int Source id
- * @param array Id of users to be deleted
+ * @param integer $source_id Source id.
+ * @param array   $users     Id of users to be deleted.
  *
  * @return boolean True if success. False otherwise.
  */
 function notifications_add_users_to_source($source_id, $users)
 {
-    // Source id is mandatory
+    // Source id is mandatory.
     if (!isset($source_id)) {
         return false;
     }
 
-    // Insert into database all groups passed
+    // Insert into database all groups passed.
     $res = true;
+    $also_mail = db_get_value(
+        'also_mail',
+        'tnotification_source',
+        'id',
+        $source_id
+    );
     foreach ($users as $user) {
         if (empty($user)) {
             continue;
@@ -426,6 +450,8 @@ function notifications_add_users_to_source($source_id, $users)
             [
                 'id_user'   => $user,
                 'id_source' => $source_id,
+                'enabled'   => 1,
+                'also_mail' => (int) $also_mail,
             ]
         ) !== false;
     }
@@ -438,7 +464,8 @@ function notifications_add_users_to_source($source_id, $users)
  * Get the groups that not own to a source and, for that reason, they can be
  * added to the source.
  *
- * @param  integer $source_id Source id.
+ * @param integer $source_id Source id.
+ *
  * @return array Indexed by id group all selectable groups.
  */
 function notifications_get_group_source_not_configured($source_id)
@@ -453,7 +480,8 @@ function notifications_get_group_source_not_configured($source_id)
  * Get the users that not own to a source and, for that reason, they can be
  * added to the source.
  *
- * @param  integer $source_id
+ * @param integer $source_id Source id.
+ *
  * @return array Indexed by id user, all selectable users.
  */
 function notifications_get_user_source_not_configured($source_id)
@@ -469,71 +497,223 @@ function notifications_get_user_source_not_configured($source_id)
 
 
 /**
- * Print the notification ball to see unread messages
+ * Build a data struct to handle the value of a label
  *
- * @return string with HTML code of notification ball
+ * @param mixed $status  Status value.
+ * @param mixed $enabled Enabled value.
+ *
+ * @return array with status (1|0) and enabled (1|0)
  */
-function notifications_print_ball()
+function notifications_build_user_enable_return($status, $enabled)
 {
-    $num_notifications = messages_get_count();
-    $class_status = $num_notifications == 0 ? 'notification-ball-no-messages' : 'notification-ball-new-messages';
-    return "<div class='notification-ball $class_status' id='notification-ball-header'>
-            $num_notifications
-        </div>";
+    return [
+        'status'  => ((bool) $status === true) ? 1 : 0,
+        'enabled' => ((bool) $enabled === true) ? 1 : 0,
+    ];
+}
+
+
+/**
+ * Get user label (enabled, also_mail...) status.
+ *
+ * @param integer $source Id of notification source.
+ * @param string  $user   User id.
+ * @param string  $label  Label id (enabled, also_email...).
+ *
+ * @return array Return of notifications_build_user_enable_return.
+ */
+function notifications_get_user_label_status($source, $user, $label)
+{
+    // If not enabled, it cannot be modificable.
+    if (!$source['enabled'] || !$source[$label]) {
+        return notifications_build_user_enable_return(false, false);
+    }
+
+    // See at first for direct reference.
+    $user_source = notifications_get_user_sources(
+        [
+            'id_source' => $source['id'],
+            'id_user'   => $user,
+        ]
+    );
+    if (!empty($user_source)) {
+        return notifications_build_user_enable_return(
+            isset($user_source[0][$label]) ? $user_source[0][$label] : false,
+            $source['user_editable']
+        );
+    }
+
+    $common_groups = array_intersect(
+        array_keys(users_get_groups($user)),
+        array_keys(
+            notifications_get_group_sources_for_select($source['id'])
+        )
+    );
+    // No group found, return no permissions.
+    $value = empty($common_groups) ? false : $source[$label];
+    return notifications_build_user_enable_return($value, false);
+}
+
+
+/**
+ * Set the status to a single label on config of users notifications.
+ *
+ * @param integer $source Id of notification source.
+ * @param string  $user   User id.
+ * @param string  $label  Label id (enabled, also_email...).
+ * @param mixed   $value  Numeric value: 1 or 0.
+ *
+ * @return boolean True if success.
+ */
+function notifications_set_user_label_status($source, $user, $label, $value)
+{
+    $source_info = notifications_get_all_sources(['id' => $source]);
+    if (!isset($source_info[0])
+        || !$source_info[0]['enabled']
+        || !$source_info[0][$label]
+        || !$source_info[0]['user_editable']
+    ) {
+        return false;
+    }
+
+    return (bool) db_process_sql_update(
+        'tnotification_source_user',
+        [$label => $value],
+        [
+            'id_user'   => $user,
+            'id_source' => $source,
+        ]
+    );
+}
+
+
+/**
+ * Get the counters of notification. Usefull to print the header notification
+ * ball.
+ *
+ * @return array With the fields:
+ *      'notifications' => Total new notifications,
+ *      'last_id' => Id of last read value. Usefull to make comparisons.
+ */
+function notifications_get_counters()
+{
+    $num_notifications = 0;
+    $last_id = 0;
+    $last_message = messages_get_overview(
+        'timestamp',
+        'DESC',
+        false,
+        false,
+        1
+    );
+    if (!empty($last_message)) {
+        $num_notifications = messages_get_count();
+        $last_id = $last_message[0]['id_mensaje'];
+    }
+
+    return [
+        'notifications' => $num_notifications,
+        'last_id'       => $last_id,
+    ];
+}
+
+
+// /////////////////////////////////////////////////////////////////////////////
+// UI FUNCTIONS
+// /////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Print the notification ball to see unread messages.
+ *
+ * @param integer $num_notifications Number of messages shown
+ *      in notification ball.
+ * @param integer $last_id           Id of last message shown
+ *           in the notification ball.
+ *
+ * @return string with HTML code of notification ball.
+ */
+function notifications_print_ball($num_notifications, $last_id)
+{
+    $no_notifications = (int) $num_notifications === 0;
+    $class_status = ($no_notifications) ? 'notification-ball-no-messages' : 'notification-ball-new-messages';
+    return sprintf(
+        '<div
+            %s
+            class="notification-ball %s"
+            id="notification-ball-header"
+            last_id="%s"
+        >
+            %s
+        </div>',
+        ($no_notifications) ? '' : 'onclick="addNotifications(event)"',
+        $class_status,
+        $last_id,
+        $num_notifications
+    );
 }
 
 
 /**
  * Print notification configuration global
  *
- * @param array notification source data
+ * @param array $source Notification source data.
  *
  * @return string with HTML of source configuration
  */
 function notifications_print_global_source_configuration($source)
 {
-    // Get some values to generate the title
-    $id = notifications_desc_to_id($source['description']);
+    // Get some values to generate the title.
     $switch_values = [
-        'name'  => 'enable-'.$id,
+        'name'  => 'enable-'.$source['id'],
         'value' => $source['enabled'],
+        'id'    => 'nt-'.$source['id'].'-enabled',
+        'class' => 'elem-clickable',
     ];
 
-    // Search if group all is set and handle that situation
+    // Search if group all is set and handle that situation.
     $source_groups = notifications_get_group_sources_for_select($source['id']);
     $is_group_all = isset($source_groups['0']);
     if ($is_group_all) {
         unset($source_groups['0']);
     }
 
-    // Generate the title
+    // Generate the title.
     $html_title = "<div class='global-config-notification-title'>";
     $html_title .= html_print_switch($switch_values);
-    $html_title .= "<h2>{$source['description']}</h2>";
+    $html_title .= '<h2>'.$source['description'].'</h2>';
     $html_title .= '</div>';
 
-    // Generate the html for title
+    // Generate the html for title.
     $html_selectors = "<div class='global-config-notification-selectors'>";
-    $html_selectors .= notifications_print_source_select_box(notifications_get_user_sources_for_select($source['id']), 'users', $id, $is_group_all);
-    $html_selectors .= notifications_print_source_select_box($source_groups, 'groups', $id, $is_group_all);
+    $html_selectors .= notifications_print_source_select_box(
+        notifications_get_user_sources_for_select($source['id']),
+        'users',
+        $source['id']
+    );
+    $html_selectors .= notifications_print_source_select_box(
+        $source_groups,
+        'groups',
+        $source['id']
+    );
     $html_selectors .= '</div>';
-
-    // Generate the checkboxes and time select
+    // Generate the checkboxes and time select.
     $html_checkboxes = "<div class='global-config-notification-checkboxes'>";
     $html_checkboxes .= '   <span>';
-    $html_checkboxes .= html_print_checkbox("all-$id", 1, $is_group_all, true, false, 'notifications_disable_source(event)');
+    $html_checkboxes .= html_print_checkbox_extended('all-'.$source['id'], 1, $is_group_all, false, '', 'class= "elem-clickable"', true, 'id="nt-'.$source['id'].'-all_users"');
     $html_checkboxes .= __('Notify all users');
     $html_checkboxes .= '   </span><br><span>';
-    $html_checkboxes .= html_print_checkbox("mail-$id", 1, $source['also_mail'], true);
+    $html_checkboxes .= html_print_checkbox_extended('mail-'.$source['id'], 1, $source['also_mail'], false, '', 'class= "elem-clickable"', true, 'id="nt-'.$source['id'].'-also_mail"');
     $html_checkboxes .= __('Also email users with notification content');
     $html_checkboxes .= '   </span><br><span>';
-    $html_checkboxes .= html_print_checkbox("user-$id", 1, $source['user_editable'], true);
-    $html_checkboxes .= __('Users cannot modify notification preferences');
+    $html_checkboxes .= html_print_checkbox_extended('user-'.$source['id'], 1, $source['user_editable'], false, '', 'class= "elem-clickable"', true, 'id="nt-'.$source['id'].'-user_editable"');
+    $html_checkboxes .= __('Users can modify notification preferences');
     $html_checkboxes .= '   </span>';
     $html_checkboxes .= '</div>';
 
-    // Generate the select with the time
+    // Generate the select with the time.
     $html_select_pospone = __('Users can postpone notifications up to');
+    // FIXMEit should not be disabled.
     $html_select_pospone .= html_print_select(
         [
             SECONDS_5MINUTES               => __('5 minutes'),
@@ -545,15 +725,19 @@ function notifications_print_global_source_configuration($source)
             SECONDS_1MONTH                 => __('1 month'),
             NOTIFICATIONS_POSTPONE_FOREVER => __('forever'),
         ],
-        "postpone-{$id}",
+        'nt-'.$source['id'].'-max_postpone_time',
         $source['max_postpone_time'],
         '',
         '',
         0,
+        true,
+        false,
+        true,
+        'elem-changeable',
         true
     );
 
-    // Return all html
+    // Return all html.
     return $html_title.$html_selectors.$html_checkboxes.$html_select_pospone;
 }
 
@@ -561,32 +745,72 @@ function notifications_print_global_source_configuration($source)
 /**
  * Print select boxes of notified users or groups
  *
- * @param array   $info_selec All info required for build the selector
- * @param string  $id         users|groups
- * @param string  $source_id  Id of source
- * @param boolean $disabled   Disable the selectors
+ * @param array  $info_selec All info required for build the selector.
+ * @param string $id         One of users|groups.
+ * @param string $source_id  Id of source.
  *
  * @return string HTML with the generated selector
  */
-function notifications_print_source_select_box($info_selec, $id, $source_id, $disabled)
-{
-    $title = $id == 'users' ? __('Notified users') : __('Notified groups');
-    $add_title = $id == 'users' ? __('Add users') : __('Add groups');
-    $delete_title = $id == 'users' ? __('Delete users') : __('Delete groups');
+function notifications_print_source_select_box(
+    $info_selec,
+    $id,
+    $source_id
+) {
+    $title = ($id === 'users') ? __('Notified users') : __('Notified groups');
+    $add_title = ($id === 'users') ? __('Add users') : __('Add groups');
+    $delete_title = ($id === 'users') ? __('Delete users') : __('Delete groups');
 
-    // Generate the HTML
-    $html_select = "<div class='global-config-notification-single-selector'>";
-    $html_select .= '   <div>';
-    $html_select .= "       <h4>$title</h4>";
-    // Put a true if empty sources to avoid to sow the 'None' value
-    $html_select .= html_print_select(empty($info_selec) ? true : $info_selec, "multi-{$id}-{$source_id}[]", 0, false, '', '', true, true, true, '', $disabled);
-    $html_select .= '   </div>';
-    $html_select .= "   <div class='global-notifications-icons'>";
-    $html_select .= html_print_image('images/input_add.png', true, ['title' => $add_title, 'onclick' => "add_source_dialog('$id', '$source_id')"]);
-    $html_select .= html_print_image('images/input_delete.png', true, ['title' => $delete_title, 'onclick' => "remove_source_elements('$id', '$source_id')"]);
-    $html_select .= '   </div>';
-    $html_select .= '</div>';
-    return $html_select;
+    // Generate the HTML.
+    return sprintf(
+        "
+        <div class='global-config-notification-single-selector'>
+            <div>
+                <h4>%s</h4>
+                %s
+            </div>
+            <div class='global-notifications-icons'>
+                %s
+                %s
+            </div>
+        </div>
+        ",
+        $title,
+        // Put a true if empty sources to avoid to sow the 'None' value.
+        html_print_select(
+            empty($info_selec) ? true : $info_selec,
+            'multi-'.$id.'-'.$source_id.'[]',
+            0,
+            false,
+            '',
+            '',
+            true,
+            true
+        ),
+        html_print_image(
+            'images/input_add.png',
+            true,
+            [
+                'title'   => $add_title,
+                'onclick' => sprintf(
+                    "add_source_dialog('%s', '%s')",
+                    $id,
+                    $source_id
+                ),
+            ]
+        ),
+        html_print_image(
+            'images/input_delete.png',
+            true,
+            [
+                'title'   => $delete_title,
+                'onclick' => sprintf(
+                    "remove_source_elements('%s', '%s')",
+                    $id,
+                    $source_id
+                ),
+            ]
+        )
+    );
 }
 
 
@@ -594,22 +818,180 @@ function notifications_print_source_select_box($info_selec, $id, $source_id, $di
  * Print the select with right and left arrows to select new sources
  * (groups or users).
  *
- * @param  array  $info_selec Array with source info.
- * @param  string $users      users|groups.
- * @param  source $source_id  Source id.
+ * @param array  $info_selec Array with source info.
+ * @param string $users      One of users|groups.
+ * @param source $source_id  Source id.
+ *
  * @return string HTML with the select code.
  */
 function notifications_print_two_ways_select($info_selec, $users, $source_id)
 {
-    $html_select = "<div class='global_config_notifications_dialog_add'>";
-    $html_select .= html_print_select(empty($info_selec) ? true : $info_selec, "all-multi-{$users}-{$source_id}[]", 0, false, '', '', true, true, true, '');
-    $html_select .= "<div class='global_config_notifications_two_ways_form_arrows'>";
-    $html_select .= html_print_image('images/darrowright.png', true, ['title' => $add_title, 'onclick' => "notifications_modify_two_ways_element('$users', '$source_id', 'add')"]);
-    $html_select .= html_print_image('images/darrowleft.png', true, ['title' => $add_title, 'onclick' => "notifications_modify_two_ways_element('$users', '$source_id', 'remove')"]);
-    $html_select .= '</div>';
-    $html_select .= html_print_select(true, "selected-multi-{$users}-{$source_id}[]", 0, false, '', '', true, true, true, '');
-    $html_select .= '</div>';
-    $html_select .= html_print_button(__('Add'), 'Add', false, "notifications_add_source_element_to_database('$users', '$source_id')", "class='sub add'", true);
-
-    return $html_select;
+    return sprintf(
+        "
+        <div class='global_config_notifications_dialog_add'>
+            %s
+            <div class='global_config_notifications_two_ways_form_arrows'>
+                %s
+                %s
+            </div>
+            %s
+        </div>
+        %s
+        ",
+        html_print_select(
+            empty($info_selec) ? true : $info_selec,
+            'all-multi-'.$users.'-'.$source_id.'[]',
+            0,
+            false,
+            '',
+            '',
+            true,
+            true,
+            true,
+            ''
+        ),
+        html_print_image(
+            'images/darrowright.png',
+            true,
+            [
+                'title'   => __('Add elements'),
+                'onclick' => sprintf(
+                    "notifications_modify_two_ways_element('%s', '%s', 'add')",
+                    $users,
+                    $source_id
+                ),
+            ]
+        ),
+        html_print_image(
+            'images/darrowleft.png',
+            true,
+            [
+                'title'   => __('Remove elements'),
+                'onclick' => sprintf(
+                    "notifications_modify_two_ways_element('%s', '%s', 'remove')",
+                    $users,
+                    $source_id
+                ),
+            ]
+        ),
+        html_print_select(
+            true,
+            'selected-multi-'.$users.'-'.$source_id.'[]',
+            0,
+            false,
+            '',
+            '',
+            true,
+            true,
+            true,
+            ''
+        ),
+        html_print_button(
+            __('Add'),
+            'Add',
+            false,
+            sprintf(
+                "notifications_add_source_element_to_database('%s', '%s')",
+                $users,
+                $source_id
+            ),
+            "class='sub add'",
+            true
+        )
+    );
+}
+
+
+/**
+ * Print a label status represented by a switch
+ *
+ * @param integer $source Source id.
+ * @param string  $user   User id.
+ * @param string  $label  Label (enabled, also_mail...).
+ *
+ * @return string With HTML code
+ */
+function notifications_print_user_switch($source, $user, $label)
+{
+    $status = notifications_get_user_label_status($source, $user, $label);
+    return html_print_switch(
+        [
+            'name'     => $label,
+            'value'    => $status['status'],
+            'disabled' => !$status['enabled'],
+            'class'    => 'notifications-user-label_individual',
+            'id'       => 'notifications-user-'.$source['id'].'-label-'.$label,
+        ]
+    );
+}
+
+
+/**
+ * Generates the dropdown notifications menu.
+ *
+ * @return string HTML with dropdown menu.
+ */
+function notifications_print_dropdown()
+{
+    $mess = messages_get_overview('status', 'DESC', false, true);
+    if ($mess === false) {
+        $mess = [];
+    }
+
+    return sprintf(
+        "<div id='notification-wrapper'>
+            <div id='notification-wrapper-inner'>
+                %s
+            </div>
+        </div>
+        <div
+            id='notification-wrapper-shadow'
+            onclick='notifications_hide()'
+        >
+        </div>
+        ",
+        array_reduce(
+            $mess,
+            function ($carry, $message) {
+                return $carry.notifications_print_dropdown_element($message);
+            },
+            ''
+        )
+    );
+}
+
+
+/**
+ * Print a single notification box
+ *
+ * @param array $message_info Info of printed message.
+ *
+ * @return string HTML code of single message
+ */
+function notifications_print_dropdown_element($message_info)
+{
+    return sprintf(
+        "<a
+            class='notification-item'
+            onclick='click_on_notification_toast(event)'
+            id='notification-item-id-%s'
+            href='%s'
+            target='_blank'
+        >
+            %s
+            <div class='notification-info'>
+                <h4 class='notification-title'>
+                    %s
+                </h4>
+                <p class='notification-subtitle'>
+                    %s
+                </p>
+            </div>
+        </a>",
+        $message_info['id_mensaje'],
+        messages_get_url($message_info['id_mensaje']),
+        html_print_image('images/'.$message_info['icon'], true),
+        $message_info['subject'],
+        str_replace([io_safe_input('<br>')], ' ', $message_info['mensaje'])
+    );
 }
diff --git a/pandora_console/include/styles/message_edit.css b/pandora_console/include/styles/message_edit.css
new file mode 100644
index 0000000000..f4b2294580
--- /dev/null
+++ b/pandora_console/include/styles/message_edit.css
@@ -0,0 +1,49 @@
+/* Chat containers */
+.container {
+  border: 2px solid #dedede;
+  background-color: #f1f1f1;
+  border-radius: 5px;
+  padding: 10px;
+  margin: 10px 0;
+}
+
+/* Darker chat container */
+.darker {
+  border-color: #ccc;
+  background-color: #ddd;
+}
+
+/* Clear floats */
+.container::after {
+  content: "";
+  clear: both;
+  display: table;
+}
+
+/* Style images */
+.container img {
+  float: left;
+  max-width: 60px;
+  width: 100%;
+  margin-right: 20px;
+  border-radius: 50%;
+}
+
+/* Style the right image */
+.container img.right {
+  float: right;
+  margin-left: 20px;
+  margin-right: 0;
+}
+
+/* Style time text */
+.time-right {
+  float: right;
+  color: #aaa;
+}
+
+/* Style time text */
+.time-left {
+  float: left;
+  color: #999;
+}
diff --git a/pandora_console/include/styles/pandora.css b/pandora_console/include/styles/pandora.css
index 2711b835f4..f3fdca5874 100644
--- a/pandora_console/include/styles/pandora.css
+++ b/pandora_console/include/styles/pandora.css
@@ -4287,15 +4287,89 @@ div#dialog_messages table th:last-child {
   display: flex;
   justify-content: center;
   align-items: center;
+  cursor: pointer;
 }
 
 .notification-ball-no-messages {
   background-color: #82b92e;
+  cursor: pointer;
 }
 .notification-ball-new-messages {
   background-color: #fc4444;
 }
 
+#notification-wrapper {
+  background: white;
+  border: #a5a5a5 solid 1px;
+  z-index: 900000;
+  position: absolute;
+  width: 400px;
+  margin-top: -5px;
+}
+#notification-wrapper::before {
+  content: "";
+  display: block;
+  position: absolute;
+  width: 0px;
+  height: 0;
+  border-color: transparent;
+  border-width: 12px;
+  border-style: solid;
+  bottom: 100%;
+  left: 78%;
+  left: calc(78% - 2px);
+  margin-left: -12px;
+  border-bottom-color: white;
+}
+#notification-wrapper-inner {
+  max-height: 400px;
+  overflow: auto;
+}
+#notification-wrapper-shadow {
+  height: 100%;
+  width: 100%;
+  background: #111;
+  position: fixed;
+  z-index: 9009;
+  top: 0;
+  opacity: 0.3;
+}
+.notification-item {
+  background: whitesmoke;
+  height: 100px;
+  margin: 7px;
+  border: #cccccc solid 1px;
+  display: flex;
+  flex-flow: row nowrap;
+  align-items: center;
+  padding: 5px;
+}
+.notification-item > * {
+  padding-left: 15px;
+  pointer-events: none;
+}
+
+.notification-item:hover {
+  text-decoration: none;
+}
+
+.notification-info {
+  width: 87%;
+  display: flex;
+  flex-flow: column nowrap;
+}
+.notification-item img {
+  max-width: 100%;
+  max-height: 100%;
+}
+.notification-title {
+  margin: 0;
+}
+.notification-subtitle {
+  margin: 0;
+  color: #373737;
+}
+
 .global-config-notification-title {
   display: flex;
   flex-direction: row;
@@ -4356,8 +4430,392 @@ div#dialog_messages table th:last-child {
   margin: 15px 0;
 }
 
-/* jQuery dialog */
-.no-close .ui-dialog-titlebar-close {
-  display: none;
+/* --- JQUERY-UI --- */
+.ui-button-text-only .ui-button-text {
+  font-family: "nunito", sans-serif;
+  font-size: 9pt;
+  color: #82b92e;
 }
-/* jQuery dialog */
+.ui-datepicker .ui-datepicker-title *,
+.ui-datepicker th * {
+  color: white;
+}
+.ui-datepicker .ui-datepicker-title select,
+.ui-datepicker .ui-datepicker-title option {
+  color: #111 !important;
+}
+.ui-dialog .ui-dialog-titlebar {
+  display: inherit;
+  text-align: center;
+  padding: 0.4em 1em;
+  height: 30px;
+  position: relative;
+  background-color: #82b92e !important;
+}
+.ui-dialog .ui-dialog-title {
+  font-family: "Nunito", sans-serif;
+  margin: 0.1em 0 !important;
+  white-space: nowrap !important;
+  width: 100% !important;
+  overflow: hidden !important;
+  text-overflow: ellipsis !important;
+  font-size: 11pt;
+  position: relative;
+  top: 5px;
+  float: none !important;
+}
+.ui-dialog .ui-dialog-titlebar-close {
+  position: absolute !important;
+  right: 1em !important;
+  width: 21px !important;
+  margin: 0px 0 0 0 !important;
+  padding: 1px !important;
+  height: 20px !important;
+  bottom: 30% !important;
+  top: 20% !important;
+}
+.ui-dialog .ui-dialog-content {
+  position: relative !important;
+  border: 0;
+  padding: 0.5em 1em !important;
+  background: none !important;
+  overflow: auto !important;
+  margin-bottom: 1em;
+}
+.ui-dialog .ui-dialog-buttonpane {
+  text-align: left;
+  border-width: 1px 0 0 0;
+  background-image: none;
+  margin-top: 0.5em;
+  padding: 0.3em 1em 0.5em 0.4em;
+}
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
+  float: right;
+}
+.ui-dialog .ui-dialog-buttonpane button {
+  margin: 0.5em 1em 0.5em 0 !important;
+  cursor: pointer !important;
+  background: white !important;
+  background-color: white !important;
+  border: 1px solid #82b92e !important;
+  min-height: 35px !important;
+  width: 90px !important;
+}
+.ui-widget-content {
+  background: #ffffff url(include/styles/images/ui-bg_flat_75_ffffff_40x100.png)
+    50% 50% repeat-x;
+}
+.ui-state-default,
+.ui-widget-content .ui-state-default,
+.ui-widget-header .ui-state-default {
+  margin-top: 3px;
+  border: 1px solid #d3d3d3 !important;
+  border-bottom: 0 !important;
+  background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50%
+    repeat-x !important;
+  font-weight: normal !important;
+  color: #555555 !important;
+}
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-left,
+.ui-corner-tl {
+  border-top-left-radius: 0 !important;
+}
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-right,
+.ui-corner-tr {
+  border-top-right-radius: 0 !important;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-left,
+.ui-corner-bl {
+  border-bottom-left-radius: 0 !important;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-right,
+.ui-corner-br {
+  border-bottom-right-radius: 0 !important;
+}
+#ui-datepicker-div {
+  border-color: #b1b1b1;
+  background: #ffffff;
+}
+.ui-widget-header {
+  background: #b1b1b1 !important;
+  color: #ffffff !important;
+}
+.ui-datepicker-calendar th {
+  background-color: #3f3f3f;
+}
+.ui-dialog .ui-widget-header {
+  background-color: #82b92e;
+}
+.ui_tpicker_hour,
+.ui_tpicker_minute,
+.ui_tpicker_second,
+.ui-slider-handle {
+  border: 1px solid #aaaaaa !important;
+}
+.ui-timepicker-div dd {
+  margin: 0px 15px 0px 15px;
+}
+.ui-timepicker-div .ui-datepicker-title {
+  color: white;
+}
+.ui-datepicker-buttonpane button {
+  border-color: #b1b1b1 !important;
+}
+.ui-datepicker-buttonpane .ui-datepicker-current {
+  margin-left: 0.2em !important;
+}
+.ui-dialog .ui-widget-content {
+  border: 0px !important;
+}
+.ui-dialog {
+  box-shadow: 5px 5px 19px #4e4e4e;
+  border: 0px !important;
+  padding: 0 !important;
+}
+.ui-dialog-titlebar {
+  border: 0px !important;
+}
+.ui-dialog-titlebar .ui-icon-closethick,
+.ui-dialog-titlebar .ui-state-default,
+.ui-dialog-titlebar .ui-state-hover,
+.ui-dialog-titlebar button {
+  background: transparent;
+  border: 0px;
+}
+
+.ui-dialog-title {
+  color: #ffffff;
+  font-size: 9pt;
+}
+.ui-widget input,
+.ui-widget select,
+.ui-widget textarea,
+.ui-widget button {
+  font-family: Verdana, Arial, sans-serif !important;
+}
+
+a.ui-button:active,
+.ui-button:active,
+.ui-button.ui-state-active:hover,
+.ui-state-focus .ui-widget-content,
+.ui-state-focus .ui-widget-header,
+.ui-state-focus .ui-button:hover,
+.ui-button:focus {
+  background: transparent !important;
+  border: none !important;
+}
+
+.ui-state-hover,
+.ui-widget-content .ui-state-hover,
+.ui-widget-header .ui-state-hover {
+  border: 1px solid #999999 !important;
+  border-bottom: 0 !important;
+  background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50%
+    repeat-x !important;
+}
+
+.ui-state-active,
+.ui-widget-content .ui-state-active,
+.ui-widget-header .ui-state-active {
+  border: 1px solid #aaaaaa !important;
+  border-bottom: 0 !important;
+  background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50%
+    repeat-x !important;
+  font-weight: normal !important;
+  color: #212121 !important;
+}
+.ui-state-active a,
+.ui-state-active a:link,
+.ui-state-active a:visited {
+  color: #212121 !important;
+}
+
+ul.ui-front {
+  z-index: 1000000 !important;
+  padding-right: 0px !important;
+}
+
+ul.ui-front li {
+  padding: 3px !important;
+}
+
+ul.ui-front li:hover {
+  background-color: #e1e3e1 !important;
+}
+
+ul.ui-front li a.ui-menu-item-wrapper {
+  background: transparent !important;
+  border: none !important;
+}
+
+ul.ui-front li a.ui-menu-item-wrapper span {
+  padding-left: 5px !important;
+}
+
+ul.ui-front li a.ui-menu-item-wrapper:hover {
+  text-decoration: none !important;
+}
+
+input[type="submit"].ui-button-dialog {
+  margin: 0.5em 1em 0.5em 0 !important;
+  cursor: pointer !important;
+  background: white !important;
+  background-color: white !important;
+  color: #82b92e !important;
+  text-align: center !important;
+  border: 1px solid #82b92e !important;
+  height: 30px !important;
+  width: 90px !important;
+}
+/* --- END - JQUERY-UI --- */
+
+/* --- SWITCH --- */
+.p-switch {
+  position: relative;
+  display: inline-block;
+  width: 30px;
+  height: 17px;
+}
+
+.p-switch input {
+  opacity: 0;
+  width: 0;
+  height: 0;
+}
+
+.p-slider {
+  position: absolute;
+  cursor: pointer;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: #ccc;
+  -webkit-transition: 0.4s;
+  transition: 0.4s;
+  border-radius: 34px;
+}
+
+.p-slider:before {
+  position: absolute;
+  content: "";
+  height: 13px;
+  width: 13px;
+  left: 2px;
+  bottom: 2px;
+  background-color: white;
+  -webkit-transition: 0.4s;
+  transition: 0.4s;
+  border-radius: 50%;
+}
+
+input:checked + .p-slider {
+  background-color: #82b92e;
+}
+
+input:focus + .p-slider {
+  box-shadow: 0 0 1px #82b92e;
+}
+
+input:checked + .p-slider:before {
+  -webkit-transform: translateX(13px);
+  -ms-transform: translateX(13px);
+  transform: translateX(13px);
+}
+
+/* --- END SWITCH --- */
+
+/* --- TOAST --- */
+#notifications-toasts-wrapper {
+  position: fixed;
+  right: 20px;
+  top: 70px;
+  width: 270px;
+  height: 100%;
+  z-index: 6;
+  pointer-events: none;
+}
+
+.snackbar {
+  max-width: 270px;
+  background-color: #333;
+  color: #fff;
+  text-align: center;
+  /* border-radius: 2px; */
+  padding: 16px;
+  margin: 10px;
+  border-radius: 4px;
+  visibility: hidden;
+  pointer-events: all;
+}
+
+.snackbar.show {
+  visibility: visible;
+  -webkit-animation: fadein 0.5s, fadeout 0.5s 7.5s;
+  animation: fadein 0.5s, fadeout 0.5s 7.5s;
+}
+
+.snackbar p,
+.snackbar h3 {
+  text-align: left;
+  margin: 0;
+  pointer-events: none;
+}
+.snackbar h3 {
+  color: white;
+  margin-bottom: 10px;
+}
+
+@-webkit-keyframes fadein {
+  from {
+    bottom: 0;
+    opacity: 0;
+  }
+  to {
+    bottom: 30px;
+    opacity: 1;
+  }
+}
+
+@keyframes fadein {
+  from {
+    bottom: 0;
+    opacity: 0;
+  }
+  to {
+    bottom: 30px;
+    opacity: 1;
+  }
+}
+
+@-webkit-keyframes fadeout {
+  from {
+    bottom: 30px;
+    opacity: 1;
+  }
+  to {
+    bottom: 0;
+    opacity: 0;
+  }
+}
+
+@keyframes fadeout {
+  from {
+    bottom: 30px;
+    opacity: 1;
+  }
+  to {
+    bottom: 0;
+    opacity: 0;
+  }
+}
+
+/* --- END TOAST --- */
diff --git a/pandora_console/operation/messages/message_edit.php b/pandora_console/operation/messages/message_edit.php
index b284bc3d13..6c100d7c00 100644
--- a/pandora_console/operation/messages/message_edit.php
+++ b/pandora_console/operation/messages/message_edit.php
@@ -106,34 +106,65 @@ if ($read_message) {
         $dst_name = $message['id_usuario_destino'];
     }
 
-    $table = new stdClass();
-    $table->width = '100%';
-    $table->class = 'databox filters';
-    $table->data = [];
+    echo '<h1>Conversation with '.$user_name.'</h1>';
+    echo '<h2>Subject: '.$message['subject'].'</h2>';
 
-    $table->data[0][0] = __('Sender');
-    $table->data[0][1] = $user_name.' '.__('at').' '.ui_print_timestamp(
-        $message['timestamp'],
-        true,
-        ['prominent' => 'timestamp']
-    );
+    $conversation = [];
+    $target_str = $message['mensaje'];
 
-    $table->data[1][0] = __('Destination');
-    $table->data[1][1] = $dst_name;
+    while (preg_match_all(
+        '/(.*)On(.*)wrote:(.*)/',
+        $target_str,
+        $decoded,
+        PREG_PATTERN_ORDER
+    ) !== false && empty($target_str) !== true) {
+        if (empty($decoded[2]) !== true) {
+            array_push(
+                $conversation,
+                [
+                    'message' => array_pop($decoded)[0],
+                    'date'    => array_pop($decoded)[0],
+                ]
+            );
+        } else {
+            array_push(
+                $conversation,
+                ['message' => $target_str]
+            );
+        }
+
+        $target_str = $decoded[1][0];
+    }
+
+    ui_require_css_file('message_edit');
+    foreach ($conversation as $row) {
+        $date = $row['date'];
+
+        if ($date === null) {
+            $date = date(
+                $config['date_format'],
+                $message['timestamp']
+            ).' '.$user_name;
+        }
+
+        $order = [
+            "\r\n",
+            "\n",
+            "\r",
+        ];
+        $replace = '<br />';
+        $parsed_message = str_replace(
+            $order,
+            $replace,
+            trim(io_safe_output($row['message']))
+        );
+
+        echo '<div class="container">';
+        echo '  <p>'.$parsed_message.'</p>';
+        echo '<span class="time-left">'.$date.'</span>';
+        echo '</div>';
+    }
 
-    $table->data[2][0] = __('Subject');
-    $table->data[2][1] = html_print_input_text_extended(
-        'subject',
-        $message['subject'],
-        'text-subject',
-        '',
-        50,
-        70,
-        true,
-        false,
-        '',
-        'readonly'
-    );
 
     $order = [
         "\r\n",
@@ -143,16 +174,6 @@ if ($read_message) {
     $replace = '<br />';
     $parsed_message = str_replace($order, $replace, $message['mensaje']);
 
-    $table->data[3][0] = __('Message');
-    $table->data[3][1] = html_print_textarea(
-        'message',
-        15,
-        255,
-        $message['mensaje'],
-        'readonly',
-        true
-    );
-
     // Prevent RE: RE: RE:.
     if (strstr($message['subject'], 'RE:')) {
         $new_subj = $message['subject'];
@@ -166,8 +187,8 @@ if ($read_message) {
         $message['timestamp']
     ).' '.$user_name.' '.__('wrote').":\n\n".$message['mensaje'];
 
+
     echo '<form id="delete_message" method="post" action="index.php?sec=message_list&amp;sec2=operation/messages/message_list&show_sent=1&amp;delete_message=1&amp;id='.$message_id.'">';
-        html_print_table($table);
     echo '</form>';
 
     echo '<form id="reply_message" method="post" action="index.php?sec=message_list&sec2=operation/messages/message_edit&amp;new_msg=1&amp;reply=1">';
@@ -179,7 +200,7 @@ if ($read_message) {
 
     echo "<div class= 'action-buttons' style=' width:".$table->width."'>";
     html_print_submit_button(
-        __('Delete'),
+        __('Delete conversation'),
         'delete_btn',
         false,
         'form="delete_message" class="sub delete"'
@@ -294,7 +315,6 @@ if ($reply) {
 }
 
 
-
 if ($own_info['is_admin'] || check_acl($config['id_user'], 0, 'PM')) {
     $return_all_groups = true;
 } else {
diff --git a/pandora_console/operation/messages/message_list.php b/pandora_console/operation/messages/message_list.php
index 4a6d48a397..9427159432 100644
--- a/pandora_console/operation/messages/message_list.php
+++ b/pandora_console/operation/messages/message_list.php
@@ -115,13 +115,13 @@ if ($show_sent) {
     $messages = messages_get_overview_sent('', 'DESC');
 } else {
     // Messages received.
-    $num_messages = messages_get_count($config['id_user'], true);
+    $num_messages = messages_get_count($config['id_user'], true, true);
     if ($num_messages > 0 && !is_ajax()) {
-        $unread_messages = messages_get_count($config['id_user']);
+        $unread_messages = messages_get_count($config['id_user'], false, true);
         echo '<p>'.__('You have').' <b>'.$unread_messages.'</b> '.__('unread message(s)').'.</p>';
         $messages = messages_get_overview();
     } else {
-        $messages = messages_get_overview('status', 'ASC', false);
+        $messages = messages_get_overview('status', 'ASC');
     }
 }
 
diff --git a/pandora_console/operation/users/user_edit_notifications.php b/pandora_console/operation/users/user_edit_notifications.php
index edec0e2a6c..6ffbf87976 100644
--- a/pandora_console/operation/users/user_edit_notifications.php
+++ b/pandora_console/operation/users/user_edit_notifications.php
@@ -4,7 +4,6 @@
 // ==================================================
 // Copyright (c) 2005-2010 Artica Soluciones Tecnologicas
 // Please see http://pandorafms.org for full contribution list
-
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
 // as published by the Free Software Foundation for version 2.
@@ -12,13 +11,112 @@
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 // GNU General Public License for more details.
-
 // Load global vars
 global $config;
 
+// Includes.
+require_once $config['homedir'].'/include/functions_notifications.php';
+
 // Load the header
-require($config['homedir'] . "/operation/users/user_edit_header.php");
+require $config['homedir'].'/operation/users/user_edit_header.php';
 
-// TODO
+if (get_parameter('change_label', 0)) {
+    $label = get_parameter('label', '');
+    $source = get_parameter('source', 0);
+    $user = get_parameter('user', '');
+    $value = get_parameter('value', 0) ? 1 : 0;
 
-?>
\ No newline at end of file
+    // Update the label value.
+    ob_clean();
+    echo json_encode(
+        [
+            'result' => notifications_set_user_label_status(
+                $source,
+                $user,
+                $label,
+                $value
+            ),
+        ]
+    );
+    return;
+}
+
+// User notification table. It is just a wrapper.
+$table_content = new StdClass();
+$table_content->data = [];
+$table_content->width = '100%';
+$table_content->id = 'user-notifications-wrapper';
+$table_content->class = 'databox filters';
+$table_content->size[0] = '33%';
+$table_content->size[1] = '33%';
+$table_content->size[2] = '33%';
+
+// Print the header.
+$table_content->data[] = [
+    '',
+    __('Enable'),
+    __('Also receive an email'),
+];
+
+$sources = notifications_get_all_sources();
+foreach ($sources as $source) {
+    $table_content->data[] = [
+        $source['description'],
+        notifications_print_user_switch($source, $id, 'enabled'),
+        notifications_print_user_switch($source, $id, 'also_mail'),
+    ];
+}
+
+html_print_table($table_content);
+
+// Print id user to handle it on js.
+html_print_input_hidden('id_user', $id);
+
+?>
+<script>
+// Encapsulate the code
+(function() {
+    function notifications_change_label(event) {
+        event.preventDefault();
+        var check = document.getElementById(event.target.id);
+        if (check === null) return;
+
+        var match = /notifications-user-([0-9]+)-label-(.*)/
+            .exec(event.target.id);
+
+        jQuery.post ("ajax.php",
+            {
+                "page" : "operation/users/user_edit_notifications",
+                "change_label" : 1,
+                "label" : match[2],
+                "source" : match[1],
+                "user" : document.getElementById('hidden-id_user').value,
+                "value": check.checked ? 1 : 0
+            },
+            function (data, status) {
+                if (!data.result) {
+                    console.error("Error changing configuration in database.");
+                } else {
+                    check.checked = !check.checked;
+                }
+            },
+            "json"
+        ).done(function(m){})
+        .fail(function(xhr, textStatus, errorThrown){
+            console.error(
+                "Cannot change configuration in database. Server error.",
+                xhr.responseText
+            );
+        });
+
+    }
+    var all_labels = document.getElementsByClassName(
+        'notifications-user-label_individual'
+    );
+    for (var i = 0; i < all_labels.length; i++) {
+        all_labels[i].addEventListener(
+            'click', notifications_change_label, false
+        );
+    }
+}());
+</script>
diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql
index a8d9b5c129..4271269fd7 100644
--- a/pandora_console/pandoradb.sql
+++ b/pandora_console/pandoradb.sql
@@ -655,6 +655,19 @@ CREATE TABLE IF NOT EXISTS `tevento` (
 -- Criticity: 3 - Warning (yellow) (status 2)
 -- Criticity: 4 - Critical (red) (status 1)
 
+-- ---------------------------------------------------------------------
+-- Table `tevent_extended`
+-- ---------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `tevent_extended` (
+	`id` serial PRIMARY KEY,
+	`id_evento` bigint(20) unsigned NOT NULL,
+	`external_id` bigint(20) unsigned,
+	`utimestamp` bigint(20) NOT NULL default '0',
+	`description` text,
+	FOREIGN KEY `tevent_ext_fk`(`id_evento`) REFERENCES `tevento`(`id_evento`)
+    ON UPDATE CASCADE ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 -- ---------------------------------------------------------------------
 -- Table `tgrupo`
 -- ---------------------------------------------------------------------
diff --git a/pandora_console/pandoradb_data.sql b/pandora_console/pandoradb_data.sql
index 61b01b2fd0..a8e378042c 100644
--- a/pandora_console/pandoradb_data.sql
+++ b/pandora_console/pandoradb_data.sql
@@ -23,7 +23,8 @@ INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal
 INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal`, `fields_descriptions`, `fields_values`) VALUES (9,'SMS','sendsms&#x20;_field1_&#x20;_field2_','Send&#x20;SMS&#x20;using&#x20;the&#x20;standard&#x20;SMS&#x20;device,&#x20;using&#x20;smstools.&#x20;&#x20;Uses&#x20;field2&#x20;as&#x20;text&#x20;message,&#x20;field1&#x20;as&#x20;destination&#x20;phone&#x20;&#40;include&#x20;international&#x20;prefix!&#41;',0,'[\"Destination&#x20;number\",\"Message\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]','[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]');
 INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal`, `fields_descriptions`, `fields_values`) VALUES (10,'Validate&#x20;Event','Internal&#x20;type','This&#x20;alert&#x20;validate&#x20;the&#x20;events&#x20;matched&#x20;with&#x20;a&#x20;module&#x20;given&#x20;the&#x20;agent&#x20;name&#x20;&#40;_field1_&#41;&#x20;and&#x20;module&#x20;name&#x20;&#40;_field2_&#41;',1,'[\"Agent&#x20;name\",\"Module&#x20;name\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]','[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]');
 INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal`, `fields_descriptions`, `fields_values`) VALUES (11,'Integria&#x20;IMS&#x20;Ticket','Internal&#x20;type','This&#x20;alert&#x20;create&#x20;a&#x20;ticket&#x20;into&#x20;your&#x20;Integria&#x20;IMS.',1,'[\"Integria&#x20;IMS&#x20;API&#x20;path\",\"Integria&#x20;IMS&#x20;API&#x20;pass\",\"Integria&#x20;IMS&#x20;user\",\"Integria&#x20;IMS&#x20;user&#x20;pass\",\"Ticket&#x20;title\",\"Ticket&#x20;group&#x20;ID\",\"Ticket&#x20;priority\",\"Email&#x20;copy\",\"Ticket&#x20;owner\",\"Ticket&#x20;description\"]','[\"\",\"\",\"\",\"\",\"\",\"\",\"10,Maintenance;0,Informative;1,Low;2,Medium;3,Serious;4,Very&#x20;Serious\",\"\",\"\",\"\"]');
-
+INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal`, `fields_descriptions`, `fields_values`) VALUES (12,'Remote&#x20;agent&#x20;control','/usr/share/pandora_server/util/udp_client.pl&#x20;_address_&#x20;41122&#x20;&quot;_field1_&quot;','This&#x20;command&#x20;is&#x20;used&#x20;to&#x20;send&#x20;commands&#x20;to&#x20;the&#x20;agents&#x20;with&#x20;the&#x20;UDP&#x20;server&#x20;enabled.&#x20;The&#x20;UDP&#x20;server&#x20;is&#x20;used&#x20;to&#x20;order&#x20;agents&#x20;&#40;Windows&#x20;and&#x20;UNIX&#41;&#x20;to&#x20;&quot;refresh&quot;&#x20;the&#x20;agent&#x20;execution:&#x20;that&#x20;means,&#x20;to&#x20;force&#x20;the&#x20;agent&#x20;to&#x20;execute&#x20;and&#x20;send&#x20;data',0,'[\"Command\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]','[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]');
+INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal`, `fields_descriptions`, `fields_values`) VALUES (13,'Generate&#x20;Notification','Internal&#x20;type','This&#x20;command&#x20;allows&#x20;you&#x20;to&#x20;send&#x20;an&#x20;internal&#x20;notification&#x20;to&#x20;any&#x20;user&#x20;or&#x20;group.',1,'[\"Destination&#x20;user\",\"Destination&#x20;group\",\"Title\",\"Message\",\"Link\",\"Criticity\",\"\",\"\",\"\",\"\",\"\"]','[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]');
 --
 -- Dumping data for table `tconfig`
 --
@@ -1104,10 +1105,7 @@ INSERT INTO `tgis_map_layer` VALUES (1,'Group All',1,0,1,0);
 
 -- example alert template
 
-INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal`, `fields_descriptions`, `fields_values`) VALUES (12,'Remote&#x20;agent&#x20;control','/usr/share/pandora_server/util/udp_client.pl&#x20;_address_&#x20;41122&#x20;&quot;_field1_&quot;','This&#x20;command&#x20;is&#x20;used&#x20;to&#x20;send&#x20;commands&#x20;to&#x20;the&#x20;agents&#x20;with&#x20;the&#x20;UDP&#x20;server&#x20;enabled.&#x20;The&#x20;UDP&#x20;server&#x20;is&#x20;used&#x20;to&#x20;order&#x20;agents&#x20;&#40;Windows&#x20;and&#x20;UNIX&#41;&#x20;to&#x20;&quot;refresh&quot;&#x20;the&#x20;agent&#x20;execution:&#x20;that&#x20;means,&#x20;to&#x20;force&#x20;the&#x20;agent&#x20;to&#x20;execute&#x20;and&#x20;send&#x20;data',0,'[\"Command\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]','[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]');
-
 -- alert actions (default)
-
 INSERT INTO `talert_actions` (`id`, `name`, `id_alert_command`, `field1`, `field2`, `field3`, `field4`, `field5`, `field6`, `field7`, `field8`, `field9`, `field10`, `id_group`, `action_threshold`, `field1_recovery`, `field2_recovery`, `field3_recovery`, `field4_recovery`, `field5_recovery`, `field6_recovery`, `field7_recovery`, `field8_recovery`, `field9_recovery`, `field10_recovery`) VALUES
 (1,'Mail&#x20;to&#x20;Admin',1,'yourmail@domain.es','[PANDORA] Alert from agent _agent_ on module _module_','&lt;style&#x20;type=&quot;text/css&quot;&gt;&#x0d;&#x0a;&#47;&#42;&#x20;Take&#x20;care&#x20;of&#x20;image&#x20;borders&#x20;and&#x20;formatting&#x20;&#42;&#47;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;img&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;max-width:&#x20;600px;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;outline:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;text-decoration:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;-ms-interpolation-mode:&#x20;bicubic;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;a&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;border:&#x20;0;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;outline:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;a&#x20;img&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;border:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#47;&#42;&#x20;General&#x20;styling&#x20;&#42;&#47;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;td,&#x20;h1,&#x20;h2,&#x20;h3&#x20;&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-family:&#x20;Helvetica,&#x20;Arial,&#x20;sans-serif;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;400;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;td&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;14px;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;line-height:&#x20;150%;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;text-align:&#x20;left;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;body&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;-webkit-font-smoothing:antialiased;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;-webkit-text-size-adjust:none;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;width:&#x20;100%;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;height:&#x20;100%;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;color:&#x20;#37302d;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;background:&#x20;#ffffff;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;table&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;border-collapse:&#x20;collapse&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h1,&#x20;h2,&#x20;h3&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;padding:&#x20;0;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;margin:&#x20;0;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;color:&#x20;#444444;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;400;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;line-height:&#x20;110%;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h1&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;35px;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h2&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;30px;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h3&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;24px;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h4&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;18px;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;normal;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;.important-font&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;color:&#x20;#21BEB4;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;bold;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;.hide&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;display:&#x20;none&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;.force-full-width&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;width:&#x20;100%&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&lt;/style&gt;&#x0d;&#x0a;&lt;style&#x20;type=&quot;text/css&quot;&#x20;media=&quot;screen&quot;&gt;&#x0d;&#x0a;@media&#x20;screen&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;@import&#x20;url&#40;http://fonts.googleapis.com/css?family=Open+Sans:400&#41;;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#47;&#42;&#x20;Thanks&#x20;Outlook&#x20;2013!&#x20;&#42;&#47;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td,&#x20;h1,&#x20;h2,&#x20;h3&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;font-family:&#x20;&#039;Open&#x20;Sans&#039;,&#x20;&#039;Helvetica&#x20;Neue&#039;,&#x20;Arial,&#x20;sans-serif&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&lt;/style&gt;&#x0d;&#x0a;&lt;style&#x20;type=&quot;text/css&quot;&#x20;media=&quot;only&#x20;screen&#x20;and&#x20;&#40;max-width:&#x20;600px&#41;&quot;&gt;&#x0d;&#x0a;&#47;&#42;&#x20;Mobile&#x20;styles&#x20;&#42;&#47;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;@media&#x20;only&#x20;screen&#x20;and&#x20;&#40;max-width:&#x20;600px&#41;&#x20;{&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class=&quot;w320&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;320px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class=&quot;w300&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;300px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class=&quot;w290&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;290px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class=&quot;w320&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;320px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class~=&quot;mobile-padding&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-left:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-right:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-padding-left&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-left:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-padding-right&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-right:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-block&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;display:&#x20;block&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;100%&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;text-align:&#x20;left&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-left:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-right:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-bottom:&#x20;15px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-no-padding-bottom&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-bottom:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class~=&quot;mobile-center&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;text-align:&#x20;center&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class*=&quot;mobile-center-block&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;float:&#x20;none&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;margin:&#x20;0&#x20;auto&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;*[class*=&quot;mobile-hide&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;display:&#x20;none&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;height:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;line-height:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-border&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;border:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&lt;/style&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&#x20;align=&quot;center&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;align=&quot;center&quot;&#x20;valign=&quot;top&quot;&#x20;bgcolor=&quot;#ffffff&quot;&#x20;width=&quot;100%&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background:&#x20;#1f1f1f;&#x20;height:&#x20;70px;&quot;&#x20;width=&quot;100%&quot;&gt;&lt;center&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;width:&#x20;600px;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-block&#x20;mobile-no-padding-bottom&#x20;mobile-center&quot;&#x20;style=&quot;background:&#x20;#1f1f1f;&#x20;padding:&#x20;10px&#x20;10px&#x20;10px&#x20;20px;&quot;&#x20;valign=&quot;top&quot;&#x20;width=&quot;270&quot;&gt;&lt;a&#x20;style=&quot;text-decoration:&#x20;none;&quot;&#x20;href=&quot;#&quot;&gt;&#x20;&lt;img&#x20;src=&quot;https://pandorafms.com/images/logo_pandora_email.png&quot;&#x20;alt=&quot;Your&#x20;Logo&quot;&#x20;width=&quot;200&quot;&gt;&#x20;&lt;/a&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-block&#x20;mobile-center&quot;&#x20;style=&quot;background:&#x20;#1f1f1f;&#x20;padding:&#x20;17px&#x20;15px&#x20;10px&#x20;10px;&quot;&#x20;valign=&quot;top&quot;&#x20;width=&quot;270&quot;&gt;&amp;nbsp;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;border-bottom:&#x20;1px&#x20;solid&#x20;#e7e7e7;&quot;&gt;&lt;center&#x20;style=&quot;padding-bottom:&#x20;20px;&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;width:&#x20;600px;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-padding&quot;&#x20;style=&quot;padding:&#x20;20px;&quot;&#x20;align=&quot;left&quot;&gt;&lt;br&#x20;class=&quot;mobile-hide&quot;&gt;&#x0d;&#x0a;&lt;h2&gt;Pandora&#x20;FMS&#x20;alert&#x20;system&lt;/h2&gt;&#x0d;&#x0a;&lt;br&gt;&#x20;Dear&#x20;customer,&lt;br&gt;&lt;br&gt;&#x20;We&#x20;have&#x20;&lt;strong&gt;bad&#x20;news&lt;/strong&gt;&#x20;for&#x20;you.&#x20;Something&#x20;is&#x20;on&#x20;&lt;strong&gt;CRITICAL&lt;/strong&gt;&#x20;status!&lt;br&gt;&#x20;&lt;br&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&#x20;bgcolor=&quot;#ffffff&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;width:&#x20;100px;&#x20;background:&#x20;#D84A38;&quot;&gt;&#x0d;&#x0a;&lt;div&gt;&lt;!--[if&#x20;mso]&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;v:rect&#x20;xmlns:v=&quot;urn:schemas-microsoft-com:vml&quot;&#x20;xmlns:w=&quot;urn:schemas-microsoft-com:office:word&quot;&#x20;href=&quot;#&quot;&#x20;style=&quot;height:33px;v-text-anchor:middle;width:100px;&quot;&#x20;stroke=&quot;f&quot;&#x20;fillcolor=&quot;#D84A38&quot;&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;w:anchorlock/&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;center&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;![endif]--&gt;&#x20;&lt;a&#x20;style=&quot;background-color:&#x20;#1f1f1f;&#x20;color:&#x20;whitesmoke;&#x20;display:&#x20;inline-block;&#x20;font-family:&#x20;sans-serif;&#x20;font-size:&#x20;13px;&#x20;font-weight:&#x20;bold;&#x20;line-height:&#x20;33px;&#x20;text-align:&#x20;center;&#x20;text-decoration:&#x20;none;&#x20;width:&#x20;250px;&#x20;-webkit-text-size-adjust:&#x20;none;&quot;&#x20;href=&quot;_homeurl_&quot;&gt;Go&#x20;to&#x20;Pandora&#x20;FMS&#x20;Console&lt;/a&gt;&#x20;&lt;!--[if&#x20;mso]&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/center&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/v:rect&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;![endif]--&gt;&lt;/div&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background-color:&#x20;#ffffff;&#x20;font-size:&#x20;0;&#x20;line-height:&#x20;0;&quot;&#x20;width=&quot;281&quot;&gt;&amp;nbsp;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-hide&quot;&#x20;style=&quot;padding-top:&#x20;20px;&#x20;padding-bottom:&#x20;0;&#x20;vertical-align:&#x20;bottom;&quot;&#x20;valign=&quot;bottom&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-bottom:&#x20;0px;&#x20;vertical-align:&#x20;bottom;&quot;&#x20;align=&quot;right&quot;&#x20;valign=&quot;bottom&quot;&gt;&lt;img&#x20;style=&quot;vertical-align:&#x20;bottom;&#x20;padding-bottom:&#x20;10px;&quot;&#x20;src=&quot;https://pandorafms.com/images/alerta_roja.png&quot;&#x20;alt=&quot;&quot;&#x20;width=&quot;130&quot;&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background-color:&#x20;#f8f8f8;&#x20;border-bottom:&#x20;1px&#x20;solid&#x20;#e7e7e7;&#x20;padding-top:&#x20;10px;&quot;&#x20;valign=&quot;top&quot;&gt;&lt;center&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;height:&#x20;100%;&#x20;width:&#x20;600px;&quot;&#x20;border=&quot;0&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-padding&quot;&#x20;style=&quot;padding:&#x20;20px;&quot;&#x20;valign=&quot;top&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;border=&quot;0&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-right:&#x20;20px;&quot;&#x20;colspan=&quot;2&quot;&gt;&lt;strong&gt;Monitoring&#x20;details&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-top:&#x20;25px;&#x20;width:&#x20;150px;&#x20;border-top:&#x20;1px&#x20;solid&#x20;#E7E7E7;&#x20;vertical-align:&#x20;top;&quot;&gt;&lt;strong&gt;Data&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-top:&#x20;25px;&#x20;padding-right:&#x20;20px;&#x20;border-top:&#x20;1px&#x20;solid&#x20;#E7E7E7;&#x20;vertical-align:&#x20;top;&quot;&gt;_data_&#x20;&lt;em&gt;&#40;_modulestatus_&#41;&lt;/em&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&gt;_agent_&#x20;&lt;em&gt;_address_&lt;/em&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&gt;&lt;strong&gt;Module&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&gt;_module_&#x20;&lt;em&gt;_moduledescription_&#x20;&lt;/em&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&gt;&lt;strong&gt;Timestamp&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&gt;_timestamp_&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-top:&#x20;20px;&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;vertical-align:&#x20;top;&quot;&#x20;width=&quot;350&quot;&gt;This&#x20;is&#x20;a&#x20;graph&#x20;of&#x20;latest&#x20;24hr&#x20;data&#x20;for&#x20;this&#x20;module:&#x20;&lt;br&gt;&lt;br&gt;&#x20;_modulegraph_24h_&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background-color:&#x20;#1f1f1f;&quot;&gt;&lt;center&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;height:&#x20;100%;&#x20;color:&#x20;#ffffff;&#x20;width:&#x20;600px;&quot;&#x20;border=&quot;0&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&#x20;bgcolor=&quot;#1f1f1f&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-padding&quot;&#x20;style=&quot;font-size:&#x20;12px;&#x20;padding:&#x20;20px;&#x20;background-color:&#x20;#1f1f1f;&#x20;color:&#x20;#ffffff;&#x20;text-align:&#x20;center;&quot;&#x20;align=&quot;right&quot;&#x20;valign=&quot;middle&quot;&gt;&lt;a&#x20;style=&quot;color:&#x20;#ffffff;&quot;&#x20;href=&quot;https://pandorafms.com/company/contact/&quot;&gt;Contact&#x20;Us&lt;/a&gt;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&#x20;&lt;a&#x20;style=&quot;color:&#x20;#ffffff;&quot;&#x20;href=&quot;https://forums.pandorafms.com/&quot;&gt;Support&lt;/a&gt;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&#x20;&lt;a&#x20;style=&quot;color:&#x20;#ffffff;&quot;&#x20;href=&quot;https://wiki.pandorafms.com&quot;&gt;Docs&lt;/a&gt;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;','','','','','','','',0,0,'','','&lt;style&#x20;type=&quot;text/css&quot;&gt;&lt;!--&#x0d;&#x0a;&#47;&#42;&#x20;Take&#x20;care&#x20;of&#x20;image&#x20;borders&#x20;and&#x20;formatting&#x20;&#42;&#47;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;img&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;max-width:&#x20;600px;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;outline:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;text-decoration:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;-ms-interpolation-mode:&#x20;bicubic;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;a&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;border:&#x20;0;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;outline:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;a&#x20;img&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;border:&#x20;none;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#47;&#42;&#x20;General&#x20;styling&#x20;&#42;&#47;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;td,&#x20;h1,&#x20;h2,&#x20;h3&#x20;&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-family:&#x20;Helvetica,&#x20;Arial,&#x20;sans-serif;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;400;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;td&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;14px;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;line-height:&#x20;150%;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;text-align:&#x20;left;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;body&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;-webkit-font-smoothing:antialiased;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;-webkit-text-size-adjust:none;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;width:&#x20;100%;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;height:&#x20;100%;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;color:&#x20;#37302d;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;background:&#x20;#ffffff;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;table&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;border-collapse:&#x20;collapse&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h1,&#x20;h2,&#x20;h3&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;padding:&#x20;0;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;margin:&#x20;0;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;color:&#x20;#444444;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;400;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;line-height:&#x20;110%;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h1&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;35px;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h2&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;30px;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h3&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;24px;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;h4&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;18px;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;normal;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;.important-font&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;color:&#x20;#21BEB4;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;font-weight:&#x20;bold;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;.hide&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;display:&#x20;none&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;.force-full-width&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;width:&#x20;100%&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;}&#x0d;&#x0a;--&gt;&lt;/style&gt;&#x0d;&#x0a;&lt;style&#x20;type=&quot;text/css&quot;&#x20;media=&quot;screen&quot;&gt;&lt;!--&#x0d;&#x0a;@media&#x20;screen&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;@import&#x20;url&#40;http://fonts.googleapis.com/css?family=Open+Sans:400&#41;;&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#47;&#42;&#x20;Thanks&#x20;Outlook&#x20;2013!&#x20;&#42;&#47;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td,&#x20;h1,&#x20;h2,&#x20;h3&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;font-family:&#x20;&#039;Open&#x20;Sans&#039;,&#x20;&#039;Helvetica&#x20;Neue&#039;,&#x20;Arial,&#x20;sans-serif&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;--&gt;&lt;/style&gt;&#x0d;&#x0a;&lt;style&#x20;type=&quot;text/css&quot;&#x20;media=&quot;only&#x20;screen&#x20;and&#x20;&#40;max-width:&#x20;600px&#41;&quot;&gt;&lt;!--&#x0d;&#x0a;&#47;&#42;&#x20;Mobile&#x20;styles&#x20;&#42;&#47;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;@media&#x20;only&#x20;screen&#x20;and&#x20;&#40;max-width:&#x20;600px&#41;&#x20;{&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class=&quot;w320&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;320px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class=&quot;w300&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;300px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class=&quot;w290&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;290px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class=&quot;w320&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;320px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class~=&quot;mobile-padding&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-left:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-right:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-padding-left&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-left:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-padding-right&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-right:&#x20;14px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-block&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;display:&#x20;block&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;100%&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;text-align:&#x20;left&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-left:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-right:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-bottom:&#x20;15px&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-no-padding-bottom&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;padding-bottom:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class~=&quot;mobile-center&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;text-align:&#x20;center&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;table[class*=&quot;mobile-center-block&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;float:&#x20;none&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;margin:&#x20;0&#x20;auto&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;*[class*=&quot;mobile-hide&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;display:&#x20;none&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;width:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;height:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;line-height:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;font-size:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;td[class*=&quot;mobile-border&quot;]&#x20;{&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;border:&#x20;0&#x20;!important;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;}&#x0d;&#x0a;--&gt;&lt;/style&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&#x20;align=&quot;center&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;align=&quot;center&quot;&#x20;valign=&quot;top&quot;&#x20;bgcolor=&quot;#ffffff&quot;&#x20;width=&quot;100%&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background:&#x20;#1f1f1f;&#x20;height:&#x20;70px;&quot;&#x20;width=&quot;100%&quot;&gt;&lt;center&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;width:&#x20;600px;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-block&#x20;mobile-no-padding-bottom&#x20;mobile-center&quot;&#x20;style=&quot;background:&#x20;#1f1f1f;&#x20;padding:&#x20;10px&#x20;10px&#x20;10px&#x20;20px;&quot;&#x20;valign=&quot;top&quot;&#x20;width=&quot;270&quot;&gt;&lt;a&#x20;style=&quot;text-decoration:&#x20;none;&quot;&#x20;href=&quot;#&quot;&gt;&#x20;&lt;img&#x20;src=&quot;https://pandorafms.com/images/logo_pandora_email.png&quot;&#x20;alt=&quot;Your&#x20;Logo&quot;&#x20;width=&quot;200&quot;&gt;&#x20;&lt;/a&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-block&#x20;mobile-center&quot;&#x20;style=&quot;background:&#x20;#1f1f1f;&#x20;padding:&#x20;17px&#x20;15px&#x20;10px&#x20;10px;&quot;&#x20;valign=&quot;top&quot;&#x20;width=&quot;270&quot;&gt;&amp;nbsp;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;border-bottom:&#x20;1px&#x20;solid&#x20;#e7e7e7;&quot;&gt;&lt;center&#x20;style=&quot;padding-bottom:&#x20;20px;&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;width:&#x20;600px;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-padding&quot;&#x20;style=&quot;padding:&#x20;20px;&quot;&#x20;align=&quot;left&quot;&gt;&lt;br&#x20;class=&quot;mobile-hide&quot;&gt;&#x0d;&#x0a;&lt;h2&gt;Pandora&#x20;FMS&#x20;alert&#x20;system&lt;/h2&gt;&#x0d;&#x0a;&lt;br&gt;&#x20;Dear&#x20;customer,&lt;br&gt;&lt;br&gt;&#x20;We&#x20;have&#x20;&lt;strong&gt;good&#x20;news&lt;/strong&gt;&#x20;for&#x20;you.&#x20;Alert&#x20;has&#x20;been&#x20;&lt;strong&gt;RECOVERED&lt;/strong&gt;&amp;nbsp;status!&lt;br&gt;&#x20;&lt;br&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&#x20;bgcolor=&quot;#ffffff&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;width:&#x20;100px;&#x20;background:&#x20;#D84A38;&quot;&gt;&#x0d;&#x0a;&lt;div&gt;&lt;!--[if&#x20;mso]&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;v:rect&#x20;xmlns:v=&quot;urn:schemas-microsoft-com:vml&quot;&#x20;xmlns:w=&quot;urn:schemas-microsoft-com:office:word&quot;&#x20;href=&quot;#&quot;&#x20;style=&quot;height:33px;v-text-anchor:middle;width:100px;&quot;&#x20;stroke=&quot;f&quot;&#x20;fillcolor=&quot;#D84A38&quot;&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;w:anchorlock/&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;center&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;![endif]--&gt;&#x20;&lt;a&#x20;style=&quot;background-color:&#x20;#1f1f1f;&#x20;color:&#x20;whitesmoke;&#x20;display:&#x20;inline-block;&#x20;font-family:&#x20;sans-serif;&#x20;font-size:&#x20;13px;&#x20;font-weight:&#x20;bold;&#x20;line-height:&#x20;33px;&#x20;text-align:&#x20;center;&#x20;text-decoration:&#x20;none;&#x20;width:&#x20;250px;&#x20;-webkit-text-size-adjust:&#x20;none;&quot;&#x20;href=&quot;_homeurl_&quot;&gt;Go&#x20;to&#x20;Pandora&#x20;FMS&#x20;Console&lt;/a&gt;&#x20;&lt;!--[if&#x20;mso]&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/center&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;/v:rect&gt;&#x0d;&#x0a;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&#x20;&lt;![endif]--&gt;&lt;/div&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background-color:&#x20;#ffffff;&#x20;font-size:&#x20;0;&#x20;line-height:&#x20;0;&quot;&#x20;width=&quot;281&quot;&gt;&amp;nbsp;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-hide&quot;&#x20;style=&quot;padding-top:&#x20;20px;&#x20;padding-bottom:&#x20;0;&#x20;vertical-align:&#x20;bottom;&quot;&#x20;valign=&quot;bottom&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-bottom:&#x20;0px;&#x20;vertical-align:&#x20;bottom;&quot;&#x20;align=&quot;right&quot;&#x20;valign=&quot;bottom&quot;&gt;&lt;img&#x20;style=&quot;vertical-align:&#x20;bottom;&#x20;padding-bottom:&#x20;10px;&quot;&#x20;src=&quot;https://pandorafms.com/images/alerta_verde.png&quot;&#x20;alt=&quot;&quot;&#x20;width=&quot;130&quot;&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background-color:&#x20;#f8f8f8;&#x20;border-bottom:&#x20;1px&#x20;solid&#x20;#e7e7e7;&#x20;padding-top:&#x20;10px;&quot;&#x20;valign=&quot;top&quot;&gt;&lt;center&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;height:&#x20;100%;&#x20;width:&#x20;600px;&quot;&#x20;border=&quot;0&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-padding&quot;&#x20;style=&quot;padding:&#x20;20px;&quot;&#x20;valign=&quot;top&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;border=&quot;0&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-right:&#x20;20px;&quot;&#x20;colspan=&quot;2&quot;&gt;&lt;strong&gt;Monitoring&#x20;details&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-top:&#x20;25px;&#x20;width:&#x20;150px;&#x20;border-top:&#x20;1px&#x20;solid&#x20;#E7E7E7;&#x20;vertical-align:&#x20;top;&quot;&gt;&lt;strong&gt;Data&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-top:&#x20;25px;&#x20;padding-right:&#x20;20px;&#x20;border-top:&#x20;1px&#x20;solid&#x20;#E7E7E7;&#x20;vertical-align:&#x20;top;&quot;&gt;_data_&#x20;&lt;em&gt;&#40;_modulestatus_&#41;&lt;/em&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&gt;_agent_&#x20;&lt;em&gt;_address_&lt;/em&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&gt;&lt;strong&gt;Module&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&gt;_module_&#x20;&lt;em&gt;_moduledescription_&#x20;&lt;/em&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&gt;&lt;strong&gt;Timestamp&lt;/strong&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;td&gt;_timestamp_&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;padding-top:&#x20;20px;&quot;&gt;&#x0d;&#x0a;&lt;table&#x20;style=&quot;width:&#x20;100%;&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;vertical-align:&#x20;top;&quot;&#x20;width=&quot;350&quot;&gt;This&#x20;is&#x20;a&#x20;graph&#x20;of&#x20;latest&#x20;24hr&#x20;data&#x20;for&#x20;this&#x20;module:&#x20;&lt;br&gt;&lt;br&gt;&#x20;_modulegraph_24h_&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;style=&quot;background-color:&#x20;#1f1f1f;&quot;&gt;&lt;center&gt;&#x0d;&#x0a;&lt;table&#x20;class=&quot;w320&quot;&#x20;style=&quot;height:&#x20;100%;&#x20;color:&#x20;#ffffff;&#x20;width:&#x20;600px;&quot;&#x20;border=&quot;0&quot;&#x20;cellspacing=&quot;0&quot;&#x20;cellpadding=&quot;0&quot;&#x20;bgcolor=&quot;#1f1f1f&quot;&gt;&#x0d;&#x0a;&lt;tbody&gt;&#x0d;&#x0a;&lt;tr&gt;&#x0d;&#x0a;&lt;td&#x20;class=&quot;mobile-padding&quot;&#x20;style=&quot;font-size:&#x20;12px;&#x20;padding:&#x20;20px;&#x20;background-color:&#x20;#1f1f1f;&#x20;color:&#x20;#ffffff;&#x20;text-align:&#x20;center;&quot;&#x20;align=&quot;right&quot;&#x20;valign=&quot;middle&quot;&gt;&lt;a&#x20;style=&quot;color:&#x20;#ffffff;&quot;&#x20;href=&quot;https://pandorafms.com/company/contact/&quot;&gt;Contact&#x20;Us&lt;/a&gt;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&#x20;&lt;a&#x20;style=&quot;color:&#x20;#ffffff;&quot;&#x20;href=&quot;https://forums.pandorafms.com/&quot;&gt;Support&lt;/a&gt;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&#x20;&lt;a&#x20;style=&quot;color:&#x20;#ffffff;&quot;&#x20;href=&quot;https://wiki.pandorafms.com&quot;&gt;Docs&lt;/a&gt;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/center&gt;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;&#x0d;&#x0a;&lt;/td&gt;&#x0d;&#x0a;&lt;/tr&gt;&#x0d;&#x0a;&lt;/tbody&gt;&#x0d;&#x0a;&lt;/table&gt;','text/html','','','','','','');
 INSERT INTO `talert_actions` (`id`, `name`, `id_alert_command`, `field1`, `field2`, `field3`, `field4`, `field5`, `field6`, `field7`, `field8`, `field9`, `field10`, `id_group`, `action_threshold`, `field1_recovery`, `field2_recovery`, `field3_recovery`, `field4_recovery`, `field5_recovery`, `field6_recovery`, `field7_recovery`, `field8_recovery`, `field9_recovery`, `field10_recovery`) VALUES
diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm
index ff82d69c53..ba9ddbeae8 100644
--- a/pandora_server/lib/PandoraFMS/Core.pm
+++ b/pandora_server/lib/PandoraFMS/Core.pm
@@ -249,6 +249,7 @@ our @EXPORT = qw(
 	pandora_delete_graph_source
 	pandora_delete_custom_graph
 	pandora_edit_custom_graph
+	notification_set_targets
 	);
 
 # Some global variables
@@ -1459,6 +1460,35 @@ sub pandora_execute_action ($$$$$$$$$;$) {
 
 		pandora_create_integria_ticket($pa_config, $api_path, $api_pass, $integria_user, $integria_user_pass, $ticket_name, $ticket_group_id, $ticket_priority, $ticket_email, $ticket_owner, $ticket_description);
 
+
+	# Generate notification
+	} elsif ($clean_name eq "Generate Notification") {
+
+		# Translate macros
+		$field3 = subst_alert_macros($field3, \%macros, $pa_config, $dbh, $agent, $module);
+		$field4 = subst_alert_macros($field4, \%macros, $pa_config, $dbh, $agent, $module);
+
+		# If no targets ignore notification
+		if (defined($field1) && defined($field2) && ($field1 ne "" || $field2 ne "")) {
+			my @user_list = map {clean_blank($_)} split /,/, $field1;
+			my @group_list = map {clean_blank($_)} split /,/, $field2;
+
+			my $notification = {};
+			$notification->{'subject'} = safe_input($field3);
+			$notification->{'mensaje'} = safe_input($field4);
+			$notification->{'id_source'} = get_db_value($dbh, 'SELECT id FROM tnotification_source WHERE description = ?', safe_input('System status'));
+
+			# Create message
+			my $notification_id = db_process_insert($dbh,'id_mensaje','tmensajes',$notification);
+			if (!$notification_id) {
+				logger($pa_config, "Failed action '" . $action->{'name'} . "' for alert '". $alert->{'name'} . "' agent '" . (defined($agent) ? $agent->{'alias'} : 'N/A') . "'.", 3);
+			} else {
+				notification_set_targets($pa_config, $dbh, $notification_id, \@user_list, \@group_list);
+			}
+		} else {
+			logger($pa_config, "Failed action '" . $action->{'name'} . "' for alert '". $alert->{'name'} . "' agent '" . (defined($agent) ? $agent->{'alias'} : 'N/A') . "' Empty targets. Ignored.", 3);
+		}
+
 	# Unknown
 	} else {
 		logger($pa_config, "Unknown action '" . $action->{'name'} . "' for alert '". $alert->{'name'} . "' agent '" . (defined ($agent) ? $agent->{'alias'} : 'N/A') . "'.", 3);
@@ -5746,6 +5776,62 @@ sub pandora_safe_mode_modules_update {
 	}
 }
 
+##########################################################################
+
+=head2 C<< message_set_targets (I<$dbh>, I<$pa_config>, I<$notification_id>, I<$users>, I<$groups>) >>
+Set targets for given messaje (users and groups in hash ref)
+=cut
+
+##########################################################################
+sub notification_set_targets {
+	my ($pa_config, $dbh, $notification_id, $users, $groups) = @_;
+	my $ret = undef;
+
+	if (!defined($pa_config)) {
+		return undef;
+	}
+
+	if (!defined($notification_id)) {
+		return undef;
+	}
+
+	if (ref($users) eq "ARRAY") {
+		my $values = {};
+		foreach my $user (@{$users}) {
+			if (defined($user) && $user eq "") {
+				next;
+			}
+
+			$values->{'id_mensaje'} = $notification_id;
+			$values->{'id_user'} = $user;
+		}
+
+		$ret = db_process_insert($dbh, '', 'tnotification_user', $values);
+		if (!$ret) {
+			return undef;
+		}
+	}
+
+	if (ref($groups) eq "ARRAY") {
+		my $values = {};
+		foreach my $group (@{$groups}) {
+			if ($group != 0 && empty($group)) {
+				next;
+			}
+
+			$values->{'id_mensaje'} = $notification_id;
+			$values->{'id_group'} = $group;
+		}
+
+		$ret = db_process_insert($dbh, '', 'tnotification_group', $values);
+		if (!$ret) {
+			return undef;
+		}
+	}
+
+	return 1;
+}
+
 # End of function declaration
 # End of defined Code
 
diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl
index d1ff8d455f..ee3ab0d1fa 100644
--- a/pandora_server/util/pandora_db.pl
+++ b/pandora_server/util/pandora_db.pl
@@ -437,6 +437,13 @@ sub pandora_purgedb ($$) {
 	
 	# Delete old tgraph_source data
 	db_do ($dbh,"DELETE FROM tgraph_source WHERE id_graph NOT IN (SELECT id_graph FROM tgraph)");
+
+	# Delete old messages
+	log_message ('PURGE', "Deleting old messages.");
+	if ($conf->{'_delete_old_messages'} > 0) {
+		my $message_limit = time() - 86400 * $conf->{'_delete_old_messages'};
+		db_do ($dbh, "DELETE FROM tmensajes WHERE timestamp < ?", $message_limit);
+	}
 }
 
 ###############################################################################
@@ -656,6 +663,7 @@ sub pandora_load_config_pdb ($) {
 	$conf->{'_history_db_delay'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'history_db_delay'");
 	$conf->{'_days_delete_unknown'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'days_delete_unknown'");
 	$conf->{'_inventory_purge'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'inventory_purge'");
+	$conf->{'_delete_old_messages'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'delete_old_messages'");
 	$conf->{'_enterprise_installed'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'enterprise_installed'");
 	$conf->{'_metaconsole'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'metaconsole'");
 	$conf->{'_metaconsole_events_history'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'metaconsole_events_history'");