diff --git a/extras/pandora_gotty/deb/pandora_gotty/DEBIAN/control b/extras/pandora_gotty/deb/pandora_gotty/DEBIAN/control index f715ee1b49..fda06b3e36 100644 --- a/extras/pandora_gotty/deb/pandora_gotty/DEBIAN/control +++ b/extras/pandora_gotty/deb/pandora_gotty/DEBIAN/control @@ -1,6 +1,6 @@ Source: pandora_gotty Section: utils -Version: 1.0.0 +Version: 1.1.0 Priority: optional Maintainer: PandoraFMS Build-Depends: debhelper (>= 12) diff --git a/extras/pandora_gotty/pandora_gotty.spec b/extras/pandora_gotty/pandora_gotty.spec index 813c9726c9..d322fae653 100644 --- a/extras/pandora_gotty/pandora_gotty.spec +++ b/extras/pandora_gotty/pandora_gotty.spec @@ -1,5 +1,5 @@ %define name pandora_gotty -%define version 1.0 +%define version 1.1 %define release 1%{?dist} Summary: pandora_gptty for Pandora FMS Name: %{name} diff --git a/extras/pandora_gotty/src/pandora_gotty_exec.py b/extras/pandora_gotty/src/pandora_gotty_exec.py index 1f7756745f..2444d88363 100644 --- a/extras/pandora_gotty/src/pandora_gotty_exec.py +++ b/extras/pandora_gotty/src/pandora_gotty_exec.py @@ -74,7 +74,7 @@ def exec_ssh (user:str, add:str, port:int): try: print("> Starting SSH connection...") ssh_command = f"ssh {user}@{add} -p {port}" - subprocess.run(ssh_command, shell=True) + subprocess.run(ssh_command, shell=True, encoding='utf-8', text=True) except subprocess.CalledProcessError as e: raise SystemExit(e) @@ -88,7 +88,7 @@ def exec_telnet (add:str, port:int): try: print("> Starting Telnet connection...") ssh_command = f"telnet -E {add} {port}" - subprocess.run(ssh_command, shell=True) + subprocess.run(ssh_command, shell=True, encoding='utf-8', text=True) except subprocess.CalledProcessError as e: raise SystemExit(e) diff --git a/extras/pandora_update_version.sh b/extras/pandora_update_version.sh index 29dee8f0ce..06de8cc2b1 100755 --- a/extras/pandora_update_version.sh +++ b/extras/pandora_update_version.sh @@ -22,13 +22,16 @@ else fi SPEC_FILES="$CODEHOME/pandora_console/pandora_console.spec \ $CODEHOME/pandora_agents/unix/pandora_agent.spec \ +$CODEHOME/pandora_agents/unix/pandora_agent.redhat_bin.el8.spec \ +$CODEHOME/pandora_agents/unix/pandora_agent.redhat_bin.el9.spec \ +$CODEHOME/pandora_agents/unix/pandora_agent.redhat_bin.spec \ +$CODEHOME/pandora_agents/unix/pandora_agent.redhat.spec \ $CODEHOME/pandora_server/pandora_server.spec \ $PANDHOME_ENT/pandora_console/enterprise/pandora_console_enterprise.spec \ $PANDHOME_ENT/pandora_server/PandoraFMS-Enterprise/pandora_server_enterprise.spec \ $CODEHOME/pandora_console/pandora_console.redhat.spec \ $CODEHOME/pandora_console/pandora_console.rhel7.spec \ $CODEHOME/pandora_agents/unix/pandora_agent.redhat.spec \ -$CODEHOME/pandora_agents/unix/pandora_agent.redhat_bin.spec \ $CODEHOME/pandora_server/pandora_server.redhat.spec \ $PANDHOME_ENT/pandora_agents/pandora_agent.spec \ $PANDHOME_ENT/pandora_server/pandora_server_enterprise.redhat.spec \ diff --git a/pandora_agents/pc/AIX/pandora_agent.conf b/pandora_agents/pc/AIX/pandora_agent.conf index 5c5ec2d3b6..37fb9b0854 100644 --- a/pandora_agents/pc/AIX/pandora_agent.conf +++ b/pandora_agents/pc/AIX/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3, AIX version +# Version 7.0NG.774, AIX version # Licensed under GPL license v2, # Copyright (c) 2003-2023 Pandora FMS # http://www.pandorafms.com diff --git a/pandora_agents/pc/FreeBSD/pandora_agent.conf b/pandora_agents/pc/FreeBSD/pandora_agent.conf index ed33c06e4d..5a36cea140 100644 --- a/pandora_agents/pc/FreeBSD/pandora_agent.conf +++ b/pandora_agents/pc/FreeBSD/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3, FreeBSD Version +# Version 7.0NG.774, FreeBSD Version # Licensed under GPL license v2, # Copyright (c) 2003-2023 Pandora FMS # http://www.pandorafms.com diff --git a/pandora_agents/pc/HP-UX/pandora_agent.conf b/pandora_agents/pc/HP-UX/pandora_agent.conf index 784c729f73..52cd8e1774 100644 --- a/pandora_agents/pc/HP-UX/pandora_agent.conf +++ b/pandora_agents/pc/HP-UX/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3, HP-UX Version +# Version 7.0NG.774, HP-UX Version # Licensed under GPL license v2, # Copyright (c) 2003-2023 Pandora FMS # http://www.pandorafms.com diff --git a/pandora_agents/pc/Linux/pandora_agent.conf b/pandora_agents/pc/Linux/pandora_agent.conf index 6284c748f9..4ad837f943 100644 --- a/pandora_agents/pc/Linux/pandora_agent.conf +++ b/pandora_agents/pc/Linux/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3, GNU/Linux +# Version 7.0NG.774, GNU/Linux # Licensed under GPL license v2, # Copyright (c) 2003-2023 Pandora FMS # http://www.pandorafms.com diff --git a/pandora_agents/pc/NT4/pandora_agent.conf b/pandora_agents/pc/NT4/pandora_agent.conf index 28ee6bd21f..efa9d79f34 100644 --- a/pandora_agents/pc/NT4/pandora_agent.conf +++ b/pandora_agents/pc/NT4/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3, GNU/Linux +# Version 7.0NG.774, GNU/Linux # Licensed under GPL license v2, # Copyright (c) 2003-2023 Pandora FMS # http://www.pandorafms.com diff --git a/pandora_agents/pc/SunOS/pandora_agent.conf b/pandora_agents/pc/SunOS/pandora_agent.conf index 3d17139780..3a97e4a011 100644 --- a/pandora_agents/pc/SunOS/pandora_agent.conf +++ b/pandora_agents/pc/SunOS/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3, Solaris Version +# Version 7.0NG.774, Solaris Version # Licensed under GPL license v2, # Copyright (c) 2003-2023 Pandora FMS # http://www.pandorafms.com diff --git a/pandora_agents/pc/Win32/pandora_agent.conf b/pandora_agents/pc/Win32/pandora_agent.conf index edf5a7847a..621c1d24ac 100644 --- a/pandora_agents/pc/Win32/pandora_agent.conf +++ b/pandora_agents/pc/Win32/pandora_agent.conf @@ -1,6 +1,6 @@ # Base config file for Pandora FMS Windows Agent # (c) 2006-2023 Pandora FMS -# Version 7.0NG.773.3 +# Version 7.0NG.774 # This program is Free Software, you can redistribute it and/or modify it # under the terms of the GNU General Public Licence as published by the Free Software # Foundation; either version 2 of the Licence or any later version diff --git a/pandora_agents/pc/pandora_agent b/pandora_agents/pc/pandora_agent index aa8208f5d0..67e603b225 100644 --- a/pandora_agents/pc/pandora_agent +++ b/pandora_agents/pc/pandora_agent @@ -54,7 +54,6 @@ if (!$@) { use constant AGENT_VERSION => '4.0.1'; use constant AGENT_BUILD => '111213'; - # Commands to retrieve total memory information in kB use constant TOTALMEMORY_CMDS => { linux => 'cat /proc/meminfo | grep MemTotal: | awk \'{ print $2 }\'', @@ -117,7 +116,6 @@ my $ConfDir = ''; # Pandora FMS agent configuration file my $ConfFile = 'pandora_agent.conf'; - # Broker agent configuration files my @BrokerPid; @@ -264,7 +262,6 @@ sub valid_regexp ($) { sub rmrf { my $path = shift; local *DIR; - if (-d $path) { opendir (DIR, $path) || return; while (defined (my $file_name = readdir(DIR))) { @@ -348,7 +345,6 @@ sub log_message ($$;$) { } } } - ################################################################################ # Add the given directory to the PATH. ################################################################################ @@ -582,7 +578,6 @@ sub write_broker_conf($){ } while (my $line = ){ - # Skip broker definitions if ($line =~ m/^\s*broker_agent/) { next; @@ -1810,7 +1805,6 @@ sub exec_plugin ($) { $Sem->down () if (defined ($Sem)); $Xml .= $output; $Sem->up () if (defined ($Sem)); - $ThreadSem->up () if (defined ($ThreadSem) && $Conf{'agent_threads'} > 1); } @@ -2287,4 +2281,4 @@ This is released under the GNU Lesser General Public License. Copyright (c) 2005-2023 Pandora FMS -=cut +=cut \ No newline at end of file diff --git a/pandora_agents/shellscript/aix/pandora_agent.conf b/pandora_agents/shellscript/aix/pandora_agent.conf index d2c1806dc0..74841e0515 100644 --- a/pandora_agents/shellscript/aix/pandora_agent.conf +++ b/pandora_agents/shellscript/aix/pandora_agent.conf @@ -1,6 +1,6 @@ # Fichero de configuracion base de agentes de Pandora # Base config file for Pandora agents -# Version 7.0NG.773.3, AIX version +# Version 7.0NG.774, AIX version # General Parameters # ================== diff --git a/pandora_agents/shellscript/bsd-ipso/pandora_agent.conf b/pandora_agents/shellscript/bsd-ipso/pandora_agent.conf index 69b39f6a25..7ba120ca70 100644 --- a/pandora_agents/shellscript/bsd-ipso/pandora_agent.conf +++ b/pandora_agents/shellscript/bsd-ipso/pandora_agent.conf @@ -1,6 +1,6 @@ # Fichero de configuracion base de agentes de Pandora # Base config file for Pandora agents -# Version 7.0NG.773.3 +# Version 7.0NG.774 # FreeBSD/IPSO version # Licenced under GPL licence, 2003-2007 Sancho Lerena diff --git a/pandora_agents/shellscript/hp-ux/pandora_agent.conf b/pandora_agents/shellscript/hp-ux/pandora_agent.conf index b27bfe9167..bcbd2dbb35 100644 --- a/pandora_agents/shellscript/hp-ux/pandora_agent.conf +++ b/pandora_agents/shellscript/hp-ux/pandora_agent.conf @@ -1,6 +1,6 @@ # Fichero de configuracion base de agentes de Pandora # Base config file for Pandora agents -# Version 7.0NG.773.3, HPUX Version +# Version 7.0NG.774, HPUX Version # General Parameters # ================== diff --git a/pandora_agents/shellscript/linux/pandora_agent.conf b/pandora_agents/shellscript/linux/pandora_agent.conf index 23dff46fb5..98c137741e 100644 --- a/pandora_agents/shellscript/linux/pandora_agent.conf +++ b/pandora_agents/shellscript/linux/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3 +# Version 7.0NG.774 # Licensed under GPL license v2, # (c) 2003-2023 Pandora FMS # please visit http://pandora.sourceforge.net diff --git a/pandora_agents/shellscript/mac_osx/pandora_agent.conf b/pandora_agents/shellscript/mac_osx/pandora_agent.conf index 3250232611..b4b3c35ac8 100644 --- a/pandora_agents/shellscript/mac_osx/pandora_agent.conf +++ b/pandora_agents/shellscript/mac_osx/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3 +# Version 7.0NG.774 # Licensed under GPL license v2, # (c) 2003-2023 Pandora FMS # please visit http://pandora.sourceforge.net diff --git a/pandora_agents/shellscript/openWRT/pandora_agent.conf b/pandora_agents/shellscript/openWRT/pandora_agent.conf index 29fd1d64da..9ea0d9a2aa 100644 --- a/pandora_agents/shellscript/openWRT/pandora_agent.conf +++ b/pandora_agents/shellscript/openWRT/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3 +# Version 7.0NG.774 # Licensed under GPL license v2, # please visit http://pandora.sourceforge.net diff --git a/pandora_agents/shellscript/solaris/pandora_agent.conf b/pandora_agents/shellscript/solaris/pandora_agent.conf index a4b43f1944..954c52853b 100644 --- a/pandora_agents/shellscript/solaris/pandora_agent.conf +++ b/pandora_agents/shellscript/solaris/pandora_agent.conf @@ -1,6 +1,6 @@ # Fichero de configuracion base de agentes de Pandora # Base config file for Pandora agents -# Version 7.0NG.773.3, Solaris version +# Version 7.0NG.774, Solaris version # General Parameters # ================== diff --git a/pandora_agents/unix/AIX/pandora_agent.conf b/pandora_agents/unix/AIX/pandora_agent.conf index 29fb0051d5..c3bea470ac 100644 --- a/pandora_agents/unix/AIX/pandora_agent.conf +++ b/pandora_agents/unix/AIX/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.773.3, AIX version +# Version 7.0NG.774, AIX version # Licensed under GPL license v2, # Copyright (c) 2003-2023 Pandora FMS # http://www.pandorafms.com diff --git a/pandora_agents/unix/DEBIAN/control b/pandora_agents/unix/DEBIAN/control index b0744645a0..e63567d459 100644 --- a/pandora_agents/unix/DEBIAN/control +++ b/pandora_agents/unix/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-agent-unix -Version: 7.0NG.773.3-231018 +Version: 7.0NG.774-231107 Architecture: all Priority: optional Section: admin diff --git a/pandora_agents/unix/DEBIAN/make_deb_package.sh b/pandora_agents/unix/DEBIAN/make_deb_package.sh index b30a664f0e..b2f9b8652a 100644 --- a/pandora_agents/unix/DEBIAN/make_deb_package.sh +++ b/pandora_agents/unix/DEBIAN/make_deb_package.sh @@ -14,7 +14,7 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -pandora_version="7.0NG.773.3-231018" +pandora_version="7.0NG.774-231107" echo "Test if you has the tools for to make the packages." whereis dpkg-deb | cut -d":" -f2 | grep dpkg-deb > /dev/null diff --git a/pandora_agents/unix/Darwin/dmg/build_darwin_dmg.sh b/pandora_agents/unix/Darwin/dmg/build_darwin_dmg.sh index 91907dbd82..b1d0fea6db 100644 --- a/pandora_agents/unix/Darwin/dmg/build_darwin_dmg.sh +++ b/pandora_agents/unix/Darwin/dmg/build_darwin_dmg.sh @@ -31,7 +31,7 @@ fi if [ "$#" -ge 2 ]; then VERSION="$2" else - VERSION="7.0NG.773.3" + VERSION="7.0NG.774" fi # Path for the generated DMG file diff --git a/pandora_agents/unix/Darwin/dmg/extras/distribution.xml b/pandora_agents/unix/Darwin/dmg/extras/distribution.xml index 94266e09e4..708d31a47a 100644 --- a/pandora_agents/unix/Darwin/dmg/extras/distribution.xml +++ b/pandora_agents/unix/Darwin/dmg/extras/distribution.xml @@ -19,11 +19,11 @@ - pandorafms_src.pdk + pandorafms_src.pdk - pandorafms_uninstall.pdk + pandorafms_uninstall.pdk + Dark / 20 / alert@svg + Created with Sketch. + + + + \ No newline at end of file diff --git a/pandora_console/images/log_server.svg b/pandora_console/images/log_server.svg new file mode 100644 index 0000000000..26f27c6ae9 --- /dev/null +++ b/pandora_console/images/log_server.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/pandora_console/images/status_check@svg.svg b/pandora_console/images/status_check@svg.svg new file mode 100644 index 0000000000..78e62ea848 --- /dev/null +++ b/pandora_console/images/status_check@svg.svg @@ -0,0 +1,26 @@ + + + AE320C3A-79E4-4E24-956A-B81125ACFA52@svg + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pandora_console/images/status_error@svg.svg b/pandora_console/images/status_error@svg.svg new file mode 100644 index 0000000000..e6502bff7e --- /dev/null +++ b/pandora_console/images/status_error@svg.svg @@ -0,0 +1,26 @@ + + + CD9D3D2F-E199-427F-BC6C-532C8382EE45@svg + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pandora_console/images/system_event.svg b/pandora_console/images/system_event.svg new file mode 100644 index 0000000000..41a9b2c0ec --- /dev/null +++ b/pandora_console/images/system_event.svg @@ -0,0 +1,15 @@ + + + F7C0551D-EEAD-4AA0-87B8-DE2D255390BB + + + + + + + + + + + + \ No newline at end of file diff --git a/pandora_console/include/ajax/events.php b/pandora_console/include/ajax/events.php index 593fa9a975..7e8341c4a1 100644 --- a/pandora_console/include/ajax/events.php +++ b/pandora_console/include/ajax/events.php @@ -2065,14 +2065,6 @@ if ($table_events) { // (propagate ACL funct!). $groups = users_get_groups($config['id_user']); - $tags_condition = tags_get_acl_tags( - $config['id_user'], - array_keys($groups), - 'ER', - 'event_condition', - 'AND' - ); - $tableEvents24h = new stdClass(); $tableEvents24h->class = 'filter_table'; $tableEvents24h->styleTable = 'border: 0;padding: 0;margin: 0 0 10px;'; @@ -2107,7 +2099,7 @@ if ($table_events) { ); } else { events_print_event_table( - 'estado <> 1 '.$tags_condition, + 'estado <> 1', 200, '100%', false, diff --git a/pandora_console/include/ajax/general_tactical_view.ajax.php b/pandora_console/include/ajax/general_tactical_view.ajax.php new file mode 100644 index 0000000000..99c9b09a57 --- /dev/null +++ b/pandora_console/include/ajax/general_tactical_view.ajax.php @@ -0,0 +1,78 @@ + 'noaccess']); + } else { + include 'general/noaccess.php'; + } + + exit; +} + + +// AJAX controller. +if (is_ajax()) { + $dir = $config['homedir'].'/include/lib/TacticalView/elements/'; + $method = get_parameter('method'); + $class = get_parameter('class'); + + $filepath = realpath($dir.'/'.$class.'.php'); + if (is_readable($filepath) === false + || is_dir($filepath) === true + || preg_match('/.*\.php$/', $filepath) === false + ) { + exit; + } + + include_once $filepath; + + if (class_exists($class) === true) { + $instance = new $class(); + if ($instance->ajaxMethod($method) === true) { + echo $instance->{$method}(); + } else { + $instance->error('Unavailable method.'); + } + } else { + $class->error('Class not found. ['.$class.']'); + } + + exit; +} diff --git a/pandora_console/include/ajax/module.php b/pandora_console/include/ajax/module.php index 78daacb0a0..3d77c7ba30 100755 --- a/pandora_console/include/ajax/module.php +++ b/pandora_console/include/ajax/module.php @@ -483,6 +483,13 @@ if (check_login()) { 'tagente_modulo', ['id_agente_modulo' => $module_id] ); + + $made_enabled = db_get_value_filter( + 'made_enabled', + 'tagente_modulo', + ['id_agente_modulo' => $module_id] + ); + $unit = db_get_value_filter( 'unit', 'tagente_modulo', @@ -1627,7 +1634,6 @@ if (check_login()) { // Uncompress. try { - ob_start(); $dateNow = get_system_time(); $final = ($dateNow - $period); $date = ($dateNow - ($time_all_box * $start)); @@ -1751,31 +1757,11 @@ if (check_login()) { 'recordsFiltered' => $total_box, ] ); - - $response = ob_get_clean(); - - // Clean output buffer. - while (ob_get_level() !== 0) { - ob_end_clean(); - } } catch (Exception $e) { echo json_encode( ['error' => $e->getMessage()] ); } - - // If not valid it will throw an exception. - json_decode($response); - if (json_last_error() === JSON_ERROR_NONE) { - // If valid dump. - echo $response; - } else { - echo json_encode( - ['error' => $response] - ); - } - - return; } if ($get_cluster_module_detail === true) { diff --git a/pandora_console/include/ajax/visual_console.ajax.php b/pandora_console/include/ajax/visual_console.ajax.php index 402a5ddc1a..5e0351f9b0 100644 --- a/pandora_console/include/ajax/visual_console.ajax.php +++ b/pandora_console/include/ajax/visual_console.ajax.php @@ -96,9 +96,6 @@ if ($force_remote_check) { if ($load_css_cv === true) { $uniq = get_parameter('uniq', 0); $ratio = get_parameter('ratio', 0); - - $output = css_label_styles_visual_console($uniq, $ratio); - echo $output; return; } diff --git a/pandora_console/include/class/ConsoleSupervisor.php b/pandora_console/include/class/ConsoleSupervisor.php index b4320b1a4d..5f5eb4b339 100644 --- a/pandora_console/include/class/ConsoleSupervisor.php +++ b/pandora_console/include/class/ConsoleSupervisor.php @@ -256,6 +256,7 @@ class ConsoleSupervisor /* * Check if performance variables are corrects */ + $this->checkPerformanceVariables(); /* @@ -289,6 +290,12 @@ class ConsoleSupervisor */ $this->checkMYSQLSettings(); + + /* + * Check log alerts version + */ + + $this->checkLogAlerts(); } @@ -2099,8 +2106,8 @@ class ConsoleSupervisor $this->notify( [ 'type' => 'NOTIF.EXT.ELASTICSEARCH', - 'title' => __('Log collector cannot connect to ElasticSearch'), - 'message' => __('ElasticSearch is not available using current configuration.'), + 'title' => __('Log collector cannot connect to OpenSearch'), + 'message' => __('OpenSearch is not available using current configuration.'), 'url' => '__url__/index.php?sec=general&sec2=godmode/setup/setup§ion=log', ] ); @@ -3106,4 +3113,32 @@ class ConsoleSupervisor } + /** + * Checks log alerts version. + * + * @return void + */ + public function checkLogAlerts() + { + global $config; + + if ((bool) check_acl($config['id_user'], 0, 'LM') === true) { + $current_package = (int) $config['current_package']; + if ($current_package >= 774 && $current_package <= 777) { + $url = '__url__index.php?sec=galertas&sec2=enterprise/godmode/alerts/event_alerts'; + $this->notify( + [ + 'type' => 'NOTIF.LOG.ALERT', + 'title' => __('Alert correlation changed since version 774'), + 'message' => __('Log correlation and log correlation with events will be disabled in this update. Some event correlation alerts may need to be modified to adapt to the new format'), + 'url' => $url, + ] + ); + } else { + $this->cleanNotifications('NOTIF.LOG.ALERT'); + } + } + } + + } diff --git a/pandora_console/include/class/SnmpConsole.class.php b/pandora_console/include/class/SnmpConsole.class.php index 86132d3956..6945d9b1fb 100644 --- a/pandora_console/include/class/SnmpConsole.class.php +++ b/pandora_console/include/class/SnmpConsole.class.php @@ -738,7 +738,7 @@ class SnmpConsole extends HTML $sql_count = sprintf($sql_count, $whereSubquery); $traps = db_get_all_rows_sql($sql, true); - $total = (int) db_get_value_sql($sql_count, false, true); + $total = (int) db_get_value_sql($sql_count, false, false); if (empty($traps) === false) { $data = $traps; diff --git a/pandora_console/include/config_process.php b/pandora_console/include/config_process.php index ee16a3d997..8c68ac61e7 100644 --- a/pandora_console/include/config_process.php +++ b/pandora_console/include/config_process.php @@ -20,8 +20,8 @@ /** * Pandora build version and version */ -$build_version = 'PC231018'; -$pandora_version = 'v7.0NG.773.3'; +$build_version = 'PC231107'; +$pandora_version = 'v7.0NG.774'; // Do not overwrite default timezone set if defined. $script_tz = @date_default_timezone_get(); diff --git a/pandora_console/include/constants.php b/pandora_console/include/constants.php index 889e32f693..4f020e61de 100644 --- a/pandora_console/include/constants.php +++ b/pandora_console/include/constants.php @@ -441,6 +441,8 @@ define('SERVER_TYPE_ALERT', 21); define('SERVER_TYPE_CORRELATION', 22); define('SERVER_TYPE_NCM', 23); define('SERVER_TYPE_NETFLOW', 24); +define('SERVER_TYPE_LOG', 25); +define('SERVER_TYPE_MADE', 26); // REPORTS. define('REPORT_TOP_N_MAX', 1); @@ -887,3 +889,8 @@ define('HOME_SCREEN_ALERT_DETAIL', 'alert_detail'); define('HOME_SCREEN_EXTERNAL_LINK', 'external_link'); define('HOME_SCREEN_OTHER', 'other'); define('HOME_SCREEN_DASHBOARD', 'dashboard'); + + +// Alert correlation. +define('EVENT_ALERTS', 1); +define('LOG_ALERTS', 2); diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php index f2579fe53a..1fe38aac0b 100644 --- a/pandora_console/include/functions_config.php +++ b/pandora_console/include/functions_config.php @@ -1676,6 +1676,18 @@ function config_update_config() if (config_update_value('Days_purge_old_information', (int) get_parameter('Days_purge_old_information'), true) === false) { $error_update[] = __('Days to purge old information'); } + + if (config_update_value('elasticsearch_user', get_parameter('elasticsearch_user'), true) === false) { + $error_update[] = __('User ElasticSearch server'); + } + + if (config_update_value('elasticsearch_pass', get_parameter('elasticsearch_pass'), true) === false) { + $error_update[] = __('Pass ElasticSearch server'); + } + + if (config_update_value('elasticsearch_https', get_parameter('elasticsearch_https'), true) === false) { + $error_update[] = __('Https ElasticSearch server'); + } break; case 'hist_db': @@ -2466,7 +2478,19 @@ function config_process_config() } if (!isset($config['Days_purge_old_information'])) { - config_update_value('Days_purge_old_information', 90); + config_update_value('Days_purge_old_information', 30); + } + + if (!isset($config['elasticsearch_user'])) { + config_update_value('elasticsearch_user', ''); + } + + if (!isset($config['elasticsearch_pass'])) { + config_update_value('elasticsearch_pass', ''); + } + + if (!isset($config['elasticsearch_https'])) { + config_update_value('elasticsearch_https', ''); } if (!isset($config['font_size'])) { diff --git a/pandora_console/include/functions_events.php b/pandora_console/include/functions_events.php index f709fe2b4c..5534897e84 100644 --- a/pandora_console/include/functions_events.php +++ b/pandora_console/include/functions_events.php @@ -2593,7 +2593,7 @@ function events_print_type_img( switch ($type) { case 'alert_recovered': - $style .= ' alert_module_background_state icon_background_normal '; + $icon = 'images/alert_recovered@svg.svg'; break; case 'alert_manual_validation': @@ -2609,20 +2609,16 @@ function events_print_type_img( case 'going_up_normal': case 'going_down_normal': // This is to be backwards compatible. - // $style .= ' event_module_background_state icon_background_normal'; $icon = 'images/module_ok.png'; break; case 'going_up_warning': $icon = 'images/module_warning.png'; - // $style .= ' event_module_background_state icon_background_warning'; case 'going_down_warning': $icon = 'images/module_warning.png'; - // $style .= ' event_module_background_state icon_background_warning'; break; case 'going_unknown': - // $style .= ' event_module_background_state icon_background_unknown'; $icon = 'images/module_unknown.png'; break; diff --git a/pandora_console/include/functions_graph.php b/pandora_console/include/functions_graph.php index e6605a488b..3ba76b17d0 100644 --- a/pandora_console/include/functions_graph.php +++ b/pandora_console/include/functions_graph.php @@ -2763,6 +2763,7 @@ function graph_agent_status( 'height' => $height, 'colors' => array_values($colors), 'legend' => ['display' => false], + 'labels' => array_keys($data), ]; if ($donut_narrow_graph == true) { @@ -4624,12 +4625,17 @@ function graph_nodata_image($options) return base64_encode($dataImg); } + $widthImage = '200px'; + if (isset($options['nodata_image']['width']) === true) { + $widthImage = $options['nodata_image']['width']; + } + return html_print_image( 'images/image_problem_area.png', true, [ 'title' => __('No data'), - 'style' => 'width: 200px;', + 'style' => 'width: '.$widthImage.';', ] ); } diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index 2552fbcdc1..888531e915 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -5409,7 +5409,7 @@ function html_print_link_with_params($text, $params=[], $type='text', $style='', $formStyle = ' style="'.$formStyle.'"'; } - $html = '
'; + $html = ''; switch ($type) { case 'image': $html .= html_print_input_image($text, $text, $text, $style, true); @@ -7304,7 +7304,9 @@ function html_print_select_date_range( $date_end='', $time_end='', $date_text=SECONDS_1DAY, - $class='w100p' + $class='w100p', + $date_format='Y/m/d', + $time_format='H:i:s' ) { global $config; @@ -7326,21 +7328,21 @@ function html_print_select_date_range( } if ($date_end === '') { - $date_end = date('Y/m/d'); + $date_end = date($date_format); } if ($date_init === '') { - $date_init = date('Y/m/d', strtotime($date_end.' -1 days')); + $date_init = date($date_format, strtotime($date_end.' -1 days')); } - $date_init = date('Y/m/d', strtotime($date_init)); + $date_init = date($date_format, strtotime($date_init)); if ($time_init === '') { - $time_init = date('H:i:s'); + $time_init = date($time_format); } if ($time_end === '') { - $time_end = date('H:i:s'); + $time_end = date($time_format); } $fields[SECONDS_1DAY] = __('Last 24hr'); diff --git a/pandora_console/include/functions_modules.php b/pandora_console/include/functions_modules.php index e82fca400b..de618605c8 100755 --- a/pandora_console/include/functions_modules.php +++ b/pandora_console/include/functions_modules.php @@ -4762,3 +4762,31 @@ function export_agents_module_csv($filters) return $result; } + + +/** + * Check if modules are compatible with MADE server. + * + * @param integer $id_tipo_modulo + * @retur boolean True if compatible, false otherwise. + */ +function modules_made_compatible($id_tipo_modulo) +{ + $compatible_types = [ + 1, + 4, + 5, + 8, + 15, + 16, + 22, + 30, + 34, + ]; + + if (array_search($id_tipo_modulo, $compatible_types) === false) { + return false; + } else { + return true; + } +} diff --git a/pandora_console/include/functions_netflow.php b/pandora_console/include/functions_netflow.php index 79fcd9a0db..bc8495969f 100644 --- a/pandora_console/include/functions_netflow.php +++ b/pandora_console/include/functions_netflow.php @@ -498,7 +498,7 @@ function netflow_get_top_N( $options = '-o "fmt:%sap,%dap,%ibyt,%bps" -q -n '.$max.' -s record/bytes -t '.date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); - $command = netflow_get_command($options, $filter); + $command = netflow_get_command($options, $filter, $start_date, $end_date); // Execute nfdump. exec($command, $lines); @@ -656,7 +656,10 @@ function netflow_get_data( $aggregate, $max, $absolute, - $connection_name + $connection_name, + false, + $start_date, + $end_date ); foreach ($data as $line) { @@ -734,6 +737,8 @@ function netflow_get_data( * to get troughput. * @param string $connection_name Node name when data is get in meta. * @param boolean $address_resolution True to resolve ips to hostnames. + * @param integer $start_date_fixed Start date for use in command netflow. + * @param integer $end_date_fixed End date for use in command netflow. * * @return array With netflow stats. */ @@ -745,7 +750,9 @@ function netflow_get_stats( $max, $absolute=true, $connection_name='', - $address_resolution=false + $address_resolution=false, + $start_date_fixed=0, + $end_date_fixed=0, ) { global $config, $nfdump_date_format; @@ -757,8 +764,7 @@ function netflow_get_stats( // Get the command to call nfdump. $options = "-o csv -q -n $max -s $aggregate/bytes -t ".date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); - $command = netflow_get_command($options, $filter); - + $command = netflow_get_command($options, $filter, $start_date_fixed, $end_date_fixed); // Execute nfdump. exec($command, $string); @@ -845,7 +851,7 @@ function netflow_get_summary($start_date, $end_date, $filter, $connection_name=' // Get the command to call nfdump. $options = '-o csv -n 1 -s srcip/bytes -t '.date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); - $command = netflow_get_command($options, $filter); + $command = netflow_get_command($options, $filter, $start_date, $end_date); // Execute nfdump. exec($command, $string); @@ -916,7 +922,7 @@ function netflow_get_relationships_raw_data( // Get the command to call nfdump. $options = ' -q -o csv -n 10000 -s record/bytes -t '.date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); - $command = netflow_get_command($options, $filter); + $command = netflow_get_command($options, $filter, $start_date, $end_date); // Execute nfdump. // $command .= ' -q -o csv -n 10000 -s record/bytes -t '.date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); @@ -1018,7 +1024,7 @@ function netflow_parse_relationships_for_circular_mesh( * * @return string Command to run. */ -function netflow_get_command($options, $filter) +function netflow_get_command($options, $filter, $date_init=0, $date_end=0) { global $config; @@ -1030,14 +1036,46 @@ function netflow_get_command($options, $filter) && isset($config['netflow_name_dir']) && $config['netflow_name_dir'] !== '' && isset($config['general_network_path']) && $config['general_network_path'] !== '' ) { - $command .= ' -R. -M '.$config['general_network_path'].$config['netflow_name_dir'].':'.$config['sflow_name_dir']; + if ($date_init > 0 && $date_end > 0) { + $path = $config['general_network_path'].$config['sflow_name_dir']; + $range_time = find_range_files_time($path, $date_init, $date_end); + if ($range_time[0] === 0 && $range_time[1] === 0) { + $interval_files_sflow = $path; + } else { + $interval_files_sflow = $path.'/'.$range_time[0].':'.$range_time[1]; + } + + $path = $config['general_network_path'].$config['netflow_name_dir']; + $range_time = find_range_files_time($path, $date_init, $date_end); + if ($range_time[0] === 0 && $range_time[1] === 0) { + $interval_files_netflow = $path; + } else { + $interval_files_netflow = $path.'/'.$range_time[0].':'.$range_time[1]; + } + + $command .= ' -R '.$interval_files_sflow.' -R '.$interval_files_netflow; + } else { + $command .= ' -R. -M '.$config['general_network_path'].$config['netflow_name_dir'].':'.$config['sflow_name_dir']; + } } } else { if ($config['activate_sflow']) { if (isset($config['sflow_name_dir']) && $config['sflow_name_dir'] !== '' && isset($config['general_network_path']) && $config['general_network_path'] !== '' ) { - $command .= ' -R. -M '.$config['general_network_path'].$config['sflow_name_dir']; + if ($date_init > 0 && $date_end > 0) { + $path = $config['general_network_path'].$config['sflow_name_dir']; + $range_time = find_range_files_time($path, $date_init, $date_end); + if ($range_time[0] === 0 && $range_time[1] === 0) { + $interval_files = '.'; + } else { + $interval_files = $range_time[0].':'.$range_time[1]; + } + } else { + $interval_files = '.'; + } + + $command .= ' -R '.$interval_files.' -M '.$config['general_network_path'].$config['sflow_name_dir']; } } @@ -1045,7 +1083,19 @@ function netflow_get_command($options, $filter) if (isset($config['netflow_name_dir']) && $config['netflow_name_dir'] !== '' && isset($config['general_network_path']) && $config['general_network_path'] !== '' ) { - $command .= ' -R. -M '.$config['general_network_path'].$config['netflow_name_dir']; + if ($date_init > 0 && $date_end > 0) { + $path = $config['general_network_path'].$config['netflow_name_dir']; + $range_time = find_range_files_time($path, $date_init, $date_end); + if ($range_time[0] === 0 && $range_time[1] === 0) { + $interval_files = '.'; + } else { + $interval_files = $range_time[0].':'.$range_time[1]; + } + } else { + $interval_files = '.'; + } + + $command .= ' -R '.$interval_files.' -M '.$config['general_network_path'].$config['netflow_name_dir']; } } } @@ -1059,6 +1109,62 @@ function netflow_get_command($options, $filter) } +/** + * Find the two files closest to the time range. + * + * @param string $folder Folder of netflow. + * @param integer $date_init Time init for range. + * @param integer $date_end Time end for range. + * + * @return array + */ +function find_range_files_time($folder, $date_init, $date_end) +{ + $closest_init = 0; + $closest_end = 0; + $closest_init_date = 0; + $closest_end_date = 0; + $min_diff_init = PHP_INT_MAX; + $min_diff_end = PHP_INT_MAX; + $files = scandir($folder); + if ($date_init > 0) { + foreach ($files as $file) { + if (preg_match('/^nfcapd\.(\d{12})$/', $file, $matches)) { + $file_date = $matches[1]; + + $file_date = strtotime(substr($file_date, 0, 4).'-'.substr($file_date, 4, 2).'-'.substr($file_date, 6, 2).' '.substr($file_date, 8, 2).':'.substr($file_date, 10, 2)); + $diff_init = abs($file_date - $date_init); + if ($diff_init < $min_diff_init) { + $closest_init_date = $file_date; + $min_diff_init = $diff_init; + $closest_init = $file; + } + + $diff_end = abs($file_date - $date_end); + if ($diff_end < $min_diff_end) { + $closest_end_date = $file_date; + $min_diff_end = $diff_end; + $closest_end = $file; + } + } + } + } + + if ($closest_end_date < $date_init || $closest_init_date > $date_end) { + return [ + 0, + 0, + ]; + } else { + return [ + $closest_init, + $closest_end, + ]; + } + +} + + /** * Returns the nfdump command line arguments that match the given filter. * @@ -1336,7 +1442,9 @@ function netflow_draw_item( $max_aggregates, true, $connection_name, - $address_resolution + $address_resolution, + $start_date, + $end_date ); if (empty($data_pie) === true) { @@ -1451,7 +1559,9 @@ function netflow_draw_item( $max_aggregates, true, $connection_name, - $address_resolution + $address_resolution, + $start_date, + $end_date ); if (empty($data_stats) === false) { @@ -1572,7 +1682,9 @@ function netflow_get_item_data( $aggregate, $max_aggregates, true, - $connection_name + $connection_name, + $start_date, + $end_date ); $data = [ @@ -1801,7 +1913,7 @@ function netflow_get_top_summary( } $options = "-q -o csv -n $max -s $sort/$order_text -t ".date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); - $command = netflow_get_command($options, $netflow_filter); + $command = netflow_get_command($options, $netflow_filter, $start_date, $end_date); exec($command, $result); if (! is_array($result)) { @@ -1962,7 +2074,7 @@ function netflow_get_top_data( date($nfdump_date_format, $start_date), date($nfdump_date_format, $end_date) ); - $agg_command = netflow_get_command($options, $filter); + $agg_command = netflow_get_command($options, $filter, $start_date, $end_date); // Call nfdump. exec($agg_command, $string); diff --git a/pandora_console/include/functions_servers.php b/pandora_console/include/functions_servers.php index 3a4273efa4..75e034ce3c 100644 --- a/pandora_console/include/functions_servers.php +++ b/pandora_console/include/functions_servers.php @@ -906,7 +906,7 @@ function servers_get_info($id_server=-1, $sql_limit=-1) 'images/logs@svg.svg', true, [ - 'title' => __('Log server'), + 'title' => __('Syslog server'), 'class' => 'main_menu_icon invert_filter', ] ); @@ -979,6 +979,32 @@ function servers_get_info($id_server=-1, $sql_limit=-1) $id_modulo = 0; break; + case SERVER_TYPE_LOG: + $server['img'] = html_print_image( + 'images/log_server.svg', + true, + [ + 'title' => __('Log server'), + 'class' => 'main_menu_icon invert_filter', + ] + ); + $server['type'] = 'log'; + $id_modulo = 0; + break; + + case SERVER_TYPE_MADE: + $server['img'] = html_print_image( + 'images/Anomaly-detection@svg.svg', + true, + [ + 'title' => __('MADE server'), + 'class' => 'main_menu_icon invert_filter', + ] + ); + $server['type'] = 'made'; + $id_modulo = 0; + break; + default: $server['img'] = ''; $server['type'] = 'unknown'; diff --git a/pandora_console/include/functions_ui.php b/pandora_console/include/functions_ui.php index 28b82739f3..3b193b8975 100755 --- a/pandora_console/include/functions_ui.php +++ b/pandora_console/include/functions_ui.php @@ -7326,7 +7326,8 @@ function ui_query_result_editor($name='default') ] ); - html_print_submit_button(__('Execute query'), 'execute_query', false, ['icon' => 'update']); + $execute_button = html_print_submit_button(__('Execute query'), 'execute_query', false, ['icon' => 'update'], true); + html_print_action_buttons($execute_button); } diff --git a/pandora_console/include/functions_visual_map.php b/pandora_console/include/functions_visual_map.php index 1b6a4e7bfa..f3d5752fe4 100755 --- a/pandora_console/include/functions_visual_map.php +++ b/pandora_console/include/functions_visual_map.php @@ -4548,54 +4548,4 @@ function visual_map_load_client_resources() closedir($dh); } } -} - - -/** - * Labels styles visual console. - * - * @param string $uniq Uniq str. - * @param integer $ratio Ratio. - * - * @return string Css output. - */ -function css_label_styles_visual_console($uniq, $ratio=1) -{ - global $config; - $output = ''; - // Horrible trick! due to the use of tinyMCE - // it is necessary to modify specific classes of each - // of the visual consoles. - $output .= '.c-'.$uniq.' a {color: #3f3f3f } '; - $output .= '.c-'.$uniq.' .label p strong span {display: inline-block !important; line-height: normal !important} '; - $output .= '.c-'.$uniq.' *:not(.parent_graph p table tr td span) { font-size: '.(8 * $ratio).'pt; line-height:'.(8 * ($ratio)).'pt; }'; - $output .= '.c-'.$uniq.' .visual-console-item-label table tr td { padding: 0; margin: 0; white-space: pre-wrap; }'; - $output .= '.c-'.$uniq.' .visual_font_size_4pt, .c-'.$uniq.' .visual_font_size_4pt * { font-size: '.(4 * $ratio).'pt !important; line-height:'.(4 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_6pt, .c-'.$uniq.' .visual_font_size_6pt * { font-size: '.(6 * $ratio).'pt !important; line-height:'.(6 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_8pt, .c-'.$uniq.' .visual_font_size_8pt * { font-size: '.(8 * $ratio).'pt !important; line-height:'.(8 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_10pt, .c-'.$uniq.' .visual_font_size_10pt * { font-size: '.(10 * $ratio).'pt !important; line-height:'.(10 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_12pt, .c-'.$uniq.' .visual_font_size_12pt * { font-size: '.(12 * $ratio).'pt !important; line-height:'.(12 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_14pt, .c-'.$uniq.' .visual_font_size_14pt * { font-size: '.(14 * $ratio).'pt !important; line-height:'.(14 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_18pt, .c-'.$uniq.' .visual_font_size_18pt * { font-size: '.(18 * $ratio).'pt !important; line-height:'.(18 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_24pt, .c-'.$uniq.' .visual_font_size_24pt * { font-size: '.(24 * $ratio).'pt !important; line-height:'.(24 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_28pt, .c-'.$uniq.' .visual_font_size_28pt * { font-size: '.(28 * $ratio).'pt !important; line-height:'.(28 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_36pt, .c-'.$uniq.' .visual_font_size_36pt * { font-size: '.(36 * $ratio).'pt !important; line-height:'.(36 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_48pt, .c-'.$uniq.' .visual_font_size_48pt * { font-size: '.(48 * $ratio).'pt !important; line-height:'.(48 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_60pt, .c-'.$uniq.' .visual_font_size_60pt * { font-size: '.(60 * $ratio).'pt !important; line-height:'.(60 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_72pt, .c-'.$uniq.' .visual_font_size_72pt * { font-size: '.(72 * $ratio).'pt !important; line-height:'.(72 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_84pt, .c-'.$uniq.' .visual_font_size_84pt * { font-size: '.(84 * $ratio).'pt !important; line-height:'.(84 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_96pt, .c-'.$uniq.' .visual_font_size_96pt * { font-size: '.(96 * $ratio).'pt !important; line-height:'.(96 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_116pt, .c-'.$uniq.' .visual_font_size_116pt * { font-size: '.(116 * $ratio).'pt !important; line-height:'.(116 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_128pt, .c-'.$uniq.' .visual_font_size_128pt * { font-size: '.(128 * $ratio).'pt !important; line-height:'.(128 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_140pt, .c-'.$uniq.' .visual_font_size_140pt * { font-size: '.(140 * $ratio).'pt !important; line-height:'.(140 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_154pt, .c-'.$uniq.' .visual_font_size_154pt * { font-size: '.(154 * $ratio).'pt !important; line-height:'.(154 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual_font_size_196pt, .c-'.$uniq.' .visual_font_size_196pt * { font-size: '.(196 * $ratio).'pt !important; line-height:'.(196 * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .flot-text, .c-'.$uniq.' .flot-text * { font-size: '.(($config['font_size'] - 2) * $ratio).'pt !important; line-height:'.(($config['font_size'] - 2) * ($ratio)).'pt !important; }'; - $output .= '.c-'.$uniq.' .visual-console-item .digital-clock span.time {font-size: '.(50 * $ratio).'px !important; line-height: '.(50 * $ratio).'px !important;}'; - $output .= '.c-'.$uniq.' .visual-console-item .digital-clock span.date {font-size: '.(25 * $ratio).'px !important; line-height: '.(25 * $ratio).'px !important;}'; - $output .= '.c-'.$uniq.' .visual-console-item .digital-clock span.timezone {font-size: '.(25 * $ratio).'px !important; line-height: '.(25 * $ratio).'px !important;}'; - $output .= '.c-'.$uniq.' .visual-console-item .donut-graph * {font-size: '.(8 * $ratio).'px !important; line-height: '.(8 * $ratio).'px !important;}'; - $output .= '.c-'.$uniq.' .visual-console-item .donut-graph g rect {width:'.(25 * $ratio).' !important; height: '.(15 * $ratio).' !important;}'; - - return $output; -} +} \ No newline at end of file diff --git a/pandora_console/include/graphs/fgraph.php b/pandora_console/include/graphs/fgraph.php index 494ec9c010..5e5818357b 100644 --- a/pandora_console/include/graphs/fgraph.php +++ b/pandora_console/include/graphs/fgraph.php @@ -725,6 +725,12 @@ function get_build_setup_charts($type, $options, $data) $chart->options()->getElements()->center()->setColor($options['elements']['center']['color']); } } + + if (isset($options['elements']['point']) === true) { + if (isset($options['elements']['point']['radius']) === true) { + $chart->options()->getElements()->point()->setRadius($options['elements']['point']['radius']); + } + } } // Set Responsive for responsive charts. @@ -1019,7 +1025,9 @@ function get_build_setup_charts($type, $options, $data) ) { $scales = $chart->options()->getScales(); - if ($options['scales']['x'] !== false) { + if (isset($options['scales']['x']) === true + && $options['scales']['x'] !== false + ) { // Defaults scalesFont X. $scalesXFonts = $scales->getX()->ticks()->getFonts(); $scalesXFonts->setFamily((empty($config['fontpath']) === true) ? 'lato' : $config['fontpath']); @@ -1028,7 +1036,9 @@ function get_build_setup_charts($type, $options, $data) $scalesXFonts->setSize(((int) $config['font_size'] + 2)); } - if ($options['scales']['y'] !== false) { + if (isset($options['scales']['y']) === true + && $options['scales']['y'] !== false + ) { // Defaults scalesFont Y. $scalesYFonts = $scales->getY()->ticks()->getFonts(); $scalesYFonts->setFamily((empty($config['fontpath']) === true) ? 'lato' : $config['fontpath']); @@ -1037,7 +1047,9 @@ function get_build_setup_charts($type, $options, $data) $scalesYFonts->setSize(((int) $config['font_size'] + 2)); } - if ($options['scales']['r'] !== false) { + if (isset($options['scales']['r']) === true + && $options['scales']['r'] !== false + ) { // Defaults scalesFont R. $scalesRFonts = $scales->getR()->pointLabels()->getFonts(); $scalesRFonts->setStyle('normal'); @@ -1053,6 +1065,10 @@ function get_build_setup_charts($type, $options, $data) $scales->getX()->setBounds($options['scales']['x']['bounds']); } + if (isset($options['scales']['x']['display']) === true) { + $scales->getX()->setDisplay($options['scales']['x']['display']); + } + if (isset($options['scales']['x']['grid']) === true && empty($options['scales']['x']['grid']) === false && is_array($options['scales']['x']['grid']) === true @@ -1098,6 +1114,10 @@ function get_build_setup_charts($type, $options, $data) $scales->getY()->setBounds($options['scales']['y']['bounds']); } + if (isset($options['scales']['y']['display']) === true) { + $scales->getY()->setDisplay($options['scales']['y']['display']); + } + if (isset($options['scales']['y']['grid']) === true && empty($options['scales']['y']['grid']) === false && is_array($options['scales']['y']['grid']) === true @@ -1202,6 +1222,13 @@ function get_build_setup_charts($type, $options, $data) $borders = array_values($defaultBorder); } + if (isset($options['borderColors']) === true + && empty($options['borderColors']) === false + && is_array($options['borderColors']) === true + ) { + $borders = $options['borderColors']; + } + // Set labels. if (isset($options['labels']) === true && empty($options['labels']) === false diff --git a/pandora_console/include/graphs/flot/jquery.flot.min.js b/pandora_console/include/graphs/flot/jquery.flot.min.js index abf73f210b..59d81c4c9e 100644 --- a/pandora_console/include/graphs/flot/jquery.flot.min.js +++ b/pandora_console/include/graphs/flot/jquery.flot.min.js @@ -13,6 +13,6 @@ Licensed under the MIT license. if (yrange.from == null) yrange.from = yrange.axis.min; if (yrange.to == null) yrange.to = yrange.axis.max; if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) continue; xrange.from = Math.max(xrange.from, xrange.axis.min); xrange.to = Math.min(xrange.to, xrange.axis.max); yrange.from = Math.max(yrange.from, yrange.axis.min); yrange.to = Math.min(yrange.to, yrange.axis.max); var xequal = xrange.from === xrange.to, yequal = yrange.from === yrange.to; if (xequal && yequal) { continue } xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); if (xequal || yequal) { var lineWidth = m.lineWidth || options.grid.markingsLineWidth, subPixel = lineWidth % 2 ? .5 : 0; ctx.beginPath(); ctx.strokeStyle = m.color || options.grid.markingsColor; ctx.lineWidth = lineWidth; if (xequal) { ctx.moveTo(xrange.to + subPixel, yrange.from); ctx.lineTo(xrange.to + subPixel, yrange.to) } else { ctx.moveTo(xrange.from, yrange.to + subPixel); ctx.lineTo(xrange.to, yrange.to + subPixel) } ctx.stroke() } else { ctx.fillStyle = m.color || options.grid.markingsColor; ctx.fillRect(xrange.from, yrange.to, xrange.to - xrange.from, yrange.from - yrange.to) } } } axes = allAxes(); bw = options.grid.borderWidth; for (var j = 0; j < axes.length; ++j) { var axis = axes[j], box = axis.box, t = axis.tickLength, x, y, xoff, yoff; if (!axis.show || axis.ticks.length == 0) continue; ctx.lineWidth = 1; if (axis.direction == "x") { x = 0; if (t == "full") y = axis.position == "top" ? 0 : plotHeight; else y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0) } else { y = 0; if (t == "full") x = axis.position == "left" ? 0 : plotWidth; else x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0) } if (!axis.innermost) { ctx.strokeStyle = axis.options.color; ctx.beginPath(); xoff = yoff = 0; if (axis.direction == "x") xoff = plotWidth + 1; else yoff = plotHeight + 1; if (ctx.lineWidth == 1) { if (axis.direction == "x") { y = Math.floor(y) + .5 } else { x = Math.floor(x) + .5 } } ctx.moveTo(x, y); ctx.lineTo(x + xoff, y + yoff); ctx.stroke() } ctx.strokeStyle = axis.options.tickColor; ctx.beginPath(); for (i = 0; i < axis.ticks.length; ++i) { var v = axis.ticks[i].v; xoff = yoff = 0; if (isNaN(v) || v < axis.min || v > axis.max || t == "full" && (typeof bw == "object" && bw[axis.position] > 0 || bw > 0) && (v == axis.min || v == axis.max)) continue; if (axis.direction == "x") { x = axis.p2c(v); yoff = t == "full" ? -plotHeight : t; if (axis.position == "top") yoff = -yoff } else { y = axis.p2c(v); xoff = t == "full" ? -plotWidth : t; if (axis.position == "left") xoff = -xoff } if (ctx.lineWidth == 1) { if (axis.direction == "x") x = Math.floor(x) + .5; else y = Math.floor(y) + .5 } ctx.moveTo(x, y); ctx.lineTo(x + xoff, y + yoff) } ctx.stroke() } if (bw) { bc = options.grid.borderColor; if (typeof bw == "object" || typeof bc == "object") { if (typeof bw !== "object") { bw = { top: bw, right: bw, bottom: bw, left: bw } } if (typeof bc !== "object") { bc = { top: bc, right: bc, bottom: bc, left: bc } } if (bw.top > 0) { ctx.strokeStyle = bc.top; ctx.lineWidth = bw.top; ctx.beginPath(); ctx.moveTo(0 - bw.left, 0 - bw.top / 2); ctx.lineTo(plotWidth, 0 - bw.top / 2); ctx.stroke() } if (bw.right > 0) { ctx.strokeStyle = bc.right; ctx.lineWidth = bw.right; ctx.beginPath(); ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); ctx.lineTo(plotWidth + bw.right / 2, plotHeight); ctx.stroke() } if (bw.bottom > 0) { ctx.strokeStyle = bc.bottom; ctx.lineWidth = bw.bottom; ctx.beginPath(); ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); ctx.lineTo(0, plotHeight + bw.bottom / 2); ctx.stroke() } if (bw.left > 0) { ctx.strokeStyle = bc.left; ctx.lineWidth = bw.left; ctx.beginPath(); ctx.moveTo(0 - bw.left / 2, plotHeight + bw.bottom); ctx.lineTo(0 - bw.left / 2, 0); ctx.stroke() } } else { ctx.lineWidth = bw; ctx.strokeStyle = options.grid.borderColor; ctx.strokeRect(-bw / 2, -bw / 2, plotWidth + bw, plotHeight + bw) } } ctx.restore() - } function drawAxisLabels() { $.each(allAxes(), function (_, axis) { var box = axis.box, legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, font = axis.options.font || "flot-tick-label tickLabel", tick, x, y, halign, valign; surface.removeText(layer); if (!axis.show || axis.ticks.length == 0) return; for (var i = 0; i < axis.ticks.length; ++i) { tick = axis.ticks[i]; if (!tick.label || tick.v < axis.min || tick.v > axis.max) continue; if (axis.direction == "x") { halign = "center"; x = plotOffset.left + axis.p2c(tick.v); if (axis.position == "bottom") { y = box.top + box.padding } else { y = box.top + box.height - box.padding; valign = "bottom" } } else { valign = "middle"; y = plotOffset.top + axis.p2c(tick.v); if (axis.position == "left") { x = box.left + box.width - box.padding; halign = "right" } else { x = box.left + box.padding } } surface.addText(layer, x, y, tick.label, font, null, null, halign, valign) } }) } function drawSeries(series) { if (series.lines.show) drawSeriesLines(series); if (series.bars.show) drawSeriesBars(series); if (series.points.show) drawSeriesPoints(series) } function drawSeriesLines(series) { function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, prevx = null, prevy = null; ctx.beginPath(); for (var i = ps; i < points.length; i += ps) { var x1 = points[i - ps], y1 = points[i - ps + 1], x2 = points[i], y2 = points[i + 1]; if (x1 == null || x2 == null) continue; if (y1 <= y2 && y1 < axisy.min) { if (y2 < axisy.min) continue; x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min } else if (y2 <= y1 && y2 < axisy.min) { if (y1 < axisy.min) continue; x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min } if (y1 >= y2 && y1 > axisy.max) { if (y2 > axisy.max) continue; x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max } else if (y2 >= y1 && y2 > axisy.max) { if (y1 > axisy.max) continue; x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max } if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min } if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max } if (x1 != prevx || y1 != prevy) ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); prevx = x2; prevy = y2; ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset) } ctx.stroke() } function plotLineArea(datapoints, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, bottom = Math.min(Math.max(0, axisy.min), axisy.max), i = 0, top, areaOpen = false, ypos = 1, segmentStart = 0, segmentEnd = 0; while (true) { if (ps > 0 && i > points.length + ps) break; i += ps; var x1 = points[i - ps], y1 = points[i - ps + ypos], x2 = points[i], y2 = points[i + ypos]; if (areaOpen) { if (ps > 0 && x1 != null && x2 == null) { segmentEnd = i; ps = -ps; ypos = 2; continue } if (ps < 0 && i == segmentStart + ps) { ctx.fill(); areaOpen = false; ps = -ps; ypos = 1; i = segmentStart = segmentEnd + ps; continue } } if (x1 == null || x2 == null) continue; if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min } if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max } if (!areaOpen) { ctx.beginPath(); ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); areaOpen = true } if (y1 >= axisy.max && y2 >= axisy.max) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); continue } else if (y1 <= axisy.min && y2 <= axisy.min) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); continue } var x1old = x1, x2old = x2; if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min } else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min } if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max } else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max } if (x1 != x1old) { ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)) } ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); if (x2 != x2old) { ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)) } } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.lineJoin = "round"; var lw = series.lines.lineWidth, sw = series.shadowSize; if (lw > 0 && sw > 0) { ctx.lineWidth = sw; ctx.strokeStyle = "rgba(0,0,0,0.1)"; var angle = Math.PI / 18; plotLine(series.datapoints, Math.sin(angle) * (lw / 2 + sw / 2), Math.cos(angle) * (lw / 2 + sw / 2), series.xaxis, series.yaxis); ctx.lineWidth = sw / 2; plotLine(series.datapoints, Math.sin(angle) * (lw / 2 + sw / 4), Math.cos(angle) * (lw / 2 + sw / 4), series.xaxis, series.yaxis) } ctx.lineWidth = lw; ctx.strokeStyle = series.color; var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); if (fillStyle) { ctx.fillStyle = fillStyle; plotLineArea(series.datapoints, series.xaxis, series.yaxis) } if (lw > 0) plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); ctx.restore() } function drawSeriesPoints(series) { function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { var x = points[i], y = points[i + 1]; if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) continue; ctx.beginPath(); x = axisx.p2c(x); y = axisy.p2c(y) + offset; if (symbol == "circle") ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); else symbol(ctx, x, y, radius, shadow); ctx.closePath(); if (fillStyle) { ctx.fillStyle = fillStyle; ctx.fill() } ctx.stroke() } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); var lw = series.points.lineWidth, sw = series.shadowSize, radius = series.points.radius, symbol = series.points.symbol; if (lw == 0) lw = 1e-4; if (lw > 0 && sw > 0) { var w = sw / 2; ctx.lineWidth = w; ctx.strokeStyle = "rgba(0,0,0,0.1)"; plotPoints(series.datapoints, radius, null, w + w / 2, true, series.xaxis, series.yaxis, symbol); ctx.strokeStyle = "rgba(0,0,0,0.2)"; plotPoints(series.datapoints, radius, null, w / 2, true, series.xaxis, series.yaxis, symbol) } ctx.lineWidth = lw; ctx.strokeStyle = series.color; plotPoints(series.datapoints, radius, getFillStyle(series.points, series.color), 0, false, series.xaxis, series.yaxis, symbol); ctx.restore() } function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { var left, right, bottom, top, drawLeft, drawRight, drawTop, drawBottom, tmp; if (horizontal) { drawBottom = drawRight = drawTop = true; drawLeft = false; left = b; right = x; top = y + barLeft; bottom = y + barRight; if (right < left) { tmp = right; right = left; left = tmp; drawLeft = true; drawRight = false } } else { drawLeft = drawRight = drawTop = true; drawBottom = false; left = x + barLeft; right = x + barRight; bottom = b; top = y; if (top < bottom) { tmp = top; top = bottom; bottom = tmp; drawBottom = true; drawTop = false } } if (right < axisx.min || left > axisx.max || top < axisy.min || bottom > axisy.max) return; if (left < axisx.min) { left = axisx.min; drawLeft = false } if (right > axisx.max) { right = axisx.max; drawRight = false } if (bottom < axisy.min) { bottom = axisy.min; drawBottom = false } if (top > axisy.max) { top = axisy.max; drawTop = false } left = axisx.p2c(left); bottom = axisy.p2c(bottom); right = axisx.p2c(right); top = axisy.p2c(top); if (fillStyleCallback) { c.fillStyle = fillStyleCallback(bottom, top); c.fillRect(left, top, right - left, bottom - top) } if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { c.beginPath(); c.moveTo(left, bottom); if (drawLeft) c.lineTo(left, top); else c.moveTo(left, top); if (drawTop) c.lineTo(right, top); else c.moveTo(right, top); if (drawRight) c.lineTo(right, bottom); else c.moveTo(right, bottom); if (drawBottom) c.lineTo(left, bottom); else c.moveTo(left, bottom); c.stroke() } } function drawSeriesBars(series) { function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { if (points[i] == null) continue; drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth) } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.lineWidth = series.bars.lineWidth; ctx.strokeStyle = series.color; var barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2 }var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top) } : null; plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); ctx.restore() } function getFillStyle(filloptions, seriesColor, bottom, top) { var fill = filloptions.fill; if (!fill) return null; if (filloptions.fillColor) return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); var c = $.color.parse(seriesColor); c.a = typeof fill == "number" ? fill : .4; c.normalize(); return c.toString() } function insertLegend() { if (options.legend.container != null) { $(options.legend.container).html("") } else { placeholder.find(".legend").remove() } if (!options.legend.show) { return } var fragments = [], entries = [], rowStarted = false, lf = options.legend.labelFormatter, s, label; for (var i = 0; i < series.length; ++i) { s = series[i]; if (s.label) { label = lf ? lf(s.label, s) : s.label; if (label) { entries.push({ label: label, color: s.color }) } } } if (options.legend.sorted) { if ($.isFunction(options.legend.sorted)) { entries.sort(options.legend.sorted) } else if (options.legend.sorted == "reverse") { entries.reverse() } else { var ascending = options.legend.sorted != "descending"; entries.sort(function (a, b) { return a.label == b.label ? 0 : a.label < b.label != ascending ? 1 : -1 }) } } for (var i = 0; i < entries.length; ++i) { var entry = entries[i]; if (i % options.legend.noColumns == 0) { if (rowStarted) fragments.push(""); fragments.push(""); rowStarted = true } fragments.push('
' + '' + entry.label + "") } if (rowStarted) fragments.push(""); if (fragments.length == 0) return; var table = '' + fragments.join("") + "
"; if (options.legend.container != null) $(options.legend.container).html(table); else { var pos = "", p = options.legend.position, m = options.legend.margin; if (m[0] == null) m = [m, m]; if (p.charAt(0) == "n") pos += "top:" + (m[1] + plotOffset.top) + "px;"; else if (p.charAt(0) == "s") pos += "bottom:" + (m[1] + plotOffset.bottom) + "px;"; if (p.charAt(1) == "e") pos += "right:" + (m[0] + plotOffset.right) + "px;"; else if (p.charAt(1) == "w") pos += "left:" + (m[0] + plotOffset.left) + "px;"; var legend = $('
' + table.replace('style="', 'style="position:absolute;' + pos + ";") + "
").appendTo(placeholder); if (options.legend.backgroundOpacity != 0) { var c = options.legend.backgroundColor; if (c == null) { c = options.grid.backgroundColor; if (c && typeof c == "string") c = $.color.parse(c); else c = $.color.extract(legend, "background-color"); c.a = 1; c = c.toString() } var div = legend.children(); $('
').prependTo(legend).css("opacity", options.legend.backgroundOpacity) } } } var highlights = [], redrawTimeout = null; function findNearbyItem(mouseX, mouseY, seriesFilter) { var maxDistance = options.grid.mouseActiveRadius, smallestDistance = maxDistance * maxDistance + 1, item = null, foundPoint = false, i, j, ps; for (i = series.length - 1; i >= 0; --i) { if (!seriesFilter(series[i])) continue; var s = series[i], axisx = s.xaxis, axisy = s.yaxis, points = s.datapoints.points, mx = axisx.c2p(mouseX), my = axisy.c2p(mouseY), maxx = maxDistance / axisx.scale, maxy = maxDistance / axisy.scale; ps = s.datapoints.pointsize; if (axisx.options.inverseTransform) maxx = Number.MAX_VALUE; if (axisy.options.inverseTransform) maxy = Number.MAX_VALUE; if (s.lines.show || s.points.show) { for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1]; if (x == null) continue; if (x - mx > maxx || x - mx < -maxx || y - my > maxy || y - my < -maxy) continue; var dx = Math.abs(axisx.p2c(x) - mouseX), dy = Math.abs(axisy.p2c(y) - mouseY), dist = dx * dx + dy * dy; if (dist < smallestDistance) { smallestDistance = dist; item = [i, j / ps] } } } if (s.bars.show && !item) { var barLeft, barRight; switch (s.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -s.bars.barWidth; break; default: barLeft = -s.bars.barWidth / 2 }barRight = barLeft + s.bars.barWidth; for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1], b = points[j + 2]; if (x == null) continue; if (series[i].bars.horizontal ? mx <= Math.max(b, x) && mx >= Math.min(b, x) && my >= y + barLeft && my <= y + barRight : mx >= x + barLeft && mx <= x + barRight && my >= Math.min(b, y) && my <= Math.max(b, y)) item = [i, j / ps] } } } if (item) { i = item[0]; j = item[1]; ps = series[i].datapoints.pointsize; return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), dataIndex: j, series: series[i], seriesIndex: i } } return null } function onMouseMove(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return s["hoverable"] != false }) } function onMouseLeave(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return false }) } function onClick(e) { triggerClickHoverEvent("plotclick", e, function (s) { return s["clickable"] != false }) } function triggerClickHoverEvent(eventname, event, seriesFilter) { var offset = eventHolder.offset(), canvasX = event.pageX - offset.left - plotOffset.left, canvasY = event.pageY - offset.top - plotOffset.top, pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); pos.pageX = event.pageX; pos.pageY = event.pageY; var item = findNearbyItem(canvasX, canvasY, seriesFilter); if (item) { item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10) } if (options.grid.autoHighlight) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.auto == eventname && !(item && h.series == item.series && h.point[0] == item.datapoint[0] && h.point[1] == item.datapoint[1])) unhighlight(h.series, h.point) } if (item) highlight(item.series, item.datapoint, eventname) } placeholder.trigger(eventname, [pos, item]) } function triggerRedrawOverlay() { var t = options.interaction.redrawOverlayInterval; if (t == -1) { drawOverlay(); return } if (!redrawTimeout) redrawTimeout = setTimeout(drawOverlay, t) } function drawOverlay() { redrawTimeout = null; octx.save(); overlay.clear(); octx.translate(plotOffset.left, plotOffset.top); var i, hi; for (i = 0; i < highlights.length; ++i) { hi = highlights[i]; if (hi.series.bars.show) drawBarHighlight(hi.series, hi.point); else drawPointHighlight(hi.series, hi.point) } octx.restore(); executeHooks(hooks.drawOverlay, [octx]) } function highlight(s, point, auto) { if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)) } var i = indexOfHighlight(s, point); if (i == -1) { highlights.push({ series: s, point: point, auto: auto }); triggerRedrawOverlay() } else if (!auto) highlights[i].auto = false } function unhighlight(s, point) { if (s == null && point == null) { highlights = []; triggerRedrawOverlay(); return } if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)) } var i = indexOfHighlight(s, point); if (i != -1) { highlights.splice(i, 1); triggerRedrawOverlay() } } function indexOfHighlight(s, p) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.series == s && h.point[0] == p[0] && h.point[1] == p[1]) return i } return -1 } function drawPointHighlight(series, point) { var x = point[0], y = point[1], axisx = series.xaxis, axisy = series.yaxis, highlightColor = typeof series.highlightColor === "string" ? series.highlightColor : $.color.parse(series.color).scale("a", .5).toString(); if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) return; var pointRadius = series.points.radius + series.points.lineWidth / 2; octx.lineWidth = pointRadius; octx.strokeStyle = highlightColor; var radius = 1.5 * pointRadius; x = axisx.p2c(x); y = axisy.p2c(y); octx.beginPath(); if (series.points.symbol == "circle") octx.arc(x, y, radius, 0, 2 * Math.PI, false); else series.points.symbol(octx, x, y, radius, false); octx.closePath(); octx.stroke() } function drawBarHighlight(series, point) { var highlightColor = typeof series.highlightColor === "string" ? series.highlightColor : $.color.parse(series.color).scale("a", .5).toString(), fillStyle = highlightColor, barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2 }octx.lineWidth = series.bars.lineWidth; octx.strokeStyle = highlightColor; drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, function () { return fillStyle }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth) } function getColorOrGradient(spec, bottom, top, defaultColor) { if (typeof spec == "string") return spec; else { var gradient = ctx.createLinearGradient(0, top, 0, bottom); for (var i = 0, l = spec.colors.length; i < l; ++i) { var c = spec.colors[i]; if (typeof c != "string") { var co = $.color.parse(defaultColor); if (c.brightness != null) co = co.scale("rgb", c.brightness); if (c.opacity != null) co.a *= c.opacity; c = co.toString() } gradient.addColorStop(i / (l - 1), c) } return gradient } } + } function drawAxisLabels() { $.each(allAxes(), function (_, axis) { var box = axis.box, legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, font = axis.options.font || "flot-tick-label tickLabel", tick, x, y, halign, valign; surface.removeText(layer); if (!axis.show || axis.ticks.length == 0) return; for (var i = 0; i < axis.ticks.length; ++i) { tick = axis.ticks[i]; if (!tick.label || tick.v < axis.min || tick.v > axis.max) continue; if (axis.direction == "x") { halign = "center"; x = plotOffset.left + axis.p2c(tick.v); if (axis.position == "bottom") { y = box.top + box.padding } else { y = box.top + box.height - box.padding; valign = "bottom" } } else { valign = "middle"; y = plotOffset.top + axis.p2c(tick.v); if (axis.position == "left") { x = box.left + box.width - box.padding; halign = "right" } else { x = box.left + box.padding } } surface.addText(layer, x, y, tick.label, font, null, null, halign, valign) } }) } function drawSeries(series) { if (series.lines.show) drawSeriesLines(series); if (series.bars.show) drawSeriesBars(series); if (series.points.show) drawSeriesPoints(series) } function drawSeriesLines(series) { function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, prevx = null, prevy = null; ctx.beginPath(); for (var i = ps; i < points.length; i += ps) { var x1 = points[i - ps], y1 = points[i - ps + 1], x2 = points[i], y2 = points[i + 1]; if (x1 == null || x2 == null) continue; if (y1 <= y2 && y1 < axisy.min) { if (y2 < axisy.min) continue; x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min } else if (y2 <= y1 && y2 < axisy.min) { if (y1 < axisy.min) continue; x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min } if (y1 >= y2 && y1 > axisy.max) { if (y2 > axisy.max) continue; x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max } else if (y2 >= y1 && y2 > axisy.max) { if (y1 > axisy.max) continue; x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max } if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min } if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max } if (x1 != prevx || y1 != prevy) ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); prevx = x2; prevy = y2; ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset) } ctx.stroke() } function plotLineArea(datapoints, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, bottom = Math.min(Math.max(0, axisy.min), axisy.max), i = 0, top, areaOpen = false, ypos = 1, segmentStart = 0, segmentEnd = 0; while (true) { if (ps > 0 && i > points.length + ps) break; i += ps; var x1 = points[i - ps], y1 = points[i - ps + ypos], x2 = points[i], y2 = points[i + ypos]; if (areaOpen) { if (ps > 0 && x1 != null && x2 == null) { segmentEnd = i; ps = -ps; ypos = 2; continue } if (ps < 0 && i == segmentStart + ps) { ctx.fill(); areaOpen = false; ps = -ps; ypos = 1; i = segmentStart = segmentEnd + ps; continue } } if (x1 == null || x2 == null) continue; if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min } if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max } if (!areaOpen) { ctx.beginPath(); ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); areaOpen = true } if (y1 >= axisy.max && y2 >= axisy.max) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); continue } else if (y1 <= axisy.min && y2 <= axisy.min) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); continue } var x1old = x1, x2old = x2; if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min } else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min } if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max } else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max } if (x1 != x1old) { ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)) } ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); if (x2 != x2old) { ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)) } } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.lineJoin = "round"; var lw = series.lines.lineWidth, sw = series.shadowSize; if (lw > 0 && sw > 0) { ctx.lineWidth = sw; ctx.strokeStyle = "rgba(0,0,0,0.1)"; var angle = Math.PI / 18; plotLine(series.datapoints, Math.sin(angle) * (lw / 2 + sw / 2), Math.cos(angle) * (lw / 2 + sw / 2), series.xaxis, series.yaxis); ctx.lineWidth = sw / 2; plotLine(series.datapoints, Math.sin(angle) * (lw / 2 + sw / 4), Math.cos(angle) * (lw / 2 + sw / 4), series.xaxis, series.yaxis) } ctx.lineWidth = lw; ctx.strokeStyle = series.color; var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); if (fillStyle) { ctx.fillStyle = fillStyle; plotLineArea(series.datapoints, series.xaxis, series.yaxis) } if (lw > 0) plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); ctx.restore() } function drawSeriesPoints(series) { function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { var x = points[i], y = points[i + 1]; if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) continue; ctx.beginPath(); x = axisx.p2c(x); y = axisy.p2c(y) + offset; if (symbol == "circle") ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); else symbol(ctx, x, y, radius, shadow); ctx.closePath(); if (fillStyle) { ctx.fillStyle = fillStyle; ctx.fill() } ctx.stroke() } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); var lw = series.points.lineWidth, sw = series.shadowSize, radius = series.points.radius, symbol = series.points.symbol; if (lw == 0) lw = 1e-4; if (lw > 0 && sw > 0) { var w = sw / 2; ctx.lineWidth = w; ctx.strokeStyle = "rgba(0,0,0,0.1)"; plotPoints(series.datapoints, radius, null, w + w / 2, true, series.xaxis, series.yaxis, symbol); ctx.strokeStyle = "rgba(0,0,0,0.2)"; plotPoints(series.datapoints, radius, null, w / 2, true, series.xaxis, series.yaxis, symbol) } ctx.lineWidth = lw; ctx.strokeStyle = series.color; plotPoints(series.datapoints, radius, getFillStyle(series.points, series.color), 0, false, series.xaxis, series.yaxis, symbol); ctx.restore() } function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { var left, right, bottom, top, drawLeft, drawRight, drawTop, drawBottom, tmp; if (horizontal) { drawBottom = drawRight = drawTop = true; drawLeft = false; left = b; right = x; top = y + barLeft; bottom = y + barRight; if (right < left) { tmp = right; right = left; left = tmp; drawLeft = true; drawRight = false } } else { drawLeft = drawRight = drawTop = true; drawBottom = false; left = x + barLeft; right = x + barRight; bottom = b; top = y; if (top < bottom) { tmp = top; top = bottom; bottom = tmp; drawBottom = true; drawTop = false } } if (right < axisx.min || left > axisx.max || top < axisy.min || bottom > axisy.max) return; if (left < axisx.min) { left = axisx.min; drawLeft = false } if (right > axisx.max) { right = axisx.max; drawRight = false } if (bottom < axisy.min) { bottom = axisy.min; drawBottom = false } if (top > axisy.max) { top = axisy.max; drawTop = false } left = axisx.p2c(left); bottom = axisy.p2c(bottom); right = axisx.p2c(right); top = axisy.p2c(top); if (fillStyleCallback) { c.fillStyle = fillStyleCallback(bottom, top); c.fillRect(left, top, right - left, bottom - top) } if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { c.beginPath(); c.moveTo(left, bottom); if (drawLeft) c.lineTo(left, top); else c.moveTo(left, top); if (drawTop) c.lineTo(right, top); else c.moveTo(right, top); if (drawRight) c.lineTo(right, bottom); else c.moveTo(right, bottom); if (drawBottom) c.lineTo(left, bottom); else c.moveTo(left, bottom); c.stroke() } } function drawSeriesBars(series) { function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { if (points[i] == null) continue; drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth) } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.lineWidth = series.bars.lineWidth; ctx.strokeStyle = series.color; var barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2 }var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top) } : null; plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); ctx.restore() } function getFillStyle(filloptions, seriesColor, bottom, top) { var fill = filloptions.fill; if (!fill) return null; if (filloptions.fillColor) return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); var c = $.color.parse(seriesColor); c.a = typeof fill == "number" ? fill : .4; c.normalize(); return c.toString() } function insertLegend() { if (options.legend.container != null) { $(options.legend.container).html("") } else { placeholder.find(".legend").remove() } if (!options.legend.show) { return } var fragments = [], entries = [], rowStarted = false, lf = options.legend.labelFormatter, s, label; for (var i = 0; i < series.length; ++i) { s = series[i]; if (s.label) { label = lf ? lf(s.label, s) : s.label; if (label) { entries.push({ label: label, color: s.color }) } } } if (options.legend.sorted) { if ($.isFunction(options.legend.sorted)) { entries.sort(options.legend.sorted) } else if (options.legend.sorted == "reverse") { entries.reverse() } else { var ascending = options.legend.sorted != "descending"; entries.sort(function (a, b) { return a.label == b.label ? 0 : a.label < b.label != ascending ? 1 : -1 }) } } for (var i = 0; i < entries.length; ++i) { var entry = entries[i]; if (i % options.legend.noColumns == 0) { if (rowStarted) fragments.push(""); fragments.push(""); rowStarted = true } fragments.push('
' + '' + entry.label.replace("font-size: 9pt;", "") + "") } if (rowStarted) fragments.push(""); if (fragments.length == 0) return; var table = '' + fragments.join("") + "
"; if (options.legend.container != null) $(options.legend.container).html(table); else { var pos = "", p = options.legend.position, m = options.legend.margin; if (m[0] == null) m = [m, m]; if (p.charAt(0) == "n") pos += "top:" + (m[1] + plotOffset.top) + "px;"; else if (p.charAt(0) == "s") pos += "bottom:" + (m[1] + plotOffset.bottom) + "px;"; if (p.charAt(1) == "e") pos += "right:" + (m[0] + plotOffset.right) + "px;"; else if (p.charAt(1) == "w") pos += "left:" + (m[0] + plotOffset.left) + "px;"; var legend = $('
' + table.replace('style="', 'style="position:absolute;' + pos + ";") + "
").appendTo(placeholder); if (options.legend.backgroundOpacity != 0) { var c = options.legend.backgroundColor; if (c == null) { c = options.grid.backgroundColor; if (c && typeof c == "string") c = $.color.parse(c); else c = $.color.extract(legend, "background-color"); c.a = 1; c = c.toString() } var div = legend.children(); $('
').prependTo(legend).css("opacity", options.legend.backgroundOpacity) } } } var highlights = [], redrawTimeout = null; function findNearbyItem(mouseX, mouseY, seriesFilter) { var maxDistance = options.grid.mouseActiveRadius, smallestDistance = maxDistance * maxDistance + 1, item = null, foundPoint = false, i, j, ps; for (i = series.length - 1; i >= 0; --i) { if (!seriesFilter(series[i])) continue; var s = series[i], axisx = s.xaxis, axisy = s.yaxis, points = s.datapoints.points, mx = axisx.c2p(mouseX), my = axisy.c2p(mouseY), maxx = maxDistance / axisx.scale, maxy = maxDistance / axisy.scale; ps = s.datapoints.pointsize; if (axisx.options.inverseTransform) maxx = Number.MAX_VALUE; if (axisy.options.inverseTransform) maxy = Number.MAX_VALUE; if (s.lines.show || s.points.show) { for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1]; if (x == null) continue; if (x - mx > maxx || x - mx < -maxx || y - my > maxy || y - my < -maxy) continue; var dx = Math.abs(axisx.p2c(x) - mouseX), dy = Math.abs(axisy.p2c(y) - mouseY), dist = dx * dx + dy * dy; if (dist < smallestDistance) { smallestDistance = dist; item = [i, j / ps] } } } if (s.bars.show && !item) { var barLeft, barRight; switch (s.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -s.bars.barWidth; break; default: barLeft = -s.bars.barWidth / 2 }barRight = barLeft + s.bars.barWidth; for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1], b = points[j + 2]; if (x == null) continue; if (series[i].bars.horizontal ? mx <= Math.max(b, x) && mx >= Math.min(b, x) && my >= y + barLeft && my <= y + barRight : mx >= x + barLeft && mx <= x + barRight && my >= Math.min(b, y) && my <= Math.max(b, y)) item = [i, j / ps] } } } if (item) { i = item[0]; j = item[1]; ps = series[i].datapoints.pointsize; return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), dataIndex: j, series: series[i], seriesIndex: i } } return null } function onMouseMove(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return s["hoverable"] != false }) } function onMouseLeave(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return false }) } function onClick(e) { triggerClickHoverEvent("plotclick", e, function (s) { return s["clickable"] != false }) } function triggerClickHoverEvent(eventname, event, seriesFilter) { var offset = eventHolder.offset(), canvasX = event.pageX - offset.left - plotOffset.left, canvasY = event.pageY - offset.top - plotOffset.top, pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); pos.pageX = event.pageX; pos.pageY = event.pageY; var item = findNearbyItem(canvasX, canvasY, seriesFilter); if (item) { item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10) } if (options.grid.autoHighlight) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.auto == eventname && !(item && h.series == item.series && h.point[0] == item.datapoint[0] && h.point[1] == item.datapoint[1])) unhighlight(h.series, h.point) } if (item) highlight(item.series, item.datapoint, eventname) } placeholder.trigger(eventname, [pos, item]) } function triggerRedrawOverlay() { var t = options.interaction.redrawOverlayInterval; if (t == -1) { drawOverlay(); return } if (!redrawTimeout) redrawTimeout = setTimeout(drawOverlay, t) } function drawOverlay() { redrawTimeout = null; octx.save(); overlay.clear(); octx.translate(plotOffset.left, plotOffset.top); var i, hi; for (i = 0; i < highlights.length; ++i) { hi = highlights[i]; if (hi.series.bars.show) drawBarHighlight(hi.series, hi.point); else drawPointHighlight(hi.series, hi.point) } octx.restore(); executeHooks(hooks.drawOverlay, [octx]) } function highlight(s, point, auto) { if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)) } var i = indexOfHighlight(s, point); if (i == -1) { highlights.push({ series: s, point: point, auto: auto }); triggerRedrawOverlay() } else if (!auto) highlights[i].auto = false } function unhighlight(s, point) { if (s == null && point == null) { highlights = []; triggerRedrawOverlay(); return } if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)) } var i = indexOfHighlight(s, point); if (i != -1) { highlights.splice(i, 1); triggerRedrawOverlay() } } function indexOfHighlight(s, p) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.series == s && h.point[0] == p[0] && h.point[1] == p[1]) return i } return -1 } function drawPointHighlight(series, point) { var x = point[0], y = point[1], axisx = series.xaxis, axisy = series.yaxis, highlightColor = typeof series.highlightColor === "string" ? series.highlightColor : $.color.parse(series.color).scale("a", .5).toString(); if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) return; var pointRadius = series.points.radius + series.points.lineWidth / 2; octx.lineWidth = pointRadius; octx.strokeStyle = highlightColor; var radius = 1.5 * pointRadius; x = axisx.p2c(x); y = axisy.p2c(y); octx.beginPath(); if (series.points.symbol == "circle") octx.arc(x, y, radius, 0, 2 * Math.PI, false); else series.points.symbol(octx, x, y, radius, false); octx.closePath(); octx.stroke() } function drawBarHighlight(series, point) { var highlightColor = typeof series.highlightColor === "string" ? series.highlightColor : $.color.parse(series.color).scale("a", .5).toString(), fillStyle = highlightColor, barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2 }octx.lineWidth = series.bars.lineWidth; octx.strokeStyle = highlightColor; drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, function () { return fillStyle }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth) } function getColorOrGradient(spec, bottom, top, defaultColor) { if (typeof spec == "string") return spec; else { var gradient = ctx.createLinearGradient(0, top, 0, bottom); for (var i = 0, l = spec.colors.length; i < l; ++i) { var c = spec.colors[i]; if (typeof c != "string") { var co = $.color.parse(defaultColor); if (c.brightness != null) co = co.scale("rgb", c.brightness); if (c.opacity != null) co.a *= c.opacity; c = co.toString() } gradient.addColorStop(i / (l - 1), c) } return gradient } } } $.plot = function (placeholder, data, options) { var plot = new Plot($(placeholder), data, options, $.plot.plugins); return plot }; $.plot.version = "0.8.3"; $.plot.plugins = []; $.fn.plot = function (data, options) { return this.each(function () { $.plot(this, data, options) }) }; function floorInBase(n, base) { return base * Math.floor(n / base) } })(jQuery); \ No newline at end of file diff --git a/pandora_console/include/javascript/alert.js b/pandora_console/include/javascript/alert.js index 435649833d..06f3223df7 100644 --- a/pandora_console/include/javascript/alert.js +++ b/pandora_console/include/javascript/alert.js @@ -1,5 +1,5 @@ /* eslint-disable no-unused-vars */ -/* global $, load_modal, generalShowMsg, confirmDialog */ +/* global $, load_modal, generalShowMsg, confirmDialog, jQuery */ function allowDrop(ev) { ev.preventDefault(); @@ -172,3 +172,317 @@ function disabled_alert(settings) { } }); } + +function ajax_get_integria_custom_fields( + ticket_type_id, + values, + recovery_values, + max_macro_fields +) { + values = values || []; + recovery_values = recovery_values || []; + + if ( + ticket_type_id === null || + ticket_type_id === "" || + (Array.isArray(values) && + values.length === 0 && + Array.isArray(recovery_values) && + recovery_values.length === 0) + ) { + for (let i = 8; i <= max_macro_fields; i++) { + $("[name=field" + i + "_value\\[\\]").val(""); + $("[name=field" + i + "_recovery_value\\[\\]").val(""); + } + } + + // On ticket type change, hide all table rows and inputs corresponding to custom fields, regardless of what its type is. + for (let i = 8; i <= max_macro_fields; i++) { + $("[name=field" + i + "_value\\[\\]").hide(); + $("[name=field" + i + "_recovery_value\\[\\]").hide(); + $("#table_macros-field" + i).hide(); + $("[name=field" + i + "_value_container").hide(); + $("[name=field" + i + "_recovery_value_container").hide(); + } + + jQuery.post( + "ajax.php", + { + page: "godmode/alerts/configure_alert_action", + get_integria_ticket_custom_types: 1, + ticket_type_id: ticket_type_id + }, + function(data) { + data.forEach(function(custom_field, key) { + var custom_field_key = key + 8; // Custom fields start from field 8. + + if (custom_field_key > max_macro_fields) { + return; + } + + // Display field row for current input. + var custom_field_row = $("#table_macros-field" + custom_field_key); + custom_field_row.show(); + + // Replace label text of field row for current input. + var label_html = $("#table_macros-field" + custom_field_key + " td") + .first() + .html(); + var label_name = label_html.split("
")[0]; + var new_html_content = custom_field_row + .html() + .replace(label_name, custom_field.label); + custom_field_row.html(new_html_content); + + switch (custom_field.type) { + case "CHECKBOX": + var checkbox_selector = $( + 'input[type="checkbox"][name=field' + + custom_field_key + + "_value\\[\\]]" + ); + var checkbox_recovery_selector = $( + 'input[type="checkbox"][name=field' + + custom_field_key + + "_recovery_value\\[\\]]" + ); + + checkbox_selector.on("change", function() { + if (checkbox_selector.prop("checked")) { + checkbox_selector.attr("value", "1"); + } else { + checkbox_selector.attr("value", "0"); + } + }); + + checkbox_recovery_selector.on("change", function() { + if (checkbox_recovery_selector.prop("checked")) { + checkbox_recovery_selector.attr("value", "1"); + } else { + checkbox_recovery_selector.attr("value", "0"); + } + }); + + if (typeof values[key] !== "undefined") { + if (values[key] == 1) { + checkbox_selector.prop("checked", true); + checkbox_selector.attr("value", "1"); + } else { + checkbox_selector.prop("checked", false); + checkbox_selector.attr("value", "0"); + } + } + + if (typeof recovery_values[key] !== "undefined") { + if (recovery_values[key] == 1) { + checkbox_recovery_selector.prop("checked", true); + checkbox_recovery_selector.attr("value", "1"); + } else { + checkbox_recovery_selector.prop("checked", false); + checkbox_recovery_selector.attr("value", "0"); + } + } + + $("[name=field" + custom_field_key + "_value_container]").show(); + $( + "[name=field" + custom_field_key + "_recovery_value_container]" + ).show(); + $( + 'input[type="checkbox"][name=field' + + custom_field_key + + "_value\\[\\]]" + ).show(); + $( + 'input[type="checkbox"][name=field' + + custom_field_key + + "_recovery_value\\[\\]]" + ).show(); + break; + case "COMBO": + var combo_input = $( + "select[name=field" + custom_field_key + "_value\\[\\]]" + ); + var combo_input_recovery = $( + "select[name=field" + custom_field_key + "_recovery_value\\[\\]]" + ); + + combo_input.find("option").remove(); + combo_input_recovery.find("option").remove(); + + var combo_values_array = custom_field.comboValue.split(","); + + combo_values_array.forEach(function(value) { + combo_input.append( + $("