diff --git a/pandora_console/include/ajax/heatmap.ajax.php b/pandora_console/include/ajax/heatmap.ajax.php
new file mode 100644
index 0000000000..6d06c0e2ce
--- /dev/null
+++ b/pandora_console/include/ajax/heatmap.ajax.php
@@ -0,0 +1,368 @@
+';
+ echo '
';
+ echo '
'.__('Refresh').'
';
+ echo html_print_select(
+ [
+ '30' => __('30 seconds'),
+ (string) SECONDS_1MINUTE => __('1 minute'),
+ '180' => __('3 minutes'),
+ (string) SECONDS_5MINUTES => __('5 minutes'),
+ ],
+ 'refresh',
+ $refresh,
+ '',
+ '',
+ 0,
+ true,
+ false,
+ false,
+ '',
+ false,
+ 'margin-top: 3px;'
+ );
+ echo '
';
+
+ echo '';
+ echo '
'.__('Search').'
';
+ echo html_print_input_text('search', $search, '', 30, 255, true);
+ echo '
';
+
+ echo '';
+ echo '
'.__('Type').'
';
+ echo html_print_select(
+ [
+ 0 => __('Group agents'),
+ 1 => __('Group modules by tag'),
+ 2 => __('Group modules by module group'),
+ ],
+ 'type',
+ $type,
+ '',
+ '',
+ 0,
+ true,
+ false,
+ false,
+ '',
+ false,
+ 'margin-top: 3px;width:70%'
+ );
+ echo '
';
+
+ echo '';
+ echo '
'.__('Show groups').'
';
+ echo html_print_checkbox('group', 1, $group, true);
+ echo '
';
+ echo '';
+ }
+
+
+ if ($getFilterType === true) {
+ $filter = get_parameter('filter', 0);
+ echo '';
+ switch ($type) {
+ case 0:
+ default:
+ echo '
'.__('Group').'
';
+ echo html_print_input(
+ [
+ 'type' => 'select_groups',
+ 'returnAllGroup' => true,
+ 'name' => 'filter[]',
+ 'selected' => $filter,
+ 'return' => true,
+ 'required' => true,
+ 'privilege' => 'AR',
+ 'multiple' => true,
+ ]
+ );
+ break;
+
+ case 1:
+ echo '
'.__('Tag').'
';
+ if (tags_has_user_acl_tags($config['id_user']) === false) {
+ echo html_print_select_from_sql(
+ 'SELECT id_tag, name
+ FROM ttag
+ WHERE id_tag
+ ORDER BY name',
+ 'filter[]',
+ $filter,
+ '',
+ '',
+ '',
+ true,
+ true,
+ false,
+ false,
+ 'width: 200px',
+ '5'
+ );
+ } else {
+ $user_tags = tags_get_user_tags($config['id_user'], 'AR');
+ if (!empty($user_tags)) {
+ $id_user_tags = array_keys($user_tags);
+
+ echo html_print_select_from_sql(
+ 'SELECT id_tag, name
+ FROM ttag
+ WHERE id_tag IN ('.implode(',', $id_user_tags).')
+ ORDER BY name',
+ 'filter[]',
+ $filter,
+ '',
+ '',
+ '',
+ true,
+ true,
+ false,
+ false,
+ 'width: 200px',
+ '5'
+ );
+ } else {
+ echo html_print_select_from_sql(
+ 'SELECT id_tag, name
+ FROM ttag
+ WHERE id_tag
+ ORDER BY name',
+ 'filter[]',
+ $filter,
+ '',
+ '',
+ '',
+ true,
+ true,
+ false,
+ false,
+ 'width: 200px',
+ '5'
+ );
+ }
+ }
+ break;
+
+ case 2:
+ echo '
'.__('Module group').'
';
+ echo html_print_select_from_sql(
+ 'SELECT id_mg, name FROM tmodule_group ORDER BY name',
+ 'filter[]',
+ $filter,
+ '',
+ __('Not assigned'),
+ '0',
+ true,
+ true,
+ true,
+ false,
+ 'width: 200px',
+ '5'
+ );
+ break;
+ }
+
+ echo '
';
+ }
+
+ if ($getInfo === true) {
+ enterprise_include_once('include/functions_agents.php');
+ $id = get_parameter('id', 0);
+ switch ($type) {
+ case 2:
+ $data = db_get_row('tagente_modulo', 'id_agente_modulo', $id);
+
+ // Nombre.
+ $link = sprintf(
+ 'index.php?sec=view&sec2=operation/agentes/status_monitor%s&ag_modulename=%s',
+ '&refr=0&ag_group=0&module_option=1&status=-1',
+ $data['nombre']
+ );
+ echo '';
+
+ // Descripcion.
+ $description = (empty($data['descripcion']) === true) ? '-' : $data['descripcion'];
+ echo '';
+ echo '
'.__('Description').'
';
+ echo '
'.$description.'
';
+ echo '
';
+
+ // Agent.
+ echo '';
+
+ // Group.
+ $group = (empty($data['id_module_group']) === true)
+ ? '-'
+ : modules_get_modulegroup_name($data['id_module_group']);
+
+ echo '';
+ echo '
'.__('Module group').'
';
+ echo '
'.$group.'
';
+ echo '
';
+ break;
+
+ case 1:
+ $data = db_get_row('tagente_modulo', 'id_agente_modulo', $id);
+
+ // Nombre.
+ $link = sprintf(
+ 'index.php?sec=view&sec2=operation/agentes/status_monitor%s&ag_modulename=%s',
+ '&refr=0&ag_group=0&module_option=1&status=-1',
+ $data['nombre']
+ );
+ echo '';
+
+ // Descripcion.
+ $description = (empty($data['descripcion']) === true) ? '-' : $data['descripcion'];
+ echo '';
+ echo '
'.__('Description').'
';
+ echo '
'.$description.'
';
+ echo '
';
+
+ // Agent.
+ echo '';
+
+ // Group.
+ $group = (empty($data['id_module_group']) === true)
+ ? '-'
+ : modules_get_modulegroup_name($data['id_module_group']);
+
+ echo '';
+ echo '
'.__('Module group').'
';
+ echo '
'.$group.'
';
+ echo '
';
+
+ // Tag.
+ $tags = db_get_all_rows_sql('SELECT id_tag FROM ttag_module WHERE id_agente_modulo ='.$id);
+ $tags_name = '';
+ echo '';
+ echo '
'.__('Tag').'
';
+ foreach ($tags as $key => $tag) {
+ $tags_name .= tags_get_name($tag['id_tag']).', ';
+ }
+
+ $tags_name = trim($tags_name, ', ');
+ echo '
'.$tags_name.'
';
+ echo '
';
+ break;
+
+ case 0:
+ default:
+ $data = agents_get_agent($id);
+
+ // Alias.
+ echo '';
+
+ // Ip.
+ echo '';
+ echo '
'.__('IP').'
';
+ echo '
'.$data['direccion'].'
';
+ echo '
';
+
+ // OS.
+ echo '';
+ echo '
'.__('OS').'
';
+ echo '
'.ui_print_os_icon($data['id_os'], true, true).'
';
+ echo '
';
+
+ // Description.
+ echo '';
+ echo '
'.__('Description').'
';
+ echo '
'.$data['comentarios'].'
';
+ echo '
';
+
+ // Group.
+ $secondary_groups = '';
+ $secondary = agents_get_secondary_groups($data['id_agente']);
+ if (isset($secondary['for_select']) === true && empty($secondary['for_select']) === false) {
+ $secondary_groups = implode(', ', $secondary['for_select']);
+ $secondary_groups = ', '.$secondary_groups;
+ }
+
+ echo '';
+ echo '
'.__('Group').'
';
+ echo '
'.groups_get_name($data['id_grupo']).$secondary_groups.'
';
+ echo '
';
+
+
+ // Events.
+ echo '';
+ echo graph_graphic_agentevents(
+ $id,
+ 100,
+ 40,
+ SECONDS_1DAY,
+ '',
+ true,
+ false,
+ false,
+ 1
+ );
+ echo '
';
+ break;
+ }
+ }
+
+ return;
+}
diff --git a/pandora_console/include/class/Heatmap.class.php b/pandora_console/include/class/Heatmap.class.php
new file mode 100644
index 0000000000..916b4b9873
--- /dev/null
+++ b/pandora_console/include/class/Heatmap.class.php
@@ -0,0 +1,964 @@
+type = $type;
+ $this->filter = $filter;
+ (empty($randomId) === true) ? $this->randomId = uniqid() : $this->randomId = $randomId;
+ $this->refresh = $refresh;
+ $this->width = $width;
+ $this->height = $height;
+ $this->search = $search;
+ $this->group = $group;
+ }
+
+
+ /**
+ * Run.
+ *
+ * @return void
+ */
+ public function run()
+ {
+ ui_require_css_file('heatmap');
+
+ $settings = [
+ 'type' => 'POST',
+ 'dataType' => 'html',
+ 'url' => ui_get_full_url(
+ 'ajax.php',
+ false,
+ false,
+ false
+ ),
+ 'data' => [
+ 'page' => 'operation/heatmap',
+ 'method' => 'showHeatmap',
+ 'randomId' => $this->randomId,
+ 'type' => $this->type,
+ 'filter' => $this->filter,
+ 'refresh' => $this->refresh,
+ 'search' => $this->search,
+ 'group' => $this->group,
+ ],
+ ];
+
+ echo '';
+ ?>
+
+ ';
+ }
+
+
+ /**
+ * Setter for filter
+ *
+ * @param array $filter Filter.
+ *
+ * @return void
+ */
+ public function setFilter(array $filter)
+ {
+ $this->filter = $filter;
+ }
+
+
+ /**
+ * Setter for type
+ *
+ * @param integer $type Type.
+ *
+ * @return void
+ */
+ public function setType(int $type)
+ {
+ $this->type = $type;
+ }
+
+
+ /**
+ * Setter for refresh
+ *
+ * @param integer $refresh Refresh.
+ *
+ * @return void
+ */
+ public function setRefresh(int $refresh)
+ {
+ $this->refresh = $refresh;
+ }
+
+
+ /**
+ * Getter for randomId
+ *
+ * @return string
+ */
+ public function getRandomId()
+ {
+ return $this->randomId;
+ }
+
+
+ /**
+ * Get all agents
+ *
+ * @return array
+ */
+ protected function getAllAgents()
+ {
+ $filter['disabled'] = 0;
+
+ $alias = '';
+ if (empty($this->search) === false) {
+ $alias = ' AND alias LIKE "%'.$this->search.'%"';
+ }
+
+ $id_grupo = '';
+ if (empty($this->filter) === false && current($this->filter) != 0) {
+ $id_grupo = ' AND id_grupo IN ('.implode(',', $this->filter).')';
+ }
+
+ // All agents.
+ $sql = sprintf(
+ 'SELECT DISTINCT id_agente as id,alias,id_grupo,normal_count,warning_count,critical_count, unknown_count,notinit_count,total_count,fired_count,
+ (SELECT last_status_change FROM tagente_estado WHERE id_agente = tagente.id_agente ORDER BY last_status_change DESC LIMIT 1) AS last_status_change
+ FROM tagente WHERE `disabled` = 0 %s %s ORDER BY id_grupo,id_agente ASC',
+ $alias,
+ $id_grupo
+ );
+
+ $result = db_get_all_rows_sql($sql);
+
+ $agents = [];
+ // Agent status.
+ foreach ($result as $key => $agent) {
+ if ($agent['total_count'] === 0 || $agent['total_count'] === $agent['notinit_count']) {
+ $status = 'notinit';
+ } else if ($agent['critical_count'] > 0) {
+ $status = 'critical';
+ } else if ($agent['warning_count'] > 0) {
+ $status = 'warning';
+ } else if ($agent['unknown_count'] > 0) {
+ $status = 'unknown';
+ } else {
+ $status = 'normal';
+ }
+
+ if ($agent['last_status_change'] != 0) {
+ $seconds = (time() - $agent['last_status_change']);
+
+ if ($seconds >= SECONDS_1DAY) {
+ $status .= '_10';
+ } else if ($seconds >= 77760) {
+ $status .= '_9';
+ } else if ($seconds >= 69120) {
+ $status .= '_8';
+ } else if ($seconds >= 60480) {
+ $status .= '_7';
+ } else if ($seconds >= 51840) {
+ $status .= '_6';
+ } else if ($seconds >= 43200) {
+ $status .= '_5';
+ } else if ($seconds >= 34560) {
+ $status .= '_4';
+ } else if ($seconds >= 25920) {
+ $status .= '_3';
+ } else if ($seconds >= 17280) {
+ $status .= '_2';
+ } else if ($seconds >= 8640) {
+ $status .= '_1';
+ }
+ }
+
+ $agents[$key] = $agent;
+ $agents[$key]['status'] = $status;
+ }
+
+ return $agents;
+ }
+
+
+ /**
+ * Get all modules
+ *
+ * @return array
+ */
+ protected function getAllModulesByGroup()
+ {
+ $filter_group = '';
+ if (empty($this->filter) === false && current($this->filter) != -1) {
+ $filter_group = 'AND am.id_module_group IN ('.implode(',', $this->filter).')';
+ }
+
+ $filter_name = '';
+ if (empty($this->search) === false) {
+ $filter_name = 'AND nombre LIKE "%'.$this->search.'%"';
+ }
+
+ // All modules.
+ $sql = sprintf(
+ 'SELECT am.id_agente_modulo AS id, ae.known_status AS `status`, am.id_module_group AS id_grupo, ae.last_status_change FROM tagente_modulo am
+ INNER JOIN tagente_estado ae ON am.id_agente_modulo = ae.id_agente_modulo
+ WHERE am.disabled = 0 %s %s GROUP BY am.id_module_group, am.id_agente_modulo',
+ $filter_group,
+ $filter_name
+ );
+
+ $result = db_get_all_rows_sql($sql);
+
+ // Module status.
+ foreach ($result as $key => $module) {
+ $status = '';
+ switch ($module['status']) {
+ case AGENT_MODULE_STATUS_CRITICAL_BAD:
+ case AGENT_MODULE_STATUS_CRITICAL_ALERT:
+ case 1:
+ case 100:
+ $status = 'critical';
+ break;
+
+ case AGENT_MODULE_STATUS_NORMAL:
+ case AGENT_MODULE_STATUS_NORMAL_ALERT:
+ case 0:
+ case 300:
+ $status = 'normal';
+ break;
+
+ case AGENT_MODULE_STATUS_WARNING:
+ case AGENT_MODULE_STATUS_WARNING_ALERT:
+ case 2:
+ case 200:
+ $status = 'warning';
+ break;
+
+ default:
+ case AGENT_MODULE_STATUS_UNKNOWN:
+ case 3:
+ $status = 'unknown';
+ break;
+ case AGENT_MODULE_STATUS_NOT_INIT:
+ case 5:
+ $status = 'notinit';
+ break;
+ }
+
+ if ($module['last_status_change'] != 0) {
+ $seconds = (time() - $module['last_status_change']);
+
+ if ($seconds >= SECONDS_1DAY) {
+ $status .= '_10';
+ } else if ($seconds >= 77760) {
+ $status .= '_9';
+ } else if ($seconds >= 69120) {
+ $status .= '_8';
+ } else if ($seconds >= 60480) {
+ $status .= '_7';
+ } else if ($seconds >= 51840) {
+ $status .= '_6';
+ } else if ($seconds >= 43200) {
+ $status .= '_5';
+ } else if ($seconds >= 34560) {
+ $status .= '_4';
+ } else if ($seconds >= 25920) {
+ $status .= '_3';
+ } else if ($seconds >= 17280) {
+ $status .= '_2';
+ } else if ($seconds >= 8640) {
+ $status .= '_1';
+ }
+ }
+
+ $result[$key]['status'] = $status;
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Get all modules
+ *
+ * @return array
+ */
+ protected function getAllModulesByTag()
+ {
+ $filter_tag = '';
+ if (empty($this->filter) === false && $this->filter[0] !== '0') {
+ $tags = implode(',', $this->filter);
+ $filter_tag .= ' AND tm.id_tag IN ('.$tags.')';
+ }
+
+ $filter_name = '';
+ if (empty($this->search) === false) {
+ $filter_name = 'AND nombre LIKE "%'.$this->search.'%"';
+ }
+
+ // All modules.
+ $sql = sprintf(
+ 'SELECT ae.id_agente_modulo AS id, ae.known_status AS `status`, tm.id_tag AS id_grupo, ae.last_status_change FROM tagente_estado ae
+ INNER JOIN ttag_module tm ON tm.id_agente_modulo = ae.id_agente_modulo
+ WHERE 1=1 %s %s GROUP BY tm.id_tag, ae.id_agente_modulo',
+ $filter_tag,
+ $filter_name
+ );
+
+ $result = db_get_all_rows_sql($sql);
+
+ // Module status.
+ foreach ($result as $key => $module) {
+ $status = '';
+ switch ($module['status']) {
+ case AGENT_MODULE_STATUS_CRITICAL_BAD:
+ case AGENT_MODULE_STATUS_CRITICAL_ALERT:
+ case 1:
+ case 100:
+ $status = 'critical';
+ break;
+
+ case AGENT_MODULE_STATUS_NORMAL:
+ case AGENT_MODULE_STATUS_NORMAL_ALERT:
+ case 0:
+ case 300:
+ $status = 'normal';
+ break;
+
+ case AGENT_MODULE_STATUS_WARNING:
+ case AGENT_MODULE_STATUS_WARNING_ALERT:
+ case 2:
+ case 200:
+ $status = 'warning';
+ break;
+
+ default:
+ case AGENT_MODULE_STATUS_UNKNOWN:
+ case 3:
+ $status = 'unknown';
+ break;
+ case AGENT_MODULE_STATUS_NOT_INIT:
+ case 5:
+ $status = 'notinit';
+ break;
+ }
+
+ if ($module['last_status_change'] != 0) {
+ $seconds = (time() - $module['last_status_change']);
+
+ if ($seconds >= SECONDS_1DAY) {
+ $status .= '_10';
+ } else if ($seconds >= 77760) {
+ $status .= '_9';
+ } else if ($seconds >= 69120) {
+ $status .= '_8';
+ } else if ($seconds >= 60480) {
+ $status .= '_7';
+ } else if ($seconds >= 51840) {
+ $status .= '_6';
+ } else if ($seconds >= 43200) {
+ $status .= '_5';
+ } else if ($seconds >= 34560) {
+ $status .= '_4';
+ } else if ($seconds >= 25920) {
+ $status .= '_3';
+ } else if ($seconds >= 17280) {
+ $status .= '_2';
+ } else if ($seconds >= 8640) {
+ $status .= '_1';
+ }
+ }
+
+ $result[$key]['status'] = $status;
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * GetData
+ *
+ * @return array
+ */
+ public function getData()
+ {
+ switch ($this->type) {
+ case 2:
+ $data = $this->getAllModulesByGroup();
+ break;
+
+ case 1:
+ $data = $this->getAllModulesByTag();
+ break;
+
+ case 0:
+ default:
+ $data = $this->getAllAgents();
+ break;
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * GetDataJson
+ *
+ * @return json
+ */
+ public function getDataJson()
+ {
+ $return = $this->getData();
+ echo json_encode($return);
+ return '';
+ }
+
+
+ /**
+ * Get class by status
+ *
+ * @param integer $status Status.
+ *
+ * @return string
+ */
+ protected function statusColour(int $status)
+ {
+ switch ($status) {
+ case AGENT_STATUS_CRITICAL:
+ $return = 'critical';
+ break;
+
+ case AGENT_STATUS_WARNING:
+ $return = 'warning';
+ break;
+
+ case AGENT_STATUS_UNKNOWN:
+ $return = 'unknown';
+ break;
+
+ case AGENT_STATUS_NOT_INIT:
+ $return = 'notinit';
+ break;
+
+ case AGENT_STATUS_NORMAL:
+ default:
+ $return = 'normal';
+ break;
+ }
+
+ return $return;
+ }
+
+
+ /**
+ * Get max. number of y-axis
+ *
+ * @param integer $total Total.
+ * @param float $relation Aspect relation.
+ *
+ * @return integer
+ */
+ protected function getYAxis(int $total, float $relation)
+ {
+ $yAxis = sqrt(($total / $relation));
+ return $yAxis;
+
+ }
+
+
+ /**
+ * Checks if target method is available to be called using AJAX.
+ *
+ * @param string $method Target method.
+ *
+ * @return boolean True allowed, false not.
+ */
+ public function ajaxMethod(string $method):bool
+ {
+ return in_array($method, $this->AJAXMethods);
+ }
+
+
+ /**
+ * ShowHeatmap
+ *
+ * @return void
+ */
+ public function showHeatmap()
+ {
+ $result = $this->getData();
+
+ if (empty($result) === true) {
+ echo '
'.__('No data found').'
';
+ return;
+ }
+
+ $count_result = count($result);
+
+ $scale = ($this->width / $this->height);
+ $Yaxis = $this->getYAxis($count_result, $scale);
+ if ($count_result <= 3) {
+ $Xaxis = $count_result;
+ $Yaxis = 1;
+ } else {
+ $Xaxis = (int) ceil($Yaxis * $scale);
+ $Yaxis = ceil($Yaxis);
+ }
+
+ $viewBox = sprintf(
+ '0 0 %d %d',
+ $Xaxis,
+ $Yaxis
+ );
+
+ echo '
';
+
+ // Dialog.
+ echo '
';
+ }
+
+
+}
diff --git a/pandora_console/include/styles/heatmap.css b/pandora_console/include/styles/heatmap.css
new file mode 100644
index 0000000000..bac09b561e
--- /dev/null
+++ b/pandora_console/include/styles/heatmap.css
@@ -0,0 +1,271 @@
+.mainDiv {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ min-height: 750px;
+ height: 100%;
+ align-items: center;
+}
+
+.normal_10 {
+ fill: #82b92e;
+}
+
+.normal_9 {
+ fill: #89be38;
+}
+
+.normal_8 {
+ fill: #8dc13d;
+}
+
+.normal_7 {
+ fill: #90c342;
+}
+
+.normal_6 {
+ fill: #97c84c;
+}
+
+.normal_5 {
+ fill: #9dcc55;
+}
+
+.normal_4 {
+ fill: #a4d15f;
+}
+
+.normal_3 {
+ fill: #aad569;
+}
+
+.normal_2 {
+ fill: #b1da73;
+}
+
+.normal_1 {
+ fill: #b7de7c;
+}
+
+.normal {
+ fill: #c0e28d;
+}
+
+.critical_10 {
+ fill: #e63c52;
+}
+
+.critical_9 {
+ fill: #e8475c;
+}
+
+.critical_8 {
+ fill: #e95266;
+}
+
+.critical_7 {
+ fill: #ea586b;
+}
+
+.critical_6 {
+ fill: #eb5d70;
+}
+
+.critical_5 {
+ fill: #ec6879;
+}
+
+.critical_4 {
+ fill: #ee7383;
+}
+
+.critical_3 {
+ fill: #ef7e8c;
+}
+
+.critical_2 {
+ fill: #f08996;
+}
+
+.critical_1 {
+ fill: #f1939f;
+}
+
+.critical {
+ fill: #f3a5af;
+}
+
+.warning_10 {
+ fill: #f3b200;
+}
+
+.warning_9 {
+ fill: #f5b70e;
+}
+
+.warning_8 {
+ fill: #f6ba15;
+}
+
+.warning_7 {
+ fill: #f6bc1c;
+}
+
+.warning_6 {
+ fill: #f8c12a;
+}
+
+.warning_5 {
+ fill: #f9c638;
+}
+
+.warning_4 {
+ fill: #fbcb46;
+}
+
+.warning_3 {
+ fill: #fcd054;
+}
+
+.warning_2 {
+ fill: #fed562;
+}
+
+.warning_1 {
+ fill: #ffd970;
+}
+
+.warning {
+ fill: #ffde85;
+}
+
+.unknown_10,
+.unknown_9,
+.unknown_8,
+.unknown_7 {
+ fill: #b2b2b2;
+}
+
+.unknown_6,
+.unknown_5,
+.unknown_4,
+.unknown_3 {
+ fill: #c2c2c2;
+}
+
+.unknown_2,
+.unknown_1,
+.unknown {
+ fill: #cccccc;
+}
+
+.notinit {
+ fill: #4a83f3;
+}
+
+.hover {
+ cursor: pointer;
+}
+
+.hover:hover {
+ filter: brightness(1.5);
+ stroke-width: 0.009;
+ stroke: black;
+}
+
+.group {
+ fill: none;
+ stroke-width: 0.03;
+ stroke: black;
+}
+
+body.pure {
+ height: 100%;
+}
+
+div#main_pure {
+ height: 100%;
+}
+
+div#heatmap-controls {
+ position: fixed;
+ top: 30px;
+ right: 20px;
+ width: 350px;
+ background-color: #ececec;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 5px;
+ border-radius: 3px;
+}
+
+div#heatmap-controls div#menu_tab {
+ margin: 0px;
+}
+
+div#heatmap-controls ul.white-box-content {
+ background-color: #ececec;
+ border: 0px;
+}
+
+div#heatmap-controls div.heatmap-title,
+div#heatmap-controls div.heatmap-refr {
+ margin-top: 15px;
+ margin-left: 3px;
+ margin-right: 3px;
+}
+div#heatmap-controls div.heatmap-refr > div {
+ display: inline;
+}
+
+.refr-form {
+ margin-bottom: 0 !important;
+ padding: 0 !important;
+}
+
+.label-dialog {
+ width: 30%;
+ font-weight: bold;
+}
+
+.div-dialog {
+ display: flex;
+ flex-direction: row;
+ justify-content: left;
+ align-items: center;
+}
+
+.title-dialog {
+ width: 40%;
+ font-weight: bold;
+ padding-left: 20px;
+}
+
+.info-dialog {
+ width: 60%;
+ font-weight: bold;
+}
+
+.polyline {
+ fill: none;
+ stroke: black;
+ stroke-width: 0.05;
+}
+
+.small-stroke {
+ stroke-width: 0.03;
+}
+
+.big-stroke {
+ stroke-width: 0.05;
+}
+
+.small-size {
+ font-size: 0.2px;
+}
+
+.big-size {
+ font-size: 0.4px;
+}
diff --git a/pandora_console/operation/heatmap.php b/pandora_console/operation/heatmap.php
new file mode 100644
index 0000000000..3ed2b8e7ce
--- /dev/null
+++ b/pandora_console/operation/heatmap.php
@@ -0,0 +1,346 @@
+'.html_print_image(
+ 'images/setup.png',
+ true,
+ [
+ 'title' => __('Config'),
+ 'class' => 'invert_filter',
+ ]
+ ).'';
+
+ $url = sprintf(
+ 'index.php?sec=view&sec2=operation/heatmap&pure=1&type=%s&refresh=%s&search=%s&filter=%s',
+ $type,
+ $refresh,
+ $search,
+ implode(',', $filter)
+ );
+
+ $viewtab['full_screen'] = '
'.html_print_image(
+ 'images/full_screen.png',
+ true,
+ [
+ 'title' => __('Full screen'),
+ 'class' => 'invert_filter',
+ ]
+ ).'';
+
+ $header_name = __('Heatmap view');
+ switch ($type) {
+ case 2:
+ if (current($filter) == 0) {
+ $header_name .= ' - '.__('Module group').': '.__('Not assigned');
+ } else {
+ $header_name .= ' - '.__('Module group').': '.modules_get_modulegroup_name(current($filter));
+ }
+ break;
+
+ case 1:
+ $tags_name = '';
+ foreach ($filter as $key => $tag) {
+ $tags_name .= tags_get_name($tag).', ';
+ }
+
+ $tags_name = trim($tags_name, ', ');
+ $header_name .= ' - '.__('Tag').': '.$tags_name;
+ break;
+
+ case 0:
+ default:
+ if (current($filter) == 0) {
+ $header_name .= ' - '.__('Group').': '.__('All');
+ } else {
+ $header_name .= ' - '.__('Group').': '.groups_get_name(current($filter));
+ }
+ break;
+ }
+
+ // Header.
+ ui_print_standard_header(
+ $header_name,
+ '',
+ false,
+ '',
+ false,
+ $viewtab,
+ [
+ [
+ 'link' => '',
+ 'label' => __('Monitoring'),
+ ],
+ [
+ 'link' => '',
+ 'label' => __('Views'),
+ ],
+ ]
+ );
+}
+
+if ($is_ajax === false && $pure === true) {
+ // Floating menu - Start.
+ echo '
';
+
+ echo '';
+
+ echo '
';
+}
+
+// Control call flow.
+try {
+ // Heatmap construct.
+ $heatmap = new Heatmap($type, $filter, $randomId, $refresh, $width, $height, $search, $group);
+} catch (Exception $e) {
+ if (is_ajax() === true) {
+ echo json_encode(['error' => '[Heatmap]'.$e->getMessage() ]);
+ exit;
+ } else {
+ echo '[Heatmap]'.$e->getMessage();
+ }
+
+ // Stop this execution, but continue 'globally'.
+ return;
+}
+
+// AJAX controller.
+if ($is_ajax === true) {
+ $method = get_parameter('method');
+
+ if (method_exists($heatmap, $method) === true) {
+ if ($heatmap->ajaxMethod($method) === true) {
+ $heatmap->{$method}();
+ } else {
+ echo 'Unavailable method';
+ }
+ } else {
+ echo 'Method not found';
+ }
+
+ // Stop any execution.
+ exit;
+} else {
+ // Run.
+ $heatmap->run();
+
+ // Dialog.
+ echo '
';
+}
+
+?>
+
+
diff --git a/pandora_console/operation/menu.php b/pandora_console/operation/menu.php
index b417e20b89..9789942b4a 100644
--- a/pandora_console/operation/menu.php
+++ b/pandora_console/operation/menu.php
@@ -66,6 +66,9 @@ if (check_acl($config['id_user'], 0, 'AR')) {
$sub2['operation/agentes/alerts_status']['text'] = __('Alert detail');
$sub2['operation/agentes/alerts_status']['refr'] = 0;
+ $sub2['operation/heatmap']['text'] = __('Heatmap view');
+ $sub2['operation/heatmap']['refr'] = 0;
+
$sub['view']['sub2'] = $sub2;
enterprise_hook('inventory_menu');