diff --git a/extras/build_rpm_rhel7.sh b/extras/build_rpm_rhel7.sh new file mode 100755 index 0000000000..3684bd42e5 --- /dev/null +++ b/extras/build_rpm_rhel7.sh @@ -0,0 +1,26 @@ +#!/bin/bash +source build_vars.sh + +if [ ! -d $RPMHOME/RPMS ]; then + mkdir -p $RPMHOME/RPMS || exit 1 +fi + +echo "Creating RPM packages in $RPMHOME/RPMS" + +# Console +rpmbuild -ba $CODEHOME/pandora_console/pandora_console.rhel7.spec || exit 1 + +# Server +#rpmbuild -ba $CODEHOME/pandora_server/pandora_server.rhel7.spec || exit 1 + +# Unix agent +#rpmbuild -ba $CODEHOME/pandora_agents/unix/pandora_agent.rhel7.spec || exit 1 + +# Enterprise console +rpmbuild -ba $PANDHOME_ENT/pandora_console/enterprise/pandora_console_enterprise.rhel7.spec || exit 1 + +# Enterprise server +#rpmbuild -ba $PANDHOME_ENT/pandora_server/PandoraFMS-Enterprise/pandora_server_enterprise.rhel7.spec || exit 1 + +exit 0 + diff --git a/extras/docker/build_and_push.sh b/extras/docker/build_and_push.sh index 980087cfe5..91e6fa15e5 100755 --- a/extras/docker/build_and_push.sh +++ b/extras/docker/build_and_push.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker build --rm=true --build-arg BRANCH="develop" --build-arg DB_PASS="pandora" -t pandorafms/pandorafms:7 . && \ +docker build --rm=true --pull --no-cache --build-arg BRANCH="develop" --build-arg DB_PASS="pandora" -t pandorafms/pandorafms:7 . && \ docker push pandorafms/pandorafms:7 diff --git a/extras/pandora_update_version.sh b/extras/pandora_update_version.sh index 7d143f0f03..edab1affeb 100755 --- a/extras/pandora_update_version.sh +++ b/extras/pandora_update_version.sh @@ -26,9 +26,11 @@ $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_server/pandora_server.redhat.spec \ $PANDHOME_ENT/pandora_console/enterprise/pandora_console_enterprise.redhat.spec \ +$PANDHOME_ENT/pandora_console/enterprise/pandora_console_enterprise.rhel7.spec \ $PANDHOME_ENT/pandora_server/PandoraFMS-Enterprise/pandora_server_enterprise.redhat.spec" DEBIAN_FILES="$CODEHOME/pandora_console/DEBIAN \ $CODEHOME/pandora_server/DEBIAN \ diff --git a/pandora_agents/pc/AIX/pandora_agent.conf b/pandora_agents/pc/AIX/pandora_agent.conf index 07054ee2e1..0ff67a62d3 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.732, AIX version +# Version 7.0NG.735, AIX version # Licensed under GPL license v2, # Copyright (c) 2003-2010 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/pc/FreeBSD/pandora_agent.conf b/pandora_agents/pc/FreeBSD/pandora_agent.conf index 9d8a49bb99..c7c6fdd493 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.732, FreeBSD Version +# Version 7.0NG.735, FreeBSD Version # Licensed under GPL license v2, # Copyright (c) 2003-2010 Artica Soluciones Tecnologicas # 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 c02e4647e1..1a349583dc 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.732, HP-UX Version +# Version 7.0NG.735, HP-UX Version # Licensed under GPL license v2, # Copyright (c) 2003-2009 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/pc/Linux/pandora_agent.conf b/pandora_agents/pc/Linux/pandora_agent.conf index 153113e070..dc172b7e2f 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.732, GNU/Linux +# Version 7.0NG.735, GNU/Linux # Licensed under GPL license v2, # Copyright (c) 2003-2009 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/pc/NT4/pandora_agent.conf b/pandora_agents/pc/NT4/pandora_agent.conf index 6ddeba7edc..3d76832397 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.732, GNU/Linux +# Version 7.0NG.735, GNU/Linux # Licensed under GPL license v2, # Copyright (c) 2003-2009 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/pc/SunOS/pandora_agent.conf b/pandora_agents/pc/SunOS/pandora_agent.conf index 502317f893..9b088eae51 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.732, Solaris Version +# Version 7.0NG.735, Solaris Version # Licensed under GPL license v2, # Copyright (c) 2003-2009 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/pc/Win32/pandora_agent.conf b/pandora_agents/pc/Win32/pandora_agent.conf index 96b20ea7e3..e2c442a520 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-2010 Artica Soluciones Tecnologicas -# Version 7.0NG.732 +# Version 7.0NG.735 # 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 diff --git a/pandora_agents/shellscript/aix/pandora_agent.conf b/pandora_agents/shellscript/aix/pandora_agent.conf index 200ff9db97..fea0cb56db 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.732, AIX version +# Version 7.0NG.735, 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 2002f4db65..f0ce5b2519 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.732 +# Version 7.0NG.735 # 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 f9c19dbd06..20cdfeff99 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.732, HPUX Version +# Version 7.0NG.735, HPUX Version # General Parameters # ================== diff --git a/pandora_agents/shellscript/linux/pandora_agent.conf b/pandora_agents/shellscript/linux/pandora_agent.conf index dc272a7a71..e8ba9653ce 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.732 +# Version 7.0NG.735 # Licensed under GPL license v2, # (c) 2003-2010 Artica Soluciones Tecnologicas # 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 5a466677d2..29d13ff75c 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.732 +# Version 7.0NG.735 # Licensed under GPL license v2, # (c) 2003-2009 Artica Soluciones Tecnologicas # 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 170188cc6f..8ef8a6fdc1 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.732 +# Version 7.0NG.735 # 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 aad04ca60d..1ace947818 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.732, Solaris version +# Version 7.0NG.735, Solaris version # General Parameters # ================== diff --git a/pandora_agents/unix/AIX/pandora_agent.conf b/pandora_agents/unix/AIX/pandora_agent.conf index 3ce7da3c7e..c67f64d16c 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.732, AIX version +# Version 7.0NG.735, AIX version # Licensed under GPL license v2, # Copyright (c) 2003-2010 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/DEBIAN/control b/pandora_agents/unix/DEBIAN/control index 92057ef5c2..0533fe3cc4 100644 --- a/pandora_agents/unix/DEBIAN/control +++ b/pandora_agents/unix/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-agent-unix -Version: 7.0NG.732-190319 +Version: 7.0NG.735-190610 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 502624d1db..c58e8dddfb 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.732-190319" +pandora_version="7.0NG.735-190610" 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/pandora_agent.conf b/pandora_agents/unix/Darwin/pandora_agent.conf index 35202fcbe5..d7ee481255 100644 --- a/pandora_agents/unix/Darwin/pandora_agent.conf +++ b/pandora_agents/unix/Darwin/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.732, GNU/Linux +# Version 7.0NG.735, GNU/Linux # Licensed under GPL license v2, # Copyright (c) 2003-2012 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/FreeBSD/pandora_agent.conf b/pandora_agents/unix/FreeBSD/pandora_agent.conf index c505d5f75e..b02b379470 100644 --- a/pandora_agents/unix/FreeBSD/pandora_agent.conf +++ b/pandora_agents/unix/FreeBSD/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.732, FreeBSD Version +# Version 7.0NG.735, FreeBSD Version # Licensed under GPL license v2, # Copyright (c) 2003-2016 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/HP-UX/pandora_agent.conf b/pandora_agents/unix/HP-UX/pandora_agent.conf index 237bffb291..57e35c0a03 100644 --- a/pandora_agents/unix/HP-UX/pandora_agent.conf +++ b/pandora_agents/unix/HP-UX/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.732, HP-UX Version +# Version 7.0NG.735, HP-UX Version # Licensed under GPL license v2, # Copyright (c) 2003-2009 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/Linux/pandora_agent.conf b/pandora_agents/unix/Linux/pandora_agent.conf index 5a65e0e095..db07876f12 100644 --- a/pandora_agents/unix/Linux/pandora_agent.conf +++ b/pandora_agents/unix/Linux/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.732, GNU/Linux +# Version 7.0NG.735, GNU/Linux # Licensed under GPL license v2, # Copyright (c) 2003-2014 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/NT4/pandora_agent.conf b/pandora_agents/unix/NT4/pandora_agent.conf index 1394067390..8cda0f8fe0 100644 --- a/pandora_agents/unix/NT4/pandora_agent.conf +++ b/pandora_agents/unix/NT4/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.732, GNU/Linux +# Version 7.0NG.735, GNU/Linux # Licensed under GPL license v2, # Copyright (c) 2003-2009 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/NetBSD/pandora_agent.conf b/pandora_agents/unix/NetBSD/pandora_agent.conf index 677615a47c..629fe6a6fb 100644 --- a/pandora_agents/unix/NetBSD/pandora_agent.conf +++ b/pandora_agents/unix/NetBSD/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.732, NetBSD Version +# Version 7.0NG.735, NetBSD Version # Licensed under GPL license v2, # Copyright (c) 2003-2010 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/SunOS/pandora_agent.conf b/pandora_agents/unix/SunOS/pandora_agent.conf index 89d5b46100..8ff3c52de5 100644 --- a/pandora_agents/unix/SunOS/pandora_agent.conf +++ b/pandora_agents/unix/SunOS/pandora_agent.conf @@ -1,5 +1,5 @@ # Base config file for Pandora FMS agents -# Version 7.0NG.732, Solaris Version +# Version 7.0NG.735, Solaris Version # Licensed under GPL license v2, # Copyright (c) 2003-2009 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_agents/unix/pandora_agent b/pandora_agents/unix/pandora_agent index 81eeb9b74d..75bc63d62c 100755 --- a/pandora_agents/unix/pandora_agent +++ b/pandora_agents/unix/pandora_agent @@ -41,8 +41,8 @@ my $Sem = undef; # Semaphore used to control the number of threads my $ThreadSem = undef; -use constant AGENT_VERSION => '7.0NG.732'; -use constant AGENT_BUILD => '190319'; +use constant AGENT_VERSION => '7.0NG.735'; +use constant AGENT_BUILD => '190610'; # Agent log default file size maximum and instances use constant DEFAULT_MAX_LOG_SIZE => 600000; @@ -536,6 +536,12 @@ sub parse_conf_modules($) { # Check for invalid modules next unless (($module->{'name'} ne '' && $module->{'func'} != 0) || $module->{'func'} == \&module_plugin); + # Skip disabled modules. + if (defined($module->{'disabled'}) && $module->{'disabled'} == 1) { + log_message('setup', 'Skipping disabled module "' . $module->{'name'} . '"'); + next; + } + # Set the intensive interval if ($module->{'is_intensive'} == 1) { $module->{'intensive_interval'} = $module->{'interval'}; @@ -662,6 +668,8 @@ sub parse_conf_modules($) { $module->{'ff_timeout'} = $1; } elsif ($line =~ /^\s*module_each_ff\s+(\S+)\s*$/) { $module->{'each_ff'} = $1; + } elsif ($line =~ /^\s*module_ff_type\s+(\d+)\s*$/) { + $module->{'ff_type'} = $1; # Macros } elsif ($line =~ /^\s*module_macro(\S+)\s+(.*)\s*$/) { $module->{'macros'}{$1} = $2; @@ -1073,6 +1081,10 @@ sub send_buffered_xml_files ($;$) { unlink ("$temporal_file/$xml_file"); } } + # Do not get stuck trying to send buffered XML files to a secondary server. + elsif ($flag_always == 2) { + last; + } } if (defined($flag_always) && ($flag_always == 2)){ swap_servers (); @@ -2101,45 +2113,28 @@ sub cron_next_execution { } # Get day of the week and month from cron config - my ($mday, $wday) = (split (/\s/, $cron))[2, 4]; + my ($wday) = (split (/\s/, $cron))[4]; + # Check the wday values to avoid infinite loop + my ($wday_down, $wday_up) = cron_get_interval($wday); + if ($wday_down ne "*" && ($wday_down > 6 || (defined($wday_up) && $wday_up > 6))) { + log_message('setup', "Invalid cron configuration $cron. Day of the week is out of limits."); + $wday = "*"; + } # Get current time and day of the week my $cur_time = time(); my $cur_wday = (localtime ($cur_time))[6]; - # Any day of the week - if ($wday eq '*') { - my $nex_time = cron_next_execution_date ($cron, $cur_time, $interval); - return $nex_time - time(); - } - # A range? - else { - $wday = cron_get_closest_in_range ($cur_wday, $wday); + my $nex_time = cron_next_execution_date ($cron, $cur_time, $interval); + + # Check the day + while (!cron_check_interval($wday, (localtime ($nex_time))[6])) { + # If it does not acomplish the day of the week, go to the next day. + $nex_time += 86400; + $nex_time = cron_next_execution_date ($cron, $nex_time, 0); } - # A specific day of the week - my $count = 0; - my $nex_time = $cur_time; - do { - $nex_time = cron_next_execution_date ($cron, $nex_time, $interval); - my $nex_time_wd = $nex_time; - my ($nex_mon, $nex_wday) = (localtime ($nex_time_wd))[4, 6]; - my $nex_mon_wd; - do { - # Check the day of the week - if ($nex_wday == $wday) { - return $nex_time_wd - time(); - } - - # Move to the next day of the month - $nex_time_wd += 86400; - ($nex_mon_wd, $nex_wday) = (localtime ($nex_time_wd))[4, 6]; - } while ($mday eq '*' && $nex_mon_wd == $nex_mon); - $count++; - } while ($count < 60); - - # Something went wrong, default to 5 minutes - return $interval; + return $nex_time - time(); } ############################################################################### @@ -2151,7 +2146,30 @@ sub cron_check_syntax ($) { return 0 if !defined ($cron); return ($cron =~ m/^(\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+$/); } +############################################################################### +# Check if a value is inside an interval. +############################################################################### +sub cron_check_interval { + my ($elem_cron, $elem_curr_time) = @_; + # Return 1 if wildcard. + return 1 if ($elem_cron eq "*"); + + my ($down, $up) = cron_get_interval($elem_cron); + # Check if it is not a range + if (!defined($up)) { + return ($down == $elem_curr_time) ? 1 : 0; + } + + # Check if it is on the range + if ($down < $up) { + return 0 if ($elem_curr_time < $down || $elem_curr_time > $up); + } else { + return 0 if ($elem_curr_time > $down || $elem_curr_time < $up); + } + + return 1; +} ############################################################################### # Get the next execution date for the given cron entry in seconds since epoch. ############################################################################### @@ -2189,8 +2207,7 @@ sub cron_next_execution_date { my @nex_time_array = @curr_time_array; # Update minutes - my ($min_down, undef) = cron_get_interval ($min); - $nex_time_array[0] = ($min_down eq '*') ? 0 : $min_down; + $nex_time_array[0] = cron_get_next_time_element($min); $nex_time = cron_valid_date(@nex_time_array, $cur_year); if ($nex_time >= $cur_time) { @@ -2224,8 +2241,7 @@ sub cron_next_execution_date { return $nex_time if cron_is_in_cron(\@cron_array, \@nex_time_array); #Update the hour if fails - my ($hour_down, undef) = cron_get_interval ($hour); - $nex_time_array[1] = ($hour_down eq '*') ? 0 : $hour_down; + $nex_time_array[1] = cron_get_next_time_element($hour); # When an overflow is passed check the hour update again $nex_time = cron_valid_date(@nex_time_array, $cur_year); @@ -2253,10 +2269,9 @@ sub cron_next_execution_date { return $nex_time if cron_is_in_cron(\@cron_array, \@nex_time_array); #Update the day if fails - my ($mday_down, undef) = cron_get_interval ($mday); - $nex_time_array[2] = ($mday_down eq '*') ? 1 : $mday_down; + $nex_time_array[2] = cron_get_next_time_element($mday, 1); - # When an overflow is passed check the day update in the next execution + # When an overflow is passed check the hour update in the next execution $nex_time = cron_valid_date(@nex_time_array, $cur_year); if ($nex_time >= $cur_time) { return $nex_time if cron_is_in_cron(\@cron_array, \@nex_time_array); @@ -2276,8 +2291,7 @@ sub cron_next_execution_date { return $nex_time if cron_is_in_cron(\@cron_array, \@nex_time_array); #Update the month if fails - my ($mon_down, undef) = cron_get_interval ($mon); - $nex_time_array[3] = ($mon_down eq '*') ? 0 : $mon_down; + $nex_time_array[3] = cron_get_next_time_element($mon); # When an overflow is passed check the month update in the next execution $nex_time = cron_valid_date(@nex_time_array, $cur_year); @@ -2308,23 +2322,30 @@ sub cron_is_in_cron { #If there is no elements means that is in cron return 1 unless (defined($elem_cron) || defined($elem_curr_time)); - # Go to last element if current is a wild card - if ($elem_cron ne '*') { - my ($down, $up) = cron_get_interval($elem_cron); - # Check if there is no a range - return 0 if (!defined($up) && ($down != $elem_curr_time)); - # Check if there is on the range - if (defined($up)) { - if ($down < $up) { - return 0 if ($elem_curr_time < $down || $elem_curr_time > $up); - } else { - return 0 if ($elem_curr_time > $down || $elem_curr_time < $up); - } - } - } + # Check the element interval + return 0 unless (cron_check_interval($elem_cron, $elem_curr_time)); + return cron_is_in_cron(\@deref_elems_cron, \@deref_elems_curr_time); } +################################################################################ +#Get the next tentative time for a cron value or interval in case of overflow. +#Floor data is the minimum localtime data for a position. Ex: +#Ex: +# * should returns floor data. +# 5 should returns 5. +# 10-55 should returns 10. +# 55-10 should retunrs floor data. +################################################################################ +sub cron_get_next_time_element { + # Default floor data is 0 + my ($curr_element, $floor_data) = @_; + $floor_data = 0 unless defined($floor_data); + my ($elem_down, $elem_up) = cron_get_interval ($curr_element); + return ($elem_down eq '*' || (defined($elem_up) && $elem_down > $elem_up)) + ? $floor_data + : $elem_down; +} ############################################################################### # Returns the interval of a cron element. If there is not a range, # returns an array with the first element in the first place of array @@ -2416,12 +2437,11 @@ sub check_module_cron { return 1 unless ($is_first); # Check if current timestamp is a valid cron date - my $next_execution = cron_next_execution_date( + my $next_execution = cron_next_execution( $module->{'cron'}, - $now - $interval, - $interval + 0 ); - return 1 if ($next_execution == $now); + return 1 if (time() + $next_execution == $now); return 0; } @@ -2532,6 +2552,7 @@ sub write_module_xml ($@) { $Xml .= " " . $module->{'min_ff_event_critical'} . "\n" if (defined ($module->{'min_ff_event_critical'})); $Xml .= " " . $module->{'ff_timeout'} . "\n" if (defined ($module->{'ff_timeout'})); $Xml .= " " . $module->{'each_ff'} . "\n" if (defined ($module->{'each_ff'})); + $Xml .= " " . $module->{'ff_type'} . "\n" if (defined ($module->{'ff_type'})); # Data list if ($#data > 0) { @@ -3017,10 +3038,10 @@ while (1) { my @address_list; if( -x "/bin/ip" || -x "/sbin/ip" || -x "/usr/sbin/ip" ) { - @address_list = `ip addr show 2>$DevNull | sed -e '/127.0.0/d' -e '/[0-9]*\\.[0-9]*\\.[0-9]*/!d' -e 's/^[ \\t]*\\([^ \\t]*\\)[ \\t]*\\([^ \\t]*\\)[ \\t].*/\\2/' -e 's/\\/.*//'`; + @address_list = `ip addr show 2>$DevNull | sed -e '/127.0.0/d' -e '/\\([0-9][0-9]*\\.\\)\\{3\\}[0-9][0-9]*/!d' -e 's/^[ \\t]*\\([^ \\t]*\\)[ \\t]*\\([^ \\t]*\\)[ \\t].*/\\2/' -e 's/\\/.*//'`; } else { - @address_list = `ifconfig -a 2>$DevNull | sed -e '/127.0.0/d' -e '/[0-9]*\\.[0-9]*\\.[0-9]*/!d' -e 's/^[ \\t]*\\([^ \\t]*\\)[ \\t]*\\([^ \\t]*\\)[ \\t].*/\\2/' -e 's/.*://'`; + @address_list = `ifconfig -a 2>$DevNull | sed -e '/127.0.0/d' -e '/\\([0-9][0-9]*\\.\\)\\{3\\}[0-9][0-9]*/!d' -e 's/^[ \\t]*\\([^ \\t]*\\)[ \\t]*\\([^ \\t]*\\)[ \\t].*/\\2/' -e 's/.*://'`; } for (my $i = 0; $i <= $#address_list; $i++) { diff --git a/pandora_agents/unix/pandora_agent.redhat.spec b/pandora_agents/unix/pandora_agent.redhat.spec index 895791cbdf..851dbce14b 100644 --- a/pandora_agents/unix/pandora_agent.redhat.spec +++ b/pandora_agents/unix/pandora_agent.redhat.spec @@ -2,8 +2,8 @@ #Pandora FMS Linux Agent # %define name pandorafms_agent_unix -%define version 7.0NG.732 -%define release 190319 +%define version 7.0NG.735 +%define release 190610 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent.spec b/pandora_agents/unix/pandora_agent.spec index 778c5864bf..639ad34897 100644 --- a/pandora_agents/unix/pandora_agent.spec +++ b/pandora_agents/unix/pandora_agent.spec @@ -2,8 +2,8 @@ #Pandora FMS Linux Agent # %define name pandorafms_agent_unix -%define version 7.0NG.732 -%define release 190319 +%define version 7.0NG.735 +%define release 190610 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent_installer b/pandora_agents/unix/pandora_agent_installer index 5c960e5725..78f054e1c6 100755 --- a/pandora_agents/unix/pandora_agent_installer +++ b/pandora_agents/unix/pandora_agent_installer @@ -9,8 +9,8 @@ # Please see http://www.pandorafms.org. This code is licensed under GPL 2.0 license. # ********************************************************************** -PI_VERSION="7.0NG.732" -PI_BUILD="190319" +PI_VERSION="7.0NG.735" +PI_BUILD="190610" OS_NAME=`uname -s` FORCE=0 diff --git a/pandora_agents/unix/plugins/inventory b/pandora_agents/unix/plugins/inventory index a6d9a8bc83..6ef6233d23 100755 --- a/pandora_agents/unix/plugins/inventory +++ b/pandora_agents/unix/plugins/inventory @@ -4,23 +4,44 @@ # Copyright (c) 2009 Artica Soluciones Tecnologicas S.L. # # inventory Generate a hardware/software inventory. -# +# # Sample usage: ./inventory [cpu] [ram] [video] [nic] [hd] [cdrom] [software] [init_services] [filesystem] [process] [users] # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# GNU General Public License for more details. # ############################################################################### use strict; use constant TSTAMP_FILE => '/tmp/pandora_inventory.tstamp'; +use Scalar::Util qw(looks_like_number); +use Data::Dumper; +# Set environment language to English +$ENV{"LANG"} = "en_US"; + +# Check AIX system +my $AIX=0; +my $system = `uname -a | awk '{print $1}'`; +if ($system =~ 'AIX') { + $AIX=1; +} + +sub is_enabled { + my $value = shift; + if ((defined ($value)) && looks_like_number($value) && ($value > 0)){ + # return true + return 1; + } + #return false + return 0; +} # Operation mode (LSHW or HWINFO) my $Mode; @@ -31,12 +52,12 @@ my $Separator; sub get_module_data ($$$$) { my ($name, $hwinfo, $keys, $modules) = @_; my %module; - - # Store keys + $Separator='\s+\*\-'; + # Store keys foreach my $key (@{$keys}) { push (@{$module{'_keys'}}, $key); } - + # Parse module data while (my $line = shift (@{$hwinfo})) { if ($line =~ /$Separator/) { @@ -46,7 +67,7 @@ sub get_module_data ($$$$) { foreach my $key (@{$keys}) { if ($line =~ /$key:\s+(.+)/) { $module{$key} = $1; - # Replace semicolon by comma to avoid parse errors + # Replace semicolon by comma to avoid parse errors $module{$key} =~ s/;/,/g; } } @@ -58,12 +79,65 @@ sub get_module_data ($$$$) { push (@{$modules->{$name}}, \%module); } +sub test_contain ($$) { + my ($value, $array)=@_; + if ( grep( /$value/, @{$array} ) ) { + return 1; + } +} +sub get_module_data_aix_ram_cpu ($$$$) { + my ($name, $hwinfo, $keys, $modules) = @_; + my %module; + # Store keys + foreach my $key (@{$keys}) { + push (@{$module{'_keys'}}, $key); + } + # Parse module data + foreach my $line (@{$hwinfo}) { + foreach my $key (@{$keys}) { + if ($line =~ /$key:\s+(.+)/) { + $module{$key} = $1; + $module{$key} =~ s/,/ /g; + } + } + } + + # No data found + #my @data = keys (%module); + #return unless ($#data >= 0); + + push (@{$modules->{$name}}, \%module); +} +sub get_module_data_aix ($$$$) { + my ($name,$hwinfo,$regex,$modules) = @_; + my %module; + foreach my $line (@{$hwinfo}) { + $line =~ s/\s{2,}/;/g; + $line =~ s/\+ //g; + $line =~ s/\* //g; + } + foreach my $line (@{$hwinfo}) { + if ($line =~ /$regex/){ + my ($var1, $var2, $var3) = split /;/, $line; + my %module; + $module{'device'} = $var1; + $module{'serial'} = $var2; + $module{'description'}=$var3; + $module{'_keys'} = ['device','serial','description']; + push (@{$modules->{$name}}, \%module); + } + } +} # Get a list of information file system in machine sub get_file_system($$) { my ($name, $modules) = @_; - - my @fileSystems = `df -hP | tail -n +2`; #remove the titles of columns + my @fileSystems; + if (is_enabled $AIX){ + @fileSystems = `df -gP | tail -n +2`; + } else { + @fileSystems = `df -hP | tail -n +2`; #remove the titles of columns + } foreach my $row (@fileSystems) { next unless ($row =~ /^(\S+)\s+\S+\s+(\S+)\s+(\S+)\s+\S+\s+(\S+)/); @@ -73,94 +147,110 @@ sub get_file_system($$) { $module{'used'} = $2; $module{'avail'} = $3; $module{'mount'} = $4; - $module{'_keys'} = ['filesystem', 'used','avail', 'mount']; - push (@{$modules->{$name}}, \%module); - } + $module{'_keys'} = ['filesystem', 'used','avail', 'mount']; + push (@{$modules->{$name}}, \%module); + } } # Get a list of services init in machine sub get_servicies_init_machine($$) { my ($name, $modules) = @_; - my $runlevel = `who -r | awk '{print \$2}'`; + my $runlevel; + if (is_enabled $AIX) { + $runlevel = `who -r | awk '{print \$3}'`; + } else { + $runlevel = `who -r | awk '{print \$2}'`; + } #ini trim($runlevel) $runlevel =~ s/^\s*//; #ltrim $runlevel =~ s/\s*$//; #rtrim #end trim($runlevel) - my $script = ""; - + my $script; + if (-e "/etc/rc" . $runlevel .".d/") { - $script = "ls /etc/rc" . $runlevel .".d/ -l | grep \"^l.*\" | awk \"{print \\\$NF}\" | sed -e \"s/\\.\\.\\///g\" | sed -e \"s/.*init\\.d\\///g\""; + $script = "ls -l /etc/rc" . $runlevel .".d/ | grep \"^l.*\" | awk \"{print \\\$NF}\" | sed -e \"s/\\.\\.\\///g\" | sed -e \"s/.*init\\.d\\///g\""; } else { - $script = "ls /etc/rc.d/rc" . $runlevel .".d/ -l | grep \"^l.*\" | grep \" S.* \" | awk \"{print \\\$NF}\" | sed -e \"s/\\.\\.\\///g\" | sed -e \"s/.*init\\.d\\///g\""; - + $script = "ls -l /etc/rc.d/rc" . $runlevel .".d/ | grep \"^l.*\" | grep \" S.* \" | awk \"{print \\\$NF}\" | sed -e \"s/\\.\\.\\///g\" | sed -e \"s/.*init\\.d\\///g\""; } - + my @services = `$script`; foreach my $row (@services) { - my %module; - $row =~ s/\n//; - $module{'service'} = $row; - $module{'_keys'} = ['service']; - push (@{$modules->{$name}}, \%module); + $row =~ s/\n//; + $module{'service'} = $row; + $module{'_keys'} = ['service']; + push (@{$modules->{$name}}, \%module); } } # Get a list of running processes sub get_processes ($$) { my ($name, $modules) = @_; + my $script; + if (is_enabled $AIX) { + $script = "ps -eo args | tail -n +2"; + } else { + $script = "ps -eo command | tail -n +2"; + } - my $script = "ps -eo command"; - - my @services = `$script`; - foreach my $row (@services) { - my %module; - # Remove carriage returns - $row =~ s/[\n\l\f]//g; - # Replace semicolon by comma to avoid parse errors - $row =~ s/;/,/g; - $module{'service'} = $row; - $module{'_keys'} = ['service']; - push (@{$modules->{$name}}, \%module); - } + my @services = `$script`; + foreach my $row (@services) { + my %module; + # Remove carriage returns + $row =~ s/[\n\l\f]//g; + # Replace semicolon by comma to avoid parse errors + $row =~ s/;/,/g; + $module{'service'} = $row; + $module{'_keys'} = ['service']; + push (@{$modules->{$name}}, \%module); + } } # Get a list of valid users in the system sub get_users ($$) { - my ($name, $modules) = @_; - - my $script = "cat /etc/passwd"; + my ($name, $modules) = @_; + my $script = "cat /etc/passwd"; my $user = ""; my $estado = ""; - my @services = `$script`; - foreach my $row (@services) { - my %module; + my @services = `$script`; + foreach my $row (@services) { + my %module; - next unless ($row =~ /^([A-Za-z0-9\-\_]*)/); - - $user = $1; - $script = `passwd -S $user`; - if ( $script =~ /^(\S+)\sP./){ - $module{'user'} = $user; - $module{'_keys'} = ['user']; - push (@{$modules->{$name}}, \%module); + next unless ($row =~ /^([A-Za-z0-9\-\_]*)/); + if (is_enabled $AIX) { + $user = $1; + $script = `lsuser $user`; + if ( $script =~ /^(\S+)\sid./){ + $module{'user'} = $user; + $module{'_keys'} = ['user']; + push (@{$modules->{$name}}, \%module); + } + } else { + $user = $1; + $script = `passwd -S $user`; + if ( $script =~ /^(\S+)\sP./){ + $module{'user'} = $user; + $module{'_keys'} = ['user']; + push (@{$modules->{$name}}, \%module); } } + + } } # Show Kernel Information sub get_kernel_info ($$) { - my ($name, $modules) = @_; - my $script = `uname -a | tr -d \";\"`; - my %module; + my ($name, $modules) = @_; + my $script = `uname -a | tr -d \";\"`; + my %module; - $module{'Kernel'} = $script; - $module{'_keys'} = ['Kernel']; - push (@{$modules->{$name}}, \%module); + $module{'Kernel'} = $script; + $module{'_keys'} = ['Kernel']; + push (@{$modules->{$name}}, \%module); } @@ -171,11 +261,13 @@ sub get_software_module_data ($$) { # Guess the current distribution my $distrib_id = ""; - if ( -e "/etc/SuSE-release"){ + if (is_enabled $AIX) { + $distrib_id = "AIX"; + }elsif ( -e "/etc/SuSE-release"){ $distrib_id = "SUSE"; - } elsif ( -e "/etc/redhat-release"){ + }elsif ( -e "/etc/redhat-release"){ $distrib_id = "REDHAT"; - } else { + }else { $distrib_id = "DEBIAN"; } @@ -183,7 +275,9 @@ sub get_software_module_data ($$) { my @soft; if ($distrib_id eq 'DEBIAN') { @soft = `dpkg -l | grep ii`; - } else { + }elsif ($distrib_id eq 'AIX') { + @soft = `lslpp -Lcq | awk -F: '{print "ii "\$1" "\$3" "\$8}'`; + }else { # Sometimes rpm return data splitted in two lines, and with dupes. Thats bad for our inventory system @soft = `rpm -q -a --qf "ii %{NAME} %{VERSION} %{SUMMARY}\n" | grep "^ii" | sort -u`; } @@ -197,7 +291,7 @@ sub get_software_module_data ($$) { $module{'program'} = $1; $module{'version'} = $2; $module{'description'} = $3; - # Replace semicolon by comma to avoid parse errors + # Replace semicolon by comma to avoid parse errors $module{'program'} =~ s/;/,/g; $module{'version'} =~ s/;/,/g; $module{'description'} =~ s/;/,/g; @@ -211,67 +305,95 @@ sub get_software_module_data ($$) { #Get the list of interfaces with the ip assigned sub get_ips ($$) { my ($name, $modules) = @_; + my @interfaces; + my $ifconfig; + if (is_enabled $AIX) { + $ifconfig = `ifconfig -a`; + } else { + $ifconfig = `ifconfig`; + } - my $ifconfig = `ifconfig`; - - my @ifconfig_array = split("\n", $ifconfig); - - for(my $i = 0; $i<$#ifconfig_array; $i++) { - - #Check for an interface - if ($ifconfig_array[$i] =~ /Link/) { - my %info; - - my @line_split = split(" ", $ifconfig_array[$i]); - - #Get interface name - $info{'interface'} = $line_split[0]; - #Get IP address - my $line = $ifconfig_array[$i+1]; - - $line =~ s/\s+//g; + my @ifconfig_array = split("\n", $ifconfig); - @line_split = split(":", $line); - - if($line_split[1] =~ /(\d+\.\d+\.\d+\.\d+).+/) { - $info{'ip'} = $1; - } - - $info{'_keys'} = ['interface', 'ip']; - push (@{$modules->{$name}}, \%info); - - } + foreach (@ifconfig_array){ + if ($_=~/(.*)flags/){ + my $match; + ($match)=$_=~/^(.*?)\: flags/; + $match=~s/://; + push @interfaces,$match; + } + } + foreach (@interfaces) { + my $ifconfig_item=`ifconfig $_`; + my $interface=$_; + my @ip_array = split("\n", $ifconfig_item); + foreach (@ip_array) { + if ($_=~/(?<=inet )(.*)(?= netmask)/){ + my $ip; + ($ip)=$_=~/inet (.*) netmask/; + my %info; + $info{'interface'} = $interface; + $info{'ip'} = $ip; + $info{'_keys'} = ['interface','ip']; + push (@{$modules->{$name}}, \%info); + } + } } } #Get route table sub get_route_table ($$) { - my ($name, $modules) = @_; - - my $route_table = `route`; - - my @table_split = split("\n", $route_table); - - for (my $i=2; $i<=$#table_split; $i++) { - - my @split = split(" ", $table_split[$i]); - - my %info; - - $info{'destination'} = $split[0]; - $info{'gateway'} = $split[1]; - $info{'mask'} = $split[2]; - $info{'flags'} = $split[3]; - $info{'metric'} = $split[4]; - $info{'ref'} = $split[5]; - $info{'use'} = $split[6]; - $info{'interface'} = $split[7]; - - $info{'_keys'} = ['destination', 'gateway', 'mask', 'flags', 'metric', 'use', 'interface']; - - push (@{$modules->{$name}}, \%info); - } + my ($name, $modules) = @_; + my $route_table; + my @table_split; + if (is_enabled $AIX) { + $route_table = `netstat -rn`; + @table_split = split("\n", $route_table); + my $length=scalar @table_split; + for (my $i=4; $i<=$length-4; $i++) { + + my @split = split(" ", $table_split[$i]); + + my %info; + + $info{'destination'} = $split[0]; + $info{'gateway'} = $split[1]; + $info{'mask'} = $split[2]; + $info{'flags'} = $split[3]; + $info{'metric'} = $split[4]; + $info{'ref'} = $split[5]; + $info{'use'} = $split[6]; + $info{'interface'} = $split[7]; + + $info{'_keys'} = ['destination', 'gateway', 'mask', 'flags', 'metric', 'use', 'interface']; + + push (@{$modules->{$name}}, \%info); + } + } else { + $route_table = `route`; + my @table_split = split("\n", $route_table); + + for (my $i=2; $i<=$#table_split; $i++) { + + my @split = split(" ", $table_split[$i]); + + my %info; + + $info{'destination'} = $split[0]; + $info{'gateway'} = $split[1]; + $info{'mask'} = $split[2]; + $info{'flags'} = $split[3]; + $info{'metric'} = $split[4]; + $info{'ref'} = $split[5]; + $info{'use'} = $split[6]; + $info{'interface'} = $split[7]; + + $info{'_keys'} = ['destination', 'gateway', 'mask', 'flags', 'metric', 'use', 'interface']; + + push (@{$modules->{$name}}, \%info); + } + } } # Print module data sub print_module ($$) { @@ -309,14 +431,18 @@ my $enable_all = 0; $interval = $ARGV[0]; if ($#ARGV == 0){ - $enable_all = 1; + $enable_all = 1; +} +if ($interval!=/[:alpha:]/){ + splice @ARGV,0,1; } -foreach my $module (@ARGV) { +foreach my $module (@ARGV) { if ($module eq "all"){ - $enable_all = 1; - } - $enabled{$module} = 1; + $enable_all = 1; + }else { + $enabled{$module} = 1; + } } # Check execution interval @@ -333,76 +459,107 @@ close (FILE); # Retrieve hardware information $Mode = 'LSHW'; $Separator = '\s+\*\-'; -my @hwinfo = `lshw 2>/dev/null`; -if ($? != 0) { - $Mode = 'HWINFO'; - $Separator = 'Hardware Class:'; - @hwinfo = `hwinfo --cpu --memory --gfxcard --netcard --cdrom --disk 2>/dev/null`; +my @hwinfo; +if (is_enabled $AIX) { + $Separator = '^\s*$'; + @hwinfo=`prtconf 2>/dev/null`; +} else { + @hwinfo = `lshw 2>/dev/null`; + if ($? != 0) { + $Mode = 'HWINFO'; + $Separator = 'Hardware Class:'; + @hwinfo = `hwinfo --cpu --memory --gfxcard --netcard --cdrom --disk 2>/dev/null`; + } } -# Parse hardware information my %modules; -while (my $line = shift (@hwinfo)) { + + if (is_enabled $AIX) { + #CPU + # VIDEO + ### Not avilable in AIX ### + # NIC + ### Not relevant in AIX ### + if ((test_contain('ent',\@hwinfo)) && ($enable_all == 1 || $enabled{'nic'} == 1)) { + get_module_data_aix ('NIC',\@hwinfo,'^ent',\%modules); + } + if ((test_contain('hdisk',\@hwinfo)) && ($enable_all == 1 || $enabled{'hd'} == 1)) { + get_module_data_aix ('HD',\@hwinfo,'^hdisk',\%modules); + } + + foreach my $line (@hwinfo) { + chomp ($line); + #CPU + if (($line =~ /^Memory Size:/) && ($enable_all == 1 || $enabled{'ram'} == 1)) { + get_module_data_aix_ram_cpu ('RAM', \@hwinfo, ['Memory Size','Good Memory Size'], \%modules); + } + + if (($line =~ /^System Model/) && ($enable_all == 1 || $enabled{'cpu'} == 1)) { + get_module_data_aix_ram_cpu ('CPU', \@hwinfo, ['System Model', 'Processor Implementation Mode', 'Number Of Processors'], \%modules); + } + } +} else { + # Parse hardware information + while (my $line= shift (@hwinfo)) { chomp ($line); + # CPU + if (($line =~ /\*\-cpu/ || $line =~ /Hardware Class: cpu/) && ($enable_all == 1 || $enabled{'cpu'} == 1)) { + if ($Mode eq 'LSHW') { + get_module_data ('CPU', \@hwinfo, ['product', 'vendor', 'capacity'], \%modules); + } else { + get_module_data ('CPU', \@hwinfo, ['Model', 'Vendor', 'Clock'], \%modules); + } + } - # CPU - if (($line =~ /\*\-cpu/ || $line =~ /Hardware Class: cpu/) && ($enable_all == 1 || $enabled{'cpu'} == 1)) { - if ($Mode eq 'LSHW') { - get_module_data ('CPU', \@hwinfo, ['product', 'vendor', 'capacity'], \%modules); - } else { - get_module_data ('CPU', \@hwinfo, ['Model', 'Vendor', 'Clock'], \%modules); - } - } + # RAM + if (($line =~ /\*\-bank/ || $line =~ /\*\-memory/ || $line =~ /Hardware Class: memory/) && ($enable_all == 1 || $enabled{'ram'} == 1)) { + if ($Mode eq 'LSHW') { + get_module_data ('RAM', \@hwinfo, ['description', 'size'], \%modules); + } else { + get_module_data ('RAM', \@hwinfo, ['Model', 'Memory Size'], \%modules); + } + } - # RAM - if (($line =~ /\*\-bank/ || $line =~ /Hardware Class: memory/) && ($enable_all == 1 || $enabled{'ram'} == 1)) { - if ($Mode eq 'LSHW') { - get_module_data ('RAM', \@hwinfo, ['description', 'size'], \%modules); - } else { - get_module_data ('RAM', \@hwinfo, ['Model', 'Memory Size'], \%modules); - } - } + # VIDEO + if (($line =~ /\*\-display/ || $line =~ /Hardware Class: graphics card/) && ($enable_all == 1 || $enabled{'video'} == 1)) { + if ($Mode eq 'LSHW') { + get_module_data ('VIDEO', \@hwinfo, ['product', 'description', 'vendor'], \%modules); + } else { + # Spaces before Device and Vendor are intentional to avoid matching SubDevice and SubVendor + get_module_data ('VIDEO', \@hwinfo, ['Model', ' Device', ' Vendor'], \%modules); + } + } - # VIDEO - if (($line =~ /\*\-display/ || $line =~ /Hardware Class: graphics card/) && ($enable_all == 1 || $enabled{'video'} == 1)) { - if ($Mode eq 'LSHW') { - get_module_data ('VIDEO', \@hwinfo, ['product', 'description', 'vendor'], \%modules); - } else { - # Spaces before Device and Vendor are intentional to avoid matching SubDevice and SubVendor - get_module_data ('VIDEO', \@hwinfo, ['Model', ' Device', ' Vendor'], \%modules); - } - } + # NIC + if (($line =~ /\*\-network/ || $line =~ /Hardware Class: network/) && ($enable_all == 1 || $enabled{'nic'} == 1)) { + if ($Mode eq 'LSHW') { + get_module_data ('NIC', \@hwinfo, ['product', 'description', 'vendor', 'serial'], \%modules); + } else { + # Spaces before Device and Vendor are intentional to avoid matching SubDevice and SubVendor + get_module_data ('NIC', \@hwinfo, ['Model', ' Device', ' Vendor', 'HW Address'], \%modules); + } + } - # NIC - if (($line =~ /\*\-network/ || $line =~ /Hardware Class: network/) && ($enable_all == 1 || $enabled{'nic'} == 1)) { - if ($Mode eq 'LSHW') { - get_module_data ('NIC', \@hwinfo, ['product', 'description', 'vendor', 'serial'], \%modules); - } else { - # Spaces before Device and Vendor are intentional to avoid matching SubDevice and SubVendor - get_module_data ('NIC', \@hwinfo, ['Model', ' Device', ' Vendor', 'HW Address'], \%modules); - } - } - - # CDROM - if (($line =~ /\*\-cdrom/ || $line =~ /Hardware Class: cdrom/) && ($enable_all == 1 || $enabled{'cdrom'} == 1)) { - if ($Mode eq 'LSHW') { - get_module_data ('CDROM', \@hwinfo, ['product', 'description', 'vendor'], \%modules); - } else { - # Spaces before Device and Vendor are intentional to avoid matching SubDevice and SubVendor - get_module_data ('CDROM', \@hwinfo, ['Model', ' Device', ' Vendor'], \%modules); - } - } + # CDROM + if (($line =~ /\*\-cdrom/ || $line =~ /Hardware Class: cdrom/) && ($enable_all == 1 || $enabled{'cdrom'} == 1)) { + if ($Mode eq 'LSHW') { + get_module_data ('CDROM', \@hwinfo, ['product', 'description', 'vendor'], \%modules); + } else { + # Spaces before Device and Vendor are intentional to avoid matching SubDevice and SubVendor + get_module_data ('CDROM', \@hwinfo, ['Model', ' Device', ' Vendor'], \%modules); + } + } - # HD - if (($line =~ /\*\-disk/ || $line =~ /Hardware Class: disk/) && ($enable_all == 1 || $enabled{'hd'} == 1)) { - if ($Mode eq 'LSHW') { - get_module_data ('HD', \@hwinfo, ['product', 'description', 'size'], \%modules); - } else { - get_module_data ('HD', \@hwinfo, ['Model', 'Serial ID', 'Size'], \%modules); - } - } + # HD + if (($line =~ /\*\-disk/ || $line =~ /Hardware Class: disk/) && ($enable_all == 1 || $enabled{'hd'} == 1)) { + if ($Mode eq 'LSHW') { + get_module_data ('HD', \@hwinfo, ['product', 'description', 'size'], \%modules); + } else { + get_module_data ('HD', \@hwinfo, ['Model', 'Serial ID', 'Size'], \%modules); + } + } + } } - # Software if ($enable_all == 1 || $enabled{'software'} == 1) { get_software_module_data ('Software', \%modules); @@ -410,12 +567,12 @@ if ($enable_all == 1 || $enabled{'software'} == 1) { #init_services if ($enable_all == 1 || $enabled{'init_services'} == 1) { - get_servicies_init_machine ('Init services', \%modules); + get_servicies_init_machine ('Init_services', \%modules); } #filesystem if ($enable_all == 1 || $enabled{'filesystem'} == 1) { - get_file_system('File system', \%modules); + get_file_system('Filesystem', \%modules); } #processes @@ -442,7 +599,6 @@ if ($enable_all == 1 || $enabled{'route'} == 1) { if ($enable_all == 1 || $enabled{'kernel'} == 1){ get_kernel_info ('Kernel', \%modules); } - # Print module data print "\n"; while (my ($name, $module) = each (%modules)) { diff --git a/pandora_agents/win32/bin/pandora_agent.conf b/pandora_agents/win32/bin/pandora_agent.conf index 3d1e208de8..518cd3df0e 100644 --- a/pandora_agents/win32/bin/pandora_agent.conf +++ b/pandora_agents/win32/bin/pandora_agent.conf @@ -1,6 +1,6 @@ # Base config file for Pandora FMS Windows Agent # (c) 2006-2017 Artica Soluciones Tecnologicas -# Version 7.0NG.732 +# Version 7.0NG.735 # 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 @@ -44,6 +44,8 @@ remote_config 0 #agent_name_cmd cscript.exe //B "%ProgramFiles%\Pandora_Agent\util\agentname.vbs" agent_name_cmd __rand__ +# Agent alias. Name should be unique rather than alias. Hostname by default +# agent_alias $Alias$ #Parent agent_name #parent_agent_name caprica diff --git a/pandora_agents/win32/installer/pandora.mpi b/pandora_agents/win32/installer/pandora.mpi index c46da70b35..be257849d3 100644 --- a/pandora_agents/win32/installer/pandora.mpi +++ b/pandora_agents/win32/installer/pandora.mpi @@ -3,7 +3,7 @@ AllowLanguageSelection {Yes} AppName -{Pandora FMS Windows Agent v7.0NG.732} +{Pandora FMS Windows Agent v7.0NG.735} ApplicationID {17E3D2CF-CA02-406B-8A80-9D31C17BD08F} @@ -186,7 +186,7 @@ UpgradeApplicationID {} Version -{190319} +{190610} ViewReadme {Yes} diff --git a/pandora_agents/win32/misc/cron.cc b/pandora_agents/win32/misc/cron.cc index 2d3e5e0440..409a43f726 100644 --- a/pandora_agents/win32/misc/cron.cc +++ b/pandora_agents/win32/misc/cron.cc @@ -214,7 +214,7 @@ int Cron::getResetValue (int position) { int default_value = 0; // Days start in 1 if (position == 2) default_value = 1; - return isWildCard(position) + return (isWildCard(position) || !isNormalInterval(position)) ? default_value : this->params[position][CRDOWN]; } diff --git a/pandora_agents/win32/modules/pandora_module.cc b/pandora_agents/win32/modules/pandora_module.cc index eac38c8fa1..e7c751d367 100644 --- a/pandora_agents/win32/modules/pandora_module.cc +++ b/pandora_agents/win32/modules/pandora_module.cc @@ -78,6 +78,7 @@ Pandora_Module::Pandora_Module (string name) { this->warning_inverse = ""; this->quiet = ""; this->module_ff_interval = ""; + this->module_ff_type = ""; this->module_alert_template = ""; this->module_crontab = ""; } @@ -733,6 +734,13 @@ Pandora_Module::getXml () { module_xml += this->module_ff_interval; module_xml += "\n"; } + + /* Module FF type */ + if (this->module_ff_type != "") { + module_xml += "\t"; + module_xml += this->module_ff_type; + module_xml += "\n"; + } /* Module Alert template */ if (this->module_alert_template != "") { @@ -1028,6 +1036,16 @@ Pandora_Module::setModuleFFInterval (string value) { this->module_ff_interval = value; } +/** + * Set the module FF type for the module. + * + * @param value module FF type value to set. + */ +void +Pandora_Module::setModuleFFType (string value) { + this->module_ff_type = value; +} + /** * Set the module Alert template for the module. * diff --git a/pandora_agents/win32/modules/pandora_module.h b/pandora_agents/win32/modules/pandora_module.h index 9fb29e1183..c766766950 100644 --- a/pandora_agents/win32/modules/pandora_module.h +++ b/pandora_agents/win32/modules/pandora_module.h @@ -176,6 +176,7 @@ namespace Pandora_Modules { string unit, custom_id, str_warning, str_critical; string module_group, warning_inverse, critical_inverse, quiet; string module_ff_interval, module_alert_template, module_crontab; + string module_ff_type; string critical_instructions, warning_instructions, unknown_instructions, tags; protected: @@ -277,6 +278,7 @@ namespace Pandora_Modules { void setWarningInverse (string value); void setQuiet (string value); void setModuleFFInterval (string value); + void setModuleFFType (string value); void setModuleAlertTemplate (string value); void setModuleCrontab (string value); diff --git a/pandora_agents/win32/modules/pandora_module_factory.cc b/pandora_agents/win32/modules/pandora_module_factory.cc index 658ff724fd..a4d9553c20 100644 --- a/pandora_agents/win32/modules/pandora_module_factory.cc +++ b/pandora_agents/win32/modules/pandora_module_factory.cc @@ -119,6 +119,7 @@ using namespace Pandora_Strutils; #define TOKEN_WARNING_INVERSE ("module_warning_inverse ") #define TOKEN_QUIET ("module_quiet ") #define TOKEN_MODULE_FF_INTERVAL ("module_ff_interval ") +#define TOKEN_MODULE_FF_TYPE ("module_ff_type ") #define TOKEN_MACRO ("module_macro") #define TOKEN_NATIVE_ENCODING ("module_native_encoding") #define TOKEN_ALERT_TEMPLATE ("module_alert_template") @@ -176,7 +177,7 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { string module_unit, module_group, module_custom_id, module_str_warning, module_str_critical; string module_critical_instructions, module_warning_instructions, module_unknown_instructions, module_tags; string module_critical_inverse, module_warning_inverse, module_quiet, module_ff_interval; - string module_native_encoding, module_alert_template; + string module_native_encoding, module_alert_template, module_ff_type; string macro; Pandora_Module *module; bool numeric; @@ -254,6 +255,7 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { module_warning_inverse = ""; module_quiet = ""; module_ff_interval = ""; + module_ff_type = ""; module_native_encoding = ""; module_alert_template = ""; module_user_session = ""; @@ -507,6 +509,10 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { if (module_ff_interval == "") { module_ff_interval = parseLine (line, TOKEN_MODULE_FF_INTERVAL); } + + if (module_ff_type == "") { + module_ff_type = parseLine (line, TOKEN_MODULE_FF_TYPE); + } if (module_alert_template == "") { module_alert_template = parseLine (line, TOKEN_ALERT_TEMPLATE); @@ -1087,6 +1093,13 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { } } + if (module_ff_type != "") { + pos_macro = module_ff_type.find(macro_name); + if (pos_macro != string::npos){ + module_ff_type.replace(pos_macro, macro_name.size(), macro_value); + } + } + if (module_alert_template != "") { pos_macro = module_alert_template.find(macro_name); if (pos_macro != string::npos){ @@ -1104,6 +1117,12 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { } } + /* Skip disabled modules */ + if (module_disabled == "1") { + pandoraLog ("Skipping disabled module \"%s\"", module_name.c_str ()); + return NULL; + } + /* Create module objects */ if (module_exec != "") { module = new Pandora_Module_Exec (module_name, @@ -1447,6 +1466,10 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { if (module_ff_interval != "") { module->setModuleFFInterval (module_ff_interval); } + + if (module_ff_type != "") { + module->setModuleFFType (module_ff_type); + } if (module_alert_template != "") { module->setModuleAlertTemplate (module_alert_template); diff --git a/pandora_agents/win32/pandora.cc b/pandora_agents/win32/pandora.cc index 88a0ad00f3..42fdfc2d60 100644 --- a/pandora_agents/win32/pandora.cc +++ b/pandora_agents/win32/pandora.cc @@ -30,7 +30,7 @@ using namespace Pandora; using namespace Pandora_Strutils; #define PATH_SIZE _MAX_PATH+1 -#define PANDORA_VERSION ("7.0NG.732(Build 190319)") +#define PANDORA_VERSION ("7.0NG.735(Build 190610)") string pandora_path; string pandora_dir; diff --git a/pandora_agents/win32/versioninfo.rc b/pandora_agents/win32/versioninfo.rc index 759767d662..248f569dc1 100644 --- a/pandora_agents/win32/versioninfo.rc +++ b/pandora_agents/win32/versioninfo.rc @@ -11,7 +11,7 @@ BEGIN VALUE "LegalCopyright", "Artica ST" VALUE "OriginalFilename", "PandoraAgent.exe" VALUE "ProductName", "Pandora FMS Windows Agent" - VALUE "ProductVersion", "(7.0NG.732(Build 190319))" + VALUE "ProductVersion", "(7.0NG.735(Build 190610))" VALUE "FileVersion", "1.0.0.0" END END diff --git a/pandora_console/DEBIAN/control b/pandora_console/DEBIAN/control index cc50ca91fa..82f2c63b7c 100644 --- a/pandora_console/DEBIAN/control +++ b/pandora_console/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-console -Version: 7.0NG.732-190319 +Version: 7.0NG.735-190610 Architecture: all Priority: optional Section: admin diff --git a/pandora_console/DEBIAN/make_deb_package.sh b/pandora_console/DEBIAN/make_deb_package.sh index b373a17148..b47d90768f 100644 --- a/pandora_console/DEBIAN/make_deb_package.sh +++ b/pandora_console/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.732-190319" +pandora_version="7.0NG.735-190610" package_pear=0 package_pandora=1 diff --git a/pandora_console/composer.json b/pandora_console/composer.json index acc6a3e352..208e2d3d14 100644 --- a/pandora_console/composer.json +++ b/pandora_console/composer.json @@ -10,5 +10,16 @@ "require": { "mpdf/mpdf": "^7.1", "swiftmailer/swiftmailer": "^6.0" + }, + "autoload": { + "psr-4": { + "Models\\": "include/rest-api/models", + "Enterprise\\Models\\": "enterprise/include/rest-api/models" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } } } diff --git a/pandora_console/extensions/agents_alerts.php b/pandora_console/extensions/agents_alerts.php index 46272504f6..54e0ec6b12 100755 --- a/pandora_console/extensions/agents_alerts.php +++ b/pandora_console/extensions/agents_alerts.php @@ -138,8 +138,15 @@ function mainAgentsAlerts() $onheader['combo_refr'] = $comborefr; } - // Header - ui_print_page_header(__('Agents/Alerts'), 'images/op_alerts.png', false, '', false, $updated_time); + // Header. + ui_print_page_header( + __('Agents/Alerts'), + 'images/op_alerts.png', + false, + 'agents_alerts_view', + false, + $updated_time + ); // Old style table, we need a lot of special formatting,don't use table function // Prepare old-style table @@ -291,7 +298,7 @@ function mainAgentsAlerts() $table2->data[2][0] = __('Threshold'); $table2->data[2][1] = html_print_input_text('module_action_threshold', '0', '', 5, 7, true); - $table2->data[2][1] .= ' '.__('seconds').ui_print_help_icon('action_threshold', true); + $table2->data[2][1] .= ' '.__('seconds'); $content2 = '
'; $content2 .= html_print_table($table2, true); @@ -378,7 +385,8 @@ function mainAgentsAlerts() 'alerts_agents' ); - echo ''; + echo '
'; + echo ''; echo "'; if ($hor_offset > 0) { @@ -421,9 +429,10 @@ function mainAgentsAlerts() continue; } - echo ''; + echo ''; } + echo ''; if (($hor_offset + $block) < $ntemplates) { $new_hor_offset = ($hor_offset + $block); echo "
".__('Agents').' / '.__('Alert templates').''.io_safe_output($tname).html_print_image('images/information_alerts.png', true, ['title' => io_safe_output($tname), 'style' => 'margin-left:5px' ]).''.io_safe_output($tname).'
@@ -455,7 +464,7 @@ function mainAgentsAlerts() $cellstyle = 'background:'.COL_ALERTFIRED.';'; } - echo ' '; + echo ' '; $uniqid = uniqid(); echo '
'; @@ -478,6 +487,23 @@ function mainAgentsAlerts() } echo '
'; + + ui_pagination( + $nagents, + false, + 0, + 0, + false, + 'offset', + true, + 'pagination-bottom', + '', + [ + 'count' => '', + 'offset' => 'offset_param', + ], + 'alerts_agents' + ); } } @@ -487,7 +513,7 @@ function mainAgentsAlerts() function print_alerts_summary_modal_window($id, $alerts) { $table->width = '98%'; - $table->class = 'databox'; + $table->class = 'info_table'; $table->data = []; $table->head[0] = __('Module'); diff --git a/pandora_console/extensions/agents_modules.php b/pandora_console/extensions/agents_modules.php index 798f4e140e..b5860068d8 100644 --- a/pandora_console/extensions/agents_modules.php +++ b/pandora_console/extensions/agents_modules.php @@ -181,8 +181,15 @@ function mainAgentsModules() // Old style table, we need a lot of special formatting,don't use table function // Prepare old-style table if ($config['pure'] == 0) { - // Header - ui_print_page_header(__('Agents/Modules'), 'images/module_mc.png', false, '', false, $updated_time); + // Header. + ui_print_page_header( + __('Agents/Modules'), + 'images/module_mc.png', + false, + 'agents_module_view', + false, + $updated_time + ); echo ''; echo ''; echo "'; @@ -661,7 +668,7 @@ $ignored_params['refresh'] = ''; else { var agentes_id = $("#id_agents2").val(); - var id_agentes = $.get("full_agents_id"); + var id_agentes = getQueryParam("full_agents_id"); if (agentes_id === null && id_agentes !== null) { id_agentes = id_agentes.split(";") id_agentes.forEach(function(element) { @@ -799,7 +806,7 @@ $ignored_params['refresh'] = ''; $("#module").append (option); }); - var id_modules = $.get("full_modules_selected"); + var id_modules = getQueryParam("full_modules_selected"); if(id_modules !== null) { id_modules = id_modules.split(";"); id_modules.forEach(function(element) { @@ -812,20 +819,18 @@ $ignored_params['refresh'] = ''; ); } - (function($) { - $.get = function(key) { - key = key.replace(/[[]/, '['); - key = key.replace(/[]]/, ']'); - var pattern = "[?&]" + key + "=([^&#]*)"; - var regex = new RegExp(pattern); - var url = unescape(window.location.href); - var results = regex.exec(url); - if (results === null) { - return null; - } else { - return results[1]; - } - } - })(jQuery); + function getQueryParam (key) { + key = key.replace(/[[]/, '['); + key = key.replace(/[]]/, ']'); + var pattern = "[?&]" + key + "=([^&#]*)"; + var regex = new RegExp(pattern); + var url = unescape(window.location.href); + var results = regex.exec(url); + if (results === null) { + return null; + } else { + return results[1]; + } + } - \ No newline at end of file + diff --git a/pandora_console/extensions/db_status.php b/pandora_console/extensions/db_status.php index 435474dc53..d78ff90fc8 100755 --- a/pandora_console/extensions/db_status.php +++ b/pandora_console/extensions/db_status.php @@ -25,7 +25,7 @@ function extension_db_status() __('DB Schema check'), 'images/extensions.png', false, - '', + 'db_status_tab', true, '' ); @@ -215,7 +215,7 @@ function extension_db_check_tables_differences( $diff_tables = array_diff($tables_test, $tables_system); ui_print_result_message( - !empty($diff_tables), + empty($diff_tables), __('Success! %s DB contains all tables', get_product_name()), __( '%s DB could not retrieve all tables. The missing tables are (%s)', diff --git a/pandora_console/extensions/dbmanager.php b/pandora_console/extensions/dbmanager.php index 2aface6996..546ee463fa 100644 --- a/pandora_console/extensions/dbmanager.php +++ b/pandora_console/extensions/dbmanager.php @@ -148,7 +148,7 @@ function dbmgr_extension_main() echo "
"; $table = new stdClass(); $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->head = array_keys($result[0]); $table->data = $result; diff --git a/pandora_console/extensions/files_repo/files_repo_list.php b/pandora_console/extensions/files_repo/files_repo_list.php index 1d01cd1418..80de4563ae 100644 --- a/pandora_console/extensions/files_repo/files_repo_list.php +++ b/pandora_console/extensions/files_repo/files_repo_list.php @@ -45,11 +45,9 @@ if (!empty($files)) { $table = new stdClass(); $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->style = []; $table->style[1] = 'max-width: 200px;'; - $table->style[2] = 'text-align: center;'; - $table->style[3] = 'text-align: center;'; $table->style[4] = 'text-align: center;'; $table->head = []; $table->head[0] = __('Name'); @@ -81,6 +79,7 @@ if (!empty($files)) { // Last modification // Public URL $data[4] = ''; + $table->cellclass[][4] = 'action_buttons'; if (!empty($file['hash'])) { $public_url = ui_get_full_url(EXTENSIONS_DIR.'/files_repo/files_repo_get_file.php?file='.$file['hash']); $message = __('Copy to clipboard').': Ctrl+C -> Enter'; @@ -92,7 +91,7 @@ if (!empty($files)) { } $data[4] .= ""; - $data[4] .= html_print_image('images/download.png', true, ['title' => __('Download')]); + $data[4] .= html_print_image('images/download.png', true, ['title' => __('Download'), 'style' => 'padding:3px' ]); // Download image $data[4] .= ''; diff --git a/pandora_console/extensions/module_groups.php b/pandora_console/extensions/module_groups.php index 1a505538ed..9e750be932 100644 --- a/pandora_console/extensions/module_groups.php +++ b/pandora_console/extensions/module_groups.php @@ -192,7 +192,14 @@ function mainModuleGroups() $array_data[$value['id_grupo']][$value['id_mg']] = $value; } - ui_print_page_header(__('Combined table of agent group and module group'), 'images/module_group.png', false, '', false, ''); + ui_print_page_header( + __('Combined table of agent group and module group'), + 'images/module_group.png', + false, + 'module_groups_view', + false, + '' + ); echo "
".$fullscreen['text'].'
"; diff --git a/pandora_console/extensions/realtime_graphs.php b/pandora_console/extensions/realtime_graphs.php index 09d66abf0c..cea09fe4d5 100644 --- a/pandora_console/extensions/realtime_graphs.php +++ b/pandora_console/extensions/realtime_graphs.php @@ -29,7 +29,14 @@ function pandora_realtime_graphs() $hide_header = get_parameter('hide_header', 0); if (!$hide_header) { - ui_print_page_header(__('Realtime graphs'), 'images/extensions.png', false, '', false, $onheader); + ui_print_page_header( + __('Realtime graphs'), + 'images/extensions.png', + false, + 'real_time_view', + false, + $onheader + ); } $chart[time()]['graph'] = '0'; diff --git a/pandora_console/extensions/resource_registration.php b/pandora_console/extensions/resource_registration.php index 92d106d153..c43e2a97f2 100755 --- a/pandora_console/extensions/resource_registration.php +++ b/pandora_console/extensions/resource_registration.php @@ -998,20 +998,23 @@ function process_upload_xml($xml) { $hook_enterprise = enterprise_include('extensions/resource_registration/functions.php'); - // Extract component + // Extract component. process_upload_xml_component($xml); $group_filter = get_parameter('group'); - // Extract visual map + // Extract visual map. process_upload_xml_visualmap($xml, $group_filter); - // Extract policies + // Extract policies. if ($hook_enterprise === true) { - process_upload_xml_policy($xml, $group_filter); + $centralized_management = !is_central_policies_on_node(); + if ($centralized_management) { + process_upload_xml_policy($xml, $group_filter); + } } - // Extract reports + // Extract reports. process_upload_xml_report($xml, $group_filter); } @@ -1038,6 +1041,11 @@ function resource_registration_extension_main() return; } + $centralized_management = !is_central_policies_on_node(); + if (!$centralized_management) { + ui_print_warning_message(__('This node is configured with centralized mode. Go to metaconsole to create a policy.')); + } + echo '
'; echo __('This extension makes registering resource templates easier.').' '.__('Here you can upload a resource template in .ptr format.').' '.__('Please refer to our documentation for more information on how to obtain and use %s resources.', get_product_name()).' '.'

'.__("You can get more resurces in our Public Resource Library"); echo '
'; diff --git a/pandora_console/extensions/users_connected.php b/pandora_console/extensions/users_connected.php index 562d757a20..e26b0dab1e 100644 --- a/pandora_console/extensions/users_connected.php +++ b/pandora_console/extensions/users_connected.php @@ -66,10 +66,10 @@ function users_extension_main_god($god=true) $rows = []; echo "
".__('No other users connected').'
'; } else { - $table->cellpadding = 4; - $table->cellspacing = 4; + $table->cellpadding = 0; + $table->cellspacing = 0; $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->size = []; $table->data = []; $table->head = []; diff --git a/pandora_console/extras/delete_files/.gitignore b/pandora_console/extras/delete_files/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pandora_console/extras/delete_files/delete_files.txt b/pandora_console/extras/delete_files/delete_files.txt new file mode 100644 index 0000000000..82ce1527aa --- /dev/null +++ b/pandora_console/extras/delete_files/delete_files.txt @@ -0,0 +1,3 @@ +/general/login_identification_wizard.php +/general/login_required.php +/godmode/update_manager/update_manager.messages.php \ No newline at end of file diff --git a/pandora_console/extras/mr/26.sql b/pandora_console/extras/mr/26.sql index f9c904a0a3..591edd6499 100644 --- a/pandora_console/extras/mr/26.sql +++ b/pandora_console/extras/mr/26.sql @@ -1,8 +1,24 @@ START TRANSACTION; --- ---------------------------------------------------------------------- --- Add column in table `tagent_custom_fields` --- ---------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tnetwork_matrix` ( + `id` int(10) unsigned NOT NULL auto_increment, + `source` varchar(60) default '', + `destination` varchar(60) default '', + `utimestamp` bigint(20) default 0, + `bytes` int(18) unsigned default 0, + `pkts` int(18) unsigned default 0, + PRIMARY KEY (`id`), + UNIQUE (`source`, `destination`, `utimestamp`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8 ; + +ALTER TABLE `treport_content` ADD COLUMN `show_extended_events` tinyint(1) default '0'; + +UPDATE `treport_content` SET type="netflow_summary" WHERE type="netflow_pie" OR type="netflow_statistics"; + +UPDATE `tnetflow_filter` SET aggregate="dstip" WHERE aggregate NOT IN ("dstip", "srcip", "dstport", "srcport"); + ALTER TABLE tagent_custom_fields ADD COLUMN `combo_values` VARCHAR(255) DEFAULT ''; +ALTER TABLE `trecon_task` ADD COLUMN `summary` text; + COMMIT; diff --git a/pandora_console/extras/mr/27.sql b/pandora_console/extras/mr/27.sql new file mode 100644 index 0000000000..697be21a93 --- /dev/null +++ b/pandora_console/extras/mr/27.sql @@ -0,0 +1,54 @@ +START TRANSACTION; + +ALTER TABLE `tnetflow_filter` DROP COLUMN `output`; + +ALTER TABLE `tagente_modulo` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; +ALTER TABLE `tnetwork_component` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; +ALTER TABLE `tlocal_component` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; +ALTER TABLE `tpolicy_modules` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; + +ALTER TABLE `tagente_estado` ADD COLUMN `ff_normal` int(4) unsigned default '0'; +ALTER TABLE `tagente_estado` ADD COLUMN `ff_warning` int(4) unsigned default '0'; +ALTER TABLE `tagente_estado` ADD COLUMN `ff_critical` int(4) unsigned default '0'; + +UPDATE tuser_task SET parameters = 'a:5:{i:0;a:6:{s:11:\"description\";s:28:\"Report pending to be created\";s:5:\"table\";s:7:\"treport\";s:8:\"field_id\";s:9:\"id_report\";s:10:\"field_name\";s:4:\"name\";s:4:\"type\";s:3:\"int\";s:9:\"acl_group\";s:8:\"id_group\";}i:1;a:2:{s:11:\"description\";s:46:\"Send to email addresses (separated by a comma)\";s:4:\"type\";s:4:\"text\";}i:2;a:2:{s:11:\"description\";s:7:\"Subject\";s:8:\"optional\";i:1;}i:3;a:3:{s:11:\"description\";s:7:\"Message\";s:4:\"type\";s:4:\"text\";s:8:\"optional\";i:1;}i:4;a:2:{s:11:\"description\";s:11:\"Report Type\";s:4:\"type\";s:11:\"report_type\";}}' where function_name = "cron_task_generate_report"; + +ALTER TABLE `treport_content` ADD COLUMN `total_time` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_in_unknown_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_of_not_initialized_module` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_of_downtime` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `total_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `checks_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `checks_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `unknown_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `agent_max_value` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `agent_min_value` TINYINT(1) DEFAULT '1'; + +ALTER TABLE `treport_content_template` ADD COLUMN `total_time` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_in_unknown_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_of_not_initialized_module` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_of_downtime` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `total_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `checks_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `checks_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `unknown_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `agent_max_value` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `agent_min_value` TINYINT(1) DEFAULT '1'; + +ALTER TABLE `trecon_script` ADD COLUMN `type` int NOT NULL default 0; +ALTER TABLE `trecon_task` ADD COLUMN `type` int NOT NULL default 0; + +UPDATE `trecon_script` SET `type` = 1 WHERE `name`="Discovery.Application.VMware"; +UPDATE `trecon_script` SET `type` = 2 WHERE `name`="Discovery.Cloud"; +UPDATE `trecon_script` SET `type` = 3 WHERE `name` LIKE "IPAM%Recon"; +UPDATE `trecon_script` SET `type` = 4 WHERE `name` LIKE "IPMI%Recon"; + +UPDATE `trecon_task` SET `type`=3 WHERE `description`="Discovery.Application.VMware"; +UPDATE `trecon_task` SET `type`=2 WHERE `description`="Discovery.Cloud"; +UPDATE `trecon_task` SET `type`=7 WHERE `description`="Discovery.Cloud.RDS"; + +COMMIT; diff --git a/pandora_console/extras/mr/28.sql b/pandora_console/extras/mr/28.sql new file mode 100644 index 0000000000..bab07bead8 --- /dev/null +++ b/pandora_console/extras/mr/28.sql @@ -0,0 +1,45 @@ +START TRANSACTION; + +ALTER TABLE `treport_content` ADD COLUMN `current_month` TINYINT(1) DEFAULT '1'; + +ALTER TABLE `treport_content_template` ADD COLUMN `current_month` TINYINT(1) DEFAULT '1'; + +ALTER TABLE `talert_commands` ADD COLUMN `fields_hidden` text; + +ALTER TABLE `talert_templates` MODIFY COLUMN `type` ENUM('regex','max_min','max','min','equal','not_equal','warning','critical','onchange','unknown','always','not_normal'); + +DELETE FROM `tevent_response` WHERE `name` LIKE 'Create Integria IMS incident from event'; +INSERT INTO `tnews` (`id_news`, `author`, `subject`, `text`, `timestamp`) VALUES (NULL,'admin','Welcome to Pandora FMS Console', '&lt;p style="text-align: center; font-size: 13px;"&gt;Hello, congratulations, if you've arrived here you already have an operational monitoring console. Remember that our forums and online documentation are available 24x7 to get you out of any trouble. You can replace this message with a personalized one at Admin tools -&amp;gt; Site news.&lt;/p&gt; ',NOW()); + + +INSERT INTO `tnotification_source_user` (`id_source`, `id_user`, `enabled`, `also_mail`) VALUES ((SELECT `id` FROM `tnotification_source` WHERE `description`="Official communication"), "admin", 1, 0); +UPDATE `tnotification_source` SET `enabled`=1 WHERE `description` = 'System status' OR `description` = 'Official communication'; +UPDATE `tnotification_source` SET `icon`="icono_logo_pandora.png" WHERE `description` = 'Official communication'; + +-- --------------------------------------------------------------------- +-- Table `tvisual_console_items_cache` +-- --------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tvisual_console_elements_cache` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `vc_id` INTEGER UNSIGNED NOT NULL, + `vc_item_id` INTEGER UNSIGNED NOT NULL, + `user_id` VARCHAR(60) DEFAULT NULL, + `data` TEXT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `expiration` INTEGER UNSIGNED NOT NULL COMMENT 'Seconds to expire', + PRIMARY KEY(`id`), + FOREIGN KEY(`vc_id`) REFERENCES `tlayout`(`id`) + ON DELETE CASCADE, + FOREIGN KEY(`vc_item_id`) REFERENCES `tlayout_data`(`id`) + ON DELETE CASCADE, + FOREIGN KEY (`user_id`) REFERENCES `tusuario`(`id_user`) + ON DELETE CASCADE ON UPDATE CASCADE +) engine=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `tlayout_data` ADD COLUMN `cache_expiration` INTEGER UNSIGNED NOT NULL DEFAULT 0; + +ALTER TABLE `tusuario` ADD COLUMN `ehorus_user_level_user` VARCHAR(60); +ALTER TABLE `tusuario` ADD COLUMN `ehorus_user_level_pass` VARCHAR(45); +ALTER TABLE `tusuario` ADD COLUMN `ehorus_user_level_enabled` TINYINT(1) DEFAULT '1'; + +COMMIT; diff --git a/pandora_console/extras/mr/29.sql b/pandora_console/extras/mr/29.sql new file mode 100644 index 0000000000..032dd407b1 --- /dev/null +++ b/pandora_console/extras/mr/29.sql @@ -0,0 +1,6 @@ +START TRANSACTION; + +DELETE FROM `ttipo_modulo` WHERE `nombre` LIKE 'log4x'; + + +COMMIT; diff --git a/pandora_console/extras/pandora_diag.php b/pandora_console/extras/pandora_diag.php index d45070c3f8..de2eb67b89 100644 --- a/pandora_console/extras/pandora_diag.php +++ b/pandora_console/extras/pandora_diag.php @@ -122,7 +122,7 @@ function execution_time() if ($times[0]['datos'] > ($times[1]['datos'] * 1.2)) { return "Warning Status   The execution time could be degrading. For a more extensive information of this data consult the Execution Time graph"; } else { - return "Normal Status   The execution time is correct. For a more extensive information of this data consult the Execution Time graph"; + return "Normal Status   The execution time is correct. For more information about this data, check the Execution Time graph"; } } @@ -157,9 +157,9 @@ function percentage_modules_per_agent() $total_modules = db_get_value_sql('SELECT count(*) FROM tagente_modulo'); $average_modules_per_agent = ($total_modules / $total_agents); if ($average_modules_per_agent <= 40) { - $status_average_modules = "Normal Status   The average of modules per agent is less than 40 percent"; + $status_average_modules = "Normal Status   The average of modules per agent is less than 40"; } else { - $status_average_modules = "Warning Status  The average of modules per agent is more than 40 percent. You can have performance problems"; + $status_average_modules = "Warning Status  The average of modules per agent is more than 40. You can have performance problems"; } return $status_average_modules; @@ -173,9 +173,9 @@ function license_capacity() $status_license_capacity = ''; $current_count = db_get_value_sql('SELECT count(*) FROM tagente'); if ($current_count > ($license_limit * 90 / 100)) { - $status_license_capacity = "Warning Status   The license capacity is more than 90 percent"; + $status_license_capacity = "Warning Status   License capacity exceeds 90 percent"; } else { - $status_license_capacity = "Normal Status   The license capacity is less than 90 percent"; + $status_license_capacity = "Normal Status   License capacity is less than 90 percent"; } return $status_license_capacity; @@ -202,9 +202,9 @@ function interval_average_of_network_modules() $average_time = ((int) $total_module_interval_time / $total_network_modules); if ($average_time < 180) { - $status_average_modules = "Warning Status   The system has a lot of load and a very fine configuration is required"; + $status_average_modules = "Warning Status   The system is overloaded (average time $average_time) and a very fine configuration is required"; } else { - $status_average_modules = "Normal Status   The system has an acceptable charge"; + $status_average_modules = "Normal Status   The system is not overloaded (average time $average_time) "; } if ($average_time == 0) { @@ -221,9 +221,9 @@ $attachment_total_files = count(glob($config['homedir'].'/attachment/{*.*}', GLO function files_attachment_folder($total_files) { if ($total_files <= 700) { - $status_total_files = "Normal Status   The attachment folder has less than 700 files."; + $status_total_files = "Normal Status   The attached folder contains less than 700 files."; } else { - $status_total_files = "Warning Status   The attachment folder has more than 700 files."; + $status_total_files = "Warning Status   The attached folder contains more than 700 files."; } return $status_total_files; @@ -236,9 +236,9 @@ $tagente_datos_size = db_get_value_sql('SELECT COUNT(*) FROM tagente_datos'); function status_tagente_datos($tagente_datos_size) { if ($tagente_datos_size <= 3000000) { - $tagente_datos_size = "Normal Status   The tagente_datos table has an acceptable amount of data."; + $tagente_datos_size = "Normal Status   The tagente_datos table contains an acceptable amount of data."; } else { - $tagente_datos_size = "Warning Status   The tagente_datos table has too much data. A historical database is recommended."; + $tagente_datos_size = "Warning Status   The tagente_datos table contains too much data. A historical database is recommended."; } return $tagente_datos_size; @@ -274,6 +274,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { } $path_server_logs = '/log/pandora/pandora_server.log'; +$path_err_logs = '/log/pandora/pandora_server.error'; $path_console_logs = '/www/html/pandora_console/pandora_console.log'; $innodb_log_file_size_min_rec_value = '64M'; $innodb_log_buffer_size_min_rec_value = '16M'; @@ -294,16 +295,16 @@ $key_buffer_size_min_rec_value = 256; $read_buffer_size_min_rec_value = 32; $read_rnd_buffer_size_min_rec_value = 32; $query_cache_min_res_unit_min_rec_value = 2; -$innodb_file_per_table_min_rec_value = 0; +$innodb_file_per_table_min_rec_value = 1; function status_fragmentation_tables($tables_fragmentation_max_rec_value, $tables_fragmentation) { $status_tables_frag = ''; if ($tables_fragmentation > $tables_fragmentation_max_rec_value) { - $status_tables_frag = "Warning Status   The fragmentation tables is higher than recommended. You should defragment them."; + $status_tables_frag = "Warning Status   Table fragmentation is higher than recommended. They should be defragmented."; } else { - $status_tables_frag = "Normal Status   The fragmentation tables is correct."; + $status_tables_frag = "Normal Status   Table fragmentation is correct."; } return $status_tables_frag; @@ -319,9 +320,9 @@ if ($console_mode == 1) { echo "\nPandora FMS PHP diagnostic tool v3.2 (c) Artica ST 2009-2010 \n"; if ($argc == 1 || in_array($argv[1], ['--help', '-help', '-h', '-?'])) { - echo "\nThis command line script gives information about Pandora FMS database. -This program only can be executed from console, and need a parameter, the -full path to Pandora FMS 'config.php' file. + echo "\nThis command line script contains information about Pandora FMS database. + This program can only be executed from the console, and it needs a parameter, the + full path to Pandora FMS 'config.php' file. Usage: php pandora_diag.php path_to_pandora_console @@ -344,15 +345,21 @@ full path to Pandora FMS 'config.php' file. include '../include/config.php'; } - // Not from console, this is a web session - if ((!isset($config['id_user'])) or (!check_acl($config['id_user'], 0, 'PM'))) { + // Not from console, this is a web session. + if ((!isset($config['id_user'])) || (!check_acl($config['id_user'], 0, 'PM'))) { echo "

You don't have privileges to use diagnostic tool

"; echo '

Please login with an administrator account before try to use this tool

'; exit; } - // Header - ui_print_page_header(__('Pandora FMS Diagnostic tool'), '', false, '', true); + // Header. + ui_print_page_header( + __('Pandora FMS Diagnostic tool'), + '', + false, + 'diagnostic_tool_tab', + true + ); echo "
"; echo "'; @@ -677,7 +684,7 @@ render_info_data( $read_rnd_buffer_size = (db_get_value_sql('SELECT @@read_rnd_buffer_size') / 1024); $query_cache_min_res_unit = (db_get_value_sql('SELECT @@query_cache_min_res_unit') / 1024); $innodb_file_per_table = db_get_value_sql('SELECT @@innodb_file_per_table'); - echo "'; + echo "'; render_row(status_values($innodb_log_file_size_min_rec_value, $innodb_log_file_size), 'InnoDB log file size ', 'InnoDB log file size '); render_row(status_values($innodb_log_buffer_size_min_rec_value, $innodb_log_buffer_size), 'InnoDB log buffer size ', 'InnoDB log buffer size '); @@ -704,12 +711,14 @@ render_info_data( render_row($tables_fragmentation_max_rec_value.'%', 'Tables fragmentation (maximum recommended value)'); render_row(number_format($tables_fragmentation, 2).'%', 'Tables fragmentation (current value)'); - render_row(status_fragmentation_tables($tables_fragmentation_max_rec_value, $tables_fragmentation), 'Status fragmentation tables'); + render_row(status_fragmentation_tables($tables_fragmentation_max_rec_value, $tables_fragmentation), 'Table fragmentation status'); echo "'; render_row(number_format((get_logs_size($path_server_logs) / 1048576), 3).'M', 'Size server logs (current value)'); render_row(get_status_logs($path_server_logs), 'Status server logs'); + render_row(number_format((get_logs_size($path_err_logs) / 1048576), 3).'M', 'Size error logs (current value)'); + render_row(get_status_logs($path_err_logs), 'Status error logs'); render_row(number_format((get_logs_size($path_console_logs) / 1048576), 3).'M', 'Size console logs (current value)'); render_row(get_status_logs($path_console_logs), 'Status console logs'); @@ -731,14 +740,14 @@ render_info_data( echo "'; - render_row($attachment_total_files, 'Total files in the attachment folder'); + render_row($attachment_total_files, 'Total files in the attached folder'); render_row(files_attachment_folder($attachment_total_files), 'Status of the attachment folder'); echo "'; - render_row($tagente_datos_size, 'Total data in the tagente_datos table'); - render_row(status_tagente_datos($tagente_datos_size), 'Status of the tagente_datos table'); - render_row(execution_time(), 'Degradation of the execution time when executing a count'); + render_row($tagente_datos_size, 'Total data in tagente_datos table'); + render_row(status_tagente_datos($tagente_datos_size), 'Tangente_datos table status'); + render_row(execution_time(), 'Execution time degradation when executing a count'); echo "'; @@ -781,6 +790,6 @@ render_info_data( echo "
"; echo ''.__( - '(*) Please check your Pandora Server setup and be sure that database maintenance daemon is running. It\' very important to -keep up-to-date database to get the best performance and results in Pandora' + '(*) Please check your Pandora Server setup and make sure that the database maintenance daemon is running. It\' is very important to + keep the database up-to-date to get the best performance and results in Pandora' ).'


'; diff --git a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql index e7cedb62f5..2c9ff7e7de 100644 --- a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql +++ b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql @@ -58,6 +58,7 @@ CREATE TABLE IF NOT EXISTS `tlocal_component` ( ALTER TABLE `tlocal_component` ADD COLUMN `dynamic_next` bigint(20) NOT NULL default '0'; ALTER TABLE `tlocal_component` ADD COLUMN `dynamic_two_tailed` tinyint(1) unsigned default '0'; +ALTER TABLE `tlocal_component` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; -- ----------------------------------------------------- -- Table `tpolicy_modules` @@ -136,6 +137,7 @@ CREATE TABLE IF NOT EXISTS `tpolicy_modules` ( ALTER TABLE `tpolicy_modules` ADD COLUMN `dynamic_next` bigint(20) NOT NULL default '0'; ALTER TABLE `tpolicy_modules` ADD COLUMN `dynamic_two_tailed` tinyint(1) unsigned default '0'; +ALTER TABLE `tpolicy_modules` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; -- --------------------------------------------------------------------- -- Table `tpolicies` @@ -759,11 +761,41 @@ CREATE TABLE IF NOT EXISTS `treport_content_template` ( PRIMARY KEY(`id_rc`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8; +-- ---------------------------------------------------------------------- +-- Table `tnews` +-- ---------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tnews` ( + `id_news` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `author` varchar(255) NOT NULL DEFAULT '', + `subject` varchar(255) NOT NULL DEFAULT '', + `text` TEXT NOT NULL, + `timestamp` DATETIME NOT NULL DEFAULT 0, + `id_group` int(10) NOT NULL default 0, + `modal` tinyint(1) DEFAULT 0, + `expire` tinyint(1) DEFAULT 0, + `expire_timestamp` DATETIME NOT NULL DEFAULT 0, + PRIMARY KEY(`id_news`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8; + + ALTER TABLE treport_content_template ADD COLUMN `historical_db` tinyint(1) NOT NULL DEFAULT '0'; ALTER TABLE treport_content_template ADD COLUMN `lapse_calc` tinyint(1) default '0'; ALTER TABLE treport_content_template ADD COLUMN `lapse` int(11) default '300'; ALTER TABLE treport_content_template ADD COLUMN `visual_format` tinyint(1) default '0'; ALTER TABLE treport_content_template ADD COLUMN `hide_no_data` tinyint(1) default '0'; +ALTER TABLE `treport_content_template` ADD COLUMN `total_time` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_in_unknown_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_of_not_initialized_module` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `time_of_downtime` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `total_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `checks_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `checks_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `unknown_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `agent_max_value` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `agent_min_value` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content_template` ADD COLUMN `current_month` TINYINT(1) DEFAULT '1'; -- ----------------------------------------------------- -- Table `treport_content_sla_com_temp` (treport_content_sla_combined_template) @@ -1150,6 +1182,9 @@ ALTER TABLE tagente_estado MODIFY `status_changes` tinyint(4) unsigned default 0 ALTER TABLE tagente_estado CHANGE `last_known_status` `known_status` tinyint(4) default 0; ALTER TABLE tagente_estado ADD COLUMN `last_known_status` tinyint(4) default 0; ALTER TABLE tagente_estado ADD COLUMN last_unknown_update bigint(20) NOT NULL default 0; +ALTER TABLE `tagente_estado` ADD COLUMN `ff_normal` int(4) unsigned default '0'; +ALTER TABLE `tagente_estado` ADD COLUMN `ff_warning` int(4) unsigned default '0'; +ALTER TABLE `tagente_estado` ADD COLUMN `ff_critical` int(4) unsigned default '0'; -- --------------------------------------------------------------------- -- Table `talert_actions` @@ -1180,6 +1215,7 @@ ALTER TABLE talert_actions ADD COLUMN `field15_recovery` TEXT NOT NULL DEFAULT " UPDATE `talert_commands` SET `fields_descriptions` = '[\"Integria IMS API path\",\"Integria IMS API pass\",\"Integria IMS user\",\"Integria IMS user pass\",\"Ticket title\",\"Ticket group ID\",\"Ticket priority\",\"Email copy\",\"Ticket owner\",\"Ticket description\"]', `fields_values` = '[\"\",\"\",\"\",\"\",\"\",\"\",\"10,Maintenance;0,Informative;1,Low;2,Medium;3,Serious;4,Very Serious\",\"\",\"\",\"\"]' WHERE `id` = 11 AND `name` = 'Integria IMS Ticket'; UPDATE `talert_commands` SET `description` = 'This alert send an email using internal Pandora FMS Server SMTP capabilities (defined in each server, using: _field1_ as destination email address, and _field2_ as subject for message. _field3_ as text of message. _field4_ as content type (text/plain or html/text).', `fields_descriptions` = '[\"Destination address\",\"Subject\",\"Text\",\"Content Type\",\"\",\"\",\"\",\"\",\"\",\"\"]', `fields_values` = '[\"\",\"\",\"_html_editor_\",\"_content_type_\",\"\",\"\",\"\",\"\",\"\",\"\"]' WHERE id=1; ALTER TABLE `talert_commands` ADD COLUMN `id_group` mediumint(8) unsigned NULL default 0; +ALTER TABLE `talert_commands` ADD COLUMN `fields_hidden` text; UPDATE `talert_actions` SET `field4` = 'text/html', `field4_recovery` = 'text/html' WHERE id = 1; @@ -1199,13 +1235,13 @@ ALTER TABLE titem MODIFY `source_data` int(10) unsigned; INSERT INTO `tconfig` (`token`, `value`) VALUES ('big_operation_step_datos_purge', '100'); INSERT INTO `tconfig` (`token`, `value`) VALUES ('small_operation_step_datos_purge', '1000'); INSERT INTO `tconfig` (`token`, `value`) VALUES ('days_autodisable_deletion', '30'); -INSERT INTO `tconfig` (`token`, `value`) VALUES ('MR', 26); +INSERT INTO `tconfig` (`token`, `value`) VALUES ('MR', 28); INSERT INTO `tconfig` (`token`, `value`) VALUES ('custom_docs_logo', 'default_docs.png'); INSERT INTO `tconfig` (`token`, `value`) VALUES ('custom_support_logo', 'default_support.png'); INSERT INTO `tconfig` (`token`, `value`) VALUES ('custom_logo_white_bg_preview', 'pandora_logo_head_white_bg.png'); UPDATE tconfig SET value = 'https://licensing.artica.es/pandoraupdate7/server.php' WHERE token='url_update_manager'; DELETE FROM `tconfig` WHERE `token` = 'current_package_enterprise'; -INSERT INTO `tconfig` (`token`, `value`) VALUES ('current_package_enterprise', '733'); +INSERT INTO `tconfig` (`token`, `value`) VALUES ('current_package_enterprise', '735'); INSERT INTO `tconfig` (`token`, `value`) VALUES ('status_monitor_fields', 'policy,agent,data_type,module_name,server_type,interval,status,graph,warn,data,timestamp'); -- --------------------------------------------------------------------- @@ -1252,6 +1288,11 @@ alter table tusuario add autorefresh_white_list text not null default ''; ALTER TABLE tusuario ADD COLUMN `time_autorefresh` int(5) unsigned NOT NULL default '30'; ALTER TABLE `tusuario` DROP COLUMN `flash_chart`; ALTER TABLE `tusuario` ADD COLUMN `default_custom_view` int(10) unsigned NULL default '0'; +ALTER TABLE `tusuario` ADD COLUMN `ehorus_user_level_user` VARCHAR(60); +ALTER TABLE `tusuario` ADD COLUMN `ehorus_user_level_pass` VARCHAR(45); +ALTER TABLE `tusuario` ADD COLUMN `ehorus_user_level_enabled` TINYINT(1); + + -- --------------------------------------------------------------------- -- Table `tagente_modulo` @@ -1260,6 +1301,10 @@ ALTER TABLE tagente_modulo ADD COLUMN `dynamic_next` bigint(20) NOT NULL default ALTER TABLE tagente_modulo ADD COLUMN `dynamic_two_tailed` tinyint(1) unsigned default '0'; ALTER TABLE tagente_modulo ADD COLUMN `parent_module_id` int(10) unsigned NOT NULL default 0; ALTER TABLE `tagente_modulo` ADD COLUMN `cps` int NOT NULL default 0; +ALTER TABLE `tagente_modulo` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; +ALTER TABLE `tagente_modulo` ADD COLUMN `ff_normal` int(4) unsigned default '0'; +ALTER TABLE `tagente_modulo` ADD COLUMN `ff_warning` int(4) unsigned default '0'; +ALTER TABLE `tagente_modulo` ADD COLUMN `ff_critical` int(4) unsigned default '0'; -- --------------------------------------------------------------------- -- Table `tagente_datos` @@ -1279,6 +1324,7 @@ ALTER TABLE tnetwork_component ADD COLUMN `dynamic_max` int(4) default '0'; ALTER TABLE tnetwork_component ADD COLUMN `dynamic_min` int(4) default '0'; ALTER TABLE tnetwork_component ADD COLUMN `dynamic_next` bigint(20) NOT NULL default '0'; ALTER TABLE tnetwork_component ADD COLUMN `dynamic_two_tailed` tinyint(1) unsigned default '0'; +ALTER TABLE `tnetwork_component` ADD COLUMN `ff_type` tinyint(1) unsigned default '0'; -- --------------------------------------------------------------------- -- Table `tagente` @@ -1329,6 +1375,7 @@ ALTER TABLE `tlayout_data` ADD COLUMN `linked_layout_status_type` ENUM ('default ALTER TABLE `tlayout_data` ADD COLUMN `linked_layout_status_as_service_warning` FLOAT(20, 3) NOT NULL default 0; ALTER TABLE `tlayout_data` ADD COLUMN `linked_layout_status_as_service_critical` FLOAT(20, 3) NOT NULL default 0; ALTER TABLE `tlayout_data` ADD COLUMN `linked_layout_node_id` INT(10) NOT NULL default 0; +ALTER TABLE `tlayout_data` ADD COLUMN `cache_expiration` INTEGER UNSIGNED NOT NULL DEFAULT 0; -- --------------------------------------------------------------------- -- Table `tagent_custom_fields` @@ -1355,6 +1402,7 @@ ALTER TABLE tgraph ADD COLUMN `fullscale` tinyint(1) UNSIGNED NOT NULL default ' -- Table `tnetflow_filter` -- --------------------------------------------------------------------- ALTER TABLE tnetflow_filter ADD COLUMN `router_ip` TEXT NOT NULL DEFAULT ""; +UPDATE `tnetflow_filter` SET aggregate="dstip" WHERE aggregate NOT IN ("dstip", "srcip", "dstport", "srcport"); -- --------------------------------------------------------------------- -- Table `treport_custom_sql` @@ -1369,13 +1417,27 @@ UPDATE treport_custom_sql SET `sql` = 'select t1.alias as agent_n -- ---------------------------------------------------------------------- -- Table `treport_content` -- --------------------------------------------------------------------- - ALTER TABLE treport_content ADD COLUMN `historical_db` tinyint(1) NOT NULL DEFAULT '0'; ALTER TABLE treport_content ADD COLUMN `lapse_calc` tinyint(1) default '0'; ALTER TABLE treport_content ADD COLUMN `lapse` int(11) default '300'; ALTER TABLE treport_content ADD COLUMN `visual_format` tinyint(1) default '0'; ALTER TABLE treport_content ADD COLUMN `hide_no_data` tinyint(1) default '0'; ALTER TABLE treport_content ADD COLUMN `recursion` tinyint(1) default NULL; +ALTER TABLE treport_content ADD COLUMN `show_extended_events` tinyint(1) default '0'; +UPDATE `treport_content` SET type="netflow_summary" WHERE type="netflow_pie" OR type="netflow_statistics"; +ALTER TABLE `treport_content` ADD COLUMN `total_time` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_in_unknown_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_of_not_initialized_module` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `time_of_downtime` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `total_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `checks_failed` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `checks_in_ok_status` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `unknown_checks` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `agent_max_value` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `agent_min_value` TINYINT(1) DEFAULT '1'; +ALTER TABLE `treport_content` ADD COLUMN `current_month` TINYINT(1) DEFAULT '1'; -- --------------------------------------------------------------------- -- Table `tmodule_relationship` @@ -1410,6 +1472,7 @@ ALTER TABLE trecon_task ADD `vlan_enabled` int(2) unsigned default '0'; ALTER TABLE trecon_task ADD `wmi_enabled` tinyint(1) unsigned DEFAULT '0'; ALTER TABLE trecon_task ADD `auth_strings` text; ALTER TABLE trecon_task ADD `autoconfiguration_enabled` tinyint(1) unsigned default '0'; +ALTER TABLE trecon_task ADD `summary` text; -- --------------------------------------------------------------------- -- Table `twidget` AND Table `twidget_dashboard` @@ -1912,6 +1975,47 @@ CREATE TABLE `tgis_map_layer_groups` ( CONSTRAINT `tgis_map_layer_groups_ibfk_3` FOREIGN KEY (`agent_id`) REFERENCES `tagente` (`id_agente`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- ----------------------------------------------------- +-- Table `tnetwork_matrix` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tnetwork_matrix` ( + `id` int(10) unsigned NOT NULL auto_increment, + `source` varchar(60) default '', + `destination` varchar(60) default '', + `utimestamp` bigint(20) default 0, + `bytes` int(18) unsigned default 0, + `pkts` int(18) unsigned default 0, + PRIMARY KEY (`id`), + UNIQUE (`source`, `destination`, `utimestamp`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8 ; + +-- --------------------------------------------------------------------- +-- Table `user_task` +-- --------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tuser_task` ( + `id` int(20) unsigned NOT NULL auto_increment, + `function_name` varchar(80) NOT NULL default '', + `parameters` text NOT NULL default '', + `name` varchar(60) NOT NULL default '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- --------------------------------------------------------------------- +-- Table `user_task_scheduled` +-- --------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tuser_task_scheduled` ( + `id` int(20) unsigned NOT NULL auto_increment, + `id_usuario` varchar(60) NOT NULL default '0', + `id_user_task` int(20) unsigned NOT NULL default '0', + `args` TEXT NOT NULL, + `scheduled` enum('no','hourly','daily','weekly','monthly','yearly','custom') default 'no', + `last_run` int(20) unsigned default '0', + `custom_data` int(10) NULL default '0', + `flag_delete` tinyint(1) UNSIGNED NOT NULL default 0, + `id_grupo` int(10) unsigned NOT NULL default 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + -- ----------------------------------------------------- -- Table `tnotification_source` -- ----------------------------------------------------- @@ -1934,7 +2038,7 @@ INSERT INTO `tnotification_source`(`description`, `icon`, `max_postpone_time`, ` ("Message", "icono_info_mr.png", 86400, 1, 1, 0), ("Pending task", "icono_info_mr.png", 86400, 1, 1, 0), ("Advertisement", "icono_info_mr.png", 86400, 1, 1, 0), - ("Official communication", "icono_info_mr.png", 86400, 1, 1, 0), + ("Official communication", "icono_logo_pandora.png", 86400, 1, 1, 0), ("Sugerence", "icono_info_mr.png", 86400, 1, 1, 0); -- ----------------------------------------------------- @@ -2031,6 +2135,9 @@ INSERT INTO `talert_commands` (`name`, `command`, `description`, `internal`, `fi INSERT INTO `tnotification_source_user` (`id_source`, `id_user`, `enabled`, `also_mail`) VALUES ((SELECT `id` FROM `tnotification_source` WHERE `description`="System status"), "admin", 1, 0); INSERT INTO `tnotification_source_group` SELECT `id`,0 FROM `tnotification_source` WHERE `description`="Message"; INSERT INTO `tnotification_user` (`id_mensaje`, `id_user`) SELECT `id_mensaje`, `id_usuario_destino` FROM `tmensajes` WHERE `id_usuario_destino` != ''; +INSERT INTO `tnotification_source_user` (`id_source`, `id_user`, `enabled`, `also_mail`) VALUES ((SELECT `id` FROM `tnotification_source` WHERE `description`="Official communication"), "admin", 1, 0); +UPDATE `tnotification_source` SET `enabled`=1 WHERE `description` = 'System status' OR `description` = 'Official communication'; + -- ---------------------------------------------------------------------- -- Add custom internal recon scripts -- ---------------------------------------------------------------------- @@ -2040,3 +2147,48 @@ INSERT INTO `trecon_script` (`name`,`description`,`script`,`macros`) VALUES ('Di -- Add column in table `tagent_custom_fields` -- ---------------------------------------------------------------------- ALTER TABLE tagent_custom_fields ADD COLUMN `combo_values` VARCHAR(255) DEFAULT ''; + +-- ---------------------------------------------------------------------- +-- Add column in table `tnetflow_filter` +-- ---------------------------------------------------------------------- +ALTER TABLE `tnetflow_filter` DROP COLUMN `output`; + + +-- ---------------------------------------------------------------------- +-- Update table `tuser_task` +-- ---------------------------------------------------------------------- +UPDATE tuser_task set parameters = 'a:5:{i:0;a:6:{s:11:\"description\";s:28:\"Report pending to be created\";s:5:\"table\";s:7:\"treport\";s:8:\"field_id\";s:9:\"id_report\";s:10:\"field_name\";s:4:\"name\";s:4:\"type\";s:3:\"int\";s:9:\"acl_group\";s:8:\"id_group\";}i:1;a:2:{s:11:\"description\";s:46:\"Send to email addresses (separated by a comma)\";s:4:\"type\";s:4:\"text\";}i:2;a:2:{s:11:\"description\";s:7:\"Subject\";s:8:\"optional\";i:1;}i:3;a:3:{s:11:\"description\";s:7:\"Message\";s:4:\"type\";s:4:\"text\";s:8:\"optional\";i:1;}i:4;a:2:{s:11:\"description\";s:11:\"Report Type\";s:4:\"type\";s:11:\"report_type\";}}' where function_name = "cron_task_generate_report"; + +-- ---------------------------------------------------------------------- +-- ADD message in table 'tnews' +-- ---------------------------------------------------------------------- + +INSERT INTO `tnews` (`id_news`, `author`, `subject`, `text`, `timestamp`) VALUES (NULL,'admin','Welcome to Pandora FMS Console', '&lt;p style="text-align: center; font-size: 13px;"&gt;Hello, congratulations, if you've arrived here you already have an operational monitoring console. Remember that our forums and online documentation are available 24x7 to get you out of any trouble. You can replace this message with a personalized one at Admin tools -&amp;gt; Site news.&lt;/p&gt; ',NOW()); + +-- ---------------------------------------------------------------------- +-- Alter table `talert_templates` +-- ---------------------------------------------------------------------- + + ALTER TABLE `talert_templates` MODIFY COLUMN `type` ENUM('regex','max_min','max','min','equal','not_equal','warning','critical','onchange','unknown','always','not_normal'); + +-- --------------------------------------------------------------------- +-- Table `tvisual_console_items_cache` +-- --------------------------------------------------------------------- +CREATE TABLE `tvisual_console_elements_cache` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `vc_id` INTEGER UNSIGNED NOT NULL, + `vc_item_id` INTEGER UNSIGNED NOT NULL, + `user_id` VARCHAR(60) DEFAULT NULL, + `data` TEXT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `expiration` INTEGER UNSIGNED NOT NULL COMMENT 'Seconds to expire', + PRIMARY KEY(`id`), + FOREIGN KEY(`vc_id`) REFERENCES `tlayout`(`id`) + ON DELETE CASCADE, + FOREIGN KEY(`vc_item_id`) REFERENCES `tlayout_data`(`id`) + ON DELETE CASCADE, + FOREIGN KEY (`user_id`) REFERENCES `tusuario`(`id_user`) + ON DELETE CASCADE + ON UPDATE CASCADE +) engine=InnoDB DEFAULT CHARSET=utf8; + diff --git a/pandora_console/general/firts_task/collections.php b/pandora_console/general/firts_task/collections.php index 269de3ba12..8e27ce6ee3 100755 --- a/pandora_console/general/firts_task/collections.php +++ b/pandora_console/general/firts_task/collections.php @@ -22,7 +22,7 @@ ui_require_css_file('firts_task'); __('Collections')]); ?>
-

+

true, 'message' => __('There are no custom __('Custom Fields')]); ?>

-

+

true, 'message' => __('There are no custom __('Custom Graphs')]); ?>

-

+

__('Fields Manager')]); ?>

-

+

__('Incidents')]); ?>

-

+

__('Visual Console')]); ?>

-

+

__('Network Map')]); ?>

-

+

__('Planned Downtime')]); ?>

-

+

__('Discovery server')]); ?>

-

+

__('Services')]); ?>

-

+

__('SNMP Filter')]); ?>

-

+

__('Tags')]); ?>

-

+

__('Transactions')]); ?>

-

+

'; -echo ''.__('Page generated at').' '.date($config['date_format']); -echo '
® '.get_copyright_notice().''; if (isset($config['debug'])) { $cache_info = []; diff --git a/pandora_console/general/header.php b/pandora_console/general/header.php index 7addef057f..a093596b2b 100644 --- a/pandora_console/general/header.php +++ b/pandora_console/general/header.php @@ -18,10 +18,14 @@ require_once 'include/functions_notifications.php'; // Global errors/warnings checking. config_check(); -?> -

-
+if ($config['menu_type'] == 'classic') { + echo '
'; +} else { + echo '
'; +} +?> +
'; - // Search + // Search. $acl_head_search = true; if ($config['acl_enterprise'] == 1 && !users_is_admin()) { $acl_head_search = db_get_sql( @@ -94,7 +98,7 @@ require_once 'include/functions_notifications.php'; } if ($acl_head_search) { - // Search bar + // Search bar. $search_bar = ''; if (!isset($config['search_keywords'])) { $search_bar .= ''; @@ -116,15 +120,13 @@ require_once 'include/functions_notifications.php'; } $search_bar .= 'onfocus="javascript: if (fieldKeyWordEmpty) $(\'#keywords\').val(\'\');" - onkeyup="javascript: fieldKeyWordEmpty = false;" - style="margin-top:5px;" class="search_input" />'; + onkeyup="javascript: fieldKeyWordEmpty = false;" class="search_input" />'; // $search_bar .= 'onClick="javascript: document.quicksearch.submit()"'; $search_bar .= ""; $search_bar .= ''; - $header_searchbar = ''; + $header_searchbar = ''; } @@ -141,14 +143,74 @@ require_once 'include/functions_notifications.php'; $_GET['sec2'] = ''; } + if ($_GET['sec'] == 'main' || !isset($_GET['sec'])) { + // home screen chosen by the user + $home_page = ''; + if (isset($config['id_user'])) { + $user_info = users_get_user_by_id($config['id_user']); + $home_page = io_safe_output($user_info['section']); + $home_url = $user_info['data_section']; + } + + if ($home_page != '') { + switch ($home_page) { + case 'Event list': + $_GET['sec2'] = 'operation/events/events'; + break; + + case 'Group view': + $_GET['sec2'] = 'operation/agentes/group_view'; + break; + + case 'Alert detail': + $_GET['sec2'] = 'operation/agentes/alerts_status'; + break; + + case 'Tactical view': + $_GET['sec2'] = 'operation/agentes/tactical'; + break; + + case 'Default': + $_GET['sec2'] = 'general/logon_ok'; + break; + + case 'Dashboard': + $_GET['sec2'] = 'enterprise/dashboard/main_dashboard'; + break; + + case 'Visual console': + $_GET['sec2'] = 'operation/visual_console/render_view'; + break; + + case 'Other': + $home_url = io_safe_output($home_url); + $url_array = parse_url($home_url); + parse_str($url_array['query'], $res); + foreach ($res as $key => $param) { + $_GET[$key] = $param; + } + break; + } + } + } + if (!isset($_GET['refr'])) { $_GET['refr'] = null; } - $select = db_process_sql("SELECT autorefresh_white_list,time_autorefresh FROM tusuario WHERE id_user = '".$config['id_user']."'"); - $autorefresh_list = json_decode($select[0]['autorefresh_white_list']); + $select = db_process_sql( + "SELECT autorefresh_white_list,time_autorefresh + FROM tusuario + WHERE id_user = '".$config['id_user']."'" + ); - if ($autorefresh_list !== null && array_search($_GET['sec2'], $autorefresh_list) !== false) { + $autorefresh_list = json_decode( + $select[0]['autorefresh_white_list'] + ); + + if ($autorefresh_list !== null + && array_search($_GET['sec2'], $autorefresh_list) !== false + ) { $do_refresh = true; if ($_GET['sec2'] == 'operation/agentes/pandora_networkmap') { if ((!isset($_GET['tab'])) || ($_GET['tab'] != 'view')) { @@ -157,23 +219,65 @@ require_once 'include/functions_notifications.php'; } if ($do_refresh) { - $autorefresh_img = html_print_image('images/header_refresh_gray.png', true, ['class' => 'bot', 'alt' => 'lightning', 'title' => __('Configure autorefresh')]); + $autorefresh_img = html_print_image( + 'images/header_refresh_gray.png', + true, + [ + 'class' => 'bot', + 'alt' => 'lightning', + 'title' => __('Configure autorefresh'), + ] + ); - if ($_GET['refr']) { - $autorefresh_txt .= ' ('.date('i:s', $config['refr']).')'; + if ((isset($select[0]['time_autorefresh']) === true) + && $select[0]['time_autorefresh'] !== 0 + && $config['refr'] === null + ) { + $config['refr'] = $select[0]['time_autorefresh']; + $autorefresh_txt .= ' ('; + $autorefresh_txt .= date( + 'i:s', + $config['refr'] + ); + $autorefresh_txt .= ')'; + } else if ($_GET['refr']) { + $autorefresh_txt .= ' ('; + $autorefresh_txt .= date('i:s', $config['refr']); + $autorefresh_txt .= ')'; } $ignored_params['refr'] = ''; $values = get_refresh_time_array(); + $autorefresh_additional = ''; unset($values); + if ($home_page != '') { + $autorefresh_link_open_img = ''; + } else { + $autorefresh_link_open_img = ''; + } - $autorefresh_link_open_img = ''; - - if ($_GET['refr']) { - $autorefresh_link_open_txt = ''; + if ($_GET['refr'] + || ((isset($select[0]['time_autorefresh']) === true) + && $select[0]['time_autorefresh'] !== 0) + ) { + if ($home_page != '') { + $autorefresh_link_open_txt = ''; + } else { + $autorefresh_link_open_txt = ''; + } } else { $autorefresh_link_open_txt = ''; } @@ -192,7 +296,15 @@ require_once 'include/functions_notifications.php'; $display_counter = 'display:none'; } } else { - $autorefresh_img = html_print_image('images/header_refresh_disabled_gray.png', true, ['class' => 'bot autorefresh_disabled', 'alt' => 'lightning', 'title' => __('Disabled autorefresh')]); + $autorefresh_img = html_print_image( + 'images/header_refresh_disabled_gray.png', + true, + [ + 'class' => 'bot autorefresh_disabled', + 'alt' => 'lightning', + 'title' => __('Disabled autorefresh'), + ] + ); $ignored_params['refr'] = false; @@ -203,56 +315,80 @@ require_once 'include/functions_notifications.php'; $display_counter = 'display:none'; } - $header_autorefresh = '
'.$autorefresh_link_open_img.$autorefresh_img.$autorefresh_link_close.'
'; - $header_autorefresh_counter = '
'.$autorefresh_link_open_txt.$autorefresh_txt.$autorefresh_link_close.$autorefresh_additional.'
'; + $header_autorefresh = '
'; + $header_autorefresh .= $autorefresh_link_open_img; + $header_autorefresh .= $autorefresh_img; + $header_autorefresh .= $autorefresh_link_close; + $header_autorefresh .= '
'; + + $header_autorefresh_counter = '
'; + $header_autorefresh_counter .= $autorefresh_link_open_txt; + $header_autorefresh_counter .= $autorefresh_txt; + $header_autorefresh_counter .= $autorefresh_link_close; + $header_autorefresh_counter .= $autorefresh_additional; + $header_autorefresh_counter .= '
'; - // Qr. - if ($config['show_qr_code_header'] == 0) { - $show_qr_code_header = 'display: none;'; + // Support. + if (defined('PANDORA_ENTERPRISE')) { + $header_support_link = 'https://support.artica.es/'; } else { - $show_qr_code_header = 'display: inline;'; + $header_support_link = 'https://pandorafms.com/forums/'; } - $header_qr = '
'; + $header_support = ''; + + // Documentation. + $header_docu = ''; + - echo "'; - ?> - - __('Edit my user'), 'class' => 'bot', 'alt' => 'user']); + $header_user = html_print_image( + 'images/header_user_admin_green.png', + true, + [ + 'title' => __('Edit my user'), + 'class' => 'bot', + 'alt' => 'user', + ] + ); } else { - $header_user = html_print_image('images/header_user_green.png', true, ['title' => __('Edit my user'), 'class' => 'bot', 'alt' => 'user']); + $header_user = html_print_image( + 'images/header_user_green.png', + true, + [ + 'title' => __('Edit my user'), + 'class' => 'bot', + 'alt' => 'user', + ] + ); } $header_user = ''; // Logout. $header_logout = ''; - echo '
'.$header_autorefresh, $header_autorefresh_counter, $header_qr, $header_chat.'
-
'.$header_searchbar, $header_discovery, $servers_list.'
-
'.$header_user, $header_logout.'
'; + echo '
'.$config['custom_title_header'].''.$config['custom_subtitle_header'].'
+
'.$header_searchbar.'
+
'.$header_chat, $header_autorefresh, $header_autorefresh_counter, $header_discovery, $servers_list, $header_support, $header_docu, $header_user, $header_logout.'
'; ?>
@@ -490,7 +626,12 @@ require_once 'include/functions_notifications.php'; ); - $("a.autorefresh_txt").toggle (); - $("#combo_refr").toggle (); - href = $("a.autorefresh").attr ("href"); - - var refresh = ''; - $(document).attr ("location", href + refresh); - - - if (fixed_header) { $('div#head').addClass('fixed_header'); - $('div#page') - .css('padding-top', $('div#head').innerHeight() + 'px') - .css('position', 'relative'); + $('div#main').css('padding-top', $('div#head').innerHeight() + 'px'); } check_new_chats_icon('icon_new_messages_chat'); @@ -547,14 +669,14 @@ require_once 'include/functions_notifications.php'; blinkpubli(); $("#header_autorefresh").css('padding-right', '5px'); - var refr_time = ; + var refr_time = ; var t = new Date(); - t.setTime (t.getTime () + - parseInt()); - $("#refrcounter").countdown ({until: t, + t.setTime (t.getTime () + parseInt()); + $("#refrcounter").countdown ({ + until: t, layout: '%M%nn%M:%S%nn%S', labels: ['', '', '', '', '', '', ''], onExpiry: function () { diff --git a/pandora_console/general/login_identification_wizard.php b/pandora_console/general/login_identification_wizard.php deleted file mode 100644 index 7bdb370b54..0000000000 --- a/pandora_console/general/login_identification_wizard.php +++ /dev/null @@ -1,455 +0,0 @@ - true, - 'message' => '', - ]; - - if ($register_pandora) { - // Pandora register update - $um_message = update_manager_register_instance(); - $ui_feedback['message'] .= $um_message['message'].'

'; - if ($um_message['success']) { - config_update_value('instance_registered', 1); - $ui_feedback['status'] = true && $ui_feedback['status']; - } else { - $ui_feedback['status'] = false; - } - } else if (!$forced) { - config_update_value('identification_reminder_timestamp', $future_8_days); - } - - if ($newsletter) { - // Pandora newsletter update - $email = get_parameter('email', ''); - $um_message = update_manager_insert_newsletter($email); - $ui_feedback['message'] .= $um_message['message']; - if ($um_message['success']) { - db_process_sql_update('tusuario', ['middlename' => 1], ['id_user' => $config['id_user']]); - $ui_feedback['status'] = true && $ui_feedback['status']; - } else { - $ui_feedback['status'] = false; - } - } else if (!$forced) { - db_process_sql_update('tusuario', ['lastname' => $future_8_days], ['id_user' => $config['id_user']]); - } - - // Form answer JSON - $ui_feedback['status'] = $ui_feedback['status'] ? 1 : 0; - echo io_json_mb_encode($ui_feedback); - } - - if (!$not_return) { - return; - } -} - -// Check if user is admin -if (!license_free()) { - return; -} - -if (!users_is_admin($config['id_user'])) { - return; -} - -// Get data to display properly the wizard -$wizard_data = []; - -$wizard_data['newsletter_subscribed'] = db_get_value('middlename', 'tusuario', 'id_user', $config['id_user']); -// force_* = 1 -> force show -// force_* = 0 -> force hide -// force_* = -1 -> show or hide depends reminder and timestamp -$wizard_data['force_newsletter'] = get_parameter('force_newsletter', -1); -$wizard_data['newsletter_reminder'] = db_get_value('firstname', 'tusuario', 'id_user', $config['id_user']); -$wizard_data['newsletter_reminder_timestamp'] = db_get_value('lastname', 'tusuario', 'id_user', $config['id_user']); - - -$wizard_data['instance_registered'] = $config['instance_registered']; -$wizard_data['force_register'] = get_parameter('force_register', -1); -$wizard_data['identification_reminder'] = $config['identification_reminder']; -$wizard_data['identification_reminder_timestamp'] = $config['identification_reminder_timestamp']; - -$display_newsletter = display_newsletter($wizard_data); -$display_register = display_register($wizard_data); -$display_forced = ($wizard_data['force_newsletter'] != -1) || ($wizard_data['force_register'] != -1); - -// Return if it is fully completed -if ((!$display_register) && (!$display_newsletter)) { - return false; -} - -$return_button = get_parameter('return_button', 0) == 1; - -$email = db_get_value('email', 'tusuario', 'id_user', $config['id_user']); -// Avoid to show default email -if ($email == 'admin@example.com') { - $email = ''; -} - -// Prints accept register license -echo '
'; - echo '
'; - echo html_print_image('images/pandora_circle_big.png', true); - echo '
'; - echo '
'; - echo __('Stay up to date with the %s community', get_product_name()).'.'; - echo '
'; - - echo '
'; - echo '

'.__('When you subscribe to the %s Update Manager service, you accept that we register your %s instance as an identifier on the database owned by Artica TS. This data will solely be used to provide you with information about %s and will not be conceded to third parties. You can unregister from said database at any time from the Update Manager options.', get_product_name(), get_product_name(), get_product_name()).'

'; - echo '

'.__('In the same fashion, when subscribed to the newsletter you accept that your email will pass on to a database property of Artica TS. This data will solely be used to provide you with information about %s and will not be conceded to third parties. You can unregister from said database at any time from the newsletter subscription options.', get_product_name()).'

'; - echo '
'; - - echo '
'; - echo '
'; - html_print_submit_button(__('Finish'), 'finish_dialog_button', false, 'class="ui-button-dialog ui-widget ui-state-default ui-corner-all ui-button-text-only sub ok" style="width:100px;"'); - echo '
'; - $display_status_return = $return_button ? 'block' : 'none'; - echo '
'; - html_print_submit_button(__('Return'), 'return_dialog_button', false, 'class="ui-button-dialog ui-widget ui-state-default ui-corner-all ui-button-text-only sub upd" style="width:100px;"'); - echo '
'; - echo '
'; - html_print_checkbox('register', 1, false, false, false, 'cursor: \'pointer\''); - echo ' '.__('Join the %s community!', get_product_name()).'!
'; - html_print_checkbox('newsletter', 1, false, false, false, 'cursor: \'pointer\''); - echo ' '.__('Subscribe to our newsletter').''; - echo '
'; - echo '
'; - echo ' '; - html_print_input_text_extended('email-newsletter', $email, 'text-email-newsletter', '', 30, 255, false, '', ['style' => 'display:none; width: 200px;']); -echo ' '; - echo ' *'.__('Required').' '; - echo '
'; - echo '
'; - echo '
'; -echo '
'; - -// Print yes or not dialog -echo '
'; - echo '
'; - echo __('Do you want to continue without any registration').'?'; - echo '
'; - echo '
'; - html_print_submit_button(__('No'), 'no_registration', false, 'class="ui-button-dialog ui-widget ui-state-default ui-corner-all ui-button-text-only sub cancel" style="width:100px;"'); - echo '
'; - echo '
'; - html_print_submit_button(__('Yes'), 'yes_registration', false, 'class="ui-button-dialog ui-widget ui-state-default ui-corner-all ui-button-text-only sub upd" style="width:100px;"'); - echo '
'; -echo '
'; - -// Print feedback user dialog -echo '
'; - echo '
'; - echo html_print_image('images/success_circle_big.png', true); - echo '
'; - echo '
'; -echo '
'; -?> - - - - - diff --git a/pandora_console/general/login_page.php b/pandora_console/general/login_page.php index ea73318e2a..86dccfa53a 100755 --- a/pandora_console/general/login_page.php +++ b/pandora_console/general/login_page.php @@ -186,7 +186,7 @@ switch ($login_screen) { echo '
'; echo ''; echo ''; echo ''; } break; @@ -245,7 +245,7 @@ switch ($login_screen) { html_print_input_text_extended('auth_code', '', 'auth_code', '', '', '', false, '', 'class="login login_password" placeholder="'.__('Authentication code').'"', false, true); echo '
'; echo ''; break; @@ -305,12 +305,12 @@ if (file_exists(ENTERPRISE_DIR.'/load_enterprise.php')) { echo ''; @@ -319,7 +319,7 @@ echo '
'; echo '
'.$pandora_version.(($develop_bypass == 1) ? ' '.__('Build').' '.$build_version : '').'
'; echo ''; -if (!isset($process_error_message) && isset($mail)) { +if (empty($process_error_message) && isset($mail)) { echo '
'; echo '
'; echo '
'; @@ -336,7 +336,7 @@ if (!isset($process_error_message) && isset($mail)) { echo '
'; echo '
'; echo '
'; -} else if (isset($process_error_message)) { +} else if (isset($process_error_message) && !empty($process_error_message)) { echo '
'; echo '
'; echo '
'; diff --git a/pandora_console/general/login_required.php b/pandora_console/general/login_required.php deleted file mode 100644 index 726d6ab5d1..0000000000 --- a/pandora_console/general/login_required.php +++ /dev/null @@ -1,273 +0,0 @@ - $email], - ['id_user' => $config['id_user']] - ); - } - - // Update the alert action Mail to XXX/Administrator if it is set to default - $mail_check = 'yourmail@domain.es'; - $mail_alert = alerts_get_alert_action_field1(1); - if ($mail_check === $mail_alert && $email !== false) { - alerts_update_alert_action( - 1, - [ - 'field1' => $email, - 'field1_recovery' => $email, - ] - ); - } - - config_update_value('initial_wizard', 1); - } - - // Change the language if is change in checkbox - if ($change_language !== 0) { - config_update_value('language', $change_language); - } - - if ($cancel_wizard !== 0) { - config_update_value('initial_wizard', 1); - } - - return; -} - -$email = db_get_value('email', 'tusuario', 'id_user', $config['id_user']); -// Avoid to show default email -if ($email == 'admin@example.com') { - $email = ''; -} - -// Prints first step pandora registration -echo ''; - - ?> - - diff --git a/pandora_console/general/logon_ok.php b/pandora_console/general/logon_ok.php index 21c9713a48..649a2d2f68 100644 --- a/pandora_console/general/logon_ok.php +++ b/pandora_console/general/logon_ok.php @@ -1,26 +1,39 @@
".__('Pandora status info').'
".__('MySQL Performance metrics').'
".__('MySQL Performance metrics').' '.ui_print_help_icon('performance_metrics_tab', true).'
".__(' Pandora FMS logs dates').'
".__(' Status of the attachment folder').'
".__(' Information from the tagente_datos table').'
".__(' Pandora FMS server threads').'
@@ -88,7 +113,7 @@ if (!empty($all_data)) { class = 'databox'; @@ -101,12 +126,12 @@ if (!empty($all_data)) { $table->head[0] = ''.__('%s Overview', get_product_name()).''; $table->head_colspan[0] = 4; - // Indicators + // Indicators. $tdata = []; $stats = reporting_get_stats_indicators($data, 120, 10, false); $status = '
'; foreach ($stats as $stat) { - $status .= ''; + $status .= ''; } $status .= '
'.$stat['title'].''.''.$stat['graph'].'
'.$stat['title'].''.$stat['graph'].'
'; @@ -115,25 +140,25 @@ if (!empty($all_data)) { $table->data[] = $tdata; - // Alerts + // Alerts. $tdata = []; $tdata[0] = reporting_get_stats_alerts($data); $table->rowclass[] = ''; $table->data[] = $tdata; - // Modules by status + // Modules by status. $tdata = []; $tdata[0] = reporting_get_stats_modules_status($data, 180, 100); $table->rowclass[] = ''; $table->data[] = $tdata; - // Total agents and modules + // Total agents and modules. $tdata = []; $tdata[0] = reporting_get_stats_agents_monitors($data); $table->rowclass[] = ''; $table->data[] = $tdata; - // Users + // Users. if (users_is_admin()) { $tdata = []; $tdata[0] = reporting_get_stats_users($data); @@ -160,7 +185,7 @@ if (!empty($all_data)) { if (!empty($news)) { - // NEWS BOARD///////////////////////////// + // NEWS BOARD. echo '
'; echo ''; @@ -172,32 +197,42 @@ if (!empty($all_data)) { } foreach ($news as $article) { - $text = io_safe_output($article['text']); - + $image = false; + if ($article['text'] == '&lt;p style="text-align: center; font-size: 13px;"&gt;Hello, congratulations, if you've arrived here you already have an operational monitoring console. Remember that our forums and online documentation are available 24x7 to get you out of any trouble. You can replace this message with a personalized one at Admin tools -&amp;gt; Site news.&lt;/p&gt; ') { + $image = true; + } + $text_bbdd = io_safe_output($article['text']); + $text = html_entity_decode($text_bbdd); echo ''; echo ''; echo ''; } echo '
'.$article['subject'].'
'.__('by').' '.$article['author'].' '.ui_print_timestamp($article['timestamp'], true).' '.$comparation_suffix.'
'; + if ($image) { + echo '
img colabora con nosotros - Support
'; + } + echo nl2br($text); echo '
'; echo '
'; - // News board + // News board. echo '

'; - // END OF NEWS BOARD///////////////////////////// + // END OF NEWS BOARD. } - // LAST ACTIVITY///////////////////////////// - // Show last activity from this user + // LAST ACTIVITY. + // Show last activity from this user. echo '
'; $table = new stdClass(); - $table->class = 'databox data'; + $table->class = 'info_table'; + $table->cellpadding = 0; + $table->cellspacing = 0; $table->width = '100%'; - // Don't specify px + // Don't specify px. $table->data = []; $table->size = []; $table->size[0] = '5%'; @@ -212,38 +247,14 @@ if (!empty($all_data)) { $table->head[3] = __('Source IP'); $table->head[4] = __('Comments'); $table->title = ''.__('This is your last activity performed on the %s console', get_product_name()).''; - - switch ($config['dbtype']) { - case 'mysql': - $sql = sprintf( - 'SELECT id_usuario,accion, ip_origen,descripcion,utimestamp + $sql = sprintf( + 'SELECT id_usuario,accion, ip_origen,descripcion,utimestamp FROM tsesion WHERE (`utimestamp` > UNIX_TIMESTAMP(NOW()) - '.SECONDS_1WEEK.") AND `id_usuario` = '%s' ORDER BY `utimestamp` DESC LIMIT 10", - $config['id_user'] - ); - break; + $config['id_user'] + ); - case 'postgresql': - $sql = sprintf( - "SELECT \"id_usuario\", accion, \"ip_origen\", descripcion, utimestamp - FROM tsesion - WHERE (\"utimestamp\" > ceil(date_part('epoch', CURRENT_TIMESTAMP)) - ".SECONDS_1WEEK.") - AND \"id_usuario\" = '%s' ORDER BY \"utimestamp\" DESC LIMIT 10", - $config['id_user'] - ); - break; - - case 'oracle': - $sql = sprintf( - "SELECT id_usuario, accion, ip_origen, descripcion, utimestamp - FROM tsesion - WHERE ((utimestamp > ceil((sysdate - to_date('19700101000000','YYYYMMDDHH24MISS')) * (".SECONDS_1DAY.')) - '.SECONDS_1WEEK.") - AND id_usuario = '%s') AND rownum <= 10 ORDER BY utimestamp DESC", - $config['id_user'] - ); - break; - } $sessions = db_get_all_rows_sql($sql); @@ -253,24 +264,17 @@ if (!empty($all_data)) { foreach ($sessions as $session) { $data = []; + $session_id_usuario = $session['id_usuario']; + $session_ip_origen = $session['ip_origen']; - switch ($config['dbtype']) { - case 'mysql': - case 'oracle': - $session_id_usuario = $session['id_usuario']; - $session_ip_origen = $session['ip_origen']; - break; - - case 'postgresql': - $session_id_usuario = $session['id_usuario']; - $session_ip_origen = $session['ip_origen']; - break; - } $data[0] = ''.$session_id_usuario.''; $data[1] = ui_print_session_action_icon($session['accion'], true).' '.$session['accion']; - $data[2] = ui_print_help_tip(date($config['date_format'], $session['utimestamp']), true).human_time_comparation($session['utimestamp'], 'tiny'); + $data[2] = ui_print_help_tip( + date($config['date_format'], $session['utimestamp']), + true + ).human_time_comparation($session['utimestamp'], 'tiny'); $data[3] = $session_ip_origen; $description = str_replace([',', ', '], ', ', $session['descripcion']); if (strlen($description) > 100) { @@ -287,8 +291,7 @@ if (!empty($all_data)) { unset($table); echo '
'; echo ''; - // activity - // END OF LAST ACTIVIYY///////////////////////////// + // END OF LAST ACTIVIYY. ?> diff --git a/pandora_console/general/main_menu.php b/pandora_console/general/main_menu.php index cb5126b621..877f171090 100644 --- a/pandora_console/general/main_menu.php +++ b/pandora_console/general/main_menu.php @@ -20,31 +20,13 @@ if (! isset($config['id_user'])) { \ No newline at end of file diff --git a/pandora_console/general/register.php b/pandora_console/general/register.php new file mode 100644 index 0000000000..e6a26bb250 --- /dev/null +++ b/pandora_console/general/register.php @@ -0,0 +1,184 @@ + -1], + ['id_user' => $config['id_user']] + ); + + // XXX: Also notify UpdateManager. + } + + if ($register_newsletter) { + $feedback = newsletter_wiz_process(); + } + + if (is_array($feedback)) { + echo json_encode($feedback); + } + + + // Ajax calls finish here. + exit(); +} + + +ui_require_css_file('register'); + +$initial = isset($config['initial_wizard']) !== true + || $config['initial_wizard'] != '1'; + +$newsletter = db_get_value( + 'middlename', + 'tusuario', + 'id_user', + $config['id_user'] +); +$show_newsletter = $newsletter == '0' || $newsletter == ''; + +$registration = isset($config['pandora_uid']) !== true + || $config['pandora_uid'] == ''; + + +if ($initial && users_is_admin()) { + // Show all forms in order. + // 1- Ask for email, timezone, etc. Fullfill alerts and user mail. + config_wiz_modal( + false, + true, + (($registration === true) ? 'show_registration_wizard()' : null) + ); +} + +if ($registration && users_is_admin()) { + // Prepare registration wizard, not launch. leave control to flow. + registration_wiz_modal( + false, + // Launch only if not being launch from 'initial'. + !$initial, + (($show_newsletter === true) ? 'force_run_newsletter()' : null) + ); +} else { + if ($show_newsletter) { + // Show newsletter wizard for current user. + newsletter_wiz_modal( + false, + // Launch only if not being call from 'registration'. + !$registration && !$initial + ); + } +} + + +$newsletter = null; + +?> + diff --git a/pandora_console/godmode/admin_access_logs.php b/pandora_console/godmode/admin_access_logs.php index 93c668f00c..67a933d8cb 100644 --- a/pandora_console/godmode/admin_access_logs.php +++ b/pandora_console/godmode/admin_access_logs.php @@ -183,10 +183,10 @@ if (empty($result)) { } $table = new stdClass(); -$table->cellpadding = 4; -$table->cellspacing = 4; +$table->cellpadding = 0; +$table->cellspacing = 0; $table->width = '100%'; -$table->class = 'databox data'; +$table->class = 'info_table'; $table->size = []; $table->data = []; $table->head = []; @@ -246,7 +246,18 @@ foreach ($result as $row) { } } +foreach ($table->rowclass as $key => $value) { + if (strpos($value, 'limit_scroll') !== false) { + $table->colspan[$key] = [7]; + } else { + if ($enterprise_include !== ENTERPRISE_NOT_HOOK) { + $table->cellclass[$key][6] = 'action_buttons'; + } + } +} + html_print_table($table); +ui_pagination($count, $url, 0, 0, false, 'offset', true, 'pagination-bottom'); if ($enterprise_include !== ENTERPRISE_NOT_HOOK) { enterprise_hook('enterpriseAuditFooter'); diff --git a/pandora_console/godmode/agentes/agent_manager.php b/pandora_console/godmode/agentes/agent_manager.php index a7fc1f156a..dfdd7390c3 100644 --- a/pandora_console/godmode/agentes/agent_manager.php +++ b/pandora_console/godmode/agentes/agent_manager.php @@ -1,17 +1,32 @@ $value) { - // Check if it has "ifXXXX" syntax and skip it + // Check if it has "ifXXXX" syntax and skip it. if (! preg_match('/if/', $key)) { continue; } @@ -98,7 +109,7 @@ if (is_ajax()) { echo io_json_mb_encode($out); } - // And and remove groups use the same function + // And and remove groups use the same function. $add_secondary_groups = get_parameter('add_secondary_groups'); $remove_secondary_groups = get_parameter('remove_secondary_groups'); if ($add_secondary_groups || $remove_secondary_groups) { @@ -114,12 +125,12 @@ if (is_ajax()) { 'agents_update_secondary_groups', [ $id_agent, - $add_secondary_groups ? $groups_to_add : [], - $remove_secondary_groups ? $groups_to_add : [], + ($add_secondary_groups) ? $groups_to_add : [], + ($remove_secondary_groups) ? $groups_to_add : [], ] ); // Echo 0 in case of error. 0 Otherwise. - echo $ret ? 1 : 0; + echo ($ret) ? 1 : 0; } } @@ -149,7 +160,7 @@ if ($new_agent) { } if (!$new_agent) { - // Agent remote configuration editor + // Agent remote configuration editor. enterprise_include_once('include/functions_config_agents.php'); if (enterprise_installed()) { $filename = config_agents_get_agent_config_filenames($id_agente); @@ -157,37 +168,42 @@ if (!$new_agent) { } $disk_conf_delete = (bool) get_parameter('disk_conf_delete'); -// Agent remote configuration DELETE +// Agent remote configuration DELETE. if ($disk_conf_delete) { // TODO: Get this working on computers where the Pandora server(s) are not on the webserver - // TODO: Get a remote_config editor working in the open version + // TODO: Get a remote_config editor working in the open version. @unlink($filename['md5']); @unlink($filename['conf']); } -echo '
'; +echo ''; -$table = new stdClass(); -$table->width = '100%'; -$table->class = 'databox filters'; - -$table->head = []; -$table->style = []; -$table->style[0] = 'font-weight: bold; width: 150px;'; -$table->data = []; - -$table->align[2] = 'center'; +// Custom ID. +$custom_id_div = '
'; +$custom_id_div .= '

'.__('Custom ID').':

'; +$custom_id_div .= html_print_input_text( + 'custom_id', + $custom_id, + '', + 16, + 255, + true, + false, + false, + '', + 'agent_custom_id' +).'
'; if (!$new_agent && $alias != '') { - $table->data[0][0] = __('Agent name').ui_print_help_tip(__("The agent's name must be the same as the one defined at the console"), true); - $table->data[0][1] = html_print_input_text('agente', $nombre_agente, '', 50, 100, true); - - $table->data[0][2] = __('QR Code Agent view'); + $table_agent_name = '

'.__('Agent name').': '.ui_print_help_tip(__("The agent's name must be the same as the one defined at the console"), true).'

'; + $table_agent_name .= '
'; + $table_agent_name .= '
'.html_print_input_text('agente', $nombre_agente, '', 50, 100, true).'
'; + $table_agent_name .= '
'; if ($id_agente) { - $table->data[0][1] .= ' '.__('ID')."  $id_agente  "; - $table->data[0][1] .= '  '; - $table->data[0][1] .= html_print_image( + $table_agent_name .= ''; + $table_agent_name .= ''; + $table_agent_name .= html_print_image( 'images/zoom.png', true, [ @@ -195,20 +211,23 @@ if (!$new_agent && $alias != '') { 'title' => __('Agent detail'), ] ); - $table->data[0][1] .= ''; + $table_agent_name .= ''; } -} -// Remote configuration available -if (!$new_agent) { + $agent_options_update = 'agent_options_update'; + + // Delete link from here. + $table_agent_name .= "".html_print_image('images/cross.png', true, ['title' => __('Delete agent')]).''; + + // Remote configuration available. if (isset($filename)) { if (file_exists($filename['md5'])) { $agent_name = agents_get_name($id_agente); $agent_name = io_safe_output($agent_name); $agent_md5 = md5($agent_name, false); - $table->data[0][1] .= '  '.''; - $table->data[0][1] .= html_print_image( + $table_agent_name .= ''; + $table_agent_name .= html_print_image( 'images/application_edit.png', true, [ @@ -216,35 +235,57 @@ if (!$new_agent) { 'title' => __('This agent can be remotely configured'), ] ); - $table->data[0][1] .= ''.ui_print_help_tip( + $table_agent_name .= ''.ui_print_help_tip( __('You can remotely edit this agent configuration'), true ); } } + + $table_agent_name .= '
'; + + // QR code div. + $table_qr_code = '
'; + $table_qr_code .= '

'.__('QR Code Agent view').':

'; + $table_qr_code .= '
'; + if ($id_agente) { + $table_qr_code .= ""; + } + + // Add Custom id div. + $table_qr_code .= '
'; + $table_qr_code .= $custom_id_div; + $table_qr_code .= '
'; } -// Delete link from here -if (!$new_agent) { - $table->data[0][1] .= "  ".html_print_image('images/cross.png', true, ['title' => __('Delete agent')]).''; -} - -$table->data[1][0] = __('Alias').ui_print_help_tip(__('Characters /,\,|,%,#,&,$ will be ignored'), true).''; -$table->data[1][1] = html_print_input_text('alias', $alias, '', 50, 100, true); if ($new_agent) { - $table->data[1][1] .= html_print_checkbox('alias_as_name', 1, $config['alias_as_name'], true).__('Use alias as name'); + $label_select_child_left = 'label_select_child_left'; + $label_select_parent = 'label_select_parent'; } -$table->data[2][0] = __('IP Address'); -$table->data[2][1] = html_print_input_text('direccion', $direccion_agente, '', 16, 100, true).html_print_checkbox('unique_ip', 1, $config['unique_ip'], true).__('Unique IP').ui_print_help_tip(__('Set the primary IP address as the unique IP, preventing the same primary IP address from being used in more than one agent'), true); +$table_alias = '

'.__('Alias').': '.ui_print_help_tip(__('Characters /,\,|,%,#,&,$ will be ignored'), true).'

'; +$table_alias .= '
'; +$table_alias .= '
'.html_print_input_text('alias', $alias, '', 50, 100, true).'
'; +if ($new_agent) { + $table_alias .= '
'.html_print_checkbox_switch('alias_as_name', 1, $config['alias_as_name'], true).__('Use alias as name').'
'; +} + +$table_alias .= '
'; + +$table_ip = '

'.__('IP Address').':

'; +$table_ip .= '
'; +$table_ip .= '
'.html_print_input_text('direccion', $direccion_agente, '', 16, 100, true).'
'; +$table_ip .= '
'.html_print_checkbox_switch('unique_ip', 1, $config['unique_ip'], true).__('Unique IP').ui_print_help_tip(__('Set the primary IP address as the unique IP, preventing the same primary IP address from being used in more than one agent'), true).'
'; +$table_ip .= '
'; if ($id_agente) { - $table->data[2][1] .= '    '; - $ip_all = agents_get_addresses($id_agente); - $table->data[2][1] .= html_print_select($ip_all, 'address_list', $direccion_agente, '', '', 0, true); - $table->data[2][1] .= ' '.html_print_checkbox('delete_ip', 1, false, true).__('Delete selected'); + $table_ip .= '
'; + $table_ip .= '
'; + $table_ip .= '
'.html_print_select($ip_all, 'address_list', $direccion_agente, '', '', 0, true).'
'; + $table_ip .= '
'.html_print_checkbox_switch('delete_ip', 1, false, true).__('Delete selected').'
'; + $table_ip .= '
'; } ?> @@ -254,15 +295,6 @@ if ($id_agente) { } rowspan[2][2] = 3; - if ($id_agente) { - $table->data[2][2] = ""; - } else { - $table->data[2][2] = __('Only it is show when
the agent is saved.'); - } -} - $groups = users_get_groups($config['id_user'], 'AR', false); $modules = db_get_all_rows_sql( @@ -277,28 +309,46 @@ if (is_array($modules)) { } } -$table->data[4][0] = __('Primary group'); -// Cannot change primary group if user have not permission for that group +$table_primary_group = '

'.__('Primary group').':

'; +$table_primary_group .= '
'; +// Cannot change primary group if user have not permission for that group. if (isset($groups[$grupo]) || $new_agent) { - $table->data[4][1] = html_print_select_groups(false, 'AR', false, 'grupo', $grupo, '', '', 0, true); + $table_primary_group .= html_print_select_groups(false, 'AR', false, 'grupo', $grupo, '', '', 0, true); } else { - $table->data[4][1] = groups_get_name($grupo); - $table->data[4][1] .= html_print_input_hidden('grupo', $grupo, true); + $table_primary_group .= groups_get_name($grupo); + $table_primary_group .= html_print_input_hidden('grupo', $grupo, true); } -$table->data[4][1] .= ' '; -$table->data[4][1] .= ui_print_group_icon($grupo, true); -$table->data[4][1] .= ''; +$table_primary_group .= '
'; +$table_primary_group .= ui_print_group_icon($grupo, true); +$table_primary_group .= '
'; + +$table_interval = '

'.__('Interval').':

'; +$table_interval .= '
'; +$table_interval .= html_print_extended_select_for_time( + 'intervalo', + $intervalo, + '', + '', + '0', + 10, + true, + false, + true, + 'w40p' +); + -$table->data[5][0] = __('Interval'); -$table->data[5][1] = html_print_extended_select_for_time('intervalo', $intervalo, '', '', '0', 10, true); if ($intervalo < SECONDS_5MINUTES) { - $table->data[5][1] .= clippy_context_help('interval_agent_min'); + $table_interval .= clippy_context_help('interval_agent_min'); } -$table->data[6][0] = __('OS'); -$table->data[6][1] = html_print_select_from_sql( +$table_interval .= '
'; + +$table_os = '

'.__('OS').':

'; +$table_os .= '
'; +$table_os .= html_print_select_from_sql( 'SELECT id_os, name FROM tconfig_os', 'id_os', $id_os, @@ -307,18 +357,19 @@ $table->data[6][1] = html_print_select_from_sql( '0', true ); -$table->data[6][1] .= ' '; -$table->data[6][1] .= ui_print_os_icon($id_os, false, true); -$table->data[6][1] .= ''; +$table_os .= '
'; +$table_os .= ui_print_os_icon($id_os, false, true); +$table_os .= '
'; -// Network server +// Network server. $servers = servers_get_names(); if (!array_key_exists($server_name, $servers)) { $server_Name = 0; // Set the agent have not server. } -$table->data[7][0] = __('Server'); +$table_server = '

'.__('Server').':

'; +$table_server .= '
'; if ($new_agent) { // Set first server by default. $servers_get_names = servers_get_names(); @@ -326,7 +377,7 @@ if ($new_agent) { $server_name = reset($array_keys_servers_get_names); } -$table->data[7][1] = html_print_select( +$table_server .= html_print_select( servers_get_names(), 'server_name', $server_name, @@ -334,118 +385,128 @@ $table->data[7][1] = html_print_select( __('None'), 0, true -).' '.ui_print_help_icon('agent_server', true); +).'
'; -// Description -$table->data[8][0] = __('Description'); -$table->data[8][1] = html_print_input_text( +// Description. +$table_description = '

'.__('Description').':

'; +$table_description .= html_print_textarea( 'comentarios', + 3, + 10, $comentarios, '', - 45, - 200, - true -); + true, + 'agent_description' +).'
'; -html_print_table($table); -unset($table); +// QR code. +echo '
+
+
'.$table_agent_name.$table_alias.$table_ip.$table_primary_group.'
+
'.$table_interval.$table_os.$table_server.$table_description.'
+
'; +if (!$new_agent && $alias != '') { + echo $table_qr_code; +} -$table = new stdClass(); -$table->width = '100%'; -$table->class = 'databox filters'; - -$table->head = []; -$table->style = []; -$table->style[0] = 'font-weight: bold; '; -$table->style[4] = 'font-weight: bold;'; -$table->data = []; +echo '
'; if (enterprise_installed()) { $secondary_groups_selected = enterprise_hook('agents_get_secondary_groups', [$id_agente]); - $table->data['secondary_groups'][0] = __('Secondary groups').ui_print_help_icon('secondary_groups', true); - $table->data['secondary_groups'][1] = html_print_select_groups( + $table_adv_secondary_groups = '

'.__('Secondary groups').':

'; + $table_adv_secondary_groups_left = html_print_select_groups( false, - // Use the current user to select the groups + // Use the current user to select the groups. 'AR', - // ACL permission + // ACL permission. false, - // Not all group + // Not all group. 'secondary_groups', - // HTML id + // HTML id. '', - // No select any by default + // No select any by default. '', - // Javascript onChange code + // Javascript onChange code. '', - // Do not user no selected value + // Do not user no selected value. 0, - // Do not use no selected value + // Do not use no selected value. true, - // Return HTML (not echo) + // Return HTML (not echo). true, - // Multiple selection + // Multiple selection. true, - // Sorting by default + // Sorting by default. '', - // CSS classnames (default) + // CSS classnames (default). false, - // Not disabled (default) + // Not disabled (default). + 'width:50%; min-width:170px;', + // Inline styles (default). false, - // Inline styles (default) + // Option style select (default). false, - // Option style select (default) - false, - // Do not truncate the users tree (default) + // Do not truncate the users tree (default). 'id_grupo', - // Key to get as value (default) + // Key to get as value (default). false, - // Not strict user (default) + // Not strict user (default). $secondary_groups_selected['plain'] - // Do not show the primary group in this selection + // Do not show the primary group in this selection. ); - $table->data['secondary_groups'][2] = html_print_input_image( + $table_adv_secondary_groups_arrows = html_print_input_image( 'add_secondary', - 'images/darrowright.png', + 'images/darrowright_green.png', 1, '', true, [ + 'id' => 'right_autorefreshlist', 'title' => __('Add secondary groups'), 'onclick' => 'agent_manager_add_secondary_groups(event, '.$id_agente.');', ] - ).'



'.html_print_input_image( + ).html_print_input_image( 'remove_secondary', - 'images/darrowleft.png', + 'images/darrowleft_green.png', 1, '', true, [ + 'id' => 'left_autorefreshlist', 'title' => __('Remove secondary groups'), 'onclick' => 'agent_manager_remove_secondary_groups(event, '.$id_agente.');', ] ); - $table->data['secondary_groups'][3] = html_print_select( + $table_adv_secondary_groups_right .= html_print_select( $secondary_groups_selected['for_select'], - // Values + // Values. 'secondary_groups_selected', - // HTML id + // HTML id. '', - // Selected + // Selected. '', - // Javascript onChange code + // Javascript onChange code. '', - // Nothing selected + // Nothing selected. 0, - // Nothing selected + // Nothing selected. true, - // Return HTML (not echo) - true - // Multiple selection + // Return HTML (not echo). + true, + // Multiple selection. + true, + // Sort. + '', + // Class. + false, + // Disabled. + 'width:50%; min-width:170px;' + // Style. ); - // safe operation mode + // Safe operation mode. if ($id_agente) { $sql_modules = db_get_all_rows_sql( 'SELECT id_agente_modulo as id_module, nombre as name FROM tagente_modulo @@ -457,70 +518,68 @@ if (enterprise_installed()) { $safe_mode_modules[$m['id_module']] = $m['name']; } - $table->data[2][0] = __('Safe operation mode').ui_print_help_tip( + $table_adv_safe = '

'.__('Safe operation mode').': '.ui_print_help_tip( __( 'This mode allow %s to disable all modules of this agent while the selected module is on CRITICAL status', get_product_name() ), true + ).'

'; + $table_adv_safe .= html_print_checkbox_switch('safe_mode', 1, $safe_mode, true); + $table_adv_safe .= __('Module').' '.html_print_select($safe_mode_modules, 'safe_mode_module', $safe_mode_module, '', '', 0, true).'
'; + } + + // Remote configuration. + $table_adv_remote = '

'.__('Remote configuration').':

'; + + if (!$new_agent && isset($filename) && file_exists($filename['md5'])) { + $table_adv_remote .= date('F d Y H:i:s', fileatime($filename['md5'])); + // Delete remote configuration. + $table_adv_remote .= ''; + $table_adv_remote .= html_print_image( + 'images/cross.png', + true, + [ + 'title' => __('Delete remote configuration file'), + 'style' => 'vertical-align: middle;', + ] + ).''; + $table_adv_remote .= ''.ui_print_help_tip( + __('Delete this conf file implies that for restore you must reactive remote config in the local agent.'), + true ); - $table->data[2][1] = html_print_checkbox('safe_mode', 1, $safe_mode, true); - $table->data[2][1] .= '  '.__('Module').' '.html_print_select($safe_mode_modules, 'safe_mode_module', $safe_mode_module, '', '', 0, true); - } - - // Remote configuration - $table->data[5][0] = __('Remote configuration'); - - if (!$new_agent) { - $table->data[5][1] = ''.__('Not available').''; - if (isset($filename)) { - if (file_exists($filename['md5'])) { - $table->data[5][1] = date('F d Y H:i:s', fileatime($filename['md5'])); - // Delete remote configuration - $table->data[5][1] .= ''; - $table->data[5][1] .= html_print_image( - 'images/cross.png', - true, - [ - 'title' => __('Delete remote configuration file'), - 'style' => 'vertical-align: middle;', - ] - ).''; - $table->data[5][1] .= ''.ui_print_help_tip( - __('Delete this conf file implies that for restore you must reactive remote config in the local agent.'), - true - ); - } - } } else { - $table->data[5][1] = ''.__('Not available').''; + $table_adv_remote .= ''.__('Not available').''; } + $table_adv_remote .= '
'; - $cps_array[-1] = __('Disabled'); - if ($cps > 0) { - $cps_array[$cps] = __('Enabled'); + // Calculate cps value - agents. + if ($id_agente) { + $cps_val = service_agents_cps($id_agente); } else { - $cps_inc = 0; - if ($id_agente) { - $cps_inc = service_agents_cps($id_agente); + // No agent defined, use received cps as base value. + if ($cps >= 0) { + $cps_val = $cps; } - - $cps_array[$cps_inc] = __('Enabled'); } - $table->data[6][0] = __('Cascade protection services'); - $table->data[6][0] .= ui_print_help_tip(__('Disable the alerts and events of the elements that belong to this service'), true); - $table->data[6][1] = html_print_select($cps_array, 'cps', $cps, '', '', 0, true); + $cps_html = '
'; + $cps_html .= html_print_checkbox_switch('cps', $cps_val, ($cps >= 0), true); + $cps_html .= __('Cascade protection services').' '; + $cps_html .= ui_print_help_tip( + __('Alerts and events will be managed by the service joined by this agent.'), + true + ); + $cps_html .= '
'; + + $table_adv_cascade .= $cps_html; } -// Custom ID -$table->data[0][0] = __('Custom ID'); -$table->data[0][1] = html_print_input_text('custom_id', $custom_id, '', 16, 255, true); -$table->data[1][0] = __('Parent'); +$table_adv_parent = '

'.__('Parent').':

'; $params = []; $params['return'] = true; $params['show_helptip'] = true; @@ -532,75 +591,97 @@ $params['value'] = db_get_value('alias', 'tagente', 'id_agente', $id_parent); $params['selectbox_id'] = 'cascade_protection_module'; $params['javascript_is_function_select'] = true; $params['cascade_protection'] = true; - -$table->data[1][1] = ui_print_agent_autocomplete_input($params); +$table_adv_parent .= '
'; +$table_adv_parent .= ui_print_agent_autocomplete_input($params); if (enterprise_installed()) { - $table->data[1][1] .= html_print_checkbox('cascade_protection', 1, $cascade_protection, true).__('Cascade protection').' '.ui_print_help_icon('cascade_protection', true); + $table_adv_parent .= html_print_checkbox_switch( + 'cascade_protection', + 1, + $cascade_protection, + true + ).__('Cascade protection').' '; } -$table->data[1][1] .= '  '.__('Module').' '.html_print_select($modules_values, 'cascade_protection_module', $cascade_protection_module, '', '', 0, true); -// Learn mode / Normal mode -$table->data[3][0] = __('Module definition').ui_print_help_icon('module_definition', true); -$table->data[3][1] = __('Learning mode').' '.html_print_radio_button_extended( +$table_adv_parent .= __('Module').' '.html_print_select( + $modules_values, + 'cascade_protection_module', + $cascade_protection_module, + '', + '', + 0, + true +).'
'; + +// Learn mode / Normal mode. +$table_adv_module_mode = '

'.__('Module definition').':

'; +$table_adv_module_mode .= '
'; +$table_adv_module_mode .= html_print_radio_button_extended( 'modo', 1, - '', + __('Learning mode'), $modo, false, 'show_modules_not_learning_mode_context_help();', - 'style="margin-right: 40px;"', + '', true ); -$table->data[3][1] .= __('Normal mode').' '.html_print_radio_button_extended( +$table_adv_module_mode .= html_print_radio_button_extended( 'modo', 0, - '', + __('Normal mode'), $modo, false, 'show_modules_not_learning_mode_context_help();', - 'style="margin-right: 40px;"', + '', true ); -$table->data[3][1] .= __('Autodisable mode').' '.html_print_radio_button_extended( +$table_adv_module_mode .= html_print_radio_button_extended( 'modo', 2, - '', + __('Autodisable mode'), $modo, false, 'show_modules_not_learning_mode_context_help();', - 'style="margin-right: 40px;"', + '', true ); +$table_adv_module_mode .= '
'; -// Status (Disabled / Enabled) -$table->data[4][0] = __('Status'); -$table->data[4][1] = __('Disabled').' '.ui_print_help_tip(__('If the remote configuration is enabled, it will also go into standby mode when disabling it.'), true).' '.html_print_radio_button_extended('disabled', 1, '', $disabled, false, '', 'style="margin-right: 40px;"', true); -$table->data[4][1] .= __('Enabled').' '.html_print_radio_button_extended('disabled', 0, '', $disabled, false, '', 'style="margin-right: 40px;"', true); +// Status (Disabled / Enabled). +$table_adv_status = '

'.__('Disabled').': '.ui_print_help_tip(__('If the remote configuration is enabled, it will also go into standby mode when disabling it.'), true).'

'; +$table_adv_status .= html_print_checkbox_switch( + 'disabled', + 1, + $disabled, + true +).'
'; + +// Url address. if (enterprise_installed()) { - $table->data[4][2] = __('Url address').ui_print_help_tip(__('URL address must be complete, for example: https://pandorafms.com/'), true); - $table->data[4][3] = html_print_input_text( + $table_adv_url = '

'.__('Url address').': '.ui_print_help_tip(__('URL address must be complete, for example: https://pandorafms.com/'), true).'

'; + $table_adv_url .= html_print_input_text( 'url_description', $url_description, '', 45, 255, true - ); + ).'
'; } else { - $table->data[5][0] = __('Url address'); - $table->data[5][1] = html_print_input_text( + $table_adv_url = '

'.__('Url address').':

'; + $table_adv_url .= html_print_input_text( 'url_description', $url_description, '', 45, 255, true - ); + ).''; } -$table->data[5][2] = __('Quiet'); -$table->data[5][3] .= ui_print_help_tip(__('The agent still runs but the alerts and events will be stop'), true); -$table->data[5][3] = html_print_checkbox('quiet', 1, $quiet, true); +$table_adv_quiet = '

'.__('Quiet').': '; +$table_adv_quiet .= ui_print_help_tip(__('The agent still runs but the alerts and events will be stop'), true).'

'; +$table_adv_quiet .= html_print_checkbox_switch('quiet', 1, $quiet, true).'
'; $listIcons = gis_get_array_list_icons(); @@ -610,11 +691,11 @@ foreach ($listIcons as $index => $value) { } $path = 'images/gis_map/icons/'; -// TODO set better method the path -$table->data[0][2] = __('Agent icon').ui_print_help_tip(__('Agent icon for GIS Maps.'), true); +// TODO set better method the path. +$table_adv_agent_icon = '

'.__('Agent icon').': '.ui_print_help_tip(__('Agent icon for GIS Maps.'), true).'

'; if ($icon_path == '') { $display_icons = 'none'; - // Hack to show no icon. Use any given image to fix not found image errors + // Hack to show no icon. Use any given image to fix not found image errors. $path_without = 'images/spinner.png'; $path_default = 'images/spinner.png'; $path_ok = 'images/spinner.png'; @@ -629,7 +710,7 @@ if ($icon_path == '') { $path_warning = $path.$icon_path.'.warning.png'; } -$table->data[0][3] = html_print_select( +$table_adv_agent_icon .= html_print_select( $arraySelectIcon, 'icon_path', $icon_path, @@ -637,64 +718,84 @@ $table->data[0][3] = html_print_select( __('None'), '', true -).' '.html_print_image( +).html_print_image( $path_ok, true, [ 'id' => 'icon_ok', 'style' => 'display:'.$display_icons.';', ] -).' '.html_print_image( +).html_print_image( $path_bad, true, [ 'id' => 'icon_bad', 'style' => 'display:'.$display_icons.';', ] -).' '.html_print_image( +).html_print_image( $path_warning, true, [ 'id' => 'icon_warning', 'style' => 'display:'.$display_icons.';', ] -); +).'
'; if ($config['activate_gis']) { - $table->data[3][2] = __('Ignore new GIS data:'); - $table->data[3][3] = __('Yes').' '.html_print_radio_button_extended( - 'update_gis_data', - 0, - '', - $update_gis_data, - false, - '', - 'style="margin-right: 40px;"', - true - ); - $table->data[3][3] .= __('No').' '.html_print_radio_button_extended( - 'update_gis_data', - 1, - '', - $update_gis_data, - false, - '', - 'style="margin-right: 40px;"', - true - ); + $table_adv_gis = '

'.__('Ignore new GIS data:').'

'; + if ($new_agent) { + $update_gis_data = true; + } + + $table_adv_gis .= html_print_checkbox_switch('update_gis_data', 1, $update_gis_data, true).'No / Yes
'; } -ui_toggle(html_print_table($table, true), __('Advanced options')); -unset($table); + + +// General display distribution. +$table_adv_options = $table_adv_secondary_groups.'
+
+ '.$table_adv_secondary_groups_left.' +
+
+ '.$table_adv_secondary_groups_arrows.' +
+
+ '.$table_adv_secondary_groups_right.' +
+
+
+
'.$table_adv_parent.$table_adv_module_mode.$table_adv_cascade; + +if ($new_agent) { + // If agent is new, show custom id as old style format. + $table_adv_options .= $custom_id_div; +} + +$table_adv_options .= $table_adv_gis.'
+
'.$table_adv_agent_icon.$table_adv_url.$table_adv_quiet.$table_adv_status.$table_adv_remote.$table_adv_safe.'
+
'; + +echo '
'; + ui_toggle($table_adv_options, __('Advanced options'), '', true, false, 'white_box white_box_opened'); +echo '
'; + $table = new stdClass(); $table->width = '100%'; -$table->class = 'databox filters'; +$table->class = 'custom_fields_table'; -$table->head = []; +$table->head = [ + 0 => __('Click to display').ui_print_help_tip( + __('This field allows url insertion using the BBCode\'s url tag').'.
'.__('The format is: [url=\'url to navigate\']\'text to show\'[/url]').'.

'.__('e.g.: [url=google.com]Google web search[/url]'), + true + ), +]; +$table->class = 'info_table'; $table->style = []; -$table->style[0] = 'font-weight: bold; width: 100px;'; +$table->style[0] = 'font-weight: bold;'; $table->data = []; +$table->rowstyle = []; $fields = db_get_all_fields_in_table('tagent_custom_fields'); @@ -702,12 +803,13 @@ if ($fields === false) { $fields = []; } +$i = 0; foreach ($fields as $field) { - $data[0] = ''.$field['name'].''; - $data[0] .= ui_print_help_tip( - __('This field allows url insertion using the BBCode\'s url tag').'.
'.__('The format is: [url=\'url to navigate\']\'text to show\'[/url]').'.

'.__('e.g.: [url=google.com]Google web search[/url]'), - true - ); + $id_custom_field = $field['id_field']; + + $data[0] = '
'; + $data[0] .= ''.$field['name'].''; + $data[0] .= '
'; $combo = []; $combo = $field['combo_values']; $combo = explode(',', $combo); @@ -729,8 +831,15 @@ foreach ($fields as $field) { $custom_value = ''; } + $table->rowstyle[$i] = 'cursor: pointer;'; + if (!empty($custom_value)) { + $table->rowstyle[($i + 1)] = 'display: table-row;'; + } else { + $table->rowstyle[($i + 1)] = 'display: none;'; + } + if ($field['is_password_type']) { - $data[1] = html_print_input_text_extended( + $data_field[1] = html_print_input_text_extended( 'customvalue_'.$field['id_field'], $custom_value, 'customvalue_'.$field['id_field'], @@ -744,18 +853,18 @@ foreach ($fields as $field) { true ); } else { - $data[1] = html_print_textarea( + $data_field[1] = html_print_textarea( 'customvalue_'.$field['id_field'], 2, 65, $custom_value, - 'style="min-height: 30px; width:96%;"', + 'style="min-height: 30px;"', true ); } if ($field['combo_values'] !== '') { - $data[1] = html_print_select( + $data_field[1] = html_print_select( $combo_values, 'customvalue_'.$field['id_field'], $custom_value, @@ -776,19 +885,32 @@ foreach ($fields as $field) { ); }; - array_push($table->data, $data); + $table->rowid[] = 'name_field-'.$id_custom_field; + $table->data[] = $data; + + $table->rowid[] = 'field-'.$id_custom_field; + $table->data[] = $data_field; + $i += 2; } if (!empty($fields)) { - ui_toggle(html_print_table($table, true), __('Custom fields')); + echo '
'; + ui_toggle( + html_print_table($table, true), + __('Custom fields'), + '', + true, + false, + 'white_box white_box_opened' + ); + echo '
'; } -echo '
'; - +echo '
'; // The context help about the learning mode. if ($modo == 0) { - echo ""; + echo ""; } else { echo "'; if ($id_agente) { - echo '
'; + echo '
'; html_print_submit_button( __('Update'), 'updbutton', @@ -825,6 +947,19 @@ ui_require_jquery_file('bgiframe'); ?> diff --git a/pandora_console/godmode/alerts/configure_alert_template.php b/pandora_console/godmode/alerts/configure_alert_template.php index d6828dd599..d79ca90ca8 100644 --- a/pandora_console/godmode/alerts/configure_alert_template.php +++ b/pandora_console/godmode/alerts/configure_alert_template.php @@ -56,14 +56,20 @@ if ($a_template !== false) { if (defined('METACONSOLE')) { alerts_meta_print_header(); } else { - // ~ ui_print_page_header (__('Alerts') . - // ~ ' » ' . __('Configure alert template'), "", - // ~ false, "alerts_config", true); + $step = (int) get_parameter('step', 1); + if ($step == 1) { + $help_header = 'configure_alert_template_step_1'; + } else if ($step == 2) { + $help_header = 'configure_alert_template_step_2'; + } else if ($step == 3) { + $help_header = 'configure_alert_template_step_3'; + } + ui_print_page_header( __('Alerts').' » '.__('Configure alert template'), '', false, - '', + $help_header, true ); } @@ -83,7 +89,13 @@ if ($a_template !== false) { if (defined('METACONSOLE')) { alerts_meta_print_header(); } else { - ui_print_page_header(__('Alerts').' » '.__('Configure alert template'), 'images/gm_alerts.png', false, 'conf_alert_template', true); + ui_print_page_header( + __('Alerts').' » '.__('Configure alert template'), + 'images/gm_alerts.png', + false, + 'conf_alert_template', + true + ); } } else { db_pandora_audit( @@ -101,7 +113,22 @@ if ($a_template !== false) { if (defined('METACONSOLE')) { alerts_meta_print_header(); } else { - ui_print_page_header(__('Alerts').' » '.__('Configure alert template'), 'images/gm_alerts.png', false, 'conf_alert_template', true); + $step = (int) get_parameter('step', 1); + if ($step == 1) { + $help_header = 'configure_alert_template_step_1'; + } else if ($step == 2) { + $help_header = 'configure_alert_template_step_2'; + } else if ($step == 3) { + $help_header = 'configure_alert_template_step_3'; + } + + ui_print_page_header( + __('Alerts').' » '.__('Configure alert template'), + 'images/gm_alerts.png', + false, + $help_header, + true + ); } } @@ -679,7 +706,7 @@ if ($step == 2) { 'type', $type, '', - __('Select'), + __('None'), 0, true, false, @@ -1018,6 +1045,7 @@ var onchange_msg = ; var unknown = ; var error_message_min_max_zero = ; +var not_normal = ; function check_fields_step2() { var correct = true; @@ -1174,6 +1202,13 @@ if ($step == 2) { /* Show example */ $("span#example").empty ().append (critical); break; + case "not_normal": + $("#template-value, #template-max, span#matches_value, #template-min").hide (); + $("#template-example").show (); + + /* Show example */ + $("span#example").empty ().append (not_normal); + break; case "onchange": $("#template-value, #template-max, #template-min").hide (); $("#template-example, span#matches_value").show (); diff --git a/pandora_console/godmode/category/category.php b/pandora_console/godmode/category/category.php index 727be02057..e1a61a4db6 100755 --- a/pandora_console/godmode/category/category.php +++ b/pandora_console/godmode/category/category.php @@ -82,7 +82,13 @@ $filter['limit'] = (int) $config['block_size']; // Search action: This will filter the display category view $result = false; -$result = categories_get_all_categories(); +$result = db_get_all_rows_filter( + 'tcategory', + [ + 'limit' => $filter['limit'], + 'offset' => $filter['offset'], + ] +); // Display categories previously filtered or not $rowPair = true; @@ -94,7 +100,7 @@ if (!empty($result)) { $table = new stdClass(); $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->data = []; $table->head = []; @@ -123,7 +129,8 @@ if (!empty($result)) { $data[1] .= ''.html_print_image('images/cross.png', true, ['title' => 'Delete']).''; } else { $data[0] = "".$category['name'].''; - $data[1] = "".html_print_image('images/config.png', true, ['title' => 'Edit']).'  '; + $table->cellclass[][1] = 'action_buttons'; + $data[1] = "".html_print_image('images/config.png', true, ['title' => 'Edit']).''; $data[1] .= ''.html_print_image('images/cross.png', true, ['title' => 'Delete']).''; } @@ -131,6 +138,7 @@ if (!empty($result)) { } html_print_table($table); + ui_pagination($total_categories, $url, $offset, 0, false, 'offset', true, 'pagination-bottom'); } else { // No categories available or selected ui_print_info_message(['no_close' => true, 'message' => __('No categories found') ]); diff --git a/pandora_console/godmode/events/event_filter.php b/pandora_console/godmode/events/event_filter.php index 40d60650f6..60ba973996 100644 --- a/pandora_console/godmode/events/event_filter.php +++ b/pandora_console/godmode/events/event_filter.php @@ -113,31 +113,33 @@ if ($filters === false) { $table = new stdClass(); $table->width = '100%'; -$table->class = 'databox data'; +$table->class = 'info_table'; $table->head = []; -$table->head[0] = __('Name'); -$table->head[1] = __('Group'); -$table->head[2] = __('Event type'); -$table->head[3] = __('Event status'); -$table->head[4] = __('Severity'); -$table->head[5] = __('Action').html_print_checkbox('all_delete', 0, false, true, false); +$table->head[0] = html_print_checkbox('all_delete', 0, false, true, false); +$table->head[1] = __('Name'); +$table->head[2] = __('Group'); +$table->head[3] = __('Event type'); +$table->head[4] = __('Event status'); +$table->head[5] = __('Severity'); +$table->head[6] = __('Action'); $table->style = []; -$table->style[0] = 'font-weight: bold'; +$table->style[1] = 'font-weight: bold'; $table->align = []; -$table->align[1] = 'left'; $table->align[2] = 'left'; $table->align[3] = 'left'; - $table->align[4] = 'left'; + $table->align[5] = 'left'; +$table->align[6] = 'left'; $table->size = []; -$table->size[0] = '50%'; -$table->size[1] = '5px'; -$table->size[2] = '80px'; +$table->size[0] = '20px'; +$table->size[1] = '50%'; +$table->size[2] = '5px'; $table->size[3] = '80px'; $table->size[4] = '80px'; -$table->size[5] = '40px'; +$table->size[5] = '80px'; +$table->size[6] = '40px'; $table->data = []; $total_filters = db_get_all_rows_filter('tevent_filter', false, 'COUNT(*) AS total'); @@ -147,13 +149,14 @@ $total_filters = $total_filters[0]['total']; foreach ($filters as $filter) { $data = []; - $data[0] = ''.$filter['id_name'].''; - $data[1] = ui_print_group_icon($filter['id_group_filter'], true); - $data[2] = events_get_event_types($filter['event_type']); - $data[3] = events_get_status($filter['status']); - $data[4] = events_get_severity_types($filter['severity']); - $data[5] = "".html_print_image('images/cross.png', true, ['title' => __('Delete')]).''.html_print_checkbox_extended('delete_multiple[]', $filter['id_filter'], false, false, '', 'class="check_delete"', true); + $data[0] = html_print_checkbox_extended('delete_multiple[]', $filter['id_filter'], false, false, '', 'class="check_delete"', true); + $data[1] = ''.$filter['id_name'].''; + $data[2] = ui_print_group_icon($filter['id_group_filter'], true); + $data[3] = events_get_event_types($filter['event_type']); + $data[4] = events_get_status($filter['status']); + $data[5] = events_get_severity_types($filter['severity']); + $table->cellclass[][6] = 'action_buttons'; + $data[6] = "".html_print_image('images/cross.png', true, ['title' => __('Delete')]).''; array_push($table->data, $data); } @@ -188,8 +191,6 @@ echo '
'; ?> '; } -// Other Checks for the edit the reports +// Other Checks for the edit the reports. if ($idReport != 0) { $report = db_get_row_filter('treport', ['id_report' => $idReport]); $type_access_selected = reports_get_type_access($report); $edit = false; switch ($type_access_selected) { case 'group_view': - $edit = check_acl($config['id_user'], $report['id_group'], 'RW'); + $edit = check_acl( + $config['id_user'], + $report['id_group'], + 'RW' + ); break; case 'group_edit': - $edit = check_acl($config['id_user'], $report['id_group_edit'], 'RW'); + $edit = check_acl( + $config['id_user'], + $report['id_group_edit'], + 'RW' + ); break; case 'user_edit': @@ -162,14 +196,21 @@ if ($idReport != 0) { $edit = true; } break; + + default: + // Default. + break; } if (! $edit) { - // The user that created the report should can delete it. Despite its permissions. + // The user that created the report should can delete it. + // Despite its permissions. $delete_report_bypass = false; if ($action == 'delete_report') { - if ($config['id_user'] == $report['id_user'] || is_user_admin($config['id_user'])) { + if ($config['id_user'] == $report['id_user'] + || is_user_admin($config['id_user']) + ) { $delete_report_bypass = true; } } @@ -191,7 +232,10 @@ switch ($action) { case 'list_items': $resultOperationDB = null; $position_to_sort = (int) get_parameter('position_to_sort', 1); - $ids_serialize = (string) get_parameter('ids_items_to_sort', ''); + $ids_serialize = (string) get_parameter( + 'ids_items_to_sort', + '' + ); $move_to = (string) get_parameter('move_to', 'after'); $countItems = db_get_sql( @@ -201,40 +245,25 @@ switch ($action) { WHERE id_report = '.$idReport ); - if (($countItems < $position_to_sort) || ($position_to_sort < 1)) { + if (($countItems < $position_to_sort) + || ($position_to_sort < 1) + ) { $resultOperationDB = false; } else if (!empty($ids_serialize)) { $ids = explode('|', $ids_serialize); - - switch ($config['dbtype']) { - case 'mysql': - $items = db_get_all_rows_sql( - ' - SELECT id_rc, `order` - FROM treport_content - WHERE id_report = '.$idReport.' - ORDER BY `order`' - ); - break; - - case 'oracle': - case 'postgresql': - $items = db_get_all_rows_sql( - ' - SELECT id_rc, "order" - FROM treport_content - WHERE id_report = '.$idReport.' - ORDER BY "order"' - ); - break; - } + $items = db_get_all_rows_sql( + ' + SELECT id_rc, `order` + FROM treport_content + WHERE id_report = '.$idReport.' + ORDER BY `order`' + ); if ($items === false) { $items = []; } - - // Clean the repeated order values + // Clean the repeated order values. $order_temp = 1; foreach ($items as $item) { switch ($config['dbtype']) { @@ -254,6 +283,10 @@ switch ($action) { ['id_rc' => $item['id_rc']] ); break; + + default: + // Default. + break; } $order_temp++; @@ -281,19 +314,19 @@ switch ($action) { ORDER BY "order"' ); break; + + default: + // Default. + break; } if ($items === false) { $items = []; } - - - $temp = []; - $temp = []; foreach ($items as $item) { - // Remove the contents from the block to sort + // Remove the contents from the block to sort. if (array_search($item['id_rc'], $ids) === false) { $temp[$item['order']] = $item['id_rc']; } @@ -301,8 +334,6 @@ switch ($action) { $items = $temp; - - $sorted_items = []; foreach ($items as $pos => $id_unsort) { if ($pos == $position_to_sort) { @@ -324,8 +355,6 @@ switch ($action) { $items = $sorted_items; - - foreach ($items as $order => $id) { switch ($config['dbtype']) { case 'mysql': @@ -344,6 +373,10 @@ switch ($action) { ['id_rc' => $id] ); break; + + default: + // Default. + break; } } @@ -352,6 +385,10 @@ switch ($action) { $resultOperationDB = false; } break; + + default: + // Default. + break; } break; @@ -360,7 +397,8 @@ switch ($action) { $ids_serialize = (string) get_parameter('ids_items_to_delete', ''); if (!empty($ids_serialize)) { - $sql = "DELETE FROM treport_content WHERE id_rc IN ($ids_serialize)"; + $sql = 'DELETE FROM treport_content + WHERE id_rc IN ('.$ids_serialize.')'; $resultOperationDB = db_process_sql($sql); } else { $resultOperationDB = false; @@ -380,7 +418,9 @@ switch ($action) { if (($countItems < $position_to_delete) || ($position_to_delete < 1)) { $resultOperationDB = false; } else { - $sql = "SELECT id_rc FROM treport_content WHERE id_report=$idReport ORDER BY '`order`'"; + $sql = 'SELECT id_rc + FROM treport_content + WHERE id_report='.$idReport." ORDER BY '`order`'"; $items = db_get_all_rows_sql($sql); switch ($pos_delete) { case 'above': @@ -390,7 +430,10 @@ switch ($action) { $i = 1; foreach ($items as $key => $item) { if ($i < $position_to_delete) { - $resultOperationDB = db_process_sql_delete('treport_content', ['id_rc' => $item['id_rc']]); + $resultOperationDB = db_process_sql_delete( + 'treport_content', + ['id_rc' => $item['id_rc']] + ); } $i++; @@ -415,6 +458,10 @@ switch ($action) { } } break; + + default: + // Default. + break; } } break; @@ -433,6 +480,7 @@ switch ($action) { } $subsection = ''; + $helpers = ''; switch ($activeTab) { case 'main': $buttons['list_reports']['active'] = true; @@ -440,13 +488,20 @@ switch ($action) { break; default: - $subsection = reporting_enterprise_add_subsection_main($activeTab, $buttons); + $data_tab = reporting_enterprise_add_subsection_main( + $activeTab, + $buttons + ); + + $subsection = $data_tab['subsection']; + $buttons = $data_tab['buttons']; + $helpers = $data_tab['helpers']; break; } - // Page header for metaconsole - if ($enterpriseEnable and defined('METACONSOLE')) { - // Bread crumbs + // Page header for metaconsole. + if ($enterpriseEnable && defined('METACONSOLE')) { + // Bread crumbs. ui_meta_add_breadcrumb( [ 'link' => 'index.php?sec=reporting&sec2=godmode/reporting/reporting_builder&pure='.$pure, @@ -456,12 +511,21 @@ switch ($action) { ui_meta_print_page_header($nav_bar); - // Print header + // Print header. ui_meta_print_header(__('Reporting'), '', $buttons); - } - // Page header for normal console - else { - ui_print_page_header(__('Custom reporting'), 'images/op_reporting.png', false, '', false, $buttons, false, '', 60); + } else { + // Page header for normal console. + ui_print_page_header( + __('Custom reporting'), + 'images/op_reporting.png', + false, + '', + false, + $buttons, + false, + '', + 60 + ); } @@ -469,9 +533,11 @@ switch ($action) { $delete = false; switch ($type_access_selected) { case 'group_view': - if ($config['id_user'] == $report['id_user'] || is_user_admin($config['id_user'])) { + if ($config['id_user'] == $report['id_user'] + || is_user_admin($config['id_user']) + ) { $delete = true; - // owner can delete + // Owner can delete. } else { $delete = check_acl( $config['id_user'], @@ -482,9 +548,11 @@ switch ($action) { break; case 'group_edit': - if ($config['id_user'] == $report['id_user'] || is_user_admin($config['id_user'])) { + if ($config['id_user'] == $report['id_user'] + || is_user_admin($config['id_user']) + ) { $delete = true; - // owner can delete + // Owner can delete. } else { $delete = check_acl( $config['id_user'], @@ -501,6 +569,10 @@ switch ($action) { $delete = true; } break; + + default: + // Default. + break; } if (! $delete) { @@ -514,9 +586,15 @@ switch ($action) { $result = reports_delete_report($idReport); if ($result !== false) { - db_pandora_audit('Report management', "Delete report #$idReport"); + db_pandora_audit( + 'Report management', + 'Delete report #'.$idReport + ); } else { - db_pandora_audit('Report management', "Fail try to delete report #$idReport"); + db_pandora_audit( + 'Report management', + 'Fail try to delete report #'.$idReport + ); } ui_print_result_message( @@ -531,7 +609,7 @@ switch ($action) { $search_sql = ''; if ($search != '') { - $search_name = "%$search%' OR description LIKE '%$search%"; + $search_name = '%'.$search."%' OR description LIKE '%".$search.'%'; } $table_aux = new stdClass(); @@ -543,25 +621,57 @@ switch ($action) { $table_aux->colspan[0][0] = 4; $table_aux->data[0][0] = ''.__('Group').''; - $table_aux->data[0][1] = html_print_select_groups(false, $access, true, 'id_group', $id_group, '', '', '', true, false, true, '', false, '', false, false, 'id_grupo', $strict_user).'
'; + $table_aux->data[0][1] = html_print_select_groups( + false, + $access, + true, + 'id_group', + $id_group, + '', + '', + '', + true, + false, + true, + '', + false, + '', + false, + false, + 'id_grupo' + ).'
'; - $table_aux->data[0][2] = ''.__('Free text for search: ').ui_print_help_tip( + $table_aux->data[0][2] = ''.__('Free text for search: '); + $table_aux->data[0][2] .= ui_print_help_tip( __('Search by report name or description, list matches.'), true - ).''; - $table_aux->data[0][3] = html_print_input_text('search', $search, '', 30, '', true); + ); + $table_aux->data[0][2] .= ''; + $table_aux->data[0][3] = html_print_input_text( + 'search', + $search, + '', + 30, + '', + true + ); - $table_aux->data[0][6] = html_print_submit_button(__('Search'), 'search_submit', false, 'class="sub upd"', true); + $table_aux->data[0][6] = html_print_submit_button( + __('Search'), + 'search_submit', + false, + 'class="sub upd"', + true + ); + $url_rb = 'index.php?sec=reporting&sec2=godmode/reporting/reporting_builder'; if (is_metaconsole()) { - $filter = ""; + $filter = ''; $filter .= html_print_table($table_aux, true); $filter .= ''; ui_toggle($filter, __('Show Option')); } else { - echo "
"; + echo ''; html_print_table($table_aux); echo '
'; } @@ -571,9 +681,9 @@ switch ($action) { ui_require_jquery_file('bgiframe'); ui_require_jquery_file('autocomplete'); - // Show only selected groups + // Show only selected groups. if ($id_group > 0) { - $group = ["$id_group" => $id_group]; + $group = [$id_group => $id_group]; } else { $group = false; } @@ -595,16 +705,16 @@ switch ($action) { } // Fix : group filter was not working - // Show only selected groups + // Show only selected groups. if ($id_group > 0) { - $group = ["$id_group" => $id_group]; + $group = [$id_group => $id_group]; $filter['id_group'] = $id_group; } else { $group = false; } - // Filter normal and metaconsole reports - if ($config['metaconsole'] == 1 and defined('METACONSOLE')) { + // Filter normal and metaconsole reports. + if ($config['metaconsole'] == 1 && defined('METACONSOLE')) { $filter['metaconsole'] = 1; } else { $filter['metaconsole'] = 0; @@ -623,8 +733,7 @@ switch ($action) { ], $return_all_group, $access, - $group, - $strict_user + $group ); $total_reports = (int) count( @@ -633,25 +742,27 @@ switch ($action) { ['name'], $return_all_group, $access, - $group, - $strict_user + $group ) ); - if (sizeof($reports)) { + if (count($reports)) { $url = 'index.php?sec=reporting&sec2=godmode/reporting/reporting_builder'; ui_pagination($total_reports, $url, $offset, $pagination); $table = new stdClass(); $table->id = 'report_list'; $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->cellpadding = 0; $table->cellspacing = 0; $table->head = []; $table->align = []; + $table->headstyle = []; + $table->style = []; + $table->align[2] = 'left'; $table->align[3] = 'left'; $table->align[4] = 'left'; @@ -663,19 +774,29 @@ switch ($action) { $table->size[0] = '20%'; $table->size[1] = '30%'; $table->size[2] = '2%'; + $table->headstyle[2] = 'min-width: 35px;'; $table->size[3] = '2%'; + $table->headstyle[3] = 'min-width: 35px;'; $table->size[4] = '2%'; + $table->headstyle[4] = 'min-width: 35px;'; $table->size[5] = '2%'; + $table->headstyle[5] = 'min-width: 35px;'; $table->size[6] = '2%'; - $table->size['csv'] = '5%'; + $table->headstyle[6] = 'min-width: 35px;'; + $table->size[7] = '5%'; + $table->headstyle['csv'] = 'min-width: 65px;'; + $table->style[7] = 'text-align: center;'; + + $table->headstyle[9] = 'min-width: 100px;'; + $table->style[9] = 'text-align: center;'; $next = 4; - // Calculate dinamically the number of the column + // Calculate dinamically the number of the column. if (enterprise_hook('load_custom_reporting_1') !== ENTERPRISE_NOT_HOOK) { $next = 7; } - // Admin options only for RM flag + // Admin options only for RM flag. if (check_acl($config['id_user'], 0, 'RM')) { $table->head[$next] = __('Private'); $table->size[$next] = '2%'; @@ -706,7 +827,6 @@ switch ($action) { // $table->size = array (); $table->size[$next] = '10%'; $table->align[$next] = 'left'; - $table->headstyle[$next] = 'text-align:left;'; } $columnview = false; @@ -715,18 +835,44 @@ switch ($action) { foreach ($reports as $report) { if (!is_user_admin($config['id_user'])) { - if ($report['private'] && $report['id_user'] != $config['id_user']) { - if (!check_acl($config['id_user'], $report['id_group'], 'RR') - && !check_acl($config['id_user'], $report['id_group'], 'RW') - && !check_acl($config['id_user'], $report['id_group'], 'RM') + if ($report['private'] + && $report['id_user'] != $config['id_user'] + ) { + if (!check_acl( + $config['id_user'], + $report['id_group'], + 'RR' + ) + && !check_acl( + $config['id_user'], + $report['id_group'], + 'RW' + ) + && !check_acl( + $config['id_user'], + $report['id_group'], + 'RM' + ) ) { continue; } } - if (!check_acl($config['id_user'], $report['id_group'], 'RR') - && !check_acl($config['id_user'], $report['id_group'], 'RW') - && !check_acl($config['id_user'], $report['id_group'], 'RM') + if (!check_acl( + $config['id_user'], + $report['id_group'], + 'RR' + ) + && !check_acl( + $config['id_user'], + $report['id_group'], + 'RW' + ) + && !check_acl( + $config['id_user'], + $report['id_group'], + 'RM' + ) ) { continue; } @@ -734,7 +880,9 @@ switch ($action) { $data = []; - if (check_acl($config['id_user'], $report['id_group'], 'RW') || check_acl($config['id_user'], $report['id_group'], 'RM')) { + if (check_acl($config['id_user'], $report['id_group'], 'RW') + || check_acl($config['id_user'], $report['id_group'], 'RM') + ) { $data[0] = ''.ui_print_truncate_text($report['name'], 70).''; } else { $data[0] = ui_print_truncate_text($report['name'], 70); @@ -743,16 +891,36 @@ switch ($action) { $data[1] = ui_print_truncate_text($report['description'], 70); - // Remove html and xml button if items are larger than limit - $item_count = db_get_num_rows('SELECT * FROM treport_content WHERE id_report='.$report['id_report']); + // Remove html and xml button if items are larger than limit. + $item_count = db_get_num_rows( + 'SELECT * FROM treport_content + WHERE id_report='.$report['id_report'] + ); $report['overload'] = $item_count >= $config['report_limit']; if ($report['overload']) { - $data[2] = html_print_image('images/application_not_writable.png', true, ['title' => __('This report exceeds the item limit for realtime operations')]); + $data[2] = html_print_image( + 'images/application_not_writable.png', + true, + ['title' => __('This report exceeds the item limit for realtime operations')] + ); $data[3] = null; } else if (!$report['non_interactive']) { - $data[2] = ''.html_print_image('images/html.png', true, ['title' => __('HTML view')]).''; - $data[3] = ''.html_print_image('images/xml.png', true, ['title' => __('Export to XML')]).''; - // I chose ajax.php because it's supposed to give XML anyway + $data[2] = ''; + $data[2] .= html_print_image( + 'images/html.png', + true, + ['title' => __('HTML view')] + ); + $data[2] .= ''; + $data[3] = ''; + $data[3] .= html_print_image( + 'images/xml.png', + true, + ['title' => __('Export to XML')] + ); + $data[3] .= ''; + // I chose ajax.php because it's supposed + // to give XML anyway. } else { $data[2] = html_print_image( 'images/html_disabled.png', @@ -764,15 +932,13 @@ switch ($action) { ); } - - // Calculate dinamically the number of the column + // Calculate dinamically the number of the column. $next = 4; if (enterprise_hook('load_custom_reporting_2') !== ENTERPRISE_NOT_HOOK) { $next = 7; } - - // Admin options only for RM flag + // Admin options only for RM flag. if (check_acl($config['id_user'], 0, 'RM')) { if ($report['private'] == 1) { $data[$next] = __('Yes'); @@ -799,14 +965,22 @@ switch ($action) { switch ($type_access_selected) { case 'group_view': - $edit = check_acl($config['id_user'], $report['id_group'], 'RW'); + $edit = check_acl( + $config['id_user'], + $report['id_group'], + 'RW' + ); $delete = $edit || is_user_admin($config['id_user']) || $config['id_user'] == $report['id_user']; break; case 'group_edit': - $edit = check_acl($config['id_user'], $report['id_group_edit'], 'RW'); + $edit = check_acl( + $config['id_user'], + $report['id_group_edit'], + 'RW' + ); $delete = $edit || is_user_admin($config['id_user']) || $config['id_user'] == $report['id_user']; @@ -820,24 +994,24 @@ switch ($action) { $delete = true; } break; + + default: + // Default. + break; } if ($edit || $delete) { $columnview = true; + $table->cellclass[][$next] = 'action_buttons'; + if (!isset($table->head[$next])) { $table->head[$next] = ''.__('Op.').''.html_print_checkbox('all_delete', 0, false, true, false); $table->size = []; // $table->size[$next] = '80px'; - $table->style[$next] = 'text-align:left;'; } if ($edit) { $data[$next] = '
'; - $data[$next] .= html_print_input_hidden( - 'id_report', - $report['id_report'], - true - ); $data[$next] .= html_print_input_image( 'edit', 'images/config.png', @@ -846,13 +1020,16 @@ switch ($action) { true, ['title' => __('Edit')] ); + $data[$next] .= html_print_input_hidden( + 'id_report', + $report['id_report'], + true + ); $data[$next] .= '
'; } if ($delete) { $data[$next] .= '
'; - $data[$next] .= html_print_input_hidden('id_report', $report['id_report'], true); - $data[$next] .= html_print_input_hidden('action', 'delete_report', true); $data[$next] .= html_print_input_image( 'delete', 'images/cross.png', @@ -861,8 +1038,26 @@ switch ($action) { true, ['title' => __('Delete')] ); + $data[$next] .= html_print_input_hidden( + 'id_report', + $report['id_report'], + true + ); + $data[$next] .= html_print_input_hidden( + 'action', + 'delete_report', + true + ); - $data[$next] .= html_print_checkbox_extended('massive_report_check', $report['id_report'], false, false, '', 'class="check_delete"', true); + $data[$next] .= html_print_checkbox_extended( + 'massive_report_check', + $report['id_report'], + false, + false, + '', + 'class="check_delete"', + true + ); $data[$next] .= '
'; } @@ -887,11 +1082,28 @@ switch ($action) { } html_print_table($table); + ui_pagination( + $total_reports, + $url, + $offset, + $pagination, + false, + 'offset', + true, + 'pagination-bottom' + ); } else { - ui_print_info_message([ 'no_close' => true, 'message' => __('No data found.') ]); + ui_print_info_message( + [ + 'no_close' => true, + 'message' => __('No data found.'), + ] + ); } - if (check_acl($config['id_user'], 0, 'RW') || check_acl($config['id_user'], 0, 'RM')) { + if (check_acl($config['id_user'], 0, 'RW') + || check_acl($config['id_user'], 0, 'RM') + ) { echo '
'; if (defined('METACONSOLE')) { echo '
'; @@ -899,7 +1111,12 @@ switch ($action) { echo '
'; } - html_print_submit_button(__('Create report'), 'create', false, 'class="sub next"'); + html_print_submit_button( + __('Create report'), + 'create', + false, + 'class="sub next"' + ); echo ''; echo '
'; @@ -909,13 +1126,17 @@ switch ($action) { } echo ''; - html_print_submit_button(__('Delete'), 'delete_btn', false, 'class="sub delete" style="margin-left:5px;"'); + html_print_submit_button( + __('Delete'), + 'delete_btn', + false, + 'class="sub delete" style="margin-left:5px;"' + ); echo '
'; echo '
'; } enterprise_hook('close_meta_frame'); - return; break; @@ -924,7 +1145,7 @@ switch ($action) { case 'main': $reportName = ''; $idGroupReport = 0; - // All groups + // All groups. $description = ''; $resultOperationDB = null; $report_id_user = 0; @@ -943,6 +1164,10 @@ switch ($action) { $idGroupReport = $report['id_group']; $description = $report['description']; break; + + default: + // Default. + break; } break; @@ -953,12 +1178,15 @@ switch ($action) { $reportName = get_parameter('name'); $idGroupReport = get_parameter('id_group'); $description = get_parameter('description'); - $type_access_selected = get_parameter('type_access', 'group_view'); + $type_access_selected = get_parameter( + 'type_access', + 'group_view' + ); $id_group_edit_param = (int) get_parameter('id_group_edit', 0); $report_id_user = get_parameter('report_id_user'); $non_interactive = get_parameter('non_interactive', 0); - // Pretty font by default for pdf + // Pretty font by default for pdf. $custom_font = 'FreeSans.ttf'; switch ($type_access_selected) { @@ -976,6 +1204,10 @@ switch ($action) { $id_group_edit = 0; $private = 1; break; + + default: + // Default. + break; } if ($action == 'update') { @@ -1011,12 +1243,12 @@ switch ($action) { if ($resultOperationDB !== false) { db_pandora_audit( 'Report management', - "Update report #$idReport" + 'Update report #'.$idReport ); } else { db_pandora_audit( 'Report management', - "Fail try to update report #$idReport" + 'Fail try to update report #'.$idReport ); } } else { @@ -1026,10 +1258,10 @@ switch ($action) { $action = 'edit'; } else if ($action == 'save') { if ($reportName != '' && $idGroupReport != '') { - // This flag allow to differentiate between normal console and metaconsole reports + // This flag allow to differentiate + // between normal console and metaconsole reports. $metaconsole_report = (int) is_metaconsole(); - // Juanma (07/05/2014) New feature: Custom front page for reports if ($config['custom_report_front']) { $custom_font = $config['custom_report_front_font']; $logo = $config['custom_report_front_logo']; @@ -1037,9 +1269,16 @@ switch ($action) { $first_page = $config['custom_report_front_firstpage']; $footer = $config['custom_report_front_footer']; } else { - $start_url = ui_get_full_url(false, false, false, false); + $start_url = ui_get_full_url( + false, + false, + false, + false + ); $first_page = '<p style="text-align: center;">&nbsp;</p> <p style="text-align: center;">&nbsp;</p> <p style="text-align: center;">&nbsp;</p> <p style="text-align: center;">&nbsp;</p> <p style="text-align: center;">&nbsp;</p> <p style="text-align: center;">&nbsp;</p> <p style="text-align: center;">&nbsp;</p> <p style="text-align: center;"><img src="'.$start_url.'/images/pandora_report_logo.png" alt="" width="800" /></p> <p style="text-align: center;">&nbsp;</p> <p style="text-align: center;"><span style="font-size: xx-large;">(_REPORT_NAME_)</span></p> <p style="text-align: center;"><span style="font-size: large;">(_DATETIME_)</span></p>'; - $logo = $header = $footer = null; + $logo = null; + $header = null; + $footer = null; } $idOrResult = db_process_sql_insert( @@ -1062,9 +1301,15 @@ switch ($action) { ); if ($idOrResult !== false) { - db_pandora_audit('Report management', "Create report #$idOrResult"); + db_pandora_audit( + 'Report management', + 'Create report #'.$idOrResult + ); } else { - db_pandora_audit('Report management', 'Fail try to create report'); + db_pandora_audit( + 'Report management', + 'Fail try to create report' + ); } } else { $idOrResult = false; @@ -1078,7 +1323,7 @@ switch ($action) { $report_id_user = $config['id_user']; } - $action = $resultOperationDB ? 'edit' : 'new'; + $action = ($resultOperationDB) ? 'edit' : 'new'; } break; @@ -1089,32 +1334,31 @@ switch ($action) { ['id_report' => $idReport] ); - - - $reportName = $report['name']; $idGroupReport = $report['id_group']; $description = $report['description']; $good_format = false; switch ($action) { case 'update': - $values = []; $values['id_report'] = $idReport; - // --------------------------------------------------- - // $values['name'] = (string) get_parameter('name'); $values['description'] = get_parameter('description'); $values['type'] = get_parameter('type', null); $values['recursion'] = get_parameter('recursion', null); $label = get_parameter('label', ''); - // Add macros name + // Add macros name. $items_label = []; $items_label['type'] = get_parameter('type'); $items_label['id_agent'] = get_parameter('id_agent'); - $items_label['id_agent_module'] = get_parameter('id_agent_module'); + $items_label['id_agent_module'] = get_parameter( + 'id_agent_module' + ); $name_it = (string) get_parameter('name'); - $values['name'] = reporting_label_macro($items_label, $name_it); + $values['name'] = reporting_label_macro( + $items_label, + $name_it + ); /* Added support for projection graphs, @@ -1126,13 +1370,14 @@ switch ($action) { switch ($values['type']) { case 'projection_graph': $values['period'] = get_parameter('period1'); - $values['top_n_value'] = get_parameter('period2'); + $values['top_n_value'] = get_parameter( + 'period2' + ); $values['text'] = get_parameter('text'); $good_format = true; break; case 'event_report_log': - $agents_to_report = get_parameter('id_agents3'); $source = get_parameter('source', ''); $search = get_parameter('search', ''); @@ -1150,16 +1395,29 @@ switch ($action) { case 'prediction_date': $values['period'] = get_parameter('period1'); - $values['top_n'] = get_parameter('radiobutton_max_min_avg'); - $values['top_n_value'] = get_parameter('quantity'); + $values['top_n'] = get_parameter( + 'radiobutton_max_min_avg' + ); + $values['top_n_value'] = get_parameter( + 'quantity' + ); $interval_max = get_parameter('max_interval'); $interval_min = get_parameter('min_interval'); - // Checks intervals fields - if (preg_match('/^(\-)*[0-9]*\.?[0-9]+$/', $interval_max) and preg_match('/^(\-)*[0-9]*\.?[0-9]+$/', $interval_min)) { + // Checks intervals fields. + if (preg_match( + '/^(\-)*[0-9]*\.?[0-9]+$/', + $interval_max + ) + && preg_match( + '/^(\-)*[0-9]*\.?[0-9]+$/', + $interval_min + ) + ) { $good_format = true; } - $intervals = get_parameter('max_interval').';'.get_parameter('min_interval'); + $intervals = get_parameter('max_interval').';'; + $intervals .= get_parameter('min_interval'); $values['text'] = $intervals; break; @@ -1170,19 +1428,32 @@ switch ($action) { case 'SLA': case 'availability_graph': $values['period'] = get_parameter('period'); - $values['top_n'] = get_parameter('combo_sla_sort_options', 0); - $values['top_n_value'] = get_parameter('quantity'); + $values['top_n'] = get_parameter( + 'combo_sla_sort_options', + 0 + ); + $values['top_n_value'] = get_parameter( + 'quantity' + ); $values['text'] = get_parameter('text'); - $values['show_graph'] = get_parameter('combo_graph_options'); + $values['show_graph'] = get_parameter( + 'combo_graph_options' + ); $good_format = true; break; case 'agent_module': $agents_to_report = get_parameter('id_agents2'); - $modules_to_report = get_parameter('module', ''); + $modules_to_report = get_parameter( + 'module', + '' + ); - $es['module'] = get_same_modules($agents_to_report, $modules_to_report); + $es['module'] = get_same_modules( + $agents_to_report, + $modules_to_report + ); $es['id_agents'] = $agents_to_report; $values['external_source'] = json_encode($es); @@ -1193,7 +1464,9 @@ switch ($action) { $values['period'] = 0; $es['date'] = get_parameter('date'); $es['id_agents'] = get_parameter('id_agents'); - $es['inventory_modules'] = get_parameter('inventory_modules'); + $es['inventory_modules'] = get_parameter( + 'inventory_modules' + ); $description = get_parameter('description'); $values['external_source'] = json_encode($es); $good_format = true; @@ -1202,37 +1475,85 @@ switch ($action) { case 'inventory_changes': $values['period'] = get_parameter('period'); $es['id_agents'] = get_parameter('id_agents'); - $es['inventory_modules'] = get_parameter('inventory_modules'); + $es['inventory_modules'] = get_parameter( + 'inventory_modules' + ); $description = get_parameter('description'); $values['external_source'] = json_encode($es); $good_format = true; break; case 'netflow_area': - case 'netflow_pie': case 'netflow_data': - case 'netflow_statistics': case 'netflow_summary': - $values['text'] = get_parameter('netflow_filter'); - $values['description'] = get_parameter('description'); + $values['text'] = get_parameter( + 'netflow_filter' + ); + $values['description'] = get_parameter( + 'description' + ); $values['period'] = get_parameter('period'); $values['top_n'] = get_parameter('resolution'); - $values['top_n_value'] = get_parameter('max_values'); + $values['top_n_value'] = get_parameter( + 'max_values' + ); $good_format = true; break; case 'availability': // HACK it is saved in show_graph field. - // Show interfaces instead the modules - $values['show_graph'] = get_parameter('checkbox_show_address_agent'); - $values['period'] = get_parameter('period'); + // Show interfaces instead the modules. + $values['show_graph'] = get_parameter( + 'checkbox_show_address_agent' + ); + $values['period'] = get_parameter( + 'period' + ); + $values['total_time'] = get_parameter( + 'total_time' + ); + $values['time_failed'] = get_parameter( + 'time_failed' + ); + $values['time_in_ok_status'] = get_parameter( + 'time_in_ok_status' + ); + $values['time_in_unknown_status'] = get_parameter( + 'time_in_unknown_status' + ); + $values['time_of_not_initialized_module'] = get_parameter( + 'time_of_not_initialized_module' + ); + $values['time_of_downtime'] = get_parameter( + 'time_of_downtime' + ); + $values['total_checks'] = get_parameter( + 'total_checks' + ); + $values['checks_failed'] = get_parameter( + 'checks_failed' + ); + $values['checks_in_ok_status'] = get_parameter( + 'checks_in_ok_status' + ); + $values['unknown_checks'] = get_parameter( + 'unknown_checks' + ); + $values['agent_max_value'] = get_parameter( + 'agent_max_value' + ); + $values['agent_min_value'] = get_parameter( + 'agent_min_value' + ); $good_format = true; break; case 'simple_graph': case 'simple_baseline_graph': // HACK it is saved in show_graph field. - $values['show_graph'] = (int) get_parameter('time_compare_overlapped'); + $values['show_graph'] = (int) get_parameter( + 'time_compare_overlapped' + ); $values['period'] = get_parameter('period'); $good_format = true; break; @@ -1241,19 +1562,39 @@ switch ($action) { case 'max_value': case 'avg_value': $values['period'] = get_parameter('period'); - $values['lapse_calc'] = get_parameter('lapse_calc'); + $values['lapse_calc'] = get_parameter( + 'lapse_calc' + ); $values['lapse'] = get_parameter('lapse'); - $values['visual_format'] = get_parameter('visual_format'); + $values['visual_format'] = get_parameter( + 'visual_format' + ); + $good_format = true; + break; + + case 'nt_top_n': + $values['period'] = get_parameter('period'); + $values['top_n_value'] = get_parameter( + 'quantity' + ); $good_format = true; break; default: $values['period'] = get_parameter('period'); - $values['top_n'] = get_parameter('radiobutton_max_min_avg', 0); - $values['top_n_value'] = get_parameter('quantity'); + $values['top_n'] = get_parameter( + 'radiobutton_max_min_avg', + 0 + ); + $values['top_n_value'] = get_parameter( + 'quantity' + ); $values['text'] = get_parameter('text'); - $values['show_graph'] = get_parameter('combo_graph_options'); + $values['show_graph'] = get_parameter( + 'combo_graph_options' + ); $good_format = true; + break; } $values['id_agent'] = get_parameter('id_agent'); @@ -1261,16 +1602,27 @@ switch ($action) { $values['id_agent_module'] = ''; if (isset($values['type'])) { - if (($values['type'] == 'alert_report_agent') or ($values['type'] == 'event_report_agent') or ($values['type'] == 'agent_configuration') or ($values['type'] == 'group_configuration')) { + if (($values['type'] == 'alert_report_agent') + || ($values['type'] == 'event_report_agent') + || ($values['type'] == 'agent_configuration') + || ($values['type'] == 'group_configuration') + ) { $values['id_agent_module'] = ''; } else { - $values['id_agent_module'] = get_parameter('id_agent_module'); + $values['id_agent_module'] = get_parameter( + 'id_agent_module' + ); } } else { - $values['id_agent_module'] = get_parameter('id_agent_module'); + $values['id_agent_module'] = get_parameter( + 'id_agent_module' + ); } - $values['only_display_wrong'] = (int) get_parameter('checkbox_only_display_wrong', 0); + $values['only_display_wrong'] = (int) get_parameter( + 'checkbox_only_display_wrong', + 0 + ); $values['monday'] = get_parameter('monday', 0); $values['tuesday'] = get_parameter('tuesday', 0); $values['wednesday'] = get_parameter('wednesday', 0); @@ -1278,89 +1630,221 @@ switch ($action) { $values['friday'] = get_parameter('friday', 0); $values['saturday'] = get_parameter('saturday', 0); $values['sunday'] = get_parameter('sunday', 0); - switch ($config['dbtype']) { - case 'mysql': - case 'postgresql': - $values['time_from'] = get_parameter('time_from'); - $values['time_to'] = get_parameter('time_to'); - break; + $values['total_time'] = get_parameter('total_time', 0); + $values['time_failed'] = get_parameter( + 'time_failed', + 0 + ); + $values['time_in_ok_status'] = get_parameter( + 'time_in_ok_status', + 0 + ); + $values['time_in_unknown_status'] = get_parameter( + 'time_in_unknown_status', + 0 + ); + $values['time_of_not_initialized_module'] = get_parameter( + 'time_of_not_initialized_module', + 0 + ); + $values['time_of_downtime'] = get_parameter( + 'time_of_downtime', + 0 + ); + $values['total_checks'] = get_parameter( + 'total_checks', + 0 + ); + $values['checks_failed'] = get_parameter( + 'checks_failed', + 0 + ); + $values['checks_in_ok_status'] = get_parameter( + 'checks_in_ok_status', + 0 + ); + $values['unknown_checks'] = get_parameter( + 'unknown_checks', + 0 + ); + $values['agent_max_value'] = get_parameter( + 'agent_max_value', + 0 + ); + $values['agent_min_value'] = get_parameter( + 'agent_min_value', + 0 + ); - case 'oracle': - $values['time_from'] = '#to_date(\''.get_parameter('time_from').'\',\'hh24:mi:ss\')'; - $values['time_to'] = '#to_date(\''.get_parameter('time_to').'\', \'hh24:mi:ss\')'; - break; - } + $values['time_from'] = get_parameter( + 'time_from' + ); + $values['time_to'] = get_parameter('time_to'); - $values['group_by_agent'] = get_parameter('checkbox_row_group_by_agent'); - $values['show_resume'] = get_parameter('checkbox_show_resume'); - $values['order_uptodown'] = get_parameter('radiobutton_order_uptodown'); - $values['exception_condition'] = (int) get_parameter('exception_condition', 0); - $values['exception_condition_value'] = get_parameter('exception_condition_value'); - $values['id_module_group'] = get_parameter('combo_modulegroup'); + $values['group_by_agent'] = get_parameter( + 'checkbox_row_group_by_agent' + ); + $values['show_resume'] = get_parameter( + 'checkbox_show_resume' + ); + $values['order_uptodown'] = get_parameter( + 'radiobutton_order_uptodown' + ); + $values['exception_condition'] = (int) get_parameter( + 'exception_condition', + 0 + ); + $values['exception_condition_value'] = get_parameter( + 'exception_condition_value' + ); + $values['id_module_group'] = get_parameter( + 'combo_modulegroup' + ); $values['id_group'] = get_parameter('combo_group'); $values['server_name'] = get_parameter('server_name'); $server_id = (int) get_parameter('server_id'); if ($server_id != 0) { - $connection = metaconsole_get_connection_by_id($server_id); + $connection = metaconsole_get_connection_by_id( + $server_id + ); $values['server_name'] = $connection['server_name']; } if ($values['server_name'] == '') { - $values['server_name'] = get_parameter('combo_server'); + $values['server_name'] = get_parameter( + 'combo_server' + ); } - if ((($values['type'] == 'custom_graph') or ($values['type'] == 'automatic_custom_graph')) && ($values['id_gs'] == 0 || $values['id_gs'] == '')) { + if ((($values['type'] == 'custom_graph') + || ($values['type'] == 'automatic_custom_graph')) + && ($values['id_gs'] == 0 || $values['id_gs'] == '') + ) { $resultOperationDB = false; break; } - $show_summary_group = get_parameter('show_summary_group', 0); - $filter_event_severity = get_parameter('filter_event_severity', 0); - $filter_event_type = get_parameter('filter_event_type', ''); - $filter_event_status = get_parameter('filter_event_status', 0); + $show_summary_group = get_parameter( + 'show_summary_group', + 0 + ); + $filter_event_severity = get_parameter( + 'filter_event_severity', + 0 + ); + $filter_event_type = get_parameter( + 'filter_event_type', + '' + ); + $filter_event_status = get_parameter( + 'filter_event_status', + 0 + ); - $event_graph_by_agent = get_parameter('event_graph_by_agent', 0); - $event_graph_by_user_validator = get_parameter('event_graph_by_user_validator', 0); - $event_graph_by_criticity = get_parameter('event_graph_by_criticity', 0); - $event_graph_validated_vs_unvalidated = get_parameter('event_graph_validated_vs_unvalidated', 0); + $event_graph_by_agent = get_parameter( + 'event_graph_by_agent', + 0 + ); + $event_graph_by_user_validator = get_parameter( + 'event_graph_by_user_validator', + 0 + ); + $event_graph_by_criticity = get_parameter( + 'event_graph_by_criticity', + 0 + ); + $event_graph_validated_vs_unvalidated = get_parameter( + 'event_graph_validated_vs_unvalidated', + 0 + ); - $event_filter_search = get_parameter('filter_search', ''); + $event_filter_search = get_parameter( + 'filter_search', + '' + ); - // If metaconsole is activated - if ($config['metaconsole'] == 1 && defined('METACONSOLE')) { - if (($values['type'] == 'custom_graph') or ($values['type'] == 'automatic_custom_graph')) { - $id_gs = substr($values['id_gs'], 0, strpos($values['id_gs'], '|')); + // If metaconsole is activated. + if ($config['metaconsole'] == 1 + && defined('METACONSOLE') + ) { + if (($values['type'] == 'custom_graph') + || ($values['type'] == 'automatic_custom_graph') + ) { + $id_gs = substr( + $values['id_gs'], + 0, + strpos($values['id_gs'], '|') + ); if ($id_gs !== false) { - $server_name = strstr($values['id_gs'], '|'); + $server_name = strstr( + $values['id_gs'], + '|' + ); $values['id_gs'] = $id_gs; - $values['server_name'] = substr($server_name, 1, strlen($server_name)); + $values['server_name'] = substr( + $server_name, + 1, + strlen($server_name) + ); } } - // Get agent and server name - $agent_name_server = io_safe_output(get_parameter('agent')); + // Get agent and server name. + $agent_name_server = io_safe_output( + get_parameter('agent') + ); if (isset($agent_name_server)) { - $separator_pos = strpos($agent_name_server, '('); + $separator_pos = strpos( + $agent_name_server, + '(' + ); - if (($separator_pos != false) and ($separator_pos != 0)) { - $server_name = substr($agent_name_server, $separator_pos); - $server_name = str_replace('(', '', $server_name); - $server_name = str_replace(')', '', $server_name); - // Will update server_name variable + if (($separator_pos != false) + || ($separator_pos != 0) + ) { + $server_name = substr( + $agent_name_server, + $separator_pos + ); + $server_name = str_replace( + '(', + '', + $server_name + ); + $server_name = str_replace( + ')', + '', + $server_name + ); + // Will update server_name variable. $values['server_name'] = trim($server_name); - $agent_name = substr($agent_name_server, 0, $separator_pos); + $agent_name = substr( + $agent_name_server, + 0, + $separator_pos + ); } } } - if (($values['type'] == 'sql') or ($values['type'] == 'sql_graph_hbar') or ($values['type'] == 'sql_graph_vbar') or ($values['type'] == 'sql_graph_pie')) { - $values['treport_custom_sql_id'] = get_parameter('id_custom'); + if (($values['type'] == 'sql') + || ($values['type'] == 'sql_graph_hbar') + || ($values['type'] == 'sql_graph_vbar') + || ($values['type'] == 'sql_graph_pie') + ) { + $values['treport_custom_sql_id'] = get_parameter( + 'id_custom' + ); if ($values['treport_custom_sql_id'] == 0) { - $values['external_source'] = get_parameter('sql'); + $values['external_source'] = get_parameter( + 'sql' + ); } - $values['historical_db'] = get_parameter('historical_db_check'); + $values['historical_db'] = get_parameter( + 'historical_db_check' + ); $values['top_n_value'] = get_parameter('max_items'); } else if ($values['type'] == 'url') { $values['external_source'] = get_parameter('url'); @@ -1372,22 +1856,45 @@ switch ($action) { $values['column_separator'] = get_parameter('field'); $values['line_separator'] = get_parameter('line'); + $values['current_month'] = get_parameter('current_month'); + $style = []; - $style['show_in_same_row'] = get_parameter('show_in_same_row', 0); - $style['show_in_landscape'] = get_parameter('show_in_landscape', 0); - $style['hide_notinit_agents'] = get_parameter('hide_notinit_agents', 0); - $style['priority_mode'] = get_parameter('priority_mode', REPORT_PRIORITY_MODE_OK); - $style['dyn_height'] = get_parameter('dyn_height', 230); + $style['show_in_same_row'] = get_parameter( + 'show_in_same_row', + 0 + ); + $style['show_in_landscape'] = get_parameter( + 'show_in_landscape', + 0 + ); + $style['hide_notinit_agents'] = get_parameter( + 'hide_notinit_agents', + 0 + ); + $style['priority_mode'] = get_parameter( + 'priority_mode', + REPORT_PRIORITY_MODE_OK + ); + $style['dyn_height'] = get_parameter( + 'dyn_height', + 230 + ); switch ($values['type']) { case 'event_report_agent': case 'event_report_group': case 'event_report_module': - // Added for events items - $style['show_summary_group'] = $show_summary_group; - $style['filter_event_severity'] = json_encode($filter_event_severity); - $style['filter_event_type'] = json_encode($filter_event_type); - $style['filter_event_status'] = json_encode($filter_event_status); + // Added for events items. + $style['show_summary_group'] = $show_summary_group; + $style['filter_event_severity'] = json_encode( + $filter_event_severity + ); + $style['filter_event_type'] = json_encode( + $filter_event_type + ); + $style['filter_event_status'] = json_encode( + $filter_event_status + ); $style['event_graph_by_agent'] = $event_graph_by_agent; $style['event_graph_by_user_validator'] = $event_graph_by_user_validator; @@ -1403,10 +1910,15 @@ switch ($action) { break; case 'simple_graph': - // Warning. We are using this column to hold this value to avoid - // the modification of the database for compatibility reasons. - $style['percentil'] = (int) get_parameter('percentil'); - $style['fullscale'] = (int) get_parameter('fullscale'); + // Warning. We are using this column to hold + // this value to avoid the modification of the + // database for compatibility reasons. + $style['percentil'] = (int) get_parameter( + 'percentil' + ); + $style['fullscale'] = (int) get_parameter( + 'fullscale' + ); if ($label != '') { $style['label'] = $label; } else { @@ -1415,7 +1927,9 @@ switch ($action) { break; case 'network_interfaces_report': - $style['fullscale'] = (int) get_parameter('fullscale'); + $style['fullscale'] = (int) get_parameter( + 'fullscale' + ); break; case 'module_histogram_graph': @@ -1436,12 +1950,17 @@ switch ($action) { case 'MTBF': case 'MTTR': case 'simple_baseline_graph': + case 'nt_top_n': if ($label != '') { $style['label'] = $label; } else { $style['label'] = ''; } break; + + default: + // Default. + break; } $values['style'] = io_safe_input(json_encode($style)); @@ -1456,6 +1975,10 @@ switch ($action) { unset($values['type']); } break; + + default: + // Default. + break; } $resultOperationDB = db_process_sql_update( @@ -1469,54 +1992,81 @@ switch ($action) { break; case 'save': - $values = []; $values['id_report'] = $idReport; $values['type'] = get_parameter('type', null); - // $values['name'] = (string) get_parameter('name'); $values['description'] = get_parameter('description'); $label = get_parameter('label', ''); - // Add macros name + // Add macros name. $items_label = []; $items_label['type'] = get_parameter('type'); $items_label['id_agent'] = get_parameter('id_agent'); - $items_label['id_agent_module'] = get_parameter('id_agent_module'); + $items_label['id_agent_module'] = get_parameter( + 'id_agent_module' + ); $name_it = (string) get_parameter('name'); $values['recursion'] = get_parameter('recursion', null); - $values['name'] = reporting_label_macro($items_label, $name_it); + $values['name'] = reporting_label_macro( + $items_label, + $name_it + ); - // Support for projection graph, prediction date and SLA reports - // 'top_n_value', 'top_n' and 'text' fields will be reused for these types of report + // Support for projection graph, prediction date + // and SLA reports 'top_n_value', 'top_n' and 'text' + // fields will be reused for these types of report. switch ($values['type']) { case 'projection_graph': $values['period'] = get_parameter('period1'); - $values['top_n_value'] = get_parameter('period2'); + $values['top_n_value'] = get_parameter( + 'period2' + ); $values['text'] = get_parameter('text'); $good_format = true; break; case 'prediction_date': $values['period'] = get_parameter('period1'); - $values['top_n'] = get_parameter('radiobutton_max_min_avg'); - $values['top_n_value'] = get_parameter('quantity'); + $values['top_n'] = get_parameter( + 'radiobutton_max_min_avg' + ); + $values['top_n_value'] = get_parameter( + 'quantity' + ); $interval_max = get_parameter('max_interval'); $interval_min = get_parameter('min_interval'); - // Checks intervals fields - if (preg_match('/^(\-)*[0-9]*\.?[0-9]+$/', $interval_max) and preg_match('/^(\-)*[0-9]*\.?[0-9]+$/', $interval_min)) { + // Checks intervals fields. + if (preg_match( + '/^(\-)*[0-9]*\.?[0-9]+$/', + $interval_max + ) + && preg_match( + '/^(\-)*[0-9]*\.?[0-9]+$/', + $interval_min + ) + ) { $good_format = true; } - $intervals = get_parameter('max_interval').';'.get_parameter('min_interval'); + $intervals = get_parameter( + 'max_interval' + ).';'.get_parameter('min_interval'); $values['text'] = $intervals; break; case 'SLA': $values['period'] = get_parameter('period'); - $values['top_n'] = get_parameter('combo_sla_sort_options', 0); - $values['top_n_value'] = get_parameter('quantity'); + $values['top_n'] = get_parameter( + 'combo_sla_sort_options', + 0 + ); + $values['top_n_value'] = get_parameter( + 'quantity' + ); $values['text'] = get_parameter('text'); - $values['show_graph'] = get_parameter('combo_graph_options'); + $values['show_graph'] = get_parameter( + 'combo_graph_options' + ); $good_format = true; break; @@ -1525,7 +2075,9 @@ switch ($action) { $values['period'] = 0; $es['date'] = get_parameter('date'); $es['id_agents'] = get_parameter('id_agents'); - $es['inventory_modules'] = get_parameter('inventory_modules'); + $es['inventory_modules'] = get_parameter( + 'inventory_modules' + ); $values['external_source'] = json_encode($es); $good_format = true; break; @@ -1548,9 +2100,15 @@ switch ($action) { case 'agent_module': $agents_to_report = get_parameter('id_agents2'); - $modules_to_report = get_parameter('module', ''); + $modules_to_report = get_parameter( + 'module', + '' + ); - $es['module'] = get_same_modules($agents_to_report, $modules_to_report); + $es['module'] = get_same_modules( + $agents_to_report, + $modules_to_report + ); $es['id_agents'] = $agents_to_report; $values['external_source'] = json_encode($es); @@ -1560,7 +2118,9 @@ switch ($action) { case 'inventory_changes': $values['period'] = get_parameter('period'); $es['id_agents'] = get_parameter('id_agents'); - $es['inventory_modules'] = get_parameter('inventory_modules'); + $es['inventory_modules'] = get_parameter( + 'inventory_modules' + ); $values['external_source'] = json_encode($es); $good_format = true; break; @@ -1576,30 +2136,38 @@ switch ($action) { break; case 'netflow_area': - case 'netflow_pie': case 'netflow_data': - case 'netflow_statistics': case 'netflow_summary': - $values['text'] = get_parameter('netflow_filter'); - $values['description'] = get_parameter('description'); + $values['text'] = get_parameter( + 'netflow_filter' + ); + $values['description'] = get_parameter( + 'description' + ); $values['period'] = get_parameter('period'); $values['top_n'] = get_parameter('resolution'); - $values['top_n_value'] = get_parameter('max_values'); + $values['top_n_value'] = get_parameter( + 'max_values' + ); $good_format = true; break; case 'availability': $values['period'] = get_parameter('period'); // HACK it is saved in show_graph field. - // Show interfaces instead the modules - $values['show_graph'] = get_parameter('checkbox_show_address_agent'); + // Show interfaces instead the modules. + $values['show_graph'] = get_parameter( + 'checkbox_show_address_agent' + ); $good_format = true; break; case 'simple_graph': case 'simple_baseline_graph': // HACK it is saved in show_graph field. - $values['show_graph'] = (int) get_parameter('time_compare_overlapped'); + $values['show_graph'] = (int) get_parameter( + 'time_compare_overlapped' + ); $values['period'] = get_parameter('period'); $good_format = true; break; @@ -1608,18 +2176,37 @@ switch ($action) { case 'max_value': case 'avg_value': $values['period'] = get_parameter('period'); - $values['lapse_calc'] = get_parameter('lapse_calc'); + $values['lapse_calc'] = get_parameter( + 'lapse_calc' + ); $values['lapse'] = get_parameter('lapse'); - $values['visual_format'] = get_parameter('visual_format'); + $values['visual_format'] = get_parameter( + 'visual_format' + ); + $good_format = true; + break; + + case 'nt_top_n': + $values['top_n_value'] = get_parameter( + 'quantity' + ); + $values['period'] = get_parameter('period'); $good_format = true; break; default: $values['period'] = get_parameter('period'); - $values['top_n'] = get_parameter('radiobutton_max_min_avg', 0); - $values['top_n_value'] = get_parameter('quantity'); + $values['top_n'] = get_parameter( + 'radiobutton_max_min_avg', + 0 + ); + $values['top_n_value'] = get_parameter( + 'quantity' + ); $values['text'] = get_parameter('text'); - $values['show_graph'] = get_parameter('combo_graph_options'); + $values['show_graph'] = get_parameter( + 'combo_graph_options' + ); $good_format = true; break; } @@ -1629,38 +2216,37 @@ switch ($action) { $values['server_name'] = get_parameter('server_name'); $server_id = (int) get_parameter('server_id'); if ($server_id != 0) { - $connection = metaconsole_get_connection_by_id($server_id); + $connection = metaconsole_get_connection_by_id( + $server_id + ); $values['server_name'] = $connection['server_name']; } if ($values['server_name'] == '') { - $values['server_name'] = get_parameter('combo_server'); + $values['server_name'] = get_parameter( + 'combo_server' + ); } $values['id_agent'] = get_parameter('id_agent'); $values['id_gs'] = get_parameter('id_custom_graph'); - if (($values['type'] == 'alert_report_agent') or ($values['type'] == 'event_report_agent') or ($values['type'] == 'agent_configuration') or ($values['type'] == 'group_configuration')) { + if (($values['type'] == 'alert_report_agent') + || ($values['type'] == 'event_report_agent') + || ($values['type'] == 'agent_configuration') + || ($values['type'] == 'group_configuration') + ) { $values['id_agent_module'] = ''; } else { - $values['id_agent_module'] = get_parameter('id_agent_module'); + $values['id_agent_module'] = get_parameter( + 'id_agent_module' + ); } - switch ($config['dbtype']) { - case 'mysql': - case 'postgresql': - $values['only_display_wrong'] = (int) get_parameter('checkbox_only_display_wrong', 0); - break; - - case 'oracle': - $only_display_wrong_tmp = get_parameter('checkbox_only_display_wrong'); - if (empty($only_display_wrong_tmp)) { - $values['only_display_wrong'] = 0; - } else { - $values['only_display_wrong'] = $only_display_wrong_tmp; - } - break; - } + $values['only_display_wrong'] = (int) get_parameter( + 'checkbox_only_display_wrong', + 0 + ); $values['monday'] = get_parameter('monday', 0); $values['tuesday'] = get_parameter('tuesday', 0); @@ -1669,53 +2255,133 @@ switch ($action) { $values['friday'] = get_parameter('friday', 0); $values['saturday'] = get_parameter('saturday', 0); $values['sunday'] = get_parameter('sunday', 0); - switch ($config['dbtype']) { - case 'mysql': - case 'postgresql': - $values['time_from'] = get_parameter('time_from'); - $values['time_to'] = get_parameter('time_to'); - break; + $values['total_time'] = get_parameter('total_time', 0); + $values['time_failed'] = get_parameter( + 'time_failed', + 0 + ); + $values['time_in_ok_status'] = get_parameter( + 'time_in_ok_status', + 0 + ); + $values['time_in_unknown_status'] = get_parameter( + 'time_in_unknown_status', + 0 + ); + $values['time_of_not_initialized_module'] = get_parameter( + 'time_of_not_initialized_module', + 0 + ); + $values['time_of_downtime'] = get_parameter( + 'time_of_downtime', + 0 + ); + $values['total_checks'] = get_parameter( + 'total_checks', + 0 + ); + $values['checks_failed'] = get_parameter( + 'checks_failed', + 0 + ); + $values['checks_in_ok_status'] = get_parameter( + 'checks_in_ok_status', + 0 + ); + $values['unknown_checks'] = get_parameter( + 'unknown_checks', + 0 + ); + $values['agent_max_value'] = get_parameter( + 'agent_max_value', + 0 + ); + $values['agent_min_value'] = get_parameter( + 'agent_min_value', + 0 + ); - case 'oracle': - $values['time_from'] = '#to_date(\''.get_parameter('time_from').'\',\'hh24:mi:ss\')'; - $values['time_to'] = '#to_date(\''.get_parameter('time_to').'\', \'hh24:mi:ss\')'; - break; - } + $values['time_from'] = get_parameter( + 'time_from' + ); + $values['time_to'] = get_parameter('time_to'); - $values['group_by_agent'] = get_parameter('checkbox_row_group_by_agent', 0); - $values['show_resume'] = get_parameter('checkbox_show_resume', 0); - $values['order_uptodown'] = get_parameter('radiobutton_order_uptodown', 0); - $values['exception_condition'] = (int) get_parameter('radiobutton_exception_condition', 0); - $values['exception_condition_value'] = get_parameter('exception_condition_value'); - $values['id_module_group'] = get_parameter('combo_modulegroup'); + $values['group_by_agent'] = get_parameter( + 'checkbox_row_group_by_agent', + 0 + ); + $values['show_resume'] = get_parameter( + 'checkbox_show_resume', + 0 + ); + $values['order_uptodown'] = get_parameter( + 'radiobutton_order_uptodown', + 0 + ); + $values['exception_condition'] = (int) get_parameter( + 'radiobutton_exception_condition', + 0 + ); + $values['exception_condition_value'] = get_parameter( + 'exception_condition_value' + ); + $values['id_module_group'] = get_parameter( + 'combo_modulegroup' + ); $values['id_group'] = get_parameter('combo_group'); - if ((($values['type'] == 'custom_graph') or ($values['type'] == 'automatic_custom_graph')) && ($values['id_gs'] == 0 || $values['id_gs'] == '')) { + if ((($values['type'] == 'custom_graph') + || ($values['type'] == 'automatic_custom_graph')) + && ($values['id_gs'] == 0 || $values['id_gs'] == '') + ) { $resultOperationDB = false; break; } - if ($config['metaconsole'] == 1 && defined('METACONSOLE')) { - if (($values['type'] == 'custom_graph') or ($values['type'] == 'automatic_custom_graph')) { - $id_gs = substr($values['id_gs'], 0, strpos($values['id_gs'], '|')); + if ($config['metaconsole'] == 1 + && defined('METACONSOLE') + ) { + if (($values['type'] == 'custom_graph') + || ($values['type'] == 'automatic_custom_graph') + ) { + $id_gs = substr( + $values['id_gs'], + 0, + strpos($values['id_gs'], '|') + ); if ($id_gs !== false && $id_gs !== '') { - $server_name = strstr($values['id_gs'], '|'); + $server_name = strstr( + $values['id_gs'], + '|' + ); $values['id_gs'] = $id_gs; - $values['server_name'] = substr($server_name, 1, strlen($server_name)); + $values['server_name'] = substr( + $server_name, + 1, + strlen($server_name) + ); } } } - if (($values['type'] == 'sql') or ($values['type'] == 'sql_graph_hbar') - or ($values['type'] == 'sql_graph_vbar') or ($values['type'] == 'sql_graph_pie') + if (($values['type'] == 'sql') + || ($values['type'] == 'sql_graph_hbar') + || ($values['type'] == 'sql_graph_vbar') + || ($values['type'] == 'sql_graph_pie') ) { - $values['treport_custom_sql_id'] = get_parameter('id_custom'); + $values['treport_custom_sql_id'] = get_parameter( + 'id_custom' + ); if ($values['treport_custom_sql_id'] == 0) { - $values['external_source'] = get_parameter('sql'); + $values['external_source'] = get_parameter( + 'sql' + ); } - $values['historical_db'] = get_parameter('historical_db_check'); + $values['historical_db'] = get_parameter( + 'historical_db_check' + ); $values['top_n_value'] = get_parameter('max_items'); } else if ($values['type'] == 'url') { $values['external_source'] = get_parameter('url'); @@ -1727,38 +2393,85 @@ switch ($action) { $values['column_separator'] = get_parameter('field'); $values['line_separator'] = get_parameter('line'); + $values['current_month'] = get_parameter('current_month'); + $style = []; - $style['show_in_same_row'] = get_parameter('show_in_same_row', 0); - $style['show_in_landscape'] = get_parameter('show_in_landscape', 0); - $style['hide_notinit_agents'] = get_parameter('hide_notinit_agents', 0); - $style['priority_mode'] = get_parameter('priority_mode', REPORT_PRIORITY_MODE_OK); + $style['show_in_same_row'] = get_parameter( + 'show_in_same_row', + 0 + ); + $style['show_in_landscape'] = get_parameter( + 'show_in_landscape', + 0 + ); + $style['hide_notinit_agents'] = get_parameter( + 'hide_notinit_agents', + 0 + ); + $style['priority_mode'] = get_parameter( + 'priority_mode', + REPORT_PRIORITY_MODE_OK + ); $style['dyn_height'] = get_parameter('dyn_height', 230); switch ($values['type']) { case 'event_report_agent': case 'event_report_group': case 'event_report_module': - $show_summary_group = get_parameter('show_summary_group', 0); - $filter_event_severity = get_parameter('filter_event_severity', ''); - $filter_event_type = get_parameter('filter_event_type', ''); - $filter_event_status = get_parameter('filter_event_status', ''); + $show_summary_group = get_parameter( + 'show_summary_group', + 0 + ); + $filter_event_severity = get_parameter( + 'filter_event_severity', + '' + ); + $filter_event_type = get_parameter( + 'filter_event_type', + '' + ); + $filter_event_status = get_parameter( + 'filter_event_status', + '' + ); - $event_graph_by_agent = get_parameter('event_graph_by_agent', 0); - $event_graph_by_user_validator = get_parameter('event_graph_by_user_validator', 0); - $event_graph_by_criticity = get_parameter('event_graph_by_criticity', 0); - $event_graph_validated_vs_unvalidated = get_parameter('event_graph_validated_vs_unvalidated', 0); + $event_graph_by_agent = get_parameter( + 'event_graph_by_agent', + 0 + ); + $event_graph_by_user_validator = get_parameter( + 'event_graph_by_user_validator', + 0 + ); + $event_graph_by_criticity = get_parameter( + 'event_graph_by_criticity', + 0 + ); + $event_graph_validated_vs_unvalidated = get_parameter( + 'event_graph_validated_vs_unvalidated', + 0 + ); - $event_filter_search = get_parameter('filter_search', ''); + $event_filter_search = get_parameter( + 'filter_search', + '' + ); - // Added for events items - $style['show_summary_group'] = $show_summary_group; - $style['filter_event_severity'] = json_encode($filter_event_severity); - $style['filter_event_type'] = json_encode($filter_event_type); - $style['filter_event_status'] = json_encode($filter_event_status); + // Added for events items. + $style['show_summary_group'] = $show_summary_group; + $style['filter_event_severity'] = json_encode( + $filter_event_severity + ); + $style['filter_event_type'] = json_encode( + $filter_event_type + ); + $style['filter_event_status'] = json_encode( + $filter_event_status + ); - $style['event_graph_by_agent'] = $event_graph_by_agent; - $style['event_graph_by_user_validator'] = $event_graph_by_user_validator; - $style['event_graph_by_criticity'] = $event_graph_by_criticity; + $style['event_graph_by_agent'] = $event_graph_by_agent; + $style['event_graph_by_user_validator'] = $event_graph_by_user_validator; + $style['event_graph_by_criticity'] = $event_graph_by_criticity; $style['event_graph_validated_vs_unvalidated'] = $event_graph_validated_vs_unvalidated; @@ -1772,14 +2485,23 @@ switch ($action) { $style['label'] = ''; } break; + + default: + // Default. + break; } break; case 'simple_graph': - // Warning. We are using this column to hold this value to avoid - // the modification of the database for compatibility reasons. - $style['percentil'] = (int) get_parameter('percentil'); - $style['fullscale'] = (int) get_parameter('fullscale'); + // Warning. We are using this column to hold + // this value to avoid the modification + // of the database for compatibility reasons. + $style['percentil'] = (int) get_parameter( + 'percentil' + ); + $style['fullscale'] = (int) get_parameter( + 'fullscale' + ); if ($label != '') { $style['label'] = $label; } else { @@ -1788,7 +2510,9 @@ switch ($action) { break; case 'network_interfaces_report': - $style['fullscale'] = (int) get_parameter('fullscale'); + $style['fullscale'] = (int) get_parameter( + 'fullscale' + ); break; case 'module_histogram_graph': @@ -1809,28 +2533,22 @@ switch ($action) { case 'MTBF': case 'MTTR': case 'simple_baseline_graph': + case 'nt_top_n': if ($label != '') { $style['label'] = $label; } else { $style['label'] = ''; } break; + + default: + // Default. + break; } $values['style'] = io_safe_input(json_encode($style)); if ($good_format) { - switch ($config['dbtype']) { - case 'oracle': - if (isset($values['type'])) { - $values[db_escape_key_identifier( - 'type' - )] = $values['type']; - unset($values['type']); - } - break; - } - $result = db_process_sql_insert( 'treport_content', $values @@ -1841,24 +2559,11 @@ switch ($action) { } else { $idItem = $result; - switch ($config['dbtype']) { - case 'mysql': - $max = db_get_all_rows_sql( - 'SELECT max(`order`) AS max - FROM treport_content - WHERE id_report = '.$idReport.';' - ); - break; - - case 'postgresql': - case 'oracle': - $max = db_get_all_rows_sql( - 'SELECT max("order") AS max - FROM treport_content - WHERE id_report = '.$idReport - ); - break; - } + $max = db_get_all_rows_sql( + 'SELECT max(`order`) AS max + FROM treport_content + WHERE id_report = '.$idReport.';' + ); if ($max === false) { $max = 0; @@ -1866,39 +2571,30 @@ switch ($action) { $max = $max[0]['max']; } - switch ($config['dbtype']) { - case 'mysql': - db_process_sql_update( - 'treport_content', - ['`order`' => ($max + 1)], - ['id_rc' => $idItem] - ); - break; - - case 'postgresql': - case 'oracle': - db_process_sql_update( - 'treport_content', - ['"order"' => ($max + 1)], - ['id_rc' => $idItem] - ); - break; - } + db_process_sql_update( + 'treport_content', + ['`order`' => ($max + 1)], + ['id_rc' => $idItem] + ); $resultOperationDB = true; } break; - } - // If fields dont have good format - else { + } else { + // If fields dont have good format. $resultOperationDB = false; } + break; + + default: + // Default. + break; } break; default: - if ($enterpriseEnable and $activeTab != 'advanced') { + if ($enterpriseEnable && $activeTab != 'advanced') { $resultOperationDB = reporting_enterprise_update_action(); } break; @@ -1928,20 +2624,35 @@ switch ($action) { $report = db_get_row_filter('treport', ['id_report' => $idReport]); $reportName = $report['name']; - $resultOperationDB = db_process_sql_delete('treport_content_sla_combined', ['id_report_content' => $idItem]); - $resultOperationDB2 = db_process_sql_delete('treport_content_item', ['id_report_content' => $idItem]); + $resultOperationDB = db_process_sql_delete( + 'treport_content_sla_combined', + ['id_report_content' => $idItem] + ); + $resultOperationDB2 = db_process_sql_delete( + 'treport_content_item', + ['id_report_content' => $idItem] + ); if ($resultOperationDB !== false) { - $resultOperationDB = db_process_sql_delete('treport_content', ['id_rc' => $idItem]); + $resultOperationDB = db_process_sql_delete( + 'treport_content', + ['id_rc' => $idItem] + ); } if ($resultOperationDB2 !== false) { - $resultOperationDB2 = db_process_sql_delete('treport_content', ['id_rc' => $idItem]); + $resultOperationDB2 = db_process_sql_delete( + 'treport_content', + ['id_rc' => $idItem] + ); } break; case 'order': $resultOperationDB = null; - $report = db_get_row_filter('treport', ['id_report' => $idReport]); + $report = db_get_row_filter( + 'treport', + ['id_report' => $idReport] + ); $reportName = $report['name']; $idGroupReport = $report['id_group']; @@ -1956,7 +2667,7 @@ switch ($action) { case 'agent': case 'type': - // Sort functionality for normal console + // Sort functionality for normal console. if (!defined('METACONSOLE')) { switch ($field) { case 'module': @@ -1990,6 +2701,10 @@ switch ($action) { case 'type': $sql = 'SELECT id_rc FROM treport_content WHERE %s ORDER BY type %s'; break; + + default: + // Default. + break; } $sql = sprintf($sql, 'id_report = '.$idReport, '%s'); @@ -2001,17 +2716,24 @@ switch ($action) { case 'down': $sql = sprintf($sql, 'DESC'); break; + + default: + // Default. + break; } $ids = db_get_all_rows_sql($sql); - } - // Sort functionality for metaconsole - else if ($config['metaconsole'] == 1) { + } else if ($config['metaconsole'] == 1) { + // Sort functionality for metaconsole. switch ($field) { case 'agent': case 'module': $sql = 'SELECT id_rc, id_agent, id_agent_module, server_name FROM treport_content WHERE %s ORDER BY server_name'; - $sql = sprintf($sql, 'id_report = '.$idReport, '%s'); + $sql = sprintf( + $sql, + 'id_report = '.$idReport, + '%s' + ); $report_items = db_get_all_rows_sql($sql); @@ -2021,34 +2743,47 @@ switch ($action) { if (!empty($report_items)) { foreach ($report_items as $report_item) { - $connection = metaconsole_get_connection($report_item['server_name']); + $connection = metaconsole_get_connection( + $report_item['server_name'] + ); if (metaconsole_load_external_db($connection) != NOERR) { - // ui_print_error_message ("Error connecting to ".$server_name); + continue; } switch ($field) { case 'agent': - $agents_name = agents_get_agents(['id_agente' => $report_item['id_agent']], 'nombre'); + $agents_name = agents_get_agents( + ['id_agente' => $report_item['id_agent']], + 'nombre' + ); - // Item without agent + // Item without agent. if (!$agents_name) { $element_name = ''; } else { - $agent_name = array_shift($agents_name); + $agent_name = array_shift( + $agents_name + ); $element_name = $agent_name['nombre']; } break; case 'module': - $module_name = modules_get_agentmodule_name($report_item['id_agent_module']); + $module_name = modules_get_agentmodule_name( + $report_item['id_agent_module'] + ); - // Item without module + // Item without module. if (!$module_name) { $element_name = ''; } else { $element_name = $module_name; } break; + + default: + // Default. + break; } metaconsole_restore_db(); @@ -2056,7 +2791,7 @@ switch ($action) { $temp_sort[$report_item['id_rc']] = $element_name; } - // Performes sorting + // Performes sorting. switch ($dir) { case 'up': asort($temp_sort); @@ -2065,6 +2800,10 @@ switch ($action) { case 'down': arsort($temp_sort); break; + + default: + // Default. + break; } foreach ($temp_sort as $temp_element_key => $temp_element_val) { @@ -2073,13 +2812,13 @@ switch ($action) { $i++; } - // Free resources + // Free resources. unset($temp_sort); unset($report_items); } break; - // Type case only depends of local database + // Type case only depends of local database. case 'type': $sql = 'SELECT id_rc FROM treport_content @@ -2098,10 +2837,17 @@ switch ($action) { case 'down': $sql = sprintf($sql, 'DESC'); break; + + default: + // Default. + break; } $ids = db_get_all_rows_sql($sql); + break; + default: + // Default. break; } } @@ -2109,40 +2855,28 @@ switch ($action) { $count = 1; $resultOperationDB = true; foreach ($ids as $id) { - $result = db_process_sql_update('treport_content', ['order' => $count], ['id_rc' => $id['id_rc']]); + $result = db_process_sql_update( + 'treport_content', + ['order' => $count], + ['id_rc' => $id['id_rc']] + ); if ($result === false) { $resultOperationDB = false; break; } - $count = ($count + 1); + $count++; } break; default: - switch ($config['dbtype']) { - case 'mysql': - $oldOrder = db_get_value_sql( - ' - SELECT `order` - FROM treport_content - WHERE id_rc = '.$idItem - ); - break; + $oldOrder = db_get_value_sql( + 'SELECT `order` + FROM treport_content + WHERE id_rc = '.$idItem + ); - case 'postgresql': - case 'oracle': - $oldOrder = db_get_value_sql( - ' - SELECT "order" - FROM treport_content - WHERE id_rc = '.$idItem - ); - break; - } - - // db_get_value_filter('order', 'treport_content', array('id_rc' => $idItem)); switch ($dir) { case 'up': $newOrder = ($oldOrder - 1); @@ -2151,79 +2885,34 @@ switch ($action) { case 'down': $newOrder = ($oldOrder + 1); break; - } - switch ($config['dbtype']) { - case 'mysql': - $resultOperationDB = db_process_sql_update( - 'treport_content', - ['`order`' => $oldOrder], - [ - '`order`' => $newOrder, - 'id_report' => $idReport, - ] - ); - break; - - case 'postgresql': - $resultOperationDB = db_process_sql_update( - 'treport_content', - ['"order"' => $oldOrder], - [ - '"order"' => $newOrder, - 'id_report' => $idReport, - ] - ); - break; - - case 'oracle': - $resultOperationDB = db_process_sql_update( - 'treport_content', - ['"order"' => $oldOrder], - [ - '"order"' => $newOrder, - 'id_report' => $idReport, - ], - 'AND', - false - ); + default: + // Default. break; } + $resultOperationDB = db_process_sql_update( + 'treport_content', + ['`order`' => $oldOrder], + [ + '`order`' => $newOrder, + 'id_report' => $idReport, + ] + ); + + if ($resultOperationDB !== false) { - switch ($config['dbtype']) { - case 'mysql': - $resultOperationDB = db_process_sql_update( - 'treport_content', - ['`order`' => $newOrder], - ['id_rc' => $idItem] - ); - break; - - case 'postgresql': - $resultOperationDB = db_process_sql_update( - 'treport_content', - ['"order"' => $newOrder], - ['id_rc' => $idItem] - ); - break; - - case 'oracle': - $resultOperationDB = db_process_sql_update( - 'treport_content', - ['"order"' => $newOrder], - ['id_rc' => $idItem], - 'AND', - false - ); - break; - } + $resultOperationDB = db_process_sql_update( + 'treport_content', + ['`order`' => $newOrder], + ['id_rc' => $idItem] + ); } break; } break; - // Added for report templates + // Added for report templates. default: if ($enterpriseEnable) { $buttons = [ @@ -2236,6 +2925,7 @@ switch ($action) { $buttons = reporting_enterprise_add_main_Tabs($buttons); $subsection = ''; + $helpers = ''; switch ($activeTab) { case 'main': $buttons['list_reports']['active'] = true; @@ -2243,13 +2933,20 @@ switch ($action) { break; default: - $subsection = reporting_enterprise_add_subsection_main($activeTab, $buttons); + $data_tab = reporting_enterprise_add_subsection_main( + $activeTab, + $buttons + ); + + $subsection = $data_tab['subsection']; + $buttons = $data_tab['buttons']; + $helpers = $data_tab['helper']; break; } - // Page header for metaconsole - if ($enterpriseEnable and defined('METACONSOLE')) { - // Bread crumbs + // Page header for metaconsole. + if ($enterpriseEnable && defined('METACONSOLE')) { + // Bread crumbs. ui_meta_add_breadcrumb( [ 'link' => 'index.php?sec=reporting&sec2=godmode/reporting/reporting_builder&pure='.$pure, @@ -2259,14 +2956,22 @@ switch ($action) { ui_meta_print_page_header($nav_bar); - // Print header + // Print header. ui_meta_print_header(__('Reporting'), '', $buttons); + } else { + // Page header for normal console. + ui_print_page_header( + $subsection, + 'images/op_reporting.png', + false, + '', + false, + $buttons, + false, + '', + 60 + ); } - // Page header for normal console - else { - ui_print_page_header($subsection, 'images/op_reporting.png', false, '', false, $buttons, false, '', 60); - } - reporting_enterprise_select_main_tab($action); } @@ -2301,7 +3006,10 @@ $buttons = [ ]; if ($enterpriseEnable) { - $buttons = reporting_enterprise_add_Tabs($buttons, $idReport); + $buttons = reporting_enterprise_add_Tabs( + $buttons, + $idReport + ); } $buttons['view'] = [ @@ -2325,9 +3033,9 @@ if ($idReport != 0) { $textReportName = __('Create Custom Report'); } -// Page header for metaconsole -if ($enterpriseEnable and defined('METACONSOLE')) { - // Bread crumbs +// Page header for metaconsole. +if ($enterpriseEnable && defined('METACONSOLE')) { + // Bread crumbs. ui_meta_add_breadcrumb( [ 'link' => 'index.php?sec=reporting&sec2=godmode/reporting/reporting_builder&pure='.$pure, @@ -2337,14 +3045,24 @@ if ($enterpriseEnable and defined('METACONSOLE')) { ui_meta_print_page_header($nav_bar); - // Print header + // Print header. ui_meta_print_header(__('Reporting').$textReportName, '', $buttons); } else { + switch ($activeTab) { + case 'main': + $helpers = ''; + break; + + default: + $helpers = 'reporting_'.$activeTab.'_tab'; + break; + } + ui_print_page_header( $textReportName, 'images/op_reporting.png', false, - 'reporting_'.$activeTab.'_tab', + $helpers, false, $buttons, false, @@ -2367,7 +3085,11 @@ if ($resultOperationDB !== null) { break; } - ui_print_result_message($resultOperationDB, __('Successfull action'), __('Unsuccessful action

'.$err)); + ui_print_result_message( + $resultOperationDB, + __('Successfull action'), + __('Unsuccessful action

'.$err) + ); } switch ($activeTab) { diff --git a/pandora_console/godmode/reporting/visual_console_builder.editor.js b/pandora_console/godmode/reporting/visual_console_builder.editor.js index 6744d2fb2d..cb72ce58ce 100755 --- a/pandora_console/godmode/reporting/visual_console_builder.editor.js +++ b/pandora_console/godmode/reporting/visual_console_builder.editor.js @@ -1,3 +1,5 @@ +/* globals $ */ + // Pandora FMS - http://pandorafms.com // ================================================== // Copyright (c) 2005-2009 Artica Soluciones Tecnologicas @@ -31,6 +33,8 @@ var SIZE_GRID = 16; //Const the size (for width and height) of grid. var img_handler_start; var img_handler_end; +var default_cache_expiration = null; + function toggle_advance_options_palette(close) { if ($("#advance_options").css("display") == "none") { $("#advance_options").css("display", ""); @@ -674,12 +678,17 @@ function update_button_palette_callback() { return false; } } - if (values["module"] == 0) { + var radio_value = $("input[name='radio_choice']:checked").val(); + if (values["agent"] == "" && radio_value == "module_graph") { + dialog_message("#message_alert_no_agent"); + return false; + } + if (values["module"] == 0 && radio_value == "module_graph") { dialog_message("#message_alert_no_module"); return false; } - if (values["agent"] == "") { - dialog_message("#message_alert_no_agent"); + if (values["id_custom_graph"] == 0 && radio_value == "custom_graph") { + dialog_message("#message_alert_no_custom_graph"); return false; } if ($("input[name=width_module_graph]").val() == "") { @@ -1090,10 +1099,11 @@ function readFields() { var text = tinymce.get("text-label").getContent(); values["label"] = text; + values["percentile_label_color"] = $( + "input[name=percentile_label_color]" + ).val(); + if ($("input[name=percentile_label]").val().length > 0) { - values["percentile_label_color"] = $( - "input[name=percentile_label_color]" - ).val(); values["label"] = " __('View')]).''; -} - -$user_groups_w = users_get_groups(false, 'PM', true, false, null, 'id_grupo'); -$user_groups_w = array_keys($user_groups_w); - -$user_groups_r = users_get_groups(false, 'AW', true, false, null, 'id_grupo'); -$user_groups_r = array_keys($user_groups_r); - -// Headers. -ui_print_page_header( - __('Manage recontask'), - 'images/gm_servers.png', - false, - '', - true, - $options -); - - -// -------------------------------- -// DELETE A RECON TASKs. -// -------------------------------- -if (isset($_GET['delete'])) { - $id = get_parameter_get('delete'); - - $result = db_process_sql_delete('trecon_task', ['id_rt' => $id]); - - if ($result !== false) { - ui_print_success_message(__('Successfully deleted recon task')); - } else { - ui_print_error_message(__('Error deleting recon task')); - } -} else if (isset($_GET['disabled'])) { - $id = get_parameter_get('id'); - $disabled = get_parameter_get('disabled'); - - $result = db_process_sql_update( - 'trecon_task', - ['disabled' => $disabled], - ['id_rt' => $id] - ); - - if ($result !== false) { - ui_print_success_message(__('Successfully updated recon task')); - // If the action is enabled, we force recon_task to be queued asap. - if ($disabled == 0) { - servers_force_recon_task($id); - } - } else { - ui_print_error_message(__('Error updating recon task')); - } -} - -// -------------------------------- -// GET PARAMETERS IF UPDATE OR CREATE. -// -------------------------------- -if ((isset($_GET['update'])) || ((isset($_GET['create'])))) { - $name = get_parameter_post('name'); - $network = get_parameter_post('network'); - $description = get_parameter_post('description'); - $id_recon_server = get_parameter_post('id_recon_server'); - $interval = get_parameter_post('interval'); - $id_group = get_parameter_post('id_group'); - $create_incident = get_parameter_post('create_incident'); - $id_network_profile = get_parameter_post('id_network_profile'); - $recon_ports = get_parameter_post('recon_ports', ''); - $id_os = get_parameter_post('id_os', 10); - $snmp_community = get_parameter_post('snmp_community', 'public'); - $id_recon_script = get_parameter('id_recon_script', 0); - $mode = get_parameter('mode', ''); - $field1 = get_parameter('_field1_', ''); - $field2 = get_parameter('_field2_', ''); - $field3 = get_parameter('_field3_', ''); - $field4 = get_parameter('_field4_', ''); - $snmp_version = get_parameter_post('snmp_version'); - $snmp3_auth_user = get_parameter_post('snmp_auth_user'); - $snmp3_auth_pass = get_parameter_post('snmp_auth_pass'); - $snmp3_privacy_method = get_parameter_post('snmp_privacy_method'); - $snmp3_privacy_pass = get_parameter_post('snmp_privacy_pass'); - $snmp3_auth_method = get_parameter_post('snmp_auth_method'); - $snmp3_security_level = get_parameter_post('snmp_security_level'); - - - if ($mode == 'network_sweep') { - $id_recon_script = 0; - } else { - $id_network_profile = 0; - } - - $os_detect = (int) get_parameter('os_detect', 0); - $resolve_names = (int) get_parameter('resolve_names', 0); - $parent_detection = (int) get_parameter('parent_detection', 0); - $parent_recursion = (int) get_parameter('parent_recursion', 1); - $alias_as_name = (int) get_parameter('alias_as_name', 0); - $snmp_enabled = (int) get_parameter('snmp_enabled', 0); - $vlan_enabled = (int) get_parameter('vlan_enabled', 0); - // Get macros. - $macros = (string) get_parameter('macros'); - - if (!empty($macros)) { - $macros = json_decode(base64_decode($macros), true); - - if (isset($macros) === true && is_array($macros) === true) { - foreach ($macros as $k => $m) { - $macros[$k]['value'] = get_parameter($m['macro'], ''); - } - } - } - - $macros = io_json_mb_encode($macros); -} - -// -------------------------------- -// UPDATE A RECON TASK. -// -------------------------------- -if (isset($_GET['update'])) { - $id = get_parameter_get('update'); - - $values = [ - 'snmp_community' => $snmp_community, - 'id_os' => $id_os, - 'name' => $name, - 'subnet' => $network, - 'description' => $description, - 'id_recon_server' => $id_recon_server, - 'create_incident' => $create_incident, - 'id_group' => $id_group, - 'interval_sweep' => $interval, - 'id_network_profile' => $id_network_profile, - 'recon_ports' => $recon_ports, - 'id_recon_script' => $id_recon_script, - 'field1' => $field1, - 'field2' => $field2, - 'field3' => $field3, - 'field4' => $field4, - 'os_detect' => $os_detect, - 'resolve_names' => $resolve_names, - 'parent_detection' => $parent_detection, - 'parent_recursion' => $parent_recursion, - 'macros' => $macros, - 'alias_as_name' => $alias_as_name, - 'snmp_enabled' => $snmp_enabled, - 'vlan_enabled' => $vlan_enabled, - 'snmp_version' => $snmp_version, - ]; - - $values_v3 = [ - 'snmp_auth_user' => $snmp3_auth_user, - 'snmp_auth_pass' => $snmp3_auth_pass, - 'snmp_privacy_method' => $snmp3_privacy_method, - 'snmp_privacy_pass' => $snmp3_privacy_pass, - 'snmp_auth_method' => $snmp3_auth_method, - 'snmp_security_level' => $snmp3_security_level, - ]; - if ($values['snmp_version'] == '1' || $values['snmp_version'] == '2' || $values['snmp_version'] == '2c') { - $values_v3 = [ - 'snmp_auth_user' => '', - 'snmp_auth_pass' => '', - 'snmp_privacy_method' => '', - 'snmp_privacy_pass' => '', - 'snmp_auth_method' => '', - 'snmp_security_level' => '', - ]; - } - - $values = array_merge($values, $values_v3); - - $where = ['id_rt' => $id]; - - $reason = ''; - if ($name != '') { - if (empty($id_recon_script)) { - if (!preg_match('/[0-9]+.+[0-9]+.+[0-9]+.+[0-9]+\/+[0-9]/', $network)) { - $reason = __('Wrong format in Subnet field'); - $result = false; - } else { - $result = db_process_sql_update( - 'trecon_task', - $values, - $where - ); - } - } else { - $result = db_process_sql_update( - 'trecon_task', - $values, - $where - ); - } - } else { - $result = false; - } - - if ($result !== false) { - ui_print_success_message(__('Successfully updated recon task')); - } else { - ui_print_error_message(__('Error updating recon task')); - echo $reason; - include 'manage_recontask_form.php'; - return; - } -} - -// -------------------------------- -// CREATE A RECON TASK -// -------------------------------- -if (isset($_GET['create'])) { - $values = [ - 'name' => $name, - 'subnet' => $network, - 'description' => $description, - 'id_recon_server' => $id_recon_server, - 'create_incident' => $create_incident, - 'id_group' => $id_group, - 'id_network_profile' => $id_network_profile, - 'interval_sweep' => $interval, - 'id_os' => $id_os, - 'recon_ports' => $recon_ports, - 'snmp_community' => $snmp_community, - 'id_recon_script' => $id_recon_script, - 'field1' => $field1, - 'field2' => $field2, - 'field3' => $field3, - 'field4' => $field4, - 'os_detect' => $os_detect, - 'resolve_names' => $resolve_names, - 'parent_detection' => $parent_detection, - 'parent_recursion' => $parent_recursion, - 'macros' => $macros, - 'alias_as_name' => $alias_as_name, - 'snmp_enabled' => $snmp_enabled, - 'vlan_enabled' => $vlan_enabled, - 'snmp_version' => $snmp_version, - ]; - - $values_v3 = [ - 'snmp_auth_user' => $snmp3_auth_user, - 'snmp_auth_pass' => $snmp3_auth_pass, - 'snmp_privacy_method' => $snmp3_privacy_method, - 'snmp_privacy_pass' => $snmp3_privacy_pass, - 'snmp_auth_method' => $snmp3_auth_method, - 'snmp_security_level' => $snmp3_security_level, - ]; - if ($values['snmp_version'] == '1' || $values['snmp_version'] == '2' || $values['snmp_version'] == '2c') { - $values_v3 = [ - 'snmp_auth_user' => '', - 'snmp_auth_pass' => '', - 'snmp_privacy_method' => '', - 'snmp_privacy_pass' => '', - 'snmp_auth_method' => '', - 'snmp_security_level' => '', - ]; - } - - if ($values['snmp_version'] == '3') { - $values['vlan_enabled'] = 0; - } - - $values = array_merge($values, $values_v3); - - $name = io_safe_output($name); - $name = trim($name, ' '); - $name = io_safe_input($name); - $reason = ''; - - if ($name != '') { - $name_exists = (bool) db_get_value('name', 'trecon_task', 'name', $name); - - if (empty($id_recon_script)) { - if ($name_exists && (!preg_match('/[0-9]+.+[0-9]+.+[0-9]+.+[0-9]+\/+[0-9]/', $network))) { - $reason = __('Recon-task name already exists and incorrect format in Subnet field'); - $result = false; - } else if (!preg_match('/[0-9]+.+[0-9]+.+[0-9]+.+[0-9]+\/+[0-9]/', $network)) { - $reason = __('Wrong format in Subnet field'); - $result = false; - } else if ($name_exists) { - $reason = __('Recon-task name already exists'); - $result = false; - } else { - $result = db_process_sql_insert('trecon_task', $values); - } - } else { - if ($name_exists) { - $reason = __('Recon-task name already exists'); - $result = false; - } else { - $result = db_process_sql_insert('trecon_task', $values); - } - } - } else { - $reason = 'The field "Task name" is empty'; - $result = false; - } - - if ($result !== false) { - ui_print_success_message(__('Successfully created recon task')); - } else { - ui_print_error_message(__('Error creating recon task')); - echo $reason; - include 'manage_recontask_form.php'; - return; - } -} - -// -------------------------------- -// SHOW TABLE WITH ALL RECON TASKs. -// -------------------------------- -// Pandora Admin must see all columns. -if (! check_acl($config['id_user'], 0, 'PM')) { - $sql = sprintf( - 'SELECT * - FROM trecon_task RT, tusuario_perfil UP - WHERE - UP.id_usuario = "%s" AND UP.id_grupo = RT.id_group', - $config['id_user'] - ); - - $result = db_get_all_rows_sql($sql); -} else { - $result = db_get_all_rows_in_table('trecon_task'); -} - -$color = 1; -if ($result !== false) { - $table = new StdClass(); - $table->head = [ - __('Name'), - __('Network'), - __('Mode'), - __('Group'), - __('SNMP Version'), - __('Incident'), - __('OS'), - __('Interval'), - __('Ports'), - __('Action'), - ]; - $table->align = [ - 'left', - 'left', - 'left', - 'left', - 'left', - 'left', - 'left', - 'left', - ]; - $table->width = '100%'; - $table->cellpadding = 4; - $table->cellspacing = 4; - $table->class = 'databox data'; - $table->data = []; - - $table->style[8] = 'text-align: left;'; - $table->size[8] = '15%'; - - foreach ($result as $row) { - if (in_array($row['id_group'], $user_groups_r)) { - $data = []; - $data[0] = $row['name']; - if ($row['id_recon_script'] == 0) { - $data[1] = $row['subnet']; - } else { - $data[1] = '-'; - } - - - if ($row['id_recon_script'] == 0) { - // Network recon task. - $data[2] = html_print_image('images/network.png', true, ['title' => __('Network recon task')]).'  '; - $data[2] .= network_profiles_get_name($row['id_network_profile']); - $mode_name = ''; - } else { - // APP recon task. - $data[2] = html_print_image('images/plugin.png', true).'  '; - $mode_name = db_get_sql(sprintf('SELECT name FROM trecon_script WHERE id_recon_script = %d', $row['id_recon_script'])); - $data[2] .= $mode_name; - } - - - // GROUP. - if ($row['id_recon_script'] == 0) { - $data[3] = ui_print_group_icon($row['id_group'], true); - } else { - $data[3] = '-'; - } - - // SNMP VERSION. - if ($row['snmp_version'] == '1') { - $data[4] = 'v. 1'; - } else if ($row['snmp_version'] == '2') { - $data[4] = 'v. 2'; - } else if ($row['snmp_version'] == '2c') { - $data[4] = 'v. 2c'; - } else if ($row['snmp_version'] == '3') { - $data[4] = 'v. 3'; - } - - - - // INCIDENT. - $data[5] = (($row['create_incident'] == 1) ? __('Yes') : __('No')); - - // OS. - if ($row['id_recon_script'] == 0) { - $data[6] = (($row['id_os'] > 0) ? ui_print_os_icon($row['id_os'], false, true) : __('Any')); - } else { - $data[6] = '-'; - } - - // INTERVAL. - if ($row['interval_sweep'] == 0) { - $data[7] = __('Manual'); - } else { - $data[7] = human_time_description_raw($row['interval_sweep']); - } - - // PORTS. - if ($row['id_recon_script'] == 0) { - $data[8] = substr($row['recon_ports'], 0, 15); - } else { - $data[8] = '-'; - } - - // ACTION. - $task_group = $row['id_group']; - - if (in_array($task_group, $user_groups_w)) { - $data[9] = ''.html_print_image('images/eye.png', true).''; - $data[9] .= ''.html_print_image('images/cross.png', true, ['border' => '0']).''; - if ($mode_name != 'IPAM Recon') { - $data[9] .= ''.html_print_image('images/config.png', true).''; - } else { - $sql_ipam = 'select id from tipam_network where id_recon_task ='.$row['id_rt']; - $id_recon_ipam = db_get_sql($sql_ipam); - $data[9] .= ''.html_print_image('images/config.png', true).''; - } - - if ($row['disabled'] == 0) { - $data[9] .= ''.html_print_image('images/lightbulb.png', true).''; - } else { - $data[9] .= ''.html_print_image('images/lightbulb_off.png', true).''; - } - } - - $table->data[] = $data; - } - } - - html_print_table($table); - unset($table); -} else { - echo '
'.__('There are no recon task configured').'
'; -} - -echo '
'; -echo '
'; -echo html_print_submit_button(__('Create'), 'crt', false, 'class="sub next"', true); -echo '
'; -echo '
'; diff --git a/pandora_console/godmode/servers/manage_recontask_form.php b/pandora_console/godmode/servers/manage_recontask_form.php index 0a5967c784..cf95cc76e6 100644 --- a/pandora_console/godmode/servers/manage_recontask_form.php +++ b/pandora_console/godmode/servers/manage_recontask_form.php @@ -1,11 +1,10 @@ '; - echo __('Warning').': '.__('By default, in Windows, %s only support Standard network sweep, not custom scripts', get_product_name()); - echo '
'; -} - -$table = new stdClass(); -$table->id = 'table_recon'; -$table->width = '100%'; -$table->cellspacing = 4; -$table->cellpadding = 4; -$table->class = 'databox filters'; - -$table->rowclass[3] = 'network_sweep'; -$table->rowclass[5] = 'network_sweep'; -$table->rowclass[7] = 'network_sweep'; -$table->rowclass[8] = 'network_sweep'; -$table->rowclass[11] = 'network_sweep'; -$table->rowclass[12] = 'network_sweep'; -$table->rowclass[18] = 'network_sweep'; -$table->rowclass[19] = 'network_sweep'; -$table->rowclass[20] = 'network_sweep'; -$table->rowclass[21] = 'network_sweep'; -$table->rowclass[22] = 'network_sweep'; -$table->rowclass[23] = 'network_sweep'; -$table->rowclass[24] = 'network_sweep'; -$table->rowclass[25] = 'network_sweep recon_v3'; -$table->rowclass[26] = 'network_sweep recon_v3'; -$table->rowclass[27] = 'network_sweep recon_v3'; -$table->rowclass[28] = 'network_sweep recon_v3'; -$table->rowclass[29] = 'network_sweep recon_v3'; -$table->rowclass[30] = 'network_sweep recon_v3'; - -$table->rowclass[6] = 'recon_script'; -$table->rowclass[13] = 'recon_script'; -$table->rowclass[14] = 'recon_script'; -$table->rowclass[15] = 'recon_script'; -$table->rowclass[16] = 'recon_script'; -$table->rowclass[17] = 'recon_script'; -// Name. -$table->data[0][0] = ''.__('Task name').''; -$table->data[0][1] = html_print_input_text('name', $name, '', 25, 0, true); - -// Discovery server. -$table->data[1][0] = ''.__('Discovery server').ui_print_help_tip( - __('You must select a Discovery Server to run the Task, otherwise the Recon Task will never run'), - true -); - -$sql = 'SELECT id_server, name - FROM tserver - WHERE server_type = 3 - ORDER BY name'; -$table->data[1][1] = html_print_select_from_sql( - $sql, - 'id_recon_server', - $id_recon_server, - '', - '', - '', - true -); - -$fields['network_sweep'] = __('Network sweep'); -if (!$is_windows) { - $fields['recon_script'] = __('Custom script'); -} - - -$table->data[2][0] = ''.__('Mode').''; -$table->data[2][1] = html_print_select( - $fields, - 'mode', - $mode, - '', - '', - 0, - true -); - - -// Network. -$table->data[3][0] = ''.__('Network').''; -$table->data[3][0] .= ui_print_help_tip( - __('You can specify several networks, separated by commas, for example: 192.168.50.0/24,192.168.60.0/24'), - true -); -$table->data[3][1] = html_print_input_text( - 'network', - $network, - '', - 25, - 0, - true -); - -// Interval. -$interv_manual = 0; -if ((int) $interval == 0) { - $interv_manual = 1; -} - -$table->data[4][0] = ''.__('Interval'); -$table->data[4][0] .= ui_print_help_tip( - __('Manual interval means that it will be executed only On-demand'), - true -); - -$values = [ - 0 => __('Defined'), - 1 => __('Manual'), -]; -$table->data[4][1] = html_print_select( - $values, - 'interval_manual_defined', - $interv_manual, - '', - '', - '', - true -); - -$table->data[4][1] .= ''; -$table->data[4][1] .= html_print_extended_select_for_time( - 'interval', - $interval, - '', - '', - '0', - false, - true, - false, - false -); -$table->data[4][1] .= ui_print_help_tip( - __('The minimum recomended interval for Recon Task is 5 minutes'), - true -); -$table->data[4][1] .= ''; - - -// Module template. -$table->data[5][0] = ''.__('Module template').''; - -$sql = 'SELECT id_np, name - FROM tnetwork_profile - ORDER BY name'; -$table->data[5][1] = html_print_select_from_sql( - $sql, - 'id_network_profile', - $id_network_profile, - '', - __('None'), - 0, - true -); - -// Recon script. -$data[1] = ''; -$table->data[6][0] = ''.__('Recon script').''; - - -$sql = "SELECT id_recon_script, name - FROM trecon_script - WHERE name <> 'IPAM Recon' - ORDER BY name"; -if ($name_script != 'IPAM Recon') { - $table->data[6][1] = html_print_select_from_sql( - $sql, - 'id_recon_script', - $id_recon_script, - '', - '', - '', - true - ); - $table->data[6][1] .= "'; - $table->data[6][1] .= $data[1] .= html_print_input_hidden( - 'macros', - base64_encode($macros), - true - ); -} else { - $table->data[6][1] = 'IPAM Recon'; -} - -// OS. -$table->data[7][0] = ''.__('OS').''; - -$sql = 'SELECT id_os, name - FROM tconfig_os - ORDER BY name'; -$table->data[7][1] = html_print_select_from_sql( - $sql, - 'id_os', - $id_os, - '', - __('Any'), - -1, - true -); - -// Recon ports. -$table->data[8][0] = ''.__('Ports').''; -$table->data[8][1] = html_print_input_text( - 'recon_ports', - $recon_ports, - '', - 25, - 0, - true -); -$table->data[8][1] .= ui_print_help_tip( - __('Ports defined like: 80 or 80,443,512 or even 0-1024 (Like Nmap command line format). If dont want to do a sweep using portscan, left it in blank'), - true -); - -// Group. -$table->data[9][0] = ''.__('Group'); -$groups = users_get_groups(false, 'PM', false); -$table->data[9][1] = html_print_select_groups( - false, - 'PM', - false, - 'id_group', - $id_group, - '', - '', - 0, - true -); - -// Incident. -$values = [ - 0 => __('No'), - 1 => __('Yes'), -]; -$table->data[10][0] = ''.__('Incident'); -$table->data[10][1] = html_print_select( - $values, - 'create_incident', - $create_incident, - '', - '', - '', - true -).' '.ui_print_help_tip( - __('Choose if the discovery of a new system creates an incident or not.'), - true -); - -// Snmp_enabled. -$table->data[11][0] = ''.__('SNMP enabled'); -$table->data[11][1] = html_print_checkbox( - 'snmp_enabled', - 1, - $snmp_enabled, - true -); - -// SNMP default community. -$table->data[12][0] = ''.__('SNMP Default community'); -$table->data[12][0] .= ui_print_help_tip( - __('You can specify several values, separated by commas, for example: public,mysecret,1234'), - true -); -$table->data[12][1] = html_print_input_text( - 'snmp_community', - $snmp_community, - '', - 35, - 0, - true -); - -// SNMP version. -$snmp_versions['1'] = 'v. 1'; -$snmp_versions['2'] = 'v. 2'; -$snmp_versions['2c'] = 'v. 2c'; -$snmp_versions['3'] = 'v. 3'; -$table->data[24][0] = ''._('SNMP version'); -$table->data[24][1] = html_print_select( - $snmp_versions, - 'snmp_version', - $snmp_version, - '', - '', - 0, - true -); - -$table->data[25][0] = ''.__('Auth user'); -$table->data[25][1] = html_print_input_text( - 'snmp_auth_user', - $snmp3_auth_user, - '', - 15, - 60, - true, - '', - false, - '', - '' -); -$table->data[26][0] = ''.__('Auth password').ui_print_help_tip(__('The pass length must be eight character minimum.'), true); -$table->data[26][1] = html_print_input_password( - 'snmp_auth_pass', - $snmp3_auth_pass, - '', - 15, - 60, - true, - '', - false, - '' -); -$table->data[26][1] .= html_print_input_hidden_extended( - 'active_snmp_v3', - 0, - 'active_snmp_v3_mmen', - true -); - -$table->data[27][0] = ''.__('Privacy method'); -$table->data[27][1] = html_print_select( - [ - 'DES' => __('DES'), - 'AES' => __('AES'), - ], - 'snmp_privacy_method', - $snmp3_privacy_method, - '', - '', - '', - true, - false, - false, - '', - '' -); -$table->data[28][0] = ''.__('Privacy pass').ui_print_help_tip(__('The pass length must be eight character minimum.'), true); -$table->data[28][1] = html_print_input_password( - 'snmp_privacy_pass', - $snmp3_privacy_pass, - '', - 15, - 60, - true, - '', - false, - '' -); -$table->data[29][0] = ''.__('Auth method'); -$table->data[29][1] = html_print_select( - [ - 'MD5' => __('MD5'), - 'SHA' => __('SHA'), - ], - 'snmp_auth_method', - $snmp3_auth_method, - '', - '', - '', - true, - false, - false, - '', - '' -); -$table->data[30][0] = ''.__('Security level'); -$table->data[30][1] = html_print_select( - [ - 'noAuthNoPriv' => __('Not auth and not privacy method'), - 'authNoPriv' => __('Auth and not privacy method'), - 'authPriv' => __('Auth and privacy method'), - ], - 'snmp_security_level', - $snmp3_security_level, - '', - '', - '', - true, - false, - false, - '', - '' -); - -// Explanation. -$explanation = db_get_value( - 'description', - 'trecon_script', - 'id_recon_script', - $id_recon_script -); - -$table->data[13][0] = ''.__('Explanation').''; -$table->data[13][1] = "'.html_print_textarea('explanation', 4, 60, $explanation, 'style="width: 388px;"', true); - -// A hidden "model row" to clone it from javascript to add fields dynamicaly. -$data = []; -$data[0] = 'macro_desc'; -$data[0] .= ui_print_help_tip('macro_help', true); -$data[1] = html_print_input_text( - 'macro_name', - 'macro_value', - '', - 100, - 255, - true -); -$table->colspan['macro_field'][1] = 3; -$table->rowstyle['macro_field'] = 'display:none'; -$table->data['macro_field'] = $data; - -// If there are $macros, we create the form fields. -if (!empty($macros)) { - $macros = json_decode($macros, true); - - if (isset($macros) === true - && is_array($macros) === true - ) { - foreach ($macros as $k => $m) { - $data = []; - $data[0] = ''.$m['desc'].''; - if (!empty($m['help'])) { - $data[0] .= ui_print_help_tip($m['help'], true); - } - - if ($m['hide']) { - $data[1] = html_print_input_password( - $m['macro'], - $m['value'], - '', - 100, - 255, - true - ); - } else { - $data[1] = html_print_input_text( - $m['macro'], - $m['value'], - '', - 100, - 255, - true - ); - } - - $table->colspan['macro'.$m['macro']][1] = 3; - $table->rowclass['macro'.$m['macro']] = 'macro_field'; - - $table->data['macro'.$m['macro']] = $data; - } - } -} - -// Comments. -$table->data[18][0] = ''.__('Comments'); -$table->data[18][1] = html_print_input_text( - 'description', - $description, - '', - 45, - 0, - true -); - -// OS detection. -$table->data[19][0] = ''.__('OS detection'); -$table->data[19][1] = html_print_checkbox( - 'os_detect', - 1, - $os_detect, - true -); - -// Name resolution. -$table->data[20][0] = ''.__('Name resolution'); -$table->data[20][1] = html_print_checkbox( - 'resolve_names', - 1, - $resolve_names, - true -); - -// Parent detection. -$table->data[21][0] = ''.__('Parent detection'); -$table->data[21][1] = html_print_checkbox( - 'parent_detection', - 1, - $parent_detection, - true -); - -// Parent recursion. -$table->data[22][0] = ''.__('Parent recursion'); -$table->data[22][1] = html_print_input_text( - 'parent_recursion', - $parent_recursion, - '', - 5, - 0, - true -).ui_print_help_tip( - __('Maximum number of parent hosts that will be created if parent detection is enabled.'), - true -); - -// Vlan_enabled. -$table->data[23][0] = ''.__('Vlan enabled'); -$table->data[23][1] = html_print_checkbox( - 'vlan_enabled', - 1, - $vlan_enabled, - true -); - -// NOTE: The 7.0NG Recon Server will not generate random names, since IP -// address collisions could have other consequences. -// Different Form url if it's a create or if it's a update form. -echo '
'; -html_print_table($table); -echo '
'; - -if ($id_rt != -1) { - if ($name_script != 'IPAM Recon') { - html_print_submit_button(__('Update'), 'crt', false, 'class="sub upd"'); - } -} else { - html_print_submit_button(__('Add'), 'crt', false, 'class="sub wand"'); -} - -echo '
'; - -echo '
'; - -ui_require_javascript_file('pandora_modules'); - -?> - - diff --git a/pandora_console/godmode/servers/plugin.php b/pandora_console/godmode/servers/plugin.php index b04643e1c3..57fa1a1e67 100644 --- a/pandora_console/godmode/servers/plugin.php +++ b/pandora_console/godmode/servers/plugin.php @@ -242,7 +242,6 @@ if ($filemanager) { $chunck_url = '&create=1'; } - $homedir_filemanager = isset($config['homedir_filemanager']) ? $config['homedir_filemanager'] : false; filemanager_file_explorer( $real_directory, $directory, @@ -253,7 +252,7 @@ if ($filemanager) { 'index.php?sec=gservers&sec2=godmode/servers/plugin'.$chunck_url.'&plugin_command=[FILE_FULLPATH]&id_plugin='.$id_plugin, true, 0775, - $homedir_filemanager + false ); } @@ -404,7 +403,7 @@ if (($create != '') || ($view != '')) { $table->data['plugin_command'] = $data; $data = []; - $data[0] = __('Plug-in parameters').ui_print_help_icon('plugin_parameters', true); + $data[0] = __('Plug-in parameters'); $data[1] = ''; if ($locked) { $data[1] .= html_print_image('images/lock.png', true, ['class' => 'command_advanced_conf lock']); @@ -550,7 +549,7 @@ if (($create != '') || ($view != '')) { $table->headstyle[0] = 'text-align: center'; html_print_table($table); } else { - echo '
'.''.__('Parameters macros').ui_print_help_icon('macros', true).''; + echo '
'.''.__('Parameters macros').''; html_print_table($table); echo '
'; } @@ -613,7 +612,16 @@ if (($create != '') || ($view != '')) { config_update_value('metaconsole_deploy_plugin_server', 1); } } else { - ui_print_page_header(__('Plug-ins registered on %s', get_product_name()), 'images/gm_servers.png', false, '', true); + ui_print_page_header( + __( + 'Plug-ins registered on %s', + get_product_name() + ), + 'images/gm_servers.png', + false, + '', + true + ); $management_allowed = !is_central_policies_on_node(); if (!$management_allowed) { @@ -954,9 +962,10 @@ if (($create != '') || ($view != '')) { if (defined('METACONSOLE')) { echo ''; } else { - echo '
'; + echo '
'; } + echo ''; echo ''; echo ''; echo ''; @@ -964,6 +973,8 @@ if (($create != '') || ($view != '')) { echo "'; } + echo ''; + $color = 0; foreach ($rows as $row) { @@ -1187,13 +1198,13 @@ ui_require_javascript_file('pandora_modules'); } } else { - alert(''); + alert(""); } } } var macros_click_locked_event = function (event) { - alert(''); + alert(""); } if (locked) { @@ -1202,4 +1213,6 @@ ui_require_javascript_file('pandora_modules'); $('.command_macro').click(macros_click_locked_event); } + + diff --git a/pandora_console/godmode/servers/recon_script.php b/pandora_console/godmode/servers/recon_script.php index b24c2a59e2..a77f465395 100644 --- a/pandora_console/godmode/servers/recon_script.php +++ b/pandora_console/godmode/servers/recon_script.php @@ -1,401 +1,27 @@ $id_reconscript] - ); - - echo htmlentities(io_safe_output($description), ENT_QUOTES, 'UTF-8', true); - return; -} - -// Load global vars -global $config; - -check_login(); - -if (! check_acl($config['id_user'], 0, 'LM')) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access recon script Management' - ); - include 'general/noaccess.php'; - return; -} - -/* - * Disabled at the moment. - if (!check_referer()) { - require ("general/noaccess.php"); - - return; - } +/** + * Deprectated. + * + * @category Deprectated + * @package Pandora FMS + * @subpackage recon script + * @version 1.0.0 + * @license See below + * + * ______ ___ _______ _______ ________ + * | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __| + * | __/| _ | | _ || _ | _| _ | | ___| |__ | + * |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______| + * + * ============================================================================ + * Copyright (c) 2005-2019 Artica Soluciones Tecnologicas + * Please see http://pandorafms.org for full contribution list + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation for version 2. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * ============================================================================ */ - -$view = get_parameter('view', ''); -$create = get_parameter('create', ''); - -if ($view != '') { - $form_id = $view; - $reconscript = db_get_row('trecon_script', 'id_recon_script', $form_id); - $form_name = $reconscript['name']; - $form_description = $reconscript['description']; - $form_script = $reconscript['script']; - $macros = $reconscript['macros']; -} - -if ($create != '') { - $form_name = ''; - $form_description = ''; - $form_script = ''; - $macros = ''; -} - -// SHOW THE FORM -// ================================================================= -if (($create != '') or ($view != '')) { - if ($create != '') { - ui_print_page_header(__('Recon script creation'), 'images/gm_servers.png', false, 'reconscript_definition', true); - } else { - ui_print_page_header(__('Recon script update'), 'images/gm_servers.png', false, 'reconscript_definition', true); - $id_recon_script = get_parameter('view', ''); - } - - - if ($create == '') { - echo ""; - } else { - echo ""; - } - - $table = new stdClass(); - $table->width = '100%'; - $table->id = 'table-form'; - $table->class = 'databox filters'; - $table->style = []; - $table->style[0] = 'font-weight: bold'; - $table->style[2] = 'font-weight: bold'; - $table->data = []; - - $data = []; - $data[0] = __('Name'); - $data[1] = ''; - $table->data['recon_name'] = $data; - $table->colspan['recon_name'][1] = 3; - - $data = []; - $data[0] = __('Script fullpath'); - $data[1] = ''; - $table->data['recon_fullpath'] = $data; - $table->colspan['recon_fullpath'][1] = 3; - - $data = []; - $data[0] = __('Description'); - $data[1] = ''; - $table->data['recon_description'] = $data; - $table->colspan['recon_description'][1] = 3; - - $macros = json_decode($macros, true); - - // This code is ready to add locked feature as plugins - $locked = false; - - // The next row number is recon_3 - $next_name_number = 3; - $i = 1; - while (1) { - // Always print at least one macro - if ((!isset($macros[$i]) || $macros[$i]['desc'] == '') && $i > 1) { - break; - } - - $macro_desc_name = 'field'.$i.'_desc'; - $macro_desc_value = ''; - $macro_help_name = 'field'.$i.'_help'; - $macro_help_value = ''; - $macro_value_name = 'field'.$i.'_value'; - $macro_value_value = ''; - $macro_name_name = 'field'.$i.'_macro'; - $macro_name = '_field'.$i.'_'; - $macro_hide_value_name = 'field'.$i.'_hide'; - $macro_hide_value_value = 0; - - if (isset($macros[$i]['desc'])) { - $macro_desc_value = $macros[$i]['desc']; - } - - if (isset($macros[$i]['help'])) { - $macro_help_value = $macros[$i]['help']; - } - - if (isset($macros[$i]['value'])) { - $macro_value_value = $macros[$i]['value']; - } - - if (isset($macros[$i]['hide'])) { - $macro_hide_value_value = $macros[$i]['hide']; - } - - $datam = []; - $datam[0] = __('Description')." ($macro_name)"; - $datam[0] .= html_print_input_hidden($macro_name_name, $macro_name, true); - $datam[1] = html_print_input_text_extended($macro_desc_name, $macro_desc_value, 'text-'.$macro_desc_name, '', 30, 255, $locked, '', "class='command_advanced_conf'", true); - if ($locked) { - $datam[1] .= html_print_image('images/lock.png', true, ['class' => 'command_advanced_conf']); - } - - $datam[2] = __('Default value')." ($macro_name)"; - $datam[3] = html_print_input_text_extended($macro_value_name, $macro_value_value, 'text-'.$macro_value_name, '', 30, 255, $locked, '', "class='command_component command_advanced_conf'", true); - if ($locked) { - $datam[3] .= html_print_image('images/lock.png', true, ['class' => 'command_advanced_conf']); - } - - $table->data['recon_'.$next_name_number] = $datam; - - $next_name_number++; - - $table->colspan['recon_'.$next_name_number][1] = 3; - - $datam = []; - $datam[0] = __('Hide value').ui_print_help_tip(__('This field will show up as dots like a password'), true); - $datam[1] = html_print_checkbox_extended($macro_hide_value_name, 1, $macro_hide_value_value, 0, '', ['class' => 'command_advanced_conf'], true, 'checkbox-'.$macro_hide_value_name); - - $table->data['recon_'.$next_name_number] = $datam; - $next_name_number++; - - $table->colspan['recon_'.$next_name_number][1] = 3; - - $datam = []; - $datam[0] = __('Help')." ($macro_name)


"; - $tadisabled = $locked === true ? ' disabled' : ''; - $datam[1] = html_print_textarea($macro_help_name, 6, 100, $macro_help_value, 'class="command_advanced_conf" style="width: 97%;"'.$tadisabled, true); - - if ($locked) { - $datam[1] .= html_print_image('images/lock.png', true, ['class' => 'command_advanced_conf']); - } - - $datam[1] .= '


'; - - $table->data['recon_'.$next_name_number] = $datam; - $next_name_number++; - $i++; - } - - if (!$locked) { - $datam = []; - $datam[0] = ''.__('Add macro').''.html_print_image('images/add.png', true).''; - $datam[0] .= ''; - $datam[0] .= ''; - $delete_macro_style = ''; - if ($i <= 2) { - $delete_macro_style = 'display:none;'; - } - - $datam[2] = ''; - - $table->colspan['recon_action'][0] = 2; - $table->rowstyle['recon_action'] = 'text-align:center'; - $table->colspan['recon_action'][2] = 2; - $table->data['recon_action'] = $datam; - } - - html_print_table($table); - - echo '
'.__('Name').''.__('Type').''.__('Command').'".''.__('Op.').''.'
'; - echo '
'; - - if ($create != '') { - echo ""; - } else { - echo ""; - } - - echo '
'; -} else { - ui_print_page_header(__('Recon scripts registered on %s', get_product_name()), 'images/gm_servers.png', false, '', true); - - // Update reconscript - if (isset($_GET['update_reconscript'])) { - // if modified any parameter - $id_recon_script = get_parameter('update_reconscript', 0); - $reconscript_name = get_parameter('form_name', ''); - $reconscript_description = get_parameter('form_description', ''); - $reconscript_script = get_parameter('form_script', ''); - - // Get macros - $i = 1; - $macros = []; - while (1) { - $macro = (string) get_parameter('field'.$i.'_macro'); - if ($macro == '') { - break; - } - - $desc = (string) get_parameter('field'.$i.'_desc'); - $help = (string) get_parameter('field'.$i.'_help'); - $value = (string) get_parameter('field'.$i.'_value'); - $hide = get_parameter('field'.$i.'_hide'); - - $macros[$i]['macro'] = $macro; - $macros[$i]['desc'] = $desc; - $macros[$i]['help'] = $help; - $macros[$i]['value'] = $value; - $macros[$i]['hide'] = $hide; - $i++; - } - - $macros = io_json_mb_encode($macros); - - $sql_update = "UPDATE trecon_script SET - name = '$reconscript_name', - description = '$reconscript_description', - script = '$reconscript_script', - macros = '$macros' - WHERE id_recon_script = $id_recon_script"; - $result = false; - if ($reconscript_name != '' && $reconscript_script != '') { - $result = db_process_sql($sql_update); - } - - if (! $result) { - ui_print_error_message(__('Problem updating')); - } else { - ui_print_success_message(__('Updated successfully')); - } - } - - // Create reconscript - if (isset($_GET['create_reconscript'])) { - $reconscript_name = get_parameter('form_name', ''); - $reconscript_description = get_parameter('form_description', ''); - $reconscript_script = get_parameter('form_script', ''); - - // Get macros - $i = 1; - $macros = []; - while (1) { - $macro = (string) get_parameter('field'.$i.'_macro'); - if ($macro == '') { - break; - } - - $desc = (string) get_parameter('field'.$i.'_desc'); - $help = (string) get_parameter('field'.$i.'_help'); - $value = (string) get_parameter('field'.$i.'_value'); - $hide = get_parameter('field'.$i.'_hide'); - - $macros[$i]['macro'] = $macro; - $macros[$i]['desc'] = $desc; - $macros[$i]['help'] = $help; - $macros[$i]['value'] = $value; - $macros[$i]['hide'] = $hide; - $i++; - } - - $macros = io_json_mb_encode($macros); - - $values = [ - 'name' => $reconscript_name, - 'description' => $reconscript_description, - 'script' => $reconscript_script, - 'macros' => $macros, - ]; - $result = false; - if ($values['name'] != '' && $values['script'] != '') { - $result = db_process_sql_insert('trecon_script', $values); - } - - if (! $result) { - ui_print_error_message(__('Problem creating')); - } else { - ui_print_success_message(__('Created successfully')); - } - } - - if (isset($_GET['kill_reconscript'])) { - // if delete alert - $reconscript_id = get_parameter('kill_reconscript', 0); - - $result = db_process_sql_delete( - 'trecon_script', - ['id_recon_script' => $reconscript_id] - ); - - if (! $result) { - ui_print_error_message(__('Problem deleting reconscript')); - } else { - ui_print_success_message(__('reconscript deleted successfully')); - } - - if ($reconscript_id != 0) { - $result = db_process_sql_delete( - 'trecon_task', - ['id_recon_script' => $reconscript_id] - ); - } - } - - // If not edition or insert, then list available reconscripts - $rows = db_get_all_rows_in_table('trecon_script'); - - if ($rows !== false) { - echo ''; - echo ''; - echo ''; - echo ''; - $color = 0; - foreach ($rows as $row) { - if ($color == 1) { - $tdcolor = 'datos'; - $color = 0; - } else { - $tdcolor = 'datos2'; - $color = 1; - } - - echo ''; - echo "'; - echo "'; - } - - echo '
'.__('Name').''.__('Description').''.__('Delete').'
"; - echo ""; - echo $row['name']; - echo '"; - $desc = io_safe_output($row['description']); - $desc = str_replace("\n", '
', $desc); - echo $desc.'

'; - echo ''.__('Command').': '.$row['script'].''; - echo "
"; - echo "".html_print_image('images/cross.png', true, ['border' => '0']).''; - echo '
'; - } else { - ui_print_info_message(['no_close' => true, 'message' => __('There are no recon scripts in the system') ]); - } - - echo ''; - echo '
'; - echo "
"; - echo ""; - echo '
'; -} - -ui_require_javascript_file('pandora_modules'); diff --git a/pandora_console/godmode/servers/servers.build_table.php b/pandora_console/godmode/servers/servers.build_table.php index 2bb4f6ab00..1b5486b570 100644 --- a/pandora_console/godmode/servers/servers.build_table.php +++ b/pandora_console/godmode/servers/servers.build_table.php @@ -40,7 +40,9 @@ if ($servers === false) { $table = new StdClass(); $table->width = '100%'; -$table->class = 'databox data'; +$table->class = 'info_table'; +$table->cellpadding = 0; +$table->cellspacing = 0; $table->size = []; $table->style = []; @@ -80,7 +82,11 @@ $names_servers = []; foreach ($servers as $server) { $data = []; - $table->cellclass[][3] = 'progress_bar'; + // $table->cellclass[][3] = 'progress_bar'; + $table->cellclass[] = [ + 3 => 'progress_bar', + 8 => 'action_buttons', + ]; $data[0] = ''.strip_tags($server['name']).''; // Status @@ -155,7 +161,7 @@ foreach ($servers as $server) { 'style' => 'width:21px;height:21px;', ] ); - $data[8] .= '  '; + $data[8] .= ''; } if ($server['type'] == 'data') { @@ -165,7 +171,7 @@ foreach ($servers as $server) { true, ['title' => __('Reset module status and fired alert counts')] ); - $data[8] .= '  '; + $data[8] .= ''; } else if ($server['type'] == 'enterprise snmp') { $data[8] .= ''; $data[8] .= html_print_image( @@ -173,7 +179,7 @@ foreach ($servers as $server) { true, ['title' => __('Claim back SNMP modules')] ); - $data[8] .= '  '; + $data[8] .= ''; } $data[8] .= ''; @@ -195,7 +201,7 @@ foreach ($servers as $server) { $names_servers[$safe_server_name] = false; } - $data[8] .= '  '; + $data[8] .= ''; $data[8] .= html_print_image( 'images/cross.png', true, @@ -225,7 +231,12 @@ if ($tiny) { } if ($tiny) { - ui_toggle(html_print_table($table, true), __('Tactical server information'), false, $hidden_toggle); + ui_toggle( + html_print_table($table, true), + __('Tactical server information'), + false, + $hidden_toggle + ); } else { html_print_table($table); } diff --git a/pandora_console/godmode/setup/file_manager.php b/pandora_console/godmode/setup/file_manager.php index 27f3e232c8..6d5e7679b7 100644 --- a/pandora_console/godmode/setup/file_manager.php +++ b/pandora_console/godmode/setup/file_manager.php @@ -66,8 +66,6 @@ $real_directory = realpath($config['homedir'].'/'.$directory); echo '

'.__('Index of %s', $directory).'

'; -$homedir_filemanager = isset($config['homedir_filemanager']) ? $config['homedir_filemanager'] : false; - filemanager_file_explorer( $real_directory, $directory, @@ -78,5 +76,5 @@ filemanager_file_explorer( '', false, '', - $homedir_filemanager + false ); diff --git a/pandora_console/godmode/setup/gis.php b/pandora_console/godmode/setup/gis.php index 946d0526ea..526abc0b0a 100755 --- a/pandora_console/godmode/setup/gis.php +++ b/pandora_console/godmode/setup/gis.php @@ -28,8 +28,14 @@ require_once 'include/functions_gis.php'; ui_require_javascript_file('openlayers.pandora'); -// Header -ui_print_page_header(__('Map conections GIS'), '', false, 'setup_gis_index', true); +// Header. +ui_print_page_header( + __('Map conections GIS'), + '', + false, + '', + true +); $action = get_parameter('action'); @@ -63,14 +69,15 @@ switch ($action) { break; } +$table->class = 'info_table'; $table->width = '98%'; $table->head[0] = __('Map connection name'); $table->head[1] = __('Group'); $table->head[3] = __('Delete'); -$table->align[1] = 'center'; -$table->align[2] = 'center'; -$table->align[3] = 'center'; +$table->align[1] = 'left'; +$table->align[2] = 'left'; +$table->align[3] = 'left'; $mapsConnections = db_get_all_rows_in_table('tgis_map_connection', 'conection_name'); @@ -84,6 +91,7 @@ if ($mapsConnections !== false) { '
'.html_print_image('images/cross.png', true).'', ]; + $table->cellclass[][2] = 'action_buttons'; } } diff --git a/pandora_console/godmode/setup/gis_step_2.php b/pandora_console/godmode/setup/gis_step_2.php index 5203bdef23..133b4fdeaa 100644 --- a/pandora_console/godmode/setup/gis_step_2.php +++ b/pandora_console/godmode/setup/gis_step_2.php @@ -13,7 +13,7 @@ * GNU General Public License for more details. */ -// Load global vars +// Load global vars. global $config; check_login(); @@ -35,8 +35,14 @@ echo ''.''.''.htmlentities(__('Tile Server URL'), ENT_QUOTES, 'UTF-8').':'.''.html_print_input_text('url', $mapConnectionDataUrl, '', 45, 90, true).''.''.''; -// Google Maps Connection +// Google Maps Connection. $gmaps_types['G_PHYSICAL_MAP'] = __('Google Physical'); $gmaps_types['G_HYBRID_MAP'] = __('Google Hybrid'); $gmaps_types['G_SATELITE_MAP'] = __('Google Satelite'); // TODO: Use label tags for the forms. $optionsConnectionGmapTable = ''.''.''.''.''.''.''.''.''.''.''.'
'.__('Google Map Type').':'.trim(html_print_select($gmaps_types, 'gmap_type', $gmap_type, '', '', 0, true)).'
'.__('Google Maps Key').':
'.html_print_input_text('gmap_key', $gmap_key, '', 90, 128, true).'
'; -// Image Map Connection +// Image Map Connection. $optionsConnectionImageTable = ''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.'
'.__('Image URL').':'.html_print_input_text('url', $mapConnectionDataUrl, '', 45, 90, true).'
'.__('Corners of the area of the image').':
'.__('Left').':'.html_print_input_text('bb_left', $bb_left, '', 25, 25, true).''.__('Bottom').':'.html_print_input_text('bb_bottom', $bb_bottom, '', 25, 25, true).'
'.__('Right').':'.html_print_input_text('bb_right', $bb_right, '', 25, 25, true).''.__('Top').':'.html_print_input_text('bb_top', $bb_top, '', 25, 25, true).'
'.__('Image Size').':
'.__('Width').':'.html_print_input_text('image_width', $image_width, '', 25, 25, true).''.__('Height').':'.html_print_input_text('image_height', $image_height, '', 25, 25, true).'
'; -// WMS Server Connection +// WMS Server Connection. $optionsConnectionWMSTable = ''.''.''.''.''.''.''.''.''.'
'.__('WMS Server URL').''.''.html_print_input_text('url', $mapConnectionDataUrl, '', 90, 255, true).'
'.__('Layers').ui_print_help_tip(__('Enter a single element or a comma separated list'), true).''.html_print_input_text('layers', $layers, '', 90, 255, true).'
'; if ($mapConnectionData != null) { @@ -274,6 +297,10 @@ if ($mapConnectionData != null) { case 'WMS': $optionsConnectionTypeTable = $optionsConnectionWMSTable; break; + + default: + // Default. + break; } } diff --git a/pandora_console/godmode/setup/license.php b/pandora_console/godmode/setup/license.php index 413b1d7fe1..f03d32ef0a 100644 --- a/pandora_console/godmode/setup/license.php +++ b/pandora_console/godmode/setup/license.php @@ -1,15 +1,32 @@ $value) { - db_process_sql_update( - 'tupdate_settings', - [db_escape_key_identifier('value') => $value], - [db_escape_key_identifier('key') => $key] - ); - } - - ui_print_success_message(__('License updated')); + ui_require_javascript_file_enterprise('load_enterprise'); + enterprise_include_once('include/functions_license.php'); +} + +if ($update_settings) { + if (!is_metaconsole()) { + // Node. + foreach ($_POST['keys'] as $key => $value) { + db_process_sql_update( + 'tupdate_settings', + [db_escape_key_identifier('value') => $value], + [db_escape_key_identifier('key') => $key] + ); + } + + ui_print_success_message(__('License updated')); + } } -ui_require_javascript_file_enterprise('load_enterprise'); -enterprise_include_once('include/functions_license.php'); $license = enterprise_hook('license_get_info'); $rows = db_get_all_rows_in_table('tupdate_settings'); @@ -55,11 +88,20 @@ if (enterprise_installed()) { echo ''; echo ''; +// Retrieve UM url configured (or default). +$url = get_um_url(); $table = new stdClass(); $table->width = '100%'; $table->class = 'databox filters'; +if (is_metaconsole()) { + $table->head[0] = __('Licence'); + $table->head_colspan[0] = 3; + $table->headstyle[0] = 'text-align: center'; + $table->style[0] = 'font-weight: bold;'; +} + $table->data = []; $table->data[0][0] = ''.__('Customer key').''; @@ -98,34 +140,45 @@ if (enterprise_installed()) { html_print_input_hidden('update_settings', 1); html_print_submit_button(__('Validate'), 'update_button', false, 'class="sub upd"'); echo '  '; - html_print_button(__('Request new license'), '', false, 'generate_request_code()', 'class="ui-button-dialog ui-widget ui-state-default ui-corner-all ui-button-text-only sub next"'); + html_print_button(__('Request new license'), '', false, 'generate_request_code()', 'class="sub next"'); echo '
'; } -echo ''; -echo ''; +if (is_metaconsole()) { + ui_require_css_file('pandora_enterprise', '../../'.ENTERPRISE_DIR.'/include/styles/'); + ui_require_css_file('register', '../../include/styles/'); +} else { + ui_require_css_file('pandora'); + ui_require_css_file('pandora_enterprise', ENTERPRISE_DIR.'/include/styles/'); + ui_require_css_file('register'); +} + +if (enterprise_hook('print_activate_licence_dialog') == ENTERPRISE_NOT_HOOK) { + echo ''; + echo ''; +} diff --git a/pandora_console/godmode/setup/links.php b/pandora_console/godmode/setup/links.php index f5016e666e..c15052412b 100644 --- a/pandora_console/godmode/setup/links.php +++ b/pandora_console/godmode/setup/links.php @@ -137,9 +137,11 @@ if ((isset($_GET['form_add'])) or (isset($_GET['form_edit']))) { if (empty($rows)) { ui_print_info_message(['no_close' => true, 'message' => __("There isn't links") ]); } else { - echo ""; + echo "
"; + echo ''; echo "'; echo "'; + echo ''; $color = 1; foreach ($rows as $row) { @@ -152,7 +154,7 @@ if ((isset($_GET['form_add'])) or (isset($_GET['form_edit']))) { } echo "'; - echo ''; + echo ''; } echo '
".__('Link name').'".__('Delete').'
".$row['name'].''.html_print_image('images/cross.png', true).'
'.html_print_image('images/cross.png', true).'
'; diff --git a/pandora_console/godmode/setup/news.php b/pandora_console/godmode/setup/news.php index 7b663bad53..61491496f5 100644 --- a/pandora_console/godmode/setup/news.php +++ b/pandora_console/godmode/setup/news.php @@ -231,14 +231,15 @@ if ((isset($_GET['form_add'])) || (isset($_GET['form_edit']))) { ui_print_info_message(['no_close' => true, 'message' => __('There are no defined news') ]); } else { // Main list view for Links editor - echo ""; + echo "
"; + echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; - + echo ''; $color = 1; @@ -276,7 +277,7 @@ if ((isset($_GET['form_add'])) || (isset($_GET['form_edit']))) { echo "'; } - echo ''; + echo ''; } echo '
'.__('Subject').''.__('Type').''.__('Author').''.__('Timestamp').''.__('Expiration').''.__('Delete').'
".__('No').''.html_print_image('images/cross.png', true, ['border' => '0']).'
'.html_print_image('images/cross.png', true, ['border' => '0']).'
'; @@ -330,7 +331,7 @@ ui_require_javascript_file('tiny_mce', 'include/javascript/tiny_mce/'); theme : "advanced", theme_advanced_toolbar_location : "top", theme_advanced_toolbar_align : "left", - theme_advanced_buttons1 : "bold,italic, |, image, link, |, cut, copy, paste, |, undo, redo, |, forecolor, |, fontsizeselect, |, justifyleft, justifycenter, justifyright", + theme_advanced_buttons1 : "bold,italic, |, image, link, |, cut, copy, paste, |, undo, redo, |, forecolor, |, fontsizeselect, |, justifyleft, justifycenter, justifyright, | ,code", theme_advanced_buttons2 : "", theme_advanced_buttons3 : "", convert_urls : false, diff --git a/pandora_console/godmode/setup/os.list.php b/pandora_console/godmode/setup/os.list.php index f00b8cb00d..07365edc6c 100644 --- a/pandora_console/godmode/setup/os.list.php +++ b/pandora_console/godmode/setup/os.list.php @@ -25,7 +25,7 @@ if (! check_acl($config['id_user'], 0, 'PM') && ! is_user_admin($config['id_user $table = new stdClass(); $table->width = '100%'; -$table->class = 'databox data'; +$table->class = 'info_table'; $table->head[0] = ''; $table->head[1] = __('ID'); @@ -37,7 +37,19 @@ $table->align[4] = 'center'; $table->size[0] = '20px'; $table->size[4] = '20px'; -$osList = db_get_all_rows_in_table('tconfig_os'); +// Prepare pagination. +$offset = (int) get_parameter('offset'); +$limit = $config['block_size']; +$count_osList = db_get_value('count(*)', 'tconfig_os'); + +$osList = db_get_all_rows_filter( + 'tconfig_os', + [ + 'offset' => $offset, + 'limit' => $limit, + ] +); + if ($osList === false) { $osList = []; } @@ -54,6 +66,8 @@ foreach ($osList as $os) { } $data[] = ui_print_truncate_text(io_safe_output($os['description']), 'description', true, true); + + $table->cellclass[][4] = 'action_buttons'; if ($os['id_os'] > 16) { if (is_metaconsole()) { $data[] = ''.html_print_image('images/cross.png', true).''; @@ -69,7 +83,9 @@ foreach ($osList as $os) { } if (isset($data)) { + ui_pagination($count_osList, false, $offset); html_print_table($table); + ui_pagination($count_osList, false, $offset, 0, false, 'offset', true, 'pagination-bottom'); } else { ui_print_info_message(['no_close' => true, 'message' => __('There are no defined operating systems') ]); } diff --git a/pandora_console/godmode/setup/performance.php b/pandora_console/godmode/setup/performance.php index 666dd0b21c..af414d6ab3 100644 --- a/pandora_console/godmode/setup/performance.php +++ b/pandora_console/godmode/setup/performance.php @@ -200,8 +200,8 @@ if ($config['history_db_enabled'] == 1) { ); $time_pandora_db_history = false; - if ($history_connect !== false) { - if ($config['history_db_connection'] !== false) { + if ($history_connect) { + if ($config['history_db_connection']) { $time_pandora_db_history = mysql_db_process_sql( $sql, 'insert_id', @@ -247,8 +247,7 @@ $table->style[0] = 'font-weight: bold'; $table->size[0] = '70%'; $table->size[1] = '30%'; -enterprise_hook('enterprise_warnings_history_days'); - +// enterprise_hook('enterprise_warnings_history_days'); $table->data[1][0] = __('Max. days before delete events'); $table->data[1][0] .= ui_print_help_tip( __('If the compaction or purge of the data is more frequent than the events deletion, anomalies in module graphs could appear'), @@ -405,16 +404,16 @@ if ($config['history_db_enabled'] == 1) { ); } - $history_connect = @mysql_db_process_sql( - 'SELECT 1 FROM tconfig', - 'affected_rows', - $config['history_db_connection'], - false - ); - $config_history = false; - if ($history_connect !== false) { - if ($config['history_db_connection'] != false) { + if ($config['history_db_connection']) { + $history_connect = @mysql_db_process_sql( + 'SELECT 1 FROM tconfig', + 'affected_rows', + $config['history_db_connection'], + false + ); + + if ($history_connect !== false) { $config_history_array = mysql_db_process_sql( 'SELECT * FROM tconfig', 'affected_rows', @@ -427,11 +426,11 @@ if ($config['history_db_enabled'] == 1) { $config_history[$value['token']] = $value['value']; } } + } else { + echo ui_print_error_message( + __('The tconfig table does not exist in the historical database') + ); } - } else { - echo ui_print_error_message( - __('The tconfig table does not exist in the historical database') - ); } if ($config_history === false) { @@ -536,6 +535,19 @@ $table->data[] = [ ), ]; + +$table->data[] = [ + __('Max. days before delete old network matrix data'), + html_print_input_text( + 'delete_old_network_matrix', + $config['delete_old_network_matrix'], + '', + 5, + 5, + true + ), +]; + $table_other = new stdClass(); $table_other->width = '100%'; $table_other->class = 'databox filters'; @@ -615,7 +627,7 @@ $table_other->data[6][1] = html_print_input_text( true ); -$table_other->data[7][0] = __('Use agent access graph').ui_print_help_icon('agent_access', true); +$table_other->data[7][0] = __('Use agent access graph'); $table_other->data[7][1] = html_print_checkbox_switch('agentaccess', 1, $config['agentaccess'], true); $table_other->data[8][0] = __('Max. recommended number of files in attachment directory'); @@ -694,24 +706,24 @@ $table_other->data[13][1] = html_print_input_text( echo '
'; echo '
'; - echo ''.__('Database maintenance status').''; + echo ''.__('Database maintenance status').' '.ui_print_help_icon('database_maintenance_status_tab', true).''; html_print_table($table_status); echo '
'; echo '
'; - echo ''.__('Database maintenance options').''; + echo ''.__('Database maintenance options').' '.ui_print_help_icon('database_maintenance_options_tab', true).''; html_print_table($table); echo '
'; if ($config['history_db_enabled'] == 1) { echo '
'; - echo ''.__('Historical database maintenance options').''; + echo ''.__('Historical database maintenance options').' '.ui_print_help_icon('historical_database_maintenance_options_tab', true).''; html_print_table($table_historical); echo '
'; } echo '
'; - echo ''.__('Others').''; + echo ''.__('Others').' '.ui_print_help_icon('others_database_maintenance_options_tab', true).''; html_print_table($table_other); echo '
'; diff --git a/pandora_console/godmode/setup/setup.php b/pandora_console/godmode/setup/setup.php index 6ee8577367..f5bfc8c635 100644 --- a/pandora_console/godmode/setup/setup.php +++ b/pandora_console/godmode/setup/setup.php @@ -135,9 +135,9 @@ if (enterprise_installed()) { switch ($section) { case 'general': - default: $buttons['general']['active'] = true; $subpage = ' » '.__('General'); + $help_header = 'setup_general_tab'; break; case 'auth': @@ -148,7 +148,7 @@ switch ($section) { case 'perf': $buttons['perf']['active'] = true; $subpage = ' » '.__('Performance'); - $help_header = 'performance'; + $help_header = ''; break; case 'vis': @@ -164,16 +164,34 @@ switch ($section) { case 'ehorus': $buttons['ehorus']['active'] = true; $subpage = ' » '.__('eHorus'); + $help_header = 'setup_ehorus_tab'; break; case 'notifications': $buttons['notifications']['active'] = true; $subpage = ' » '.__('Notifications'); break; + + case 'enterprise': + $buttons['enterprise']['active'] = true; + $subpage = ' » '.__('Enterprise'); + $help_header = 'setup_enterprise_tab'; + break; + + default: + // Default. + break; } // Header. -ui_print_page_header(__('Configuration').$subpage, '', false, $help_header, true, $buttons); +ui_print_page_header( + __('Configuration').$subpage, + '', + false, + $help_header, + true, + $buttons +); if (isset($config['error_config_update_config'])) { if ($config['error_config_update_config']['correct'] == false) { diff --git a/pandora_console/godmode/setup/setup_ehorus.php b/pandora_console/godmode/setup/setup_ehorus.php index fc2ebd3d3a..89d4ff35e8 100644 --- a/pandora_console/godmode/setup/setup_ehorus.php +++ b/pandora_console/godmode/setup/setup_ehorus.php @@ -66,6 +66,12 @@ $table_remote->class = 'databox filters'; $table_remote->size['name'] = '30%'; $table_remote->style['name'] = 'font-weight: bold'; +// Enable eHorus user configuration. +$row = []; +$row['name'] = ('eHorus configuration at user level'); +$row['control'] = html_print_checkbox_switch('ehorus_user_level_conf', 1, $config['ehorus_user_level_conf'], true); +$table_remote->data['ehorus_user_level_conf'] = $row; + // User. $row = []; $row['name'] = __('User'); @@ -139,58 +145,98 @@ if ($config['ehorus_enabled'] && !$custom_field_exists) { ui_print_error_message($error_message); } +echo ""; // Form enable. -echo ''; +echo '
'; html_print_input_hidden('update_config', 1); html_print_table($table_enable); -echo ''; +echo '
'; // Form remote. -if ($config['ehorus_enabled']) { - echo '
'; + echo '
'; echo '
'; echo ''.__('eHorus API').''; html_print_input_hidden('update_config', 1); html_print_table($table_remote); - echo '
'; + + echo '
'; + echo '
'; + echo '
'; html_print_submit_button(__('Update'), 'update_button', false, 'class="sub upd"'); echo '
'; - echo ''; echo '
'; -} ?> diff --git a/pandora_console/godmode/setup/setup_general.php b/pandora_console/godmode/setup/setup_general.php index 370df5d808..8079aee1e8 100644 --- a/pandora_console/godmode/setup/setup_general.php +++ b/pandora_console/godmode/setup/setup_general.php @@ -82,7 +82,7 @@ $table->data[2][1] = html_print_input_text('phantomjs_bin', io_safe_output($conf $table->data[6][0] = __('Auto login (hash) password'); $table->data[6][1] = html_print_input_password('loginhash_pwd', io_output_password($config['loginhash_pwd']), '', 15, 15, true); -$table->data[9][0] = __('Time source').ui_print_help_icon('timesource', true); +$table->data[9][0] = __('Time source'); $sources['system'] = __('System'); $sources['sql'] = __('Database'); $table->data[9][1] = html_print_select($sources, 'timesource', $config['timesource'], '', '', '', true); @@ -107,7 +107,7 @@ $table->data[13][1] = html_print_input_text('cert_path', io_safe_output($config[ $table->data[14][0] = __('Attachment store').ui_print_help_tip(__('Directory where temporary data is stored.'), true); $table->data[14][1] = html_print_input_text('attachment_store', io_safe_output($config['attachment_store']), '', 50, 255, true); -$table->data[15][0] = __('IP list with API access').ui_print_help_icon('ip_api_list', true); +$table->data[15][0] = __('IP list with API access'); if (isset($_POST['list_ACL_IPs_for_API'])) { $list_ACL_IPs_for_API = get_parameter_post('list_ACL_IPs_for_API'); } else { @@ -131,6 +131,14 @@ if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { $table->data[19][1] = html_print_checkbox_switch_extended('activate_netflow', 1, $config['activate_netflow'], $rbt_disabled, '', '', true); +$table->data[21][0] = __('Enable Network Traffic Analyzer'); +$table->data[21][1] = html_print_switch( + [ + 'name' => 'activate_nta', + 'value' => $config['activate_nta'], + ] +); + $zone_name = [ 'Africa' => __('Africa'), diff --git a/pandora_console/godmode/setup/setup_notifications.php b/pandora_console/godmode/setup/setup_notifications.php index 9ee95f78cc..8f6630d4e3 100644 --- a/pandora_console/godmode/setup/setup_notifications.php +++ b/pandora_console/godmode/setup/setup_notifications.php @@ -160,6 +160,20 @@ if (get_parameter('get_notifications_dropdown', 0)) { return; } +if (get_parameter('get_notification', 0)) { + $msg_id = get_parameter('id', 0); + + if ($msg_id > 0) { + $msg = messages_get_message($msg_id); + + $msg['mensaje'] = io_safe_output($msg['mensaje']); + $msg['subject'] = io_safe_output($msg['subject']); + echo json_encode($msg); + } + + return; +} + // Notification table. It is just a wrapper. $table_content = new StdClass(); $table_content->data = []; diff --git a/pandora_console/godmode/setup/setup_visuals.php b/pandora_console/godmode/setup/setup_visuals.php index a1ffc9008a..bcaff54d95 100755 --- a/pandora_console/godmode/setup/setup_visuals.php +++ b/pandora_console/godmode/setup/setup_visuals.php @@ -70,10 +70,6 @@ $values[SECONDS_5MINUTES] = human_time_description_raw(SECONDS_5MINUTES); $values[SECONDS_10MINUTES] = human_time_description_raw(SECONDS_10MINUTES); $values[SECONDS_30MINUTES] = human_time_description_raw(SECONDS_30MINUTES); -$table_behaviour->data[$row][0] = __('Default interval for refresh on Visual Console').ui_print_help_tip(__('This interval will affect to Visual Console pages'), true); -$table_behaviour->data[$row][1] = html_print_select($values, 'vc_refr', $config['vc_refr'], '', 'N/A', 0, true, false, false); -$row++; - $table_behaviour->data[$row][0] = __('Paginated module view'); $table_behaviour->data[$row][1] = html_print_checkbox_switch( 'paginate_module', @@ -121,7 +117,7 @@ if (enterprise_installed()) { } echo '
'; -echo ''.__('Behaviour configuration').''; +echo ''.__('Behaviour configuration').' '.ui_print_help_icon('behavoir_conf_tab', true).''; html_print_table($table_behaviour); echo '
'; // ---------------------------------------------------------------------- @@ -243,7 +239,7 @@ function logo_custom_enterprise($name, $logo) false, true, '', - $open, + false, 'width:240px' ); return $select; @@ -259,7 +255,7 @@ function logo_custom_enterprise($name, $logo) false, true, '', - $open, + true, 'width:240px' ); return $select; @@ -267,12 +263,12 @@ function logo_custom_enterprise($name, $logo) } -$table_styles->data[$row][0] = __('Custom logo (menu)').ui_print_help_icon('custom_logo', true); +$table_styles->data[$row][0] = __('Custom logo (menu)'); $table_styles->data[$row][1] = logo_custom_enterprise('custom_logo', $config['custom_logo']); $table_styles->data[$row][1] .= ' '.html_print_button(__('View'), 'custom_logo_preview', $open, '', 'class="sub camera logo_preview"', true, false, $open, 'visualmodal'); $row++; -$table_styles->data[$row][0] = __('Custom logo collapsed (menu)').ui_print_help_icon('custom_logo_collapsed', true); +$table_styles->data[$row][0] = __('Custom logo collapsed (menu)'); $table_styles->data[$row][1] = logo_custom_enterprise('custom_logo_collapsed', $config['custom_logo_collapsed']); $table_styles->data[$row][1] .= ' '.html_print_button(__('View'), 'custom_logo_collapsed_preview', $open, '', 'class="sub camera logo_preview"', true, false, $open, 'visualmodal'); $row++; @@ -316,7 +312,7 @@ if (enterprise_installed()) { $table_styles->data[$row][1] .= ' '.html_print_button(__('View'), 'custom_logo_white_bg_preview', $open, '', 'class="sub camera logo_preview"', true, false, $open, 'visualmodal'); $row++; -$table_styles->data[$row][0] = __('Custom logo (login)').ui_print_help_icon('custom_logo', true); +$table_styles->data[$row][0] = __('Custom logo (login)'); if (enterprise_installed()) { $table_styles->data[$row][1] = html_print_select( @@ -462,6 +458,20 @@ if (enterprise_installed()) { $row++; } +// Title Header +if (enterprise_installed()) { + $table_styles->data[$row][0] = __('Title (header)'); + $table_styles->data[$row][1] = html_print_input_text('custom_title_header', $config['custom_title_header'], '', 50, 40, true); + $row++; +} + +// Subtitle Header +if (enterprise_installed()) { + $table_styles->data[$row][0] = __('Subtitle (header)'); + $table_styles->data[$row][1] = html_print_input_text('custom_subtitle_header', $config['custom_subtitle_header'], '', 50, 40, true); + $row++; +} + // login title1 if (enterprise_installed()) { $table_styles->data[$row][0] = __('Title 1 (login)'); @@ -555,7 +565,7 @@ $row++; echo '
'; - echo ''.__('Style configuration').''; + echo ''.__('Style configuration').' '.ui_print_help_icon('style_conf_tab', true).''; html_print_table($table_styles); echo '
'; // ---------------------------------------------------------------------- @@ -598,7 +608,7 @@ $row++; $row++; echo '
'; - echo ''.__('GIS configuration').''; + echo ''.__('GIS configuration').' '.ui_print_help_icon('gis_conf_tab', true).''; html_print_table($table_gis); echo '
'; // ---------------------------------------------------------------------- @@ -692,7 +702,7 @@ $row++; $row++; echo '
'; - echo ''.__('Font and Text configuration').''; + echo ''.__('Font and Text configuration').' '.ui_print_help_icon('front_and_text_conf_tab', true).''; html_print_table($table_font); echo '
'; // ---------------------------------------------------------------------- @@ -880,7 +890,7 @@ $row++; */ echo '
'; - echo ''.__('Charts configuration').''; + echo ''.__('Charts configuration').' '.ui_print_help_icon('charts_conf_tab', true).''; html_print_table($table_chars); echo '
'; // ---------------------------------------------------------------------- @@ -894,6 +904,50 @@ $row++; $table_vc->size[0] = '50%'; $table_vc->data = []; + // Remove when the new view reaches rock solid stability. + $table_vc->data[$row][0] = __('Legacy Visual Console View'); + $table_vc->data[$row][0] .= ui_print_help_tip( + __('To use the old view when using the Visual Console visor'), + true + ); + $table_vc->data[$row][1] = html_print_checkbox_switch( + 'legacy_vc', + 1, + (bool) $config['legacy_vc'], + true + ); + $row++; + + $intervals = [ + 10 => '10 '.__('seconds'), + 30 => '30 '.__('seconds'), + 60 => '1 '.__('minutes'), + 300 => '5 '.__('minutes'), + 900 => '15 '.__('minutes'), + 1800 => '30 '.__('minutes'), + 3600 => '1 '.__('hour'), + ]; + $table_vc->data[$row][0] = __('Default cache expiration'); + $table_vc->data[$row][1] = html_print_extended_select_for_time( + 'vc_default_cache_expiration', + $config['vc_default_cache_expiration'], + '', + __('No cache'), + 0, + false, + true, + false, + false, + '', + false, + $intervals + ); + $row++; + + $table_vc->data[$row][0] = __('Default interval for refresh on Visual Console').ui_print_help_tip(__('This interval will affect to Visual Console pages'), true); + $table_vc->data[$row][1] = html_print_select($values, 'vc_refr', (int) $config['vc_refr'], '', 'N/A', 0, true, false, false); + $row++; + $vc_favourite_view_array[0] = __('Classic view'); $vc_favourite_view_array[1] = __('View of favorites'); $table_vc->data[$row][0] = __('Type of view of visual consoles').ui_print_help_tip(__('Allows you to directly display the list of favorite visual consoles'), true); @@ -904,16 +958,12 @@ $row++; $table_vc->data[$row][1] = ""; $row++; - if (empty($config['vc_line_thickness'])) { - $config['vc_line_thickness'] = 2; - } - $table_vc->data[$row][0] = __('Default line thickness for the Visual Console').ui_print_help_tip(__('This interval will affect to the lines between elements on the Visual Console'), true); - $table_vc->data[$row][1] = html_print_input_text('vc_line_thickness', $config['vc_line_thickness'], '', 5, 5, true); + $table_vc->data[$row][1] = html_print_input_text('vc_line_thickness', (int) $config['vc_line_thickness'], '', 5, 5, true); echo '
'; - echo ''.__('Visual consoles configuration').''; + echo ''.__('Visual consoles configuration').' '.ui_print_help_icon('visual_consoles_conf_tab', true).''; html_print_table($table_vc); echo '
'; @@ -931,7 +981,7 @@ $row++; $table_ser->data['number'][1] = ""; echo '
'; - echo ''.__('Services configuration').''; + echo ''.__('Services configuration').' '.ui_print_help_icon('services_conf_tab', true).''; html_print_table($table_ser); echo '
'; @@ -1110,7 +1160,7 @@ $row++; ); $row++; - $table_other->data[$row][0] = __('Date format string').ui_print_help_icon('date_format', true); + $table_other->data[$row][0] = __('Date format string'); $table_other->data[$row][1] = ''.__('Example').' '.date($config['date_format']); $table_other->data[$row][1] .= html_print_input_text('date_format', $config['date_format'], '', 30, 100, true); $row++; @@ -1123,7 +1173,7 @@ $row++; $comparation = false; } - $table_other->data[$row][0] = __('Timestamp or time comparation').ui_print_help_icon('time_stamp-comparation', true); + $table_other->data[$row][0] = __('Timestamp or time comparation'); $table_other->data[$row][1] = __('Comparation in rollover').' '; $table_other->data[$row][1] .= html_print_radio_button('prominent_time', 'comparation', '', $comparation, true); $table_other->data[$row][1] .= '
'.__('Timestamp in rollover').' '; @@ -1229,7 +1279,7 @@ $row++; echo '
'; - echo ''.__('Other configuration').''; + echo ''.__('Other configuration').' '.ui_print_help_icon('other_conf_tab', true).''; html_print_table($table_other); echo '
'; @@ -1370,6 +1420,15 @@ tinyMCE.init({ }); $(document).ready (function () { + + // Show the cache expiration conf or not. + $("input[name=legacy_vc]").change(function (e) { + if ($(this).prop("checked") === true) { + $("select#vc_default_cache_expiration_select").closest("tr").hide(); + } else { + $("select#vc_default_cache_expiration_select").closest("tr").show(); + } + }).change(); var comfort = 0; diff --git a/pandora_console/godmode/snmpconsole/snmp_alert.php b/pandora_console/godmode/snmpconsole/snmp_alert.php index 677a283854..a2a1b8b811 100755 --- a/pandora_console/godmode/snmpconsole/snmp_alert.php +++ b/pandora_console/godmode/snmpconsole/snmp_alert.php @@ -76,7 +76,7 @@ if ($update_alert || $modify_alert) { __('SNMP Console').' » '.__('Update alert'), 'images/op_snmp.png', false, - 'snmp_alert', + 'snmp_alert_update_tab', false ); } else if ($create_alert || $save_alert) { @@ -84,7 +84,7 @@ if ($update_alert || $modify_alert) { __('SNMP Console').' » '.__('Create alert'), 'images/op_snmp.png', false, - 'snmp_alert', + 'snmp_alert_overview_tab', false ); } else { @@ -92,7 +92,7 @@ if ($update_alert || $modify_alert) { __('SNMP Console').' » '.__('Alert overview'), 'images/op_snmp.png', false, - 'snmp_alert', + 'snmp_alert_overview_tab', false ); } @@ -757,7 +757,6 @@ if ($create_alert || $update_alert) { // Custom echo ''.__('Custom Value/OID'); - echo ui_print_help_icon('snmp_alert_custom', true); echo ''; html_print_textarea('custom_value', 2, 2, $custom_value, 'style="width:400px;"'); @@ -804,7 +803,7 @@ if ($create_alert || $update_alert) { echo ''; // Variable bindings/Data #1 - echo ''.''.__('Variable bindings/Data').ui_print_help_icon('field_match_snmp', true).''.''; + echo ''.''.__('Variable bindings/Data').''; echo '#'; html_print_input_text('order_1', $order_1, '', 4); html_print_input_text('custom_oid_data_1', $custom_oid_data_1, '', 60); @@ -1077,7 +1076,7 @@ if ($create_alert || $update_alert) { } echo ''; - echo ''.__('Position').ui_print_help_icon('snmp_alert_position', true).''; + echo ''.__('Position').''; html_print_input_text('position', $position, '', 3); echo ''; diff --git a/pandora_console/godmode/snmpconsole/snmp_filters.php b/pandora_console/godmode/snmpconsole/snmp_filters.php index b87fb4c3dd..0d8e1e9480 100644 --- a/pandora_console/godmode/snmpconsole/snmp_filters.php +++ b/pandora_console/godmode/snmpconsole/snmp_filters.php @@ -226,13 +226,13 @@ if ($edit_filter > -2) { $table->cellpadding = 4; $table->cellspacing = 4; $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->align = []; $table->head[0] = __('Description'); $table->head[1] = __('Filter'); $table->head[2] = __('Action'); - $table->size[2] = '50px'; + $table->size[2] = '65px'; $table->align[2] = 'center'; foreach ($aglomerate_result as $ind => $row) { @@ -263,6 +263,7 @@ if ($edit_filter > -2) { $data[0] = $compose_id; $data[1] = implode(' AND ', $compose_filter); $data[2] = $compose_action; + $table->cellclass[][2] = 'action_buttons'; array_push($table->data, $data); } } diff --git a/pandora_console/godmode/snmpconsole/snmp_trap_generator.php b/pandora_console/godmode/snmpconsole/snmp_trap_generator.php index ec8ed20930..43ed63abec 100755 --- a/pandora_console/godmode/snmpconsole/snmp_trap_generator.php +++ b/pandora_console/godmode/snmpconsole/snmp_trap_generator.php @@ -33,7 +33,13 @@ $snmp_type = (int) get_parameter('snmp_type', 0); $snmp_value = (string) get_parameter('snmp_value', ''); $generate_trap = (bool) get_parameter('generate_trap', 0); -ui_print_page_header(__('SNMP Trap generator'), 'images/op_snmp.png', false, '', false); +ui_print_page_header( + __('SNMP Trap generator'), + 'images/op_snmp.png', + false, + 'snmp_trap_generator_view', + false +); if ($generate_trap) { $result = true; @@ -64,22 +70,64 @@ $table->size = []; $table->data = []; $table->data[0][0] = __('Host address'); -$table->data[0][1] = html_print_input_text('snmp_host_address', $snmp_host_address, '', 50, 255, true); +$table->data[0][1] = html_print_input_text( + 'snmp_host_address', + $snmp_host_address, + '', + 50, + 255, + true +); $table->data[0][2] = __('Community'); -$table->data[0][3] = html_print_input_text('snmp_community', $snmp_community, '', 50, 255, true); +$table->data[0][3] = html_print_input_text( + 'snmp_community', + $snmp_community, + '', + 50, + 255, + true +); $table->data[2][0] = __('Enterprise String'); -$table->data[2][1] = html_print_input_text('snmp_oid', $snmp_oid, '', 50, 255, true); +$table->data[2][1] = html_print_input_text( + 'snmp_oid', + $snmp_oid, + '', + 50, + 255, + true +); $table->data[2][2] = __('Value'); -$table->data[2][3] = html_print_input_text('snmp_value', $snmp_value, '', 50, 255, true); +$table->data[2][3] = html_print_input_text( + 'snmp_value', + $snmp_value, + '', + 50, + 255, + true +); $table->data[3][0] = __('SNMP Agent'); -$table->data[3][1] = html_print_input_text('snmp_agent', $snmp_agent, '', 50, 255, true); +$table->data[3][1] = html_print_input_text( + 'snmp_agent', + $snmp_agent, + '', + 50, + 255, + true +); -$table->data[3][2] = __('SNMP Type').' '.ui_print_help_icon('snmp_trap_types', true); -$table->data[3][3] = html_print_input_text('snmp_type', $snmp_type, '', 50, 255, true); +$table->data[3][2] = __('SNMP Type'); +$table->data[3][3] = html_print_input_text( + 'snmp_type', + $snmp_type, + '', + 50, + 255, + true +); $types = [ 0 => 'Cold start (0)', @@ -90,7 +138,17 @@ $types = [ 5 => 'EGP neighbor loss (5)', 6 => 'Enterprise (6)', ]; -$table->data[3][3] = html_print_select($types, 'snmp_type', $snmp_type, '', __('Select'), -1, true, false, false); +$table->data[3][3] = html_print_select( + $types, + 'snmp_type', + $snmp_type, + '', + __('Select'), + -1, + true, + false, + false +); $traps_generator .= html_print_table($table, true); diff --git a/pandora_console/godmode/tag/tag.php b/pandora_console/godmode/tag/tag.php index d717a9095e..8a8ddc8887 100644 --- a/pandora_console/godmode/tag/tag.php +++ b/pandora_console/godmode/tag/tag.php @@ -117,7 +117,14 @@ if (is_metaconsole()) { // ui_meta_print_header(__('Tags'), "", $buttons); } else { // Header - ui_print_page_header(__('Tags configuration'), 'images/tag.png', false, 'tags_config', true, $buttons); + ui_print_page_header( + __('Tags configuration'), + 'images/tag.png', + false, + 'tags_config', + true, + $buttons + ); } // Two actions can performed in this page: search and delete tags @@ -191,7 +198,7 @@ if (!empty($result)) { $table = new stdClass(); $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->data = []; $table->head = []; @@ -278,12 +285,14 @@ if (!empty($result)) { $data[5] = $output; - $data[6] = "".html_print_image('images/config.png', true, ['title' => 'Edit']).'  '; + $table->cellclass[][6] = 'action_buttons'; + $data[6] = "".html_print_image('images/config.png', true, ['title' => 'Edit']).''; $data[6] .= ''.html_print_image('images/cross.png', true, ['title' => 'Delete']).''; array_push($table->data, $data); } html_print_table($table); + ui_pagination($total_tags, $url, 0, 0, false, 'offset', true, 'pagination-bottom'); } else { if (is_metaconsole()) { ui_toggle($filter_form, __('Show Options')); diff --git a/pandora_console/godmode/update_manager/update_manager.css b/pandora_console/godmode/update_manager/update_manager.css index f68c92ea94..168c6fcf7a 100644 --- a/pandora_console/godmode/update_manager/update_manager.css +++ b/pandora_console/godmode/update_manager/update_manager.css @@ -140,6 +140,162 @@ } #box_online { - background-color: #e6e6e6; - padding: 10px; + background-image: url("../../images/update_manager_background.jpg"); + background-size: cover; + background-position: center center; + background-repeat: no-repeat; + background-color: #fff; + padding: 40px 50px; + border: 1px solid #f3f3f3; + border-radius: 5px; + min-height: 600px; +} + +div#box_online * { + font-size: 16pt; + font-family: "Open Sans", sans-serif; +} + +#box_online .content { + max-width: 60%; +} + +.update_popup { + display: flex; + justify-content: space-between; + align-items: center; + padding-left: 10px; + padding-right: 10px; +} + +.update_icon { + padding-left: 20px; +} + +.update_text h3 { + font-weight: bold; + font-size: 12pt; +} + +.update_text p { + font-size: 11pt; +} + +.update_text a { + font-size: 11pt; + color: #82b92e !important; +} + +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: left !important; + padding-left: 19px; + padding-bottom: 5px; +} + +.ui-dialog-buttonset > button.ui-button.ui-corner-all.ui-widget { + background-color: #cecece !important; + border: none !important; + border-radius: 2px !important; + text-transform: uppercase !important; + font-weight: bold !important; +} + +.ui-dialog-buttonset > button.success_button.ui-button.ui-corner-all.ui-widget, +.update_manager_button { + background-color: #82b92e !important; + color: #fff !important; + border-radius: 2px !important; + text-transform: uppercase !important; + font-weight: bold !important; +} + +a.update_manager_button { + padding: 10px 12px; + margin-top: 10px; + display: inline-flex; + align-items: center; + font-size: 16px !important; + border-radius: 4px !important; + text-decoration: none; + font-family: "Open Sans", sans-serif; +} + +a.update_manager_button:after { + content: url(../../images/update_manager_button.png); + padding-left: 10px; +} + +.progressbar { + margin-top: 15px; +} + +.ui-draggable, +.ui-draggable .ui-dialog-titlebar { + cursor: default !important; +} + +#box_online #pkg_version { + color: #82b92e; + font-size: 75pt; + font-weight: bold; +} + +/* METACONSOLE */ +.box_online_meta { + background: none !important; + min-height: 400px !important; + text-align: center; + border: none !important; +} + +div#box_online.box_online_meta * { + font-size: 14pt; +} + +#box_online.box_online_meta .content { + max-width: 100%; +} + +#box_online.box_online_meta #pkg_version { + font-size: 60pt; +} + +/* COMMUNITY */ +.update_manager_open { + max-width: 50%; + font-size: 10pt; + margin-top: 40px; + padding: 20px; + border: 1px solid #eaeaea; + border-radius: 5px; +} + +.update_manager_warning { + display: flex; + align-items: center; + margin-bottom: 10px; +} + +.update_manager_warning p { + font-size: 10pt !important; +} + +.update_manager_warning img { + padding-right: 20px; + width: 90px; +} + +a.update_manager_button_open { + padding: 5px 10px; + font-size: 16px !important; + border-radius: 4px !important; + text-decoration: none; + border: 1px solid #82b92e; + color: #82b92e; + border-radius: 2px; +} + +a.update_manager_button_open:hover { + color: #fff; + background-color: #82b92e; } diff --git a/pandora_console/godmode/update_manager/update_manager.messages.php b/pandora_console/godmode/update_manager/update_manager.messages.php deleted file mode 100644 index 31f9866b01..0000000000 --- a/pandora_console/godmode/update_manager/update_manager.messages.php +++ /dev/null @@ -1,340 +0,0 @@ - $message_id) { - update_manger_set_read_message($message_id, 0); - } -} - -if ($read_action !== false) { - $selected = get_parameter('select_multiple', false); - foreach ($selected as $k => $message_id) { - update_manger_set_read_message($message_id, 1); - } -} - -if ($delete_action !== false) { - $selected = get_parameter('select_multiple', false); - foreach ($selected as $k => $message_id) { - update_manger_set_deleted_message($message_id); - } -} - -$offset = (int) get_parameter('offset', 0); - -$total_messages = update_manager_get_not_deleted_messages(); -if ($total_messages) { - // Get all messages - $sql = 'SELECT data, svn_version, filename, data_rollback, db_field_value FROM tupdate '; - $sql .= 'WHERE description NOT LIKE \'%"'.$config['id_user'].'":1%\' '; - $sql .= 'OR description IS NULL '; - $sql .= 'ORDER BY svn_version DESC '; - $sql .= 'LIMIT '.$offset.','.$config['block_size'].' '; - $um_messages = []; - $um_messages = db_get_all_rows_sql($sql); - - echo '
'; - - html_print_input_hidden('tab', 'messages'); - html_print_input_hidden('offset', $offset); - echo '
'; - html_print_submit_button( - __('Delete'), - 'delete_button', - false, - 'class="sub delete"' - ); - echo '
'; - - echo '
'; - html_print_submit_button( - __('Mark as not read'), - 'not_read_button', - false, - 'class="sub wand"' - ); - echo '
'; - - echo '
'; - html_print_submit_button( - __('Mark as read'), - 'read_button', - false, - 'class="sub upd"' - ); - echo '
'; - - // Pagination - if ($total_messages > $config['block_size']) { - ui_pagination(update_manager_get_total_messages(), false, 0); - } - - $table = new stdClass(); - $table->width = '100%'; - $table->class = 'databox data'; - $table->cellpadding = 4; - $table->cellspacing = 4; - $table->head = []; - $table->data = []; - $table->align = []; - $table->size = []; - $table->id = 'um_messages_table'; - - $table->align[0] = 'left'; - $table->align[1] = 'left'; - $table->align[2] = 'left'; - $table->align[3] = 'left'; - $table->align[4] = 'left'; - - $table->size[0] = '30px'; - $table->size[1] = '100px'; - $table->size[3] = '80px'; - $table->size[4] = '60px'; - - $table->style[0] = 'padding-left: 20px'; - $table->style[1] = 'display: none'; - - $table->head[0] = html_print_checkbox_extended('all_selection[]', 0, false, false, '', '', true); - $table->head[2] = __('Subject'); - - - $i = 0; - foreach ($um_messages as $message) { - $data[0] = html_print_checkbox_extended('select_multiple[]', $message['svn_version'], false, false, '', 'class="check_selection"', true); - $table->cellclass[count($table->data)][0] = 'um_individual_check'; - - $data[1] = $message['svn_version']; - $table->cellclass[count($table->data)][1] = 'um_individual_info'; - - $data[2] = io_safe_output($message['db_field_value']); - $table->cellclass[count($table->data)][2] = 'um_individual_subject'; - - - // Change row class if message is read or not by this user - if (update_manger_get_read_message($message['svn_version'], $message['data_rollback'])) { - $table->rowclass[count($table->data)] = 'um_read_message'; - } else { - $table->rowclass[count($table->data)] = 'um_not_read_message'; - } - - array_push($table->data, $data); - } - - html_print_table($table); - - echo '
'; - html_print_submit_button( - __('Delete'), - 'delete_button', - false, - 'class="sub delete"' - ); - echo '
'; - - echo '
'; - html_print_submit_button( - __('Mark as not read'), - 'not_read_button', - false, - 'class="sub wand"' - ); - echo '
'; - - echo '
'; - html_print_submit_button( - __('Mark as read'), - 'read_button', - false, - 'class="sub upd"' - ); - echo '
'; - echo '
'; - - // Get unread messages to update the notification ball. - // Clean the cache because the unread messages can be different. - db_clean_cache(); - $total_unread_messages = update_manager_get_unread_messages(); -} else { - ui_print_info_message([ 'no_close' => true, 'message' => __('There is not any update manager messages.') ]); -} -?> - - - - diff --git a/pandora_console/godmode/update_manager/update_manager.offline.php b/pandora_console/godmode/update_manager/update_manager.offline.php index 79f9a72d2e..24da1a341f 100644 --- a/pandora_console/godmode/update_manager/update_manager.offline.php +++ b/pandora_console/godmode/update_manager/update_manager.offline.php @@ -1,23 +1,39 @@ - + + - - \ No newline at end of file diff --git a/pandora_console/godmode/update_manager/update_manager.php b/pandora_console/godmode/update_manager/update_manager.php index db91738a17..7954fd0ada 100644 --- a/pandora_console/godmode/update_manager/update_manager.php +++ b/pandora_console/godmode/update_manager/update_manager.php @@ -45,12 +45,6 @@ $buttons = [ ], ]; -if (license_free()) { - $buttons['messages'] = [ - 'active' => ($tab == 'messages') ? true : false, - 'text' => ''.html_print_image('images/email_mc.png', true, ['title' => __('Update manager messages')]).'', - ]; -} switch ($tab) { case 'setup': @@ -59,14 +53,12 @@ switch ($tab) { case 'offline': $title = __('Update manager » Offline'); + $help_header = 'update_manager_offline_tab'; break; case 'online': $title = __('Update manager » Online'); - break; - - case 'messages': - $title = __('Update manager » Messages'); + $help_header = 'update_manager_online_tab'; break; } @@ -74,7 +66,7 @@ ui_print_page_header( $title, 'images/gm_setup.png', false, - '', + $help_header, true, $buttons ); @@ -88,10 +80,6 @@ switch ($tab) { include $config['homedir'].'/godmode/update_manager/update_manager.offline.php'; break; - case 'messages': - include $config['homedir'].'/godmode/update_manager/update_manager.messages.php'; - break; - case 'online': default: include $config['homedir'].'/godmode/update_manager/update_manager.online.php'; diff --git a/pandora_console/godmode/update_manager/update_manager.setup.php b/pandora_console/godmode/update_manager/update_manager.setup.php index 4717dd8681..1a81cf457c 100644 --- a/pandora_console/godmode/update_manager/update_manager.setup.php +++ b/pandora_console/godmode/update_manager/update_manager.setup.php @@ -1,18 +1,36 @@ data[4][1] = html_print_input_password( true ); + +$table->data[5][0] = __('Registration ID:'); +$table->data[5][1] = ''.$config['pandora_uid'].''; + +if (update_manager_verify_registration() === true && users_is_admin()) { + $table->data[6][0] = __('Cancel registration:'); + $table->data[6][1] = ''.__('Unregister').''; +} + + + if (license_free()) { $config['identification_reminder'] = isset($config['identification_reminder']) ? $config['identification_reminder'] : 1; - $table->data[6][0] = __('Pandora FMS community reminder').ui_print_help_tip(__('Every 8 days, a message is displayed to admin users to remember to register this Pandora instance'), true); - $table->data[6][1] = __('Yes').'   '.html_print_radio_button('identification_reminder', 1, '', $config['identification_reminder'], true).'  '; - $table->data[6][1] .= __('No').'   '.html_print_radio_button('identification_reminder', 0, '', $config['identification_reminder'], true); + $table->data[7][0] = __('Pandora FMS community reminder').ui_print_help_tip(__('Every 8 days, a message is displayed to admin users to remember to register this Pandora instance'), true); + $table->data[7][1] = __('Yes').'   '.html_print_radio_button('identification_reminder', 1, '', $config['identification_reminder'], true).'  '; + $table->data[7][1] .= __('No').'   '.html_print_radio_button('identification_reminder', 0, '', $config['identification_reminder'], true); } html_print_input_hidden('action_update_url_update_manager', 1); diff --git a/pandora_console/godmode/users/configure_profile.php b/pandora_console/godmode/users/configure_profile.php index a9adcd0069..418add8aa8 100644 --- a/pandora_console/godmode/users/configure_profile.php +++ b/pandora_console/godmode/users/configure_profile.php @@ -47,7 +47,14 @@ if (!is_metaconsole()) { $buttons[$tab]['active'] = true; - ui_print_page_header(__('User management').' » '.__('Profiles defined on %s', get_product_name()), 'images/gm_users.png', false, '', true, $buttons); + ui_print_page_header( + __('User management').' » '.__('Profiles defined on %s', get_product_name()), + 'images/gm_users.png', + false, + 'configure_profiles_tab', + true, + $buttons + ); $sec2 = 'gusuarios'; } else { user_meta_print_header(); diff --git a/pandora_console/godmode/users/configure_user.php b/pandora_console/godmode/users/configure_user.php index a06bd57f1c..47788284fe 100644 --- a/pandora_console/godmode/users/configure_user.php +++ b/pandora_console/godmode/users/configure_user.php @@ -90,7 +90,14 @@ if ($meta) { $buttons[$tab]['active'] = true; - ui_print_page_header(__('User detail editor'), 'images/gm_users.png', false, '', true, $buttons); + ui_print_page_header( + __('User detail editor'), + 'images/gm_users.png', + false, + 'profile_tab', + true, + $buttons + ); $sec = 'gusuarios'; } @@ -144,6 +151,12 @@ if ($new_user && $config['admin_can_add_user']) { $user_info['metaconsole_assigned_server'] = ''; $user_info['metaconsole_access_node'] = 0; } + + if ($config['ehorus_user_level_conf']) { + $user_info['ehorus_user_level_user'] = ''; + $user_info['ehorus_user_level_pass'] = ''; + $user_info['ehorus_user_level_enabled'] = true; + } } if ($create_user) { @@ -208,6 +221,19 @@ if ($create_user) { $values['strict_acl'] = (bool) get_parameter('strict_acl', false); $values['session_time'] = (int) get_parameter('session_time', 0); + // eHorus user level conf + if ($config['ehorus_user_level_conf']) { + $values['ehorus_user_level_enabled'] = (bool) get_parameter('ehorus_user_level_enabled', false); + if ($values['ehorus_user_level_enabled'] === true) { + $values['ehorus_user_level_user'] = (string) get_parameter('ehorus_user_level_user'); + $values['ehorus_user_level_pass'] = (string) get_parameter('ehorus_user_level_pass'); + } else { + $values['ehorus_user_level_user'] = null; + $values['ehorus_user_level_pass'] = null; + } + } + + if ($id == '') { ui_print_error_message(__('User ID cannot be empty')); $user_info = $values; @@ -291,6 +317,13 @@ if ($update_user) { $values['timezone'] = (string) get_parameter('timezone'); $values['default_event_filter'] = (int) get_parameter('default_event_filter'); $values['default_custom_view'] = (int) get_parameter('default_custom_view'); + // eHorus user level conf + $values['ehorus_user_level_enabled'] = (bool) get_parameter('ehorus_user_level_enabled', false); + $values['ehorus_user_level_user'] = (string) get_parameter('ehorus_user_level_user'); + $values['ehorus_user_level_pass'] = (string) get_parameter('ehorus_user_level_pass'); + + + $dashboard = get_parameter('dashboard', ''); $visual_console = get_parameter('visual_console', ''); @@ -753,6 +786,7 @@ if (enterprise_installed() && !is_metaconsole()) { $values['Dashboard'] = __('Dashboard'); } + $table->data[12][1] = html_print_select($values, 'section', io_safe_output($user_info['section']), 'show_data_section();', '', -1, true, false, false); if (enterprise_installed()) { @@ -835,6 +869,16 @@ foreach ($event_filter_data as $filter) { $table->data[16][0] = __('Default event filter'); $table->data[16][1] = html_print_select($event_filter, 'default_event_filter', $user_info['default_event_filter'], '', '', __('None'), true, false, false); +if ($config['ehorus_user_level_conf']) { + $table->data[17][0] = __('eHorus user acces enabled'); + $table->data[17][1] = html_print_checkbox('ehorus_user_level_enabled', 1, $user_info['ehorus_user_level_enabled'], true); + $table->data[18][0] = __('eHorus user'); + $table->data[19][0] = __('eHorus password'); + $table->data[18][1] = html_print_input_text('ehorus_user_level_user', $user_info['ehorus_user_level_user'], '', 15, 45, true); + $table->data[19][1] = html_print_input_password('ehorus_user_level_pass', io_output_password($user_info['ehorus_user_level_pass']), '', 15, 45, true); +} + + if ($meta) { enterprise_include_once('include/functions_metaconsole.php'); @@ -926,6 +970,11 @@ $(document).ready (function () { $('#checkbox-metaconsole_agents_manager').trigger('change'); show_data_section(); + $('#checkbox-ehorus_user_level_enabled').change(function () { + switch_ehorus_conf(); + }); + $('#checkbox-ehorus_user_level_enabled').trigger('change'); + }); function show_data_section () { @@ -980,5 +1029,21 @@ function show_data_section () { } } +function switch_ehorus_conf() +{ + if(!$('#checkbox-ehorus_user_level_enabled').prop('checked')) + { + $("#user_configuration_table-18").hide(); + $("#user_configuration_table-19").hide(); + + }else + { + $("#user_configuration_table-18").show(); + $("#user_configuration_table-19").show() + } + + +} + /* ]]> */ diff --git a/pandora_console/godmode/users/profile_list.php b/pandora_console/godmode/users/profile_list.php index 8b23527ac8..da77dffd9f 100644 --- a/pandora_console/godmode/users/profile_list.php +++ b/pandora_console/godmode/users/profile_list.php @@ -51,7 +51,14 @@ if (!defined('METACONSOLE')) { $buttons[$tab]['active'] = true; - ui_print_page_header(__('User management').' » '.__('Profiles defined on %s', get_product_name()), 'images/gm_users.png', false, 'profile', true, $buttons); + ui_print_page_header( + __('User management').' » '.__('Profiles defined on %s', get_product_name()), + 'images/gm_users.png', + false, + 'profile_tab', + true, + $buttons + ); $sec = 'gusuarios'; } else { user_meta_print_header(); @@ -258,7 +265,7 @@ if ($create_profile) { $table = new stdClass(); $table->cellpadding = 0; $table->cellspacing = 0; -$table->class = 'databox profile_list data'; +$table->class = 'info_table profile_list'; $table->width = '100%'; $table->head = []; @@ -353,8 +360,9 @@ foreach ($profiles as $profile) { $data['VW'] = ($profile['vconsole_edit'] ? $img : ''); $data['VM'] = ($profile['vconsole_management'] ? $img : ''); $data['PM'] = ($profile['pandora_management'] ? $img : ''); + $table->cellclass[]['operations'] = 'action_buttons'; $data['operations'] = ''.html_print_image('images/config.png', true, ['title' => __('Edit')]).''; - $data['operations'] .= '  '.html_print_image('images/cross.png', true).''; + $data['operations'] .= ''.html_print_image('images/cross.png', true).''; array_push($table->data, $data); } diff --git a/pandora_console/godmode/users/user_list.php b/pandora_console/godmode/users/user_list.php index 6b4d19ad28..63caad6d3f 100644 --- a/pandora_console/godmode/users/user_list.php +++ b/pandora_console/godmode/users/user_list.php @@ -38,13 +38,13 @@ $sort = get_parameter('sort', 'none'); $tab = get_parameter('tab', 'user'); $pure = get_parameter('pure', 0); -$selected = 'border: 1px solid black;'; -$selectUserIDUp = ''; -$selectUserIDDown = ''; -$selectFullnameUp = ''; -$selectFullnameDown = ''; -$selectLastConnectUp = ''; -$selectLastConnectDown = ''; +$selected = true; +$selectUserIDUp = false; +$selectUserIDDown = false; +$selectFullnameUp = false; +$selectFullnameDown = false; +$selectLastConnectUp = false; +$selectLastConnectDown = false; $order = null; switch ($sortField) { @@ -110,11 +110,11 @@ switch ($sortField) { default: $selectUserIDUp = $selected; - $selectUserIDDown = ''; - $selectFullnameUp = ''; - $selectFullnameDown = ''; - $selectLastConnectUp = ''; - $selectLastConnectDown = ''; + $selectUserIDDown = false; + $selectFullnameUp = false; + $selectFullnameDown = false; + $selectLastConnectUp = false; + $selectLastConnectDown = false; $order = [ 'field' => 'id_user', 'order' => 'ASC', @@ -300,11 +300,20 @@ if (defined('METACONSOLE')) { ui_toggle($form_filter, __('Users control filter'), __('Toggle filter(s)'), !$search); } +// Urls to sort the table. +$url_up_id = '?sec='.$sec.'&sec2=godmode/users/user_list&sort_field=id_user&sort=up&pure='.$pure; +$url_down_id = '?sec='.$sec.'&sec2=godmode/users/user_list&sort_field=id_user&sort=down&pure='.$pure; +$url_up_name = '?sec='.$sec.'&sec2=godmode/users/user_list&sort_field=fullname&sort=up&pure='.$pure; +$url_down_name = '?sec='.$sec.'&sec2=godmode/users/user_list&sort_field=fullname&sort=down&pure='.$pure; +$url_up_last = '?sec='.$sec.'&sec2=godmode/users/user_list&sort_field=last_connect&sort=up&pure='.$pure; +$url_down_last = '?sec='.$sec.'&sec2=godmode/users/user_list&sort_field=last_connect&sort=down&pure='.$pure; + + $table = new stdClass(); $table->cellpadding = 0; $table->cellspacing = 0; $table->width = '100%'; -$table->class = 'databox data'; +$table->class = 'info_table'; $table->head = []; $table->data = []; @@ -312,9 +321,10 @@ $table->align = []; $table->size = []; $table->valign = []; -$table->head[0] = __('User ID').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectUserIDUp]).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectUserIDDown]).''; -$table->head[1] = __('Name').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectFullnameUp ]).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectFullnameDown]).''; -$table->head[2] = __('Last contact').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectLastConnectUp ]).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectLastConnectDown]).''; +$table->head[0] = __('User ID').ui_get_sorting_arrows($url_up_id, $url_down_id, $selectUserIDUp, $selectUserIDDown); +$table->head[1] = __('Name').ui_get_sorting_arrows($url_up_name, $url_down_name, $selectFullnameUp, $selectFullnameDown); +$table->head[2] = __('Last contact').ui_get_sorting_arrows($url_up_last, $url_down_last, $selectLastConnectUp, $selectLastConnectDown); + $table->head[3] = __('Admin'); $table->head[4] = __('Profile / Group'); $table->head[5] = __('Description'); @@ -514,6 +524,7 @@ foreach ($info as $user_id => $user_info) { $data[5] = ui_print_string_substr($user_info['comments'], 24, true); + $table->cellclass[][6] = 'action_buttons'; if ($user_info['disabled'] == 0) { $data[6] = ''.html_print_image('images/lightbulb.png', true, ['title' => __('Disable')]).''; } else { @@ -535,6 +546,7 @@ foreach ($info as $user_id => $user_info) { } html_print_table($table); +ui_pagination(count($info), false, 0, 0, false, 'offset', true, 'pagination-bottom'); echo '
'; unset($table); diff --git a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php index 9796646fce..9392d03ad4 100644 --- a/pandora_console/godmode/wizards/DiscoveryTaskList.class.php +++ b/pandora_console/godmode/wizards/DiscoveryTaskList.class.php @@ -30,7 +30,10 @@ require_once __DIR__.'/Wizard.main.php'; require_once $config['homedir'].'/include/functions_users.php'; require_once $config['homedir'].'/include/functions_reports.php'; require_once $config['homedir'].'/include/functions_cron.php'; -enterprise_include('include/functions_tasklist.php'); +enterprise_include_once('include/functions_tasklist.php'); +enterprise_include_once('include/functions_cron.php'); + +ui_require_css_file('task_list'); /** * Defined as wizard to guide user to explore running tasks. @@ -93,7 +96,20 @@ class DiscoveryTaskList extends Wizard ] ); - $this->printHeader(); + // Header. + ui_print_page_header( + __('Task list'), + '', + false, + '', + true, + '', + false, + '', + GENERIC_SIZE_TEXT, + '', + $this->printHeader(true) + ); // Show redirected messages from discovery.php. if ($status === 0) { @@ -237,7 +253,7 @@ class DiscoveryTaskList extends Wizard $id_console_task = (int) get_parameter('id_console_task'); if ($id_console_task !== null) { - enterprise_include('cron_task_run', $id_console_task, true); + enterprise_hook('cron_task_run', [$id_console_task, true]); // Trick to avoid double execution. header('Location: '.$this->url); } @@ -345,48 +361,61 @@ class DiscoveryTaskList extends Wizard $recon_tasks = []; } - $table = new StdClass(); - $table->cellpadding = 4; - $table->cellspacing = 4; - $table->width = '100%'; - $table->class = 'databox data'; - $table->head = []; - $table->data = []; - $table->align = []; - $table->headstyle = []; + $url_ajax = $config['homeurl'].'ajax.php'; + + $table = new StdClass(); + $table->cellpadding = 0; + $table->cellspacing = 0; + $table->width = '100%'; + $table->class = 'info_table'; + $table->head = []; + $table->data = []; + $table->align = []; + $table->headstyle = []; for ($i = 0; $i < 9; $i++) { $table->headstyle[$i] = 'text-align: left;'; } - $table->head[0] = __('Force'); - $table->align[0] = 'left'; + // Status. + $table->headstyle[5] .= 'min-width: 100px; width: 100px;'; + // Task type. + $table->headstyle[6] .= 'min-width: 200px; width: 150px;'; + // Progress. + $table->headstyle[7] .= 'min-width: 150px; width: 150px;'; + // Updated at. + $table->headstyle[8] .= 'min-width: 150px; width: 150px;'; + // Operations. + $table->headstyle[9] .= 'min-width: 150px; width: 150px;'; - $table->head[1] = __('Task name'); - $table->align[1] = 'left'; + $table->head[0] = __('Force'); + $table->align[0] = 'left'; - $table->head[2] = __('Server name'); - $table->align[2] = 'left'; + $table->head[1] = __('Task name'); + $table->align[1] = 'left'; - $table->head[3] = __('Interval'); - $table->align[3] = 'left'; + $table->head[2] = __('Server name'); + $table->align[2] = 'left'; - $table->head[4] = __('Network'); - $table->align[4] = 'left'; + $table->head[3] = __('Interval'); + $table->align[3] = 'left'; - $table->head[5] = __('Status'); - $table->align[5] = 'left'; + $table->head[4] = __('Network'); + $table->align[4] = 'left'; - $table->head[6] = __('Task type'); - $table->align[6] = 'left'; + $table->head[5] = __('Status'); + $table->align[5] = 'left'; - $table->head[7] = __('Progress'); - $table->align[7] = 'left'; + $table->head[6] = __('Task type'); + $table->align[6] = 'left'; - $table->head[8] = __('Updated at'); - $table->align[8] = 'left'; + $table->head[7] = __('Progress'); + $table->align[7] = 'left'; - $table->head[9] = __('Operations'); - $table->align[9] = 'left'; + $table->head[8] = __('Updated at'); + $table->align[8] = 'left'; + + $table->head[9] = __('Operations'); + $table->align[9] = 'left'; foreach ($recon_tasks as $task) { $data = []; @@ -398,15 +427,30 @@ class DiscoveryTaskList extends Wizard // Exceptions: IPAM. $ipam = false; if ($task['id_recon_script'] != null) { - $recon_script_name = db_get_value('name', 'trecon_script', 'id_recon_script', $task['id_recon_script']); - if (io_safe_output($recon_script_name) == 'IPAM Recon' - && enterprise_installed() - ) { - $subnet_obj = json_decode($task['macros'], true); - $subnet = $subnet_obj['1']['value']; - $tipam_task_id = db_get_value('id', 'tipam_network', 'id_recon_task', $task['id_rt']); - $ipam = true; + $recon_script_data = db_get_row( + 'trecon_script', + 'id_recon_script', + $task['id_recon_script'] + ); + if ($recon_script_data !== false) { + $recon_script_name = $recon_script_data['name']; + if (io_safe_output($recon_script_name) == 'IPAM Recon' + && enterprise_installed() + ) { + $subnet_obj = json_decode($task['macros'], true); + $subnet = $subnet_obj['1']['value']; + $tipam_task_id = db_get_value( + 'id', + 'tipam_network', + 'id_recon_task', + $task['id_rt'] + ); + $ipam = true; + } } + } else { + $recon_script_data = false; + $recon_script_name = false; } if ($task['disabled'] == 0 && $server_name !== '') { @@ -424,7 +468,16 @@ class DiscoveryTaskList extends Wizard $data[0] = ''; } - $data[1] = ''.$task['name'].''; + // Name task. + $data[1] = ''; + if ($task['disabled'] != 2) { + $data[1] .= ''; + } + + $data[1] .= ''.$task['name'].''; + if ($task['disabled'] != 2) { + $data[1] .= ''; + } $data[2] = $server_name; @@ -449,15 +502,56 @@ class DiscoveryTaskList extends Wizard } if ($task['id_recon_script'] == 0) { - // Discovery NetScan. - $data[6] = html_print_image( - 'images/network.png', - true, - ['title' => __('Discovery NetScan')] - ).'  '; - $data[6] .= network_profiles_get_name( - $task['id_network_profile'] - ); + // Internal discovery task. + switch ($task['type']) { + case DISCOVERY_CLOUD_AWS_RDS: + // Discovery Applications MySQL. + $data[6] = html_print_image( + 'images/network.png', + true, + ['title' => __('Discovery Cloud RDS')] + ).'  '; + $data[6] .= __('Discovery.Cloud.Aws.RDS'); + break; + + case DISCOVERY_APP_MYSQL: + // Discovery Applications MySQL. + $data[6] = html_print_image( + 'images/network.png', + true, + ['title' => __('Discovery Applications MySQL')] + ).'  '; + $data[6] .= __('Discovery.App.MySQL'); + break; + + case DISCOVERY_APP_ORACLE: + // Discovery Applications Oracle. + $data[6] = html_print_image( + 'images/network.png', + true, + ['title' => __('Discovery Applications Oracle')] + ).'  '; + $data[6] .= __('Discovery.App.Oracle'); + break; + + case DISCOVERY_HOSTDEVICES: + default: + // Discovery NetScan. + $data[6] = html_print_image( + 'images/network.png', + true, + ['title' => __('Discovery NetScan')] + ).'  '; + $str = network_profiles_get_name( + $task['id_network_profile'] + ); + if (!empty($str)) { + $data[6] .= $str; + } else { + $data[6] .= __('Discovery.NetScan'); + } + break; + } } else { // APP recon task. $data[6] = html_print_image( @@ -470,13 +564,7 @@ class DiscoveryTaskList extends Wizard if ($task['status'] <= 0 || $task['status'] > 100) { $data[7] = '-'; } else { - $data[7] = progress_bar( - $task['status'], - 100, - 20, - __('Progress').':'.$task['status'].'%', - 1 - ); + $data[7] = ui_progress($task['status'], '100%', 1.5); } if ($task['utimestamp'] > 0) { @@ -488,6 +576,28 @@ class DiscoveryTaskList extends Wizard $data[8] = __('Not executed yet'); } + if ($task['disabled'] != 2) { + $data[9] = ''; + $data[9] .= html_print_image( + 'images/eye.png', + true + ); + $data[9] .= ''; + } + + if ($task['disabled'] != 2 && $task['utimestamp'] > 0 + && $task['type'] != DISCOVERY_APP_MYSQL + && $task['type'] != DISCOVERY_APP_ORACLE + && $task['type'] != DISCOVERY_CLOUD_AWS_RDS + ) { + $data[9] .= ''; + $data[9] .= html_print_image( + 'images/dynamic_network_icon.png', + true + ); + $data[9] .= ''; + } + if (check_acl( $config['id_user'], $task['id_group'], @@ -495,7 +605,7 @@ class DiscoveryTaskList extends Wizard ) ) { if ($ipam === true) { - $data[9] = ''; } else { // Check if is a H&D, Cloud or Application or IPAM. - $data[9] = 'getTargetWiz($task), + $this->getTargetWiz($task, $recon_script_data), $task['id_rt'] ) ).'">'.html_print_image( @@ -533,6 +643,11 @@ class DiscoveryTaskList extends Wizard $data[9] = ''; } + $table->cellclass[][9] = 'action_buttons'; + + // Div neccesary for modal progress task. + echo ''; + array_push($table->data, $data); } @@ -544,7 +659,12 @@ class DiscoveryTaskList extends Wizard html_print_table($table); } - unset($table); + // Div neccesary for modal map task. + echo ''; + + unset($table); + + ui_require_javascript_file('pandora_taskList'); } return true; @@ -565,25 +685,51 @@ class DiscoveryTaskList extends Wizard /** * Return target url sub-string to edit target task. * - * @param array $task With all data. + * @param array $task With all data. + * @param array $script With all script data or false if undefined. * * @return string */ - public function getTargetWiz($task) + public function getTargetWiz($task, $script=false) { - // TODO: Do not use description. Use recon_script ID instead. - switch ($task['description']) { - case 'Discovery.Application.VMware': - return 'wiz=app&mode=vmware&page=0'; + if ($script !== false) { + switch ($script['type']) { + case DISCOVERY_SCRIPT_CLOUD_AWS: + return 'wiz=cloud&mode=amazonws&page=1'; - case CLOUDWIZARD_AWS_DESCRIPTION: + case DISCOVERY_SCRIPT_APP_VMWARE: + return 'wiz=app&mode=vmware&page=0'; + + case DISCOVERY_SCRIPT_IPAM_RECON: + return ''; + + case DISCOVERY_SCRIPT_IPMI_RECON: + default: + return 'wiz=hd&mode=customnetscan'; + } + } + + switch ($task['type']) { + case DISCOVERY_APP_MYSQL: + return 'wiz=app&mode=mysql&page=0'; + + case DISCOVERY_APP_ORACLE: + return 'wiz=app&mode=oracle&page=0'; + + case DISCOVERY_CLOUD_AWS: + case DISCOVERY_CLOUD_AWS_EC2: return 'wiz=cloud&mode=amazonws&page=1'; - case 'console_task': - return 'wiz=ctask'; + case DISCOVERY_CLOUD_AWS_RDS: + return 'wiz=cloud&mode=amazonws&sub=rds&page=0'; default: - return 'wiz=hd&mode=netscan'; + if ($task['description'] == 'console_task') { + return 'wiz=ctask'; + } else { + return 'wiz=hd&mode=netscan'; + } + break; } } diff --git a/pandora_console/godmode/wizards/HostDevices.class.php b/pandora_console/godmode/wizards/HostDevices.class.php index f405641fde..3cc2e3f71d 100755 --- a/pandora_console/godmode/wizards/HostDevices.class.php +++ b/pandora_console/godmode/wizards/HostDevices.class.php @@ -28,7 +28,10 @@ require_once __DIR__.'/Wizard.main.php'; require_once $config['homedir'].'/include/functions_users.php'; -enterprise_include('include/class/CSVImportAgents.class.php'); +require_once $config['homedir'].'/include/class/CustomNetScan.class.php'; +require_once $config['homedir'].'/include/class/ManageNetScanScripts.class.php'; + +enterprise_include_once('include/class/CSVImportAgents.class.php'); enterprise_include_once('include/functions_hostdevices.php'); /** @@ -117,6 +120,7 @@ class HostDevices extends Wizard 'icon' => 'images/wizard/netscan.png', 'label' => __('Net Scan'), ]; + if (enterprise_installed()) { $buttons[] = [ 'url' => $this->url.'&mode=importcsv', @@ -125,6 +129,18 @@ class HostDevices extends Wizard ]; } + $buttons[] = [ + 'url' => $this->url.'&mode=customnetscan', + 'icon' => '/images/wizard/customnetscan.png', + 'label' => __('Custom NetScan'), + ]; + + $buttons[] = [ + 'url' => $this->url.'&mode=managenetscanscripts', + 'icon' => '/images/wizard/managenetscanscripts.png', + 'label' => __('Manage NetScan scripts'), + ]; + $this->prepareBreadcrum( [ [ @@ -133,22 +149,42 @@ class HostDevices extends Wizard ), 'label' => __('Discovery'), ], - ] + ], + true ); - $this->printHeader(); + ui_print_page_header(__('Host & devices'), '', false, '', true, '', false, '', GENERIC_SIZE_TEXT, '', $this->printHeader(true)); $this->printBigButtonsList($buttons); return; } if (enterprise_installed()) { - if ($mode == 'importcsv') { - $csv_importer = new CSVImportAgents($this->page, $this->breadcrum); + if ($mode === 'importcsv') { + $csv_importer = new CSVImportAgents( + $this->page, + $this->breadcrum + ); return $csv_importer->runCSV(); } } + if ($mode === 'customnetscan') { + $customnetscan_importer = new CustomNetScan( + $this->page, + $this->breadcrum + ); + return $customnetscan_importer->runCustomNetScan(); + } + + if ($mode === 'managenetscanscripts') { + $managenetscanscript_importer = new ManageNetScanScripts( + $this->page, + $this->breadcrum + ); + return $managenetscanscript_importer->runManageNetScanScript(); + } + if ($mode == 'netscan') { return $this->runNetScan(); } @@ -477,9 +513,15 @@ class HostDevices extends Wizard $task_url = '&task='.$this->task['id_rt']; } - $breadcrum[] = [ - 'link' => 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=hd', - 'label' => __($this->label), + $breadcrum = [ + [ + 'link' => 'index.php?sec=gservers&sec2=godmode/servers/discovery', + 'label' => 'Discovery', + ], + [ + 'link' => 'index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=hd', + 'label' => __($this->label), + ], ]; for ($i = 0; $i < $this->maxPagesNetScan; $i++) { $breadcrum[] = [ @@ -492,7 +534,19 @@ class HostDevices extends Wizard if ($this->page < $this->maxPagesNetScan) { // Avoid to print header out of wizard. $this->prepareBreadcrum($breadcrum); - $this->printHeader(); + ui_print_page_header( + __('NetScan'), + '', + false, + '', + true, + '', + false, + '', + GENERIC_SIZE_TEXT, + '', + $this->printHeader(true) + ); } if (isset($this->page) === true @@ -542,140 +596,158 @@ class HostDevices extends Wizard ) { $form = []; - // Input task name. - $form['inputs'][] = [ - 'label' => ''.__('Task name').'', - 'arguments' => [ - 'name' => 'taskname', - 'value' => $this->task['name'], - 'type' => 'text', - 'size' => 25, - ], - ]; + $str = __('Next'); if (isset($this->task['id_rt']) === true) { - // Propagate id. - $form['inputs'][] = [ - 'arguments' => [ - 'name' => 'task', - 'value' => $this->task['id_rt'], - 'type' => 'hidden', - ], - ]; + $str = __('Update and continue'); } - // Input task name. - $form['inputs'][] = [ - 'label' => ''.__('Comment').'', - 'arguments' => [ - 'name' => 'comment', - 'value' => $this->task['description'], - 'type' => 'text', - 'size' => 25, - ], - ]; - - // Input Discovery Server. - $form['inputs'][] = [ - 'label' => ''.__('Discovery server').''.ui_print_help_tip( - __('You must select a Discovery Server to run the Task, otherwise the Recon Task will never run'), - true - ), - 'arguments' => [ - 'type' => 'select_from_sql', - 'sql' => sprintf( - 'SELECT id_server, name - FROM tserver - WHERE server_type = %d - ORDER BY name', - SERVER_TYPE_DISCOVERY - ), - 'name' => 'id_recon_server', - 'selected' => $this->task['id_recon_server'], - 'return' => true, - ], - ]; - - // Input Network. - $form['inputs'][] = [ - - 'label' => ''.__('Network').''.ui_print_help_tip( - __('You can specify several networks, separated by commas, for example: 192.168.50.0/24,192.168.60.0/24'), - true - ), - 'arguments' => [ - 'name' => 'network', - 'value' => $this->task['subnet'], - 'type' => 'text', - 'size' => 25, - ], - ]; - - // Input Group. - $form['inputs'][] = [ - 'label' => ''.__('Group').'', - 'arguments' => [ - 'name' => 'id_group', - 'returnAllGroup' => false, - 'privilege' => 'PM', - 'type' => 'select_groups', - 'selected' => $this->task['id_group'], - 'return' => true, - ], - ]; - // Interval and schedules. $interv_manual = 0; if ((int) $this->task['interval_sweep'] == 0) { $interv_manual = 1; } - // Schedule. - $form['inputs'][] = [ - 'label' => ''.__('Interval').''.ui_print_help_tip( - __('Manual interval means that it will be executed only On-demand'), - true - ), - 'arguments' => [ - 'type' => 'select', - 'selected' => $interv_manual, - 'fields' => [ - 0 => __('Defined'), - 1 => __('Manual'), + $form['rows'][0]['new_form_block'] = true; + + $form['rows'][0]['columns'][0] = [ + 'width' => '30%', + 'style' => 'padding: 9px;', + 'inputs' => [ + '0' => [ + 'arguments' => [ + 'name' => 'submit', + 'label' => $str, + 'type' => 'submit', + 'attributes' => 'class="sub next"', + 'return' => true, + ], + ], + '1' => '
'.html_print_image('images/wizard/netscan_green.png', true, ['title' => __('Close')], false).'
', + '2' => [ + 'label' => ''.__('Interval').':'.ui_print_help_tip( + __('Manual interval means that it will be executed only On-demand'), + true + ), + 'arguments' => [ + 'type' => 'select', + 'selected' => $interv_manual, + 'fields' => [ + 0 => __('Defined'), + 1 => __('Manual'), + ], + 'name' => 'interval_manual_defined', + 'return' => true, + ], + 'extra' => ''.html_print_extended_select_for_time( + 'interval', + $this->task['interval_sweep'], + '', + '', + '0', + false, + true, + false, + false + ).ui_print_help_tip( + __('The minimum recomended interval for Recon Task is 5 minutes'), + true + ).'', + ], - 'name' => 'interval_manual_defined', - 'return' => true, ], - 'extra' => ''.html_print_extended_select_for_time( - 'interval', - $this->task['interval_sweep'], - '', - '', - '0', - false, - true, - false, - false - ).ui_print_help_tip( - __('The minimum recomended interval for Recon Task is 5 minutes'), - true - ).'', ]; - $str = __('Next'); + $form['rows'][0]['columns'][1] = [ + 'width' => '40%', + 'padding-right' => '12%', + 'padding-left' => '5%', + 'inputs' => [ + '0' => [ + 'label' => ''.__('Task name').':', + 'arguments' => [ + 'name' => 'taskname', + 'value' => $this->task['name'], + 'type' => 'text', + 'size' => 25, + 'class' => 'discovery_full_width_input', + ], + ], + '1' => [ + 'label' => ''.__('Discovery server').':'.ui_print_help_tip( + __('You must select a Discovery Server to run the Task, otherwise the Recon Task will never run'), + true + ), + 'arguments' => [ + 'type' => 'select_from_sql', + 'sql' => sprintf( + 'SELECT id_server, name + FROM tserver + WHERE server_type = %d + ORDER BY name', + SERVER_TYPE_DISCOVERY + ), + 'name' => 'id_recon_server', + 'style' => 'width: 100%;', + 'selected' => $this->task['id_recon_server'], + 'return' => true, + ], + ], + '2' => [ + 'label' => ''.__('Network').':'.ui_print_help_tip( + __('You can specify several networks, separated by commas, for example: 192.168.50.0/24,192.168.60.0/24'), + true + ), + 'arguments' => [ + 'name' => 'network', + 'value' => $this->task['subnet'], + 'type' => 'text', + 'size' => 25, + 'class' => 'discovery_full_width_input', + ], + ], + ], + ]; - if (isset($this->task['id_rt']) === true) { - $str = __('Update and continue'); - } + // Group select (custom for this section). + $group_select = '
'; - // Submit button. - $form['inputs'][] = [ - 'arguments' => [ - 'name' => 'submit', - 'label' => $str, - 'type' => 'submit', - 'attributes' => 'class="sub next"', - 'return' => true, + $group_select .= $this->printInput( + [ + 'name' => 'id_group', + 'returnAllGroup' => false, + 'privilege' => 'PM', + 'type' => 'select_groups', + 'selected' => $this->task['id_group'], + 'return' => true, + 'class' => 'discovery_list_input', + 'size' => 9, + 'simple_multiple_options' => true, + ] + ); + + $form['rows'][0]['columns'][2] = [ + 'width' => '30%', + 'inputs' => ['0' => $group_select], + ]; + + $form['rows'][1]['style'] = 'style de row'; + $form['rows'][1]['columns'][0] = [ + 'padding-right' => '0', + 'inputs' => [ + '0' => [ + 'label' => ''.__('Comment').':', + 'arguments' => [ + 'name' => 'comment', + 'rows' => 1, + 'columns' => 1, + 'value' => $this->task['description'], + 'type' => 'textarea', + 'size' => 25, + 'class' => 'discovery_textarea_input', + 'return' => true, + ], + ], ], ]; @@ -698,23 +770,21 @@ class HostDevices extends Wizard } $form['js'] = ' -$("select#interval_manual_defined").change(function() { - if ($("#interval_manual_defined").val() == 1) { - $("#interval_manual_container").hide(); - $("#text-interval_text").val(0); - $("#hidden-interval").val(0); - } - else { - $("#interval_manual_container").show(); - $("#text-interval_text").val(10); - $("#hidden-interval").val('.$interval.'); - $("#interval_units").val('.$unit.'); - } -}).change();'; + $("select#interval_manual_defined").change(function() { + if ($("#interval_manual_defined").val() == 1) { + $("#interval_manual_container").hide(); + $("#text-interval_text").val(0); + $("#hidden-interval").val(0); + } + else { + $("#interval_manual_container").show(); + $("#text-interval_text").val(10); + $("#hidden-interval").val('.$interval.'); + $("#interval_units").val('.$unit.'); + } + }).change();'; - // XXX: Could be improved validating inputs before continue (JS) - // Print NetScan page 0. - $this->printForm($form); + $this->printFormAsGrid($form); } } @@ -731,7 +801,6 @@ $("select#interval_manual_defined").change(function() { ], ]; - // Hidden, page. $form['inputs'][] = [ 'arguments' => [ 'name' => 'page', @@ -742,10 +811,9 @@ $("select#interval_manual_defined").change(function() { ]; $form['inputs'][] = [ - 'extra' => '

Please, configure task '.io_safe_output($this->task['name']).'

', + 'extra' => '

Please, configure task '.io_safe_output($this->task['name']).'

', ]; - // Input: Module template. $form['inputs'][] = [ 'label' => __('Module template'), 'arguments' => [ @@ -758,7 +826,6 @@ $("select#interval_manual_defined").change(function() { 'selected' => $this->task['id_network_profile'], 'nothing_value' => 0, 'nothing' => __('None'), - ], ]; @@ -809,7 +876,7 @@ $("select#interval_manual_defined").change(function() { 'action' => $this->url.'&mode=netscan&page='.($this->page + 1).'&task='.$this->task['id_rt'], ]; - $this->printForm($form); + $this->printFormAsList($form); } if ($this->page == 2) { diff --git a/pandora_console/godmode/wizards/Wizard.main.php b/pandora_console/godmode/wizards/Wizard.main.php index c763e0ff29..f76678ffb6 100644 --- a/pandora_console/godmode/wizards/Wizard.main.php +++ b/pandora_console/godmode/wizards/Wizard.main.php @@ -26,14 +26,15 @@ * ============================================================================ */ -define('CLOUDWIZARD_AWS_DESCRIPTION', 'Discovery.Cloud.AWS.EC2'); +// Begin. -/** - * Global Wizard generic class. Needs to be inherited. - * - * Used in Hostdevices class, Applications class and others, is the core of - * Discovery proyect. - */ + + /** + * Global Wizard generic class. Needs to be inherited. + * + * Used in Hostdevices class, Applications class and others, is the core of + * Discovery proyect. + */ class Wizard { @@ -190,15 +191,18 @@ class Wizard * Builder for breadcrum * * @param array $urls Array of urls to be stored in breadcrum. - * @param boolean $add True if breadcrum should be added instead of - * overwrite it. + * @param boolean $add True if breadcrum should be added + * instead of overwrite it. * * @return void */ - public function prepareBreadcrum(array $urls, bool $add=false) - { + public function prepareBreadcrum( + array $urls, + bool $add=false + ) { $bc = []; $i = 0; + foreach ($urls as $url) { if ($url['selected'] == 1) { $class = 'selected'; @@ -206,9 +210,12 @@ class Wizard $class = ''; } - $bc[$i] = '
'; - $bc[$i] .= '
'.$url['label']; - $bc[$i++] .= '
'; + $bc[$i] = ''; + $bc[$i] .= ''; + $bc[$i] .= $url['label']; + $bc[$i] .= ''; + $bc[$i] .= ''; + $i++; } if ($add === true) { @@ -216,7 +223,6 @@ class Wizard } else { $this->setBreadcrum($bc); } - } @@ -256,7 +262,10 @@ class Wizard */ public function printBreadcrum() { - return '

'.implode('', $this->breadcrum).'

'; + return implode( + ' / ', + $this->breadcrum + ); } @@ -464,7 +473,9 @@ class Wizard ((isset($data['keys_field']) === true) ? $data['keys_field'] : 'id_grupo'), ((isset($data['strict_user']) === true) ? $data['strict_user'] : false), ((isset($data['delete_groups']) === true) ? $data['delete_groups'] : false), - ((isset($data['include_groups']) === true) ? $data['include_groups'] : false) + ((isset($data['include_groups']) === true) ? $data['include_groups'] : false), + ((isset($data['size']) === true) ? $data['size'] : false), + ((isset($data['simple_multiple_options']) === true) ? $data['simple_multiple_options'] : false) ); case 'submit': @@ -572,14 +583,18 @@ class Wizard { $output = ''; if ($input['hidden'] == 1) { - $class = ' class="hidden"'; + $class = ' hidden'; } else { $class = ''; } + if (isset($input['class']) === true) { + $class = $input['class'].$class; + } + if (is_array($input['block_content']) === true) { // Print independent block of inputs. - $output .= '
  • '; + $output .= '
  • '; $output .= '
      '; foreach ($input['block_content'] as $input) { $output .= $this->printBlock($input, $return); @@ -588,7 +603,163 @@ class Wizard $output .= '
  • '; } else { if ($input['arguments']['type'] != 'hidden') { - $output .= '
  • '; + $output .= '
  • '; + $output .= ''; + $output .= $this->printInput($input['arguments']); + // Allow dynamic content. + $output .= $input['extra']; + $output .= '
  • '; + } else { + $output .= $this->printInput($input['arguments']); + // Allow dynamic content. + $output .= $input['extra']; + } + } + + if ($return === false) { + echo $output; + } + + return $output; + } + + + /** + * Print a block of inputs with grid format. + * + * @param array $input Definition of target block to be printed. + * @param boolean $return Return as string or direct output. + * + * @return string HTML content. + */ + public function printBlockAsGrid(array $input, bool $return=false) + { + $output = ''; + if ($input['hidden'] == 1) { + $class = ' hidden'; + } else { + $class = ''; + } + + if (isset($input['class']) === true) { + $class = $input['class'].$class; + } + + if (is_array($input['block_content']) === true) { + // Print independent block of inputs. + $output .= '
  • '; + $output .= '
      '; + foreach ($input['block_content'] as $input) { + $output .= $this->printBlockAsGrid($input, $return); + } + + $output .= '
  • '; + } else { + if ($input['arguments']['type'] != 'hidden') { + if ($input['arguments']['inline'] != 'true') { + $output .= '
    '; + } else { + $output .= '
    '; + if (!isset($input['extra'])) { + $output .= '
    '; + } + + if (isset($input['extra'])) { + $output .= '
    '; + } + } + + if ($input['arguments']['inline'] == 'true' && isset($input['extra'])) { + $output .= '
    '; + } + + $output .= '
    '; + $output .= $input['label']; + $output .= '
    '; + + if ($input['arguments']['inline'] == 'true' && isset($input['extra'])) { + $output .= '
    '; + } + + if ($input['arguments']['inline'] == 'true' && !isset($input['extra'])) { + $output .= '
    '; + } + + if ($input['arguments']['type'] == 'text' || $input['arguments']['type'] == 'text_extended') { + $output .= '
    '; + $output .= $this->printInput($input['arguments']); + $output .= '
    '; + } else if ($input['arguments']['inline'] == 'true') { + $output .= '
    '; + + if (isset($input['extra'])) { + $output .= '
    '; + } else { + $output .= '
    '; + } + + $output .= $this->printInput($input['arguments']); + $output .= '
    '; + $output .= '
    '; + + if (isset($input['extra'])) { + $output .= '
    '; + } + } else { + $output .= $this->printInput($input['arguments']); + } + + // Allow dynamic content. + $output .= $input['extra']; + $output .= '
    '; + } else { + $output .= $this->printInput($input['arguments']); + // Allow dynamic content. + $output .= $input['extra']; + } + } + + if ($return === false) { + echo $output; + } + + return $output; + } + + + /** + * Print a block of inputs as a list element. + * + * @param array $input Definition of target block to be printed. + * @param boolean $return Return as string or direct output. + * + * @return string HTML content. + */ + public function printBlockAsList(array $input, bool $return=false) + { + $output = ''; + if ($input['hidden'] == 1) { + $class = ' hidden'; + } else { + $class = ''; + } + + if (isset($input['class']) === true) { + $class = $input['class'].$class; + } + + if (is_array($input['block_content']) === true) { + // Print independent block of inputs. + $output .= '
  • '; + $output .= '
      '; + foreach ($input['block_content'] as $input) { + $output .= $this->printBlockAsList($input, $return); + } + + $output .= '
  • '; + } else { + if ($input['arguments']['type'] != 'hidden') { + $output .= '
  • '; $output .= ''; $output .= $this->printInput($input['arguments']); // Allow dynamic content. @@ -612,20 +783,24 @@ class Wizard /** * Print a form. * - * @param array $data Definition of target form to be printed. - * @param boolean $return Return as string or direct output. + * @param array $data Definition of target form to be printed. + * @param boolean $return Return as string or direct output. + * @param boolean $print_white_box Print a white box. * * @return string HTML code. */ - public function printForm(array $data, bool $return=false) - { + public function printForm( + array $data, + bool $return=false, + bool $print_white_box=false + ) { $form = $data['form']; $inputs = $data['inputs']; $js = $data['js']; $cb_function = $data['cb_function']; $cb_args = $data['cb_args']; - $output_head = '
    '; if ($return === false) { @@ -643,13 +818,187 @@ class Wizard error_log('Error executing wizard callback: ', $e->getMessage()); } - $output = '
      '; + $output_submit = ''; + $output = ''; + + if ($print_white_box === true) { + $output .= '
      '; + } + + $output .= '
        '; foreach ($inputs as $input) { - $output .= $this->printBlock($input, true); + if ($input['arguments']['type'] != 'submit') { + $output .= $this->printBlock($input, true); + } else { + $output_submit .= $this->printBlock($input, true); + } } $output .= '
      '; + + if ($print_white_box === true) { + $output .= '
      '; + } + + $output .= '
        '.$output_submit.'
      '; + $output .= ''; + $output .= ''; + + if ($return === false) { + echo $output; + } + + return $output_head.$output; + + } + + + /** + * Print a form as a grid of inputs. + * + * @param array $data Definition of target form to be printed. + * @param boolean $return Return as string or direct output. + * + * @return string HTML code. + */ + public function printFormAsGrid(array $data, bool $return=false) + { + $form = $data['form']; + + $rows = $data['rows']; + + $js = $data['js']; + $cb_function = $data['cb_function']; + $cb_args = $data['cb_args']; + + $output_head = '
      '; + + if ($return === false) { + echo $output_head; + } + + try { + if (isset($cb_function) === true) { + call_user_func_array( + $cb_function, + (isset($cb_args) === true) ? $cb_args : [] + ); + } + } catch (Exception $e) { + error_log('Error executing wizard callback: ', $e->getMessage()); + } + + $output_submit = ''; + $output = ''; + + $first_block_printed = false; + + foreach ($rows as $row) { + if ($row['new_form_block'] == true) { + if ($first_block_printed === true) { + // If first form block has been placed, then close it before starting a new one. + $output .= '
  • '; + $output .= '
    '; + } else { + $output .= '
    '; + } + + $first_block_printed = true; + } + + $output .= '
    '; + + foreach ($row['columns'] as $column) { + $width = isset($column['width']) ? 'width: '.$column['width'].';' : 'width: 100%;'; + $padding_left = isset($column['padding-left']) ? 'padding-left: '.$column['padding-left'].';' : 'padding-left: 0;'; + $padding_right = isset($column['padding-right']) ? 'padding-right: '.$column['padding-right'].';' : 'padding-right: 0;'; + $extra_styles = isset($column['style']) ? $column['style'] : ''; + + $output .= '
    '; + + foreach ($column['inputs'] as $input) { + if (is_array($input)) { + if ($input['arguments']['type'] != 'submit') { + $output .= $this->printBlockAsGrid($input, true); + } else { + $output_submit .= $this->printBlockAsGrid($input, true); + } + } else { + $output .= $input; + } + } + + $output .= '
    '; + } + + $output .= '
    '; + } + + $output .= '
    '; + + $output .= '
      '.$output_submit.'
    '; + $output .= ''; + $output .= ''; + + if ($return === false) { + echo $output; + } + + return $output_head.$output; + + } + + + /** + * Print a form as a list. + * + * @param array $data Definition of target form to be printed. + * @param boolean $return Return as string or direct output. + * + * @return string HTML code. + */ + public function printFormAsList(array $data, bool $return=false) + { + $form = $data['form']; + $inputs = $data['inputs']; + $js = $data['js']; + $cb_function = $data['cb_function']; + $cb_args = $data['cb_args']; + + $output_head = '
    '; + + if ($return === false) { + echo $output_head; + } + + try { + if (isset($cb_function) === true) { + call_user_func_array( + $cb_function, + (isset($cb_args) === true) ? $cb_args : [] + ); + } + } catch (Exception $e) { + error_log('Error executing wizard callback: ', $e->getMessage()); + } + + $output = '
    '; + $output .= '
      '; + + foreach ($inputs as $input) { + if ($input['arguments']['type'] != 'submit') { + $output .= $this->printBlockAsList($input, true); + } else { + $output_submit .= $this->printBlockAsList($input, true); + } + } + + $output .= '
    '; + $output .= '
    '; + $output .= '
      '.$output_submit.'
    '; $output .= '
    '; $output .= ''; diff --git a/pandora_console/images/arrow_down_green.png b/pandora_console/images/arrow_down_green.png new file mode 100644 index 0000000000..edc1cff1b8 Binary files /dev/null and b/pandora_console/images/arrow_down_green.png differ diff --git a/pandora_console/images/arrow_down_white.png b/pandora_console/images/arrow_down_white.png new file mode 100644 index 0000000000..7b28df71f9 Binary files /dev/null and b/pandora_console/images/arrow_down_white.png differ diff --git a/pandora_console/images/arrow_right_green.png b/pandora_console/images/arrow_right_green.png new file mode 100644 index 0000000000..a772cd6cf4 Binary files /dev/null and b/pandora_console/images/arrow_right_green.png differ diff --git a/pandora_console/images/arrow_up_green.png b/pandora_console/images/arrow_up_green.png new file mode 100644 index 0000000000..c197c2333c Binary files /dev/null and b/pandora_console/images/arrow_up_green.png differ diff --git a/pandora_console/images/arrow_up_white.png b/pandora_console/images/arrow_up_white.png new file mode 100644 index 0000000000..2fbd55b69a Binary files /dev/null and b/pandora_console/images/arrow_up_white.png differ diff --git a/pandora_console/images/custom_logo/pandora_logo_head_3.png b/pandora_console/images/custom_logo/pandora_logo_head_3.png index 5bce825279..c39d604af8 100644 Binary files a/pandora_console/images/custom_logo/pandora_logo_head_3.png and b/pandora_console/images/custom_logo/pandora_logo_head_3.png differ diff --git a/pandora_console/images/custom_logo/pandora_logo_head_4.png b/pandora_console/images/custom_logo/pandora_logo_head_4.png index 275871dca6..8bf5fbb1d3 100644 Binary files a/pandora_console/images/custom_logo/pandora_logo_head_4.png and b/pandora_console/images/custom_logo/pandora_logo_head_4.png differ diff --git a/pandora_console/images/custom_logo/pandora_logo_head_5.png b/pandora_console/images/custom_logo/pandora_logo_head_5.png new file mode 100644 index 0000000000..275871dca6 Binary files /dev/null and b/pandora_console/images/custom_logo/pandora_logo_head_5.png differ diff --git a/pandora_console/images/custom_logo/pandora_logo_head_6.png b/pandora_console/images/custom_logo/pandora_logo_head_6.png new file mode 100644 index 0000000000..5bce825279 Binary files /dev/null and b/pandora_console/images/custom_logo/pandora_logo_head_6.png differ diff --git a/pandora_console/images/custom_logo/pandora_logo_head_green.png b/pandora_console/images/custom_logo/pandora_logo_head_green.png deleted file mode 100644 index 8bf5fbb1d3..0000000000 Binary files a/pandora_console/images/custom_logo/pandora_logo_head_green.png and /dev/null differ diff --git a/pandora_console/images/darrowleft_green.png b/pandora_console/images/darrowleft_green.png new file mode 100644 index 0000000000..6262dd7e2d Binary files /dev/null and b/pandora_console/images/darrowleft_green.png differ diff --git a/pandora_console/images/darrowright_green.png b/pandora_console/images/darrowright_green.png new file mode 100644 index 0000000000..ccac0d6690 Binary files /dev/null and b/pandora_console/images/darrowright_green.png differ diff --git a/pandora_console/images/firts_task/icono_aws.png b/pandora_console/images/firts_task/icono_aws.png new file mode 100644 index 0000000000..c5f0c3a547 Binary files /dev/null and b/pandora_console/images/firts_task/icono_aws.png differ diff --git a/pandora_console/images/gm_discovery.menu.png b/pandora_console/images/gm_discovery.menu.png index b33975dcec..5c0296b562 100644 Binary files a/pandora_console/images/gm_discovery.menu.png and b/pandora_console/images/gm_discovery.menu.png differ diff --git a/pandora_console/images/gm_discovery.menu_white.png b/pandora_console/images/gm_discovery.menu_white.png index d05d5e7616..80d0913c90 100644 Binary files a/pandora_console/images/gm_discovery.menu_white.png and b/pandora_console/images/gm_discovery.menu_white.png differ diff --git a/pandora_console/images/go_first_g.png b/pandora_console/images/go_first_g.png new file mode 100644 index 0000000000..dcb0fe41b8 Binary files /dev/null and b/pandora_console/images/go_first_g.png differ diff --git a/pandora_console/images/go_last_g.png b/pandora_console/images/go_last_g.png new file mode 100644 index 0000000000..ce49963baa Binary files /dev/null and b/pandora_console/images/go_last_g.png differ diff --git a/pandora_console/images/go_next_g.png b/pandora_console/images/go_next_g.png new file mode 100644 index 0000000000..08601a2a3e Binary files /dev/null and b/pandora_console/images/go_next_g.png differ diff --git a/pandora_console/images/go_previous_g.png b/pandora_console/images/go_previous_g.png new file mode 100644 index 0000000000..84bbe0492e Binary files /dev/null and b/pandora_console/images/go_previous_g.png differ diff --git a/pandora_console/images/groups_small_white/application_osx.png b/pandora_console/images/groups_small_white/application_osx.png new file mode 100644 index 0000000000..409d9742d2 Binary files /dev/null and b/pandora_console/images/groups_small_white/application_osx.png differ diff --git a/pandora_console/images/groups_small_white/application_osx_terminal.png b/pandora_console/images/groups_small_white/application_osx_terminal.png new file mode 100644 index 0000000000..3deb76aa34 Binary files /dev/null and b/pandora_console/images/groups_small_white/application_osx_terminal.png differ diff --git a/pandora_console/images/groups_small_white/applications.png b/pandora_console/images/groups_small_white/applications.png new file mode 100644 index 0000000000..362ed8b98d Binary files /dev/null and b/pandora_console/images/groups_small_white/applications.png differ diff --git a/pandora_console/images/groups_small_white/bricks.png b/pandora_console/images/groups_small_white/bricks.png new file mode 100644 index 0000000000..b68255b88b Binary files /dev/null and b/pandora_console/images/groups_small_white/bricks.png differ diff --git a/pandora_console/images/groups_small_white/chart_organisation.png b/pandora_console/images/groups_small_white/chart_organisation.png new file mode 100644 index 0000000000..d80fc4f65f Binary files /dev/null and b/pandora_console/images/groups_small_white/chart_organisation.png differ diff --git a/pandora_console/images/groups_small_white/clock.png b/pandora_console/images/groups_small_white/clock.png new file mode 100644 index 0000000000..dc16f55773 Binary files /dev/null and b/pandora_console/images/groups_small_white/clock.png differ diff --git a/pandora_console/images/groups_small_white/computer.png b/pandora_console/images/groups_small_white/computer.png new file mode 100644 index 0000000000..5eab00e255 Binary files /dev/null and b/pandora_console/images/groups_small_white/computer.png differ diff --git a/pandora_console/images/groups_small_white/database.png b/pandora_console/images/groups_small_white/database.png new file mode 100644 index 0000000000..397f3fa5d3 Binary files /dev/null and b/pandora_console/images/groups_small_white/database.png differ diff --git a/pandora_console/images/groups_small_white/database_gear.png b/pandora_console/images/groups_small_white/database_gear.png new file mode 100644 index 0000000000..72a6b63d0f Binary files /dev/null and b/pandora_console/images/groups_small_white/database_gear.png differ diff --git a/pandora_console/images/groups_small_white/drive_network.png b/pandora_console/images/groups_small_white/drive_network.png new file mode 100644 index 0000000000..4e1cd615c6 Binary files /dev/null and b/pandora_console/images/groups_small_white/drive_network.png differ diff --git a/pandora_console/images/groups_small_white/email.png b/pandora_console/images/groups_small_white/email.png new file mode 100644 index 0000000000..496991da88 Binary files /dev/null and b/pandora_console/images/groups_small_white/email.png differ diff --git a/pandora_console/images/groups_small_white/eye.png b/pandora_console/images/groups_small_white/eye.png new file mode 100644 index 0000000000..f5c8a4e940 Binary files /dev/null and b/pandora_console/images/groups_small_white/eye.png differ diff --git a/pandora_console/images/groups_small_white/firewall.png b/pandora_console/images/groups_small_white/firewall.png new file mode 100644 index 0000000000..261adbcb1e Binary files /dev/null and b/pandora_console/images/groups_small_white/firewall.png differ diff --git a/pandora_console/images/groups_small_white/heart.png b/pandora_console/images/groups_small_white/heart.png new file mode 100644 index 0000000000..6bfec0298a Binary files /dev/null and b/pandora_console/images/groups_small_white/heart.png differ diff --git a/pandora_console/images/groups_small_white/house.png b/pandora_console/images/groups_small_white/house.png new file mode 100644 index 0000000000..72ab42dd35 Binary files /dev/null and b/pandora_console/images/groups_small_white/house.png differ diff --git a/pandora_console/images/groups_small_white/images.png b/pandora_console/images/groups_small_white/images.png new file mode 100644 index 0000000000..195c7e222b Binary files /dev/null and b/pandora_console/images/groups_small_white/images.png differ diff --git a/pandora_console/images/groups_small_white/lightning.png b/pandora_console/images/groups_small_white/lightning.png new file mode 100644 index 0000000000..31a0670a68 Binary files /dev/null and b/pandora_console/images/groups_small_white/lightning.png differ diff --git a/pandora_console/images/groups_small_white/lock.png b/pandora_console/images/groups_small_white/lock.png new file mode 100644 index 0000000000..522ff71bd6 Binary files /dev/null and b/pandora_console/images/groups_small_white/lock.png differ diff --git a/pandora_console/images/groups_small_white/network.png b/pandora_console/images/groups_small_white/network.png new file mode 100644 index 0000000000..1a8573a729 Binary files /dev/null and b/pandora_console/images/groups_small_white/network.png differ diff --git a/pandora_console/images/groups_small_white/plugin.png b/pandora_console/images/groups_small_white/plugin.png new file mode 100644 index 0000000000..051ec05d6b Binary files /dev/null and b/pandora_console/images/groups_small_white/plugin.png differ diff --git a/pandora_console/images/groups_small_white/printer.png b/pandora_console/images/groups_small_white/printer.png new file mode 100644 index 0000000000..4187e87a00 Binary files /dev/null and b/pandora_console/images/groups_small_white/printer.png differ diff --git a/pandora_console/images/groups_small_white/server_database.png b/pandora_console/images/groups_small_white/server_database.png new file mode 100644 index 0000000000..864e3cb7cb Binary files /dev/null and b/pandora_console/images/groups_small_white/server_database.png differ diff --git a/pandora_console/images/groups_small_white/transmit.png b/pandora_console/images/groups_small_white/transmit.png new file mode 100644 index 0000000000..d27f5c377d Binary files /dev/null and b/pandora_console/images/groups_small_white/transmit.png differ diff --git a/pandora_console/images/groups_small_white/without_group.png b/pandora_console/images/groups_small_white/without_group.png new file mode 100644 index 0000000000..30915bd101 Binary files /dev/null and b/pandora_console/images/groups_small_white/without_group.png differ diff --git a/pandora_console/images/groups_small_white/world.png b/pandora_console/images/groups_small_white/world.png new file mode 100644 index 0000000000..dcfd04139b Binary files /dev/null and b/pandora_console/images/groups_small_white/world.png differ diff --git a/pandora_console/images/header_docu.png b/pandora_console/images/header_docu.png new file mode 100644 index 0000000000..2a1c193bed Binary files /dev/null and b/pandora_console/images/header_docu.png differ diff --git a/pandora_console/images/header_down_gray.png b/pandora_console/images/header_down_gray.png index 0cfebc477a..68b302c3ea 100644 Binary files a/pandora_console/images/header_down_gray.png and b/pandora_console/images/header_down_gray.png differ diff --git a/pandora_console/images/header_ready_gray.png b/pandora_console/images/header_ready_gray.png index 9aadb340d5..a1fd9adfb3 100644 Binary files a/pandora_console/images/header_ready_gray.png and b/pandora_console/images/header_ready_gray.png differ diff --git a/pandora_console/images/header_support.png b/pandora_console/images/header_support.png new file mode 100644 index 0000000000..75823214b6 Binary files /dev/null and b/pandora_console/images/header_support.png differ diff --git a/pandora_console/images/header_warning_gray.png b/pandora_console/images/header_warning_gray.png index ca9954d54f..c09319fef4 100644 Binary files a/pandora_console/images/header_warning_gray.png and b/pandora_console/images/header_warning_gray.png differ diff --git a/pandora_console/images/help_g.png b/pandora_console/images/help_g.png new file mode 100644 index 0000000000..60aae95a80 Binary files /dev/null and b/pandora_console/images/help_g.png differ diff --git a/pandora_console/images/help_green.png b/pandora_console/images/help_green.png new file mode 100644 index 0000000000..0d02332988 Binary files /dev/null and b/pandora_console/images/help_green.png differ diff --git a/pandora_console/images/icon_error_db.png b/pandora_console/images/icon_error_db.png new file mode 100644 index 0000000000..1aad380c2f Binary files /dev/null and b/pandora_console/images/icon_error_db.png differ diff --git a/pandora_console/images/icon_error_mr.png b/pandora_console/images/icon_error_mr.png new file mode 100644 index 0000000000..d1e33eed42 Binary files /dev/null and b/pandora_console/images/icon_error_mr.png differ diff --git a/pandora_console/images/icon_info_mr.png b/pandora_console/images/icon_info_mr.png new file mode 100644 index 0000000000..4c0abb0585 Binary files /dev/null and b/pandora_console/images/icon_info_mr.png differ diff --git a/pandora_console/images/icon_success_db.png b/pandora_console/images/icon_success_db.png new file mode 100644 index 0000000000..38511ca06e Binary files /dev/null and b/pandora_console/images/icon_success_db.png differ diff --git a/pandora_console/images/icon_success_mr.png b/pandora_console/images/icon_success_mr.png new file mode 100644 index 0000000000..39c72ea9a1 Binary files /dev/null and b/pandora_console/images/icon_success_mr.png differ diff --git a/pandora_console/images/icon_warning_db.png b/pandora_console/images/icon_warning_db.png new file mode 100644 index 0000000000..f89a7f51b0 Binary files /dev/null and b/pandora_console/images/icon_warning_db.png differ diff --git a/pandora_console/images/imagen-no-acceso.jpg b/pandora_console/images/imagen-no-acceso.jpg new file mode 100644 index 0000000000..93b9256edb Binary files /dev/null and b/pandora_console/images/imagen-no-acceso.jpg differ diff --git a/pandora_console/images/people_1.png b/pandora_console/images/people_1.png old mode 100755 new mode 100644 index 19ad0f4e6a..aa1dd32f32 Binary files a/pandora_console/images/people_1.png and b/pandora_console/images/people_1.png differ diff --git a/pandora_console/images/people_1_old.png b/pandora_console/images/people_1_old.png new file mode 100755 index 0000000000..19ad0f4e6a Binary files /dev/null and b/pandora_console/images/people_1_old.png differ diff --git a/pandora_console/images/people_2.png b/pandora_console/images/people_2.png old mode 100755 new mode 100644 index dd38b8d758..14ba3aaaeb Binary files a/pandora_console/images/people_2.png and b/pandora_console/images/people_2.png differ diff --git a/pandora_console/images/people_2_old.png b/pandora_console/images/people_2_old.png new file mode 100755 index 0000000000..dd38b8d758 Binary files /dev/null and b/pandora_console/images/people_2_old.png differ diff --git a/pandora_console/images/sort_down_black.png b/pandora_console/images/sort_down_black.png new file mode 100644 index 0000000000..13ade460fc Binary files /dev/null and b/pandora_console/images/sort_down_black.png differ diff --git a/pandora_console/images/sort_down_green.png b/pandora_console/images/sort_down_green.png new file mode 100644 index 0000000000..dc6086912d Binary files /dev/null and b/pandora_console/images/sort_down_green.png differ diff --git a/pandora_console/images/sort_up_black.png b/pandora_console/images/sort_up_black.png new file mode 100644 index 0000000000..238d89520f Binary files /dev/null and b/pandora_console/images/sort_up_black.png differ diff --git a/pandora_console/images/sort_up_green.png b/pandora_console/images/sort_up_green.png new file mode 100644 index 0000000000..5c57937f40 Binary files /dev/null and b/pandora_console/images/sort_up_green.png differ diff --git a/pandora_console/images/status_sets/default/module_alertsfired_rounded.png b/pandora_console/images/status_sets/default/module_alertsfired_rounded.png new file mode 100644 index 0000000000..7a9b5ae0cd Binary files /dev/null and b/pandora_console/images/status_sets/default/module_alertsfired_rounded.png differ diff --git a/pandora_console/images/status_sets/default/module_critical_rounded.png b/pandora_console/images/status_sets/default/module_critical_rounded.png new file mode 100644 index 0000000000..7fceda18da Binary files /dev/null and b/pandora_console/images/status_sets/default/module_critical_rounded.png differ diff --git a/pandora_console/images/status_sets/default/module_no_data_rounded.png b/pandora_console/images/status_sets/default/module_no_data_rounded.png new file mode 100644 index 0000000000..fd5e433ece Binary files /dev/null and b/pandora_console/images/status_sets/default/module_no_data_rounded.png differ diff --git a/pandora_console/images/status_sets/default/module_ok_rounded.png b/pandora_console/images/status_sets/default/module_ok_rounded.png new file mode 100644 index 0000000000..d47f2ef4ff Binary files /dev/null and b/pandora_console/images/status_sets/default/module_ok_rounded.png differ diff --git a/pandora_console/images/status_sets/default/module_unknown_rounded.png b/pandora_console/images/status_sets/default/module_unknown_rounded.png new file mode 100644 index 0000000000..2ec6d98f39 Binary files /dev/null and b/pandora_console/images/status_sets/default/module_unknown_rounded.png differ diff --git a/pandora_console/images/status_sets/default/module_warning_rounded.png b/pandora_console/images/status_sets/default/module_warning_rounded.png new file mode 100644 index 0000000000..c28924178d Binary files /dev/null and b/pandora_console/images/status_sets/default/module_warning_rounded.png differ diff --git a/pandora_console/images/status_sets/default/severity_critical_rounded.png b/pandora_console/images/status_sets/default/severity_critical_rounded.png new file mode 100644 index 0000000000..7fceda18da Binary files /dev/null and b/pandora_console/images/status_sets/default/severity_critical_rounded.png differ diff --git a/pandora_console/images/status_sets/default/severity_informational_rounded.png b/pandora_console/images/status_sets/default/severity_informational_rounded.png new file mode 100644 index 0000000000..fd5e433ece Binary files /dev/null and b/pandora_console/images/status_sets/default/severity_informational_rounded.png differ diff --git a/pandora_console/images/status_sets/default/severity_maintenance_rounded.png b/pandora_console/images/status_sets/default/severity_maintenance_rounded.png new file mode 100644 index 0000000000..2ec6d98f39 Binary files /dev/null and b/pandora_console/images/status_sets/default/severity_maintenance_rounded.png differ diff --git a/pandora_console/images/status_sets/default/severity_major_rounded.png b/pandora_console/images/status_sets/default/severity_major_rounded.png new file mode 100644 index 0000000000..cc6523e28e Binary files /dev/null and b/pandora_console/images/status_sets/default/severity_major_rounded.png differ diff --git a/pandora_console/images/status_sets/default/severity_minor_rounded.png b/pandora_console/images/status_sets/default/severity_minor_rounded.png new file mode 100644 index 0000000000..fd3ea3d2d9 Binary files /dev/null and b/pandora_console/images/status_sets/default/severity_minor_rounded.png differ diff --git a/pandora_console/images/status_sets/default/severity_normal_rounded.png b/pandora_console/images/status_sets/default/severity_normal_rounded.png new file mode 100644 index 0000000000..d47f2ef4ff Binary files /dev/null and b/pandora_console/images/status_sets/default/severity_normal_rounded.png differ diff --git a/pandora_console/images/status_sets/default/severity_warning_rounded.png b/pandora_console/images/status_sets/default/severity_warning_rounded.png new file mode 100644 index 0000000000..c28924178d Binary files /dev/null and b/pandora_console/images/status_sets/default/severity_warning_rounded.png differ diff --git a/pandora_console/images/timestamp.png b/pandora_console/images/timestamp.png new file mode 100644 index 0000000000..1750c4acbd Binary files /dev/null and b/pandora_console/images/timestamp.png differ diff --git a/pandora_console/images/tip_help.png b/pandora_console/images/tip_help.png new file mode 100644 index 0000000000..f2704b34ac Binary files /dev/null and b/pandora_console/images/tip_help.png differ diff --git a/pandora_console/images/update_manager_background.jpg b/pandora_console/images/update_manager_background.jpg new file mode 100644 index 0000000000..121e476860 Binary files /dev/null and b/pandora_console/images/update_manager_background.jpg differ diff --git a/pandora_console/images/update_manager_button.png b/pandora_console/images/update_manager_button.png new file mode 100644 index 0000000000..b18b218d78 Binary files /dev/null and b/pandora_console/images/update_manager_button.png differ diff --git a/pandora_console/images/user_email.png b/pandora_console/images/user_email.png new file mode 100644 index 0000000000..095b012a5c Binary files /dev/null and b/pandora_console/images/user_email.png differ diff --git a/pandora_console/images/user_name.png b/pandora_console/images/user_name.png new file mode 100644 index 0000000000..20f823f40e Binary files /dev/null and b/pandora_console/images/user_name.png differ diff --git a/pandora_console/images/user_password.png b/pandora_console/images/user_password.png new file mode 100644 index 0000000000..8e81afd75e Binary files /dev/null and b/pandora_console/images/user_password.png differ diff --git a/pandora_console/images/user_phone.png b/pandora_console/images/user_phone.png new file mode 100644 index 0000000000..050559ed8d Binary files /dev/null and b/pandora_console/images/user_phone.png differ diff --git a/pandora_console/images/welcome_image.png b/pandora_console/images/welcome_image.png new file mode 100644 index 0000000000..b9b745ba33 Binary files /dev/null and b/pandora_console/images/welcome_image.png differ diff --git a/pandora_console/images/wizard/customnetscan.png b/pandora_console/images/wizard/customnetscan.png new file mode 100644 index 0000000000..edc036fa39 Binary files /dev/null and b/pandora_console/images/wizard/customnetscan.png differ diff --git a/pandora_console/images/wizard/managenetscanscripts.png b/pandora_console/images/wizard/managenetscanscripts.png new file mode 100644 index 0000000000..4694ec1e7a Binary files /dev/null and b/pandora_console/images/wizard/managenetscanscripts.png differ diff --git a/pandora_console/images/wizard/netscan_green.png b/pandora_console/images/wizard/netscan_green.png new file mode 100644 index 0000000000..faeae9b042 Binary files /dev/null and b/pandora_console/images/wizard/netscan_green.png differ diff --git a/pandora_console/include/ajax/alert_list.ajax.php b/pandora_console/include/ajax/alert_list.ajax.php index fef3f3c97c..b8c3705046 100644 --- a/pandora_console/include/ajax/alert_list.ajax.php +++ b/pandora_console/include/ajax/alert_list.ajax.php @@ -153,24 +153,48 @@ if ($show_update_action_menu) { $id_alert = (int) get_parameter('id_alert'); $module_name = modules_get_agentmodule_name($id_agent_module); - $agent_alias = modules_get_agentmodule_agent_alias($id_agent); + + $agent_alias = modules_get_agentmodule_agent_alias($id_agent_module); $id_action = (int) get_parameter('id_action'); $actions = alerts_get_alert_agent_module_actions($id_alert); - $action_opction = db_get_row('talert_template_module_actions', 'id_alert_template_module', $id_alert); + $action_opction = db_get_row( + 'talert_template_module_actions', + 'id_alert_template_module', + $id_alert + ); $data .= '
    '; $data .= ''; - $data .= html_print_input_hidden('update_action', 1, true); - $data .= html_print_input_hidden('id_module_action_ajax', $id_module_action, true); + $data .= html_print_input_hidden( + 'update_action', + 1, + true + ); + $data .= html_print_input_hidden( + 'id_module_action_ajax', + $id_module_action, + true + ); if (! $id_agente) { $data .= ''; $data .= ''; $data .= ''; $data .= ''; } @@ -180,7 +204,14 @@ if ($show_update_action_menu) { $data .= __('Module'); $data .= ''; $data .= ''; $data .= ''; $data .= ''; @@ -188,29 +219,72 @@ if ($show_update_action_menu) { $data .= __('Action'); $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= ''; $data .= '
    '; - $data .= __('Agent'); + $data .= __('Agent').' '.ui_print_help_icon( + 'alert_scalate', + true, + ui_get_full_url(false, false, false, false) + ); $data .= ''; - $data .= ui_print_truncate_text($agent_alias, 'agent_small', false, true, true, '[…]'); + $data .= ui_print_truncate_text( + $agent_alias, + 'agent_small', + false, + true, + true, + '[…]' + ); $data .= '
    '; - $data .= ui_print_truncate_text($module_name, 'module_small', false, true, true, '[…]'); + $data .= ui_print_truncate_text( + $module_name, + 'module_small', + false, + true, + true, + '[…]' + ); $data .= '
    '; - $data .= html_print_select($actions, 'action_select_ajax', $id_action, '', __('None'), 0, true, false, true, '', false, 'width:150px'); + $data .= html_print_select( + $actions, + 'action_select_ajax', + $id_action, + '', + __('None'), + 0, + true, + false, + true, + '', + false, + 'width:150px' + ); $data .= '
    '; - $data .= __('Number of alerts match from').' '.ui_print_help_icon('alert-matches', true, ui_get_full_url(false, false, false, false)); + $data .= __('Number of alerts match from'); $data .= ''; - $data .= html_print_input_text('fires_min_ajax', $action_opction['fires_min'], '', 4, 10, true); + $data .= html_print_input_text( + 'fires_min_ajax', + $action_opction['fires_min'], + '', + 4, + 10, + true + ); $data .= ' '.__('to').' '; - $data .= html_print_input_text('fires_max_ajax', $action_opction['fires_max'], '', 4, 10, true); + $data .= html_print_input_text( + 'fires_max_ajax', + $action_opction['fires_max'], + '', + 4, + 10, + true + ); $data .= '
    '; - $data .= __('Threshold').' '.ui_print_help_icon('action_threshold', true, ui_get_full_url(false, false, false, false)); + $data .= __('Threshold'); $data .= ''; - $data .= html_print_input_text('module_action_threshold_ajax', $action_opction['module_action_threshold'], '', 4, 10, true); + $data .= html_print_input_text( + 'module_action_threshold_ajax', + $action_opction['module_action_threshold'], + '', + 4, + 10, + true + ); $data .= '
    '; - $data .= html_print_submit_button(__('Update'), 'updbutton', false, ['class' => 'sub next', 'style' => 'float:right'], true); + $data .= html_print_submit_button( + __('Update'), + 'updbutton', + false, + [ + 'class' => 'sub next', + 'style' => 'float:right', + ], + true + ); $data .= '
    '; echo $data; return; diff --git a/pandora_console/include/ajax/custom_fields.php b/pandora_console/include/ajax/custom_fields.php index 0cb9626e7f..9baacdcdc2 100644 --- a/pandora_console/include/ajax/custom_fields.php +++ b/pandora_console/include/ajax/custom_fields.php @@ -1,17 +1,33 @@ $value) { - $values_insert[] = '('.$value['id_server'].', '.$value['id_agente'].", '".$value['description']."', ".$value['status'].')'; + $values_insert[] = '('.$value['id_server'].', '.$value['id_agente'].", '".$value['description']."', '".$value['critical_count']."', '".$value['warning_count']."', '".$value['unknown_count']."', '".$value['notinit_count']."', '".$value['normal_count']."', '".$value['total_count']."', ".$value['status'].')'; } $values_insert_implode = implode(',', $values_insert); $query_insert = 'INSERT INTO temp_custom_fields VALUES '.$values_insert_implode; db_process_sql($query_insert); - // search table for alias, custom field data, server_name, direction + // Search table for alias, custom field data, server_name, direction. $search_query = ''; if ($search['value'] != '') { $search_query = ' AND (tma.alias LIKE "%'.$search['value'].'%"'; @@ -95,7 +134,61 @@ if (check_login()) { $search_query .= ' OR temp.name_custom_fields LIKE "%'.$search['value'].'%" ) '; } - // query all fields result + // Search for status module. + $status_agent_search = ''; + if (isset($id_status) === true && is_array($id_status) === true) { + if (in_array(-1, $id_status) === false) { + if (in_array(AGENT_MODULE_STATUS_NOT_NORMAL, $id_status) === false) { + $status_agent_search = ' AND temp.status IN ('.implode(',', $id_status).')'; + } else { + // Not normal statuses. + $status_agent_search = ' AND temp.status IN (1,2,3,4,5)'; + } + } + } + + // Search for status module. + $status_module_search = ''; + if (isset($module_status) === true && is_array($module_status) === true) { + if (in_array(-1, $module_status) === false) { + if (in_array(AGENT_MODULE_STATUS_NOT_NORMAL, $module_status) === false) { + if (count($module_status) > 0) { + $status_module_search = ' AND ( '; + foreach ($module_status as $key => $value) { + $status_module_search .= ($key != 0) ? ' OR (' : ' ('; + switch ($value) { + default: + case AGENT_STATUS_NORMAL: + $status_module_search .= ' temp.normal_count > 0) '; + break; + case AGENT_STATUS_CRITICAL: + $status_module_search .= ' temp.critical_count > 0) '; + break; + + case AGENT_STATUS_WARNING: + $status_module_search .= ' temp.warning_count > 0) '; + break; + + case AGENT_STATUS_UNKNOWN: + $status_module_search .= ' temp.unknown_count > 0) '; + break; + + case AGENT_STATUS_NOT_INIT: + $status_module_search .= ' temp.notinit_count > 0) '; + break; + } + } + + $status_module_search .= ' ) '; + } + } else { + // Not normal. + $status_module_search = ' AND ( temp.critical_count > 0 OR temp.warning_count > 0 OR temp.unknown_count > 0 AND temp.notinit_count > 0 )'; + } + } + } + + // Query all fields result. $query = sprintf( 'SELECT tma.id_agente, @@ -112,10 +205,14 @@ if (check_login()) { AND temp.id_server = tma.id_tmetaconsole_setup WHERE tma.disabled = 0 %s + %s + %s %s LIMIT %d OFFSET %d ', $search_query, + $status_agent_search, + $status_module_search, $order_by, $length, $start @@ -123,23 +220,27 @@ if (check_login()) { $result = db_get_all_rows_sql($query); - // query count + // Query count. $query_count = sprintf( 'SELECT COUNT(tma.id_agente) AS `count` - FROM tmetaconsole_agent tma - INNER JOIN temp_custom_fields temp - ON temp.id_agent = tma.id_tagente - AND temp.id_server = tma.id_tmetaconsole_setup - WHERE tma.disabled = 0 - %s - ', - $search_query + FROM tmetaconsole_agent tma + INNER JOIN temp_custom_fields temp + ON temp.id_agent = tma.id_tagente + AND temp.id_server = tma.id_tmetaconsole_setup + WHERE tma.disabled = 0 + %s + %s + %s + ', + $search_query, + $status_agent_search, + $status_module_search ); $count = db_get_sql($query_count); - // for link nodes. + // For link nodes. $array_nodes = metaconsole_get_connections(); if (isset($array_nodes) && is_array($array_nodes)) { $hash_array_nodes = []; @@ -158,20 +259,20 @@ if (check_login()) { $user_rot13 = str_rot13($config['id_user']); $hashdata = $user.$pwd; $hashdata = md5($hashdata); - $url_hash = '&'.'loginhash=auto&'.'loginhash_data='.$hashdata.'&'.'loginhash_user='.$user_rot13; + $url_hash = '&loginhash=auto&loginhash_data='.$hashdata.'&loginhash_user='.$user_rot13; $hash_array_nodes[$server['id']]['hashurl'] = $url_hash; $hash_array_nodes[$server['id']]['server_url'] = $server['server_url']; } } - // prepare rows for table dinamic + // Prepare rows for table dinamic. $data = []; foreach ($result as $values) { $image_status = agents_get_image_status($values['status']); - // link nodes - $agent_link = ''; + // Link nodes. + $agent_link = ''; $agent_alias = ui_print_truncate_text( $values['alias'], @@ -225,7 +326,7 @@ if (check_login()) { $name_where = " AND tam.nombre LIKE '%".$module_search."%'"; } - // filter by status module + // Filter by status module. $and_module_status = ''; if (is_array($module_status)) { if (!in_array(-1, $module_status)) { @@ -260,7 +361,7 @@ if (check_login()) { $and_module_status .= ' ) '; } } else { - // not normal + // Not normal. $and_module_status = 'AND tae.estado <> 0 AND tae.estado <> 300 '; } } @@ -314,7 +415,12 @@ if (check_login()) { && $value['id_tipo_modulo'] != 23 && $value['id_tipo_modulo'] != 33 ) { - $table_modules->data[$key][1] = remove_right_zeros(number_format($value['datos'], $config['graph_precision'])); + $table_modules->data[$key][1] = remove_right_zeros( + number_format( + $value['datos'], + $config['graph_precision'] + ) + ); } else { $table_modules->data[$key][1] = $value['datos']; } @@ -329,7 +435,10 @@ if (check_login()) { ); $table_modules->data[$key][3] = $value['current_interval']; - $table_modules->data[$key][4] = ui_print_timestamp($value['utimestamp'], true); + $table_modules->data[$key][4] = ui_print_timestamp( + $value['utimestamp'], + true + ); switch ($value['estado']) { case 0: case 300: @@ -398,7 +507,7 @@ if (check_login()) { } } - // status agents from tagente + // Status agents from tagente. $sql_info_agents = 'SELECT * fROM tagente WHERE id_agente ='.$id_agent; $info_agents = db_get_row_sql($sql_info_agents); $status_agent = agents_get_status_from_counts($info_agents); @@ -463,7 +572,13 @@ if (check_login()) { false ); - $table->data[0][3] = html_print_submit_button(__('Load filter'), 'load_filter', false, 'class="sub upd"', true); + $table->data[0][3] = html_print_submit_button( + __('Load filter'), + 'load_filter', + false, + 'class="sub upd"', + true + ); echo "
    "; html_print_table($table); @@ -474,7 +589,12 @@ if (check_login()) { } if ($append_tab_filter) { - $filters = json_decode(io_safe_output(get_parameter('filters', '')), true); + $filters = json_decode( + io_safe_output( + get_parameter('filters', '') + ), + true + ); $table = new StdClass; $table->id = 'save_filter_form'; @@ -485,7 +605,14 @@ if (check_login()) { if ($filters['id'] == 'extended_create_filter') { echo "
    "; $table->data[0][0] = __('Filter name'); - $table->data[0][1] = html_print_input_text('id_name', '', '', 15, 255, true); + $table->data[0][1] = html_print_input_text( + 'id_name', + '', + '', + 15, + 255, + true + ); $table->data[1][0] = __('Group'); $table->data[1][1] = html_print_select_groups( @@ -510,7 +637,13 @@ if (check_login()) { ); $table->rowspan[0][2] = 2; - $table->data[0][2] = html_print_submit_button(__('Create filter'), 'create_filter', false, 'class="sub upd"', true); + $table->data[0][2] = html_print_submit_button( + __('Create filter'), + 'create_filter', + false, + 'class="sub upd"', + true + ); } else { echo "
    "; echo "
    "; @@ -552,8 +685,20 @@ if (check_login()) { false ); - $table->data[0][2] = html_print_submit_button(__('Delete filter'), 'delete_filter', false, 'class="sub upd"', true); - $table->data[1][2] = html_print_submit_button(__('Update filter'), 'update_filter', false, 'class="sub upd"', true); + $table->data[0][2] = html_print_submit_button( + __('Delete filter'), + 'delete_filter', + false, + 'class="sub upd"', + true + ); + $table->data[1][2] = html_print_submit_button( + __('Update filter'), + 'update_filter', + false, + 'class="sub upd"', + true + ); } html_print_table($table); @@ -561,17 +706,20 @@ if (check_login()) { } if ($create_filter_cf) { - // initialize result + // Initialize result. $result_array = []; $result_array['error'] = 0; $result_array['msg'] = ''; - // initialize vars - $filters = json_decode(io_safe_output(get_parameter('filters', '')), true); + // Initialize vars. + $filters = json_decode( + io_safe_output(get_parameter('filters', '')), + true + ); $name_filter = get_parameter('name_filter', ''); $group_search = get_parameter('group_search', 0); - // check that the name is not empty + // Check that the name is not empty. if ($name_filter == '') { $result_array['error'] = 1; $result_array['msg'] = ui_print_error_message( @@ -596,7 +744,7 @@ if (check_login()) { return; } - // check custom field is not empty + // Check custom field is not empty. if ($filters['id_custom_fields'] == '') { $result_array['error'] = 1; $result_array['msg'] = ui_print_error_message( @@ -608,13 +756,15 @@ if (check_login()) { return; } - // insert + // Insert. $values = []; $values['name'] = $name_filter; $values['group_search'] = $group_search; $values['id_group'] = $filters['group']; $values['id_custom_field'] = $filters['id_custom_fields']; - $values['id_custom_fields_data'] = json_encode($filters['id_custom_fields_data']); + $values['id_custom_fields_data'] = json_encode( + $filters['id_custom_fields_data'] + ); $values['id_status'] = json_encode($filters['id_status']); $values['module_search'] = $filters['module_search']; $values['module_status'] = json_encode($filters['module_status']); @@ -622,7 +772,7 @@ if (check_login()) { $insert = db_process_sql_insert('tagent_custom_fields_filter', $values); - // check error insert + // Check error insert. if ($insert) { $result_array['error'] = 0; $result_array['msg'] = ui_print_success_message( @@ -644,17 +794,17 @@ if (check_login()) { } if ($update_filter_cf) { - // initialize result + // Initialize result. $result_array = []; $result_array['error'] = 0; $result_array['msg'] = ''; - // initialize vars + // Initialize vars. $filters = json_decode(io_safe_output(get_parameter('filters', '')), true); $id_filter = get_parameter('id_filter', ''); $group_search = get_parameter('group_search', 0); - // check selected filter + // Check selected filter. if ($id_filter == -1) { $result_array['error'] = 1; $result_array['msg'] = ui_print_error_message( @@ -666,11 +816,11 @@ if (check_login()) { return; } - // array condition update + // Array condition update. $condition = []; $condition['id'] = $id_filter; - // check selected custom fields + // Check selected custom fields. if ($filters['id_custom_fields'] == '') { $result_array['error'] = 1; $result_array['msg'] = ui_print_error_message( @@ -682,7 +832,7 @@ if (check_login()) { return; } - // array values update + // Array values update. $values = []; $values['id_group'] = $filters['group']; $values['group_search'] = $group_search; @@ -693,10 +843,10 @@ if (check_login()) { $values['module_status'] = json_encode($filters['module_status']); $values['recursion'] = $filters['recursion']; - // update + // Update. $update = db_process_sql_update('tagent_custom_fields_filter', $values, $condition); - // check error insert + // Check error insert. if ($update) { $result_array['error'] = 0; $result_array['msg'] = ui_print_success_message( @@ -718,16 +868,16 @@ if (check_login()) { } if ($delete_filter_cf) { - // Initialize result + // Initialize result. $result_array = []; $result_array['error'] = 0; $result_array['msg'] = ''; - // Initialize vars + // Initialize vars. $filters = json_decode(io_safe_output(get_parameter('filters', '')), true); $id_filter = get_parameter('id_filter', ''); - // Check selected filter + // Check selected filter. if ($id_filter == -1) { $result_array['error'] = 1; $result_array['msg'] = ui_print_error_message( @@ -739,14 +889,14 @@ if (check_login()) { return; } - // Array condition update + // Array condition update. $condition = []; $condition['id'] = $id_filter; - // Delete + // Delete. $delete = db_process_sql_delete('tagent_custom_fields_filter', $condition); - // Check error insert + // Check error insert. if ($delete) { $result_array['error'] = 0; $result_array['msg'] = ui_print_success_message( diff --git a/pandora_console/include/ajax/events.php b/pandora_console/include/ajax/events.php index 6b7be8eb28..b58a3c2386 100644 --- a/pandora_console/include/ajax/events.php +++ b/pandora_console/include/ajax/events.php @@ -418,15 +418,15 @@ if ($get_extended_event) { } // Tabs. - $tabs = "
      "; - $tabs .= "
    • ".html_print_image('images/lightning_go.png', true)."".__('General').'
    • '; + $tabs = "
        "; + $tabs .= "
      • ".html_print_image('images/lightning_go.png', true).''.__('General').'
      • '; if (events_has_extended_info($event['id_evento']) === true) { - $tabs .= "
      • ".html_print_image('images/zoom.png', true)."".__('Related').'
      • '; + $tabs .= "
      • ".html_print_image('images/zoom.png', true).''.__('Related').'
      • '; } - $tabs .= "
      • ".html_print_image('images/zoom.png', true)."".__('Details').'
      • '; - $tabs .= "
      • ".html_print_image('images/custom_field_col.png', true)."".__('Agent fields').'
      • '; - $tabs .= "
      • ".html_print_image('images/pencil.png', true)."".__('Comments').'
      • '; + $tabs .= "
      • ".html_print_image('images/zoom.png', true).''.__('Details').'
      • '; + $tabs .= "
      • ".html_print_image('images/custom_field_col.png', true).''.__('Agent fields').'
      • '; + $tabs .= "
      • ".html_print_image('images/pencil.png', true).''.__('Comments').'
      • '; if (!$readonly && ((tags_checks_event_acl( @@ -456,30 +456,30 @@ if ($get_extended_event) { switch ($event['criticity']) { default: case 0: - $img_sev = 'images/status_sets/default/severity_maintenance.png'; + $img_sev = 'images/status_sets/default/severity_maintenance_rounded.png'; break; case 1: - $img_sev = 'images/status_sets/default/severity_informational.png'; + $img_sev = 'images/status_sets/default/severity_informational_rounded.png'; break; case 2: - $img_sev = 'images/status_sets/default/severity_normal.png'; + $img_sev = 'images/status_sets/default/severity_normal_rounded.png'; break; case 3: - $img_sev = 'images/status_sets/default/severity_warning.png'; + $img_sev = 'images/status_sets/default/severity_warning_rounded.png'; break; case 4: - $img_sev = 'images/status_sets/default/severity_critical.png'; + $img_sev = 'images/status_sets/default/severity_critical_rounded.png'; break; case 5: - $img_sev = 'images/status_sets/default/severity_minor.png'; + $img_sev = 'images/status_sets/default/severity_minor_rounded.png'; break; case 6: - $img_sev = 'images/status_sets/default/severity_major.png'; + $img_sev = 'images/status_sets/default/severity_major_rounded.png'; break; } @@ -546,7 +546,7 @@ if ($get_extended_event) { $loading = ''; - $out = '
        '.$tabs.$notifications.$loading.$general.$details.$related.$custom_fields.$comments.$responses.$custom_data.html_print_input_hidden('id_event', $event['id_evento']).'
        '; + $out = '
        '.$tabs.$notifications.$loading.$general.$details.$related.$custom_fields.$comments.$responses.$custom_data.html_print_input_hidden('id_event', $event['id_evento']).'
        '; $js = ''; + + return $output; + } + + + /** + * Generates a simple interface to interact with nodes. + * + * @return string HTML code for simple interface. + */ + public function loadSimpleInterface() + { + $output = ''; + + $output .= ''; + + return $output; + } + + + /** + * Show an advanced interface to manage dialogs. + * + * @return string HTML code with dialogs. + */ + public function loadAdvancedInterface() + { + $list_networkmaps = get_networkmaps($this->idMap); + if (empty($list_networkmaps)) { + $list_networkmaps = []; + } + + $output .= ''; + + $output .= ''; + + $output .= ''; + + $output .= ''; + + return $output; + } + + + /** + * Loads advanced map controller (JS). + * + * @return string HTML code for advanced controller. + */ + public function loadController() + { + $output = ''; + + if (enterprise_installed() + && $this->useTooltipster + ) { + $output .= ''; + } else { + // Generate JS for advanced controller. + $output .= ' + +'; + } + + if ($return === false) { + echo $output; + } + + return $output; + + } + + + /** + * Load networkmap HTML skel and JS requires. + * + * @return string HTML code for skel. + */ + public function loadMapSkel() + { + global $config; + + if (enterprise_installed() + && isset($this->useTooltipster) + && $this->useTooltipster == true + ) { + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''."\n"; + + $output .= '
        fullSize) { + $output .= ' width:100%'; + $output .= ' ;height: 100%">'; + $output .= ''; + } else { + $output .= ' width:'.$this->mapOptions['width'].'px'; + $output .= ' ;height:'.$this->mapOptions['height'].'px">'; + $output .= ''; + } + + $output .= ''; + $output .= '
        '; + } else { + // Load default interface. + ui_require_css_file('networkmap'); + ui_require_css_file('jquery.contextMenu', 'include/styles/js/'); + + $output = ''; + $minimap_display = ''; + if ($this->mapOptions['pure']) { + $minimap_display = 'none'; + } + + $networkmap = $this->map; + if (is_array($networkmap['filter']) === false) { + $networkmap['filter'] = json_decode($networkmap['filter'], true); + } + + $networkmap['filter']['l2_network_interfaces'] = 1; + + $output .= ''; + if (isset($this->map['__simulated']) === false) { + // Load context menu if manageable networkmap. + $output .= ''; + } + + $output .= ''; + + // Open networkconsole_id div. + $output .= '
        fullSize) { + $output .= ' style="width: 100%; height: 100%;position: relative; overflow: hidden; background: #FAFAFA">'; + } else { + $output .= ' style="width: '.$this->mapOptions['width'].'px; height: '.$this->mapOptions['height'].'px;position: relative; overflow: hidden; background: #FAFAFA">'; + } + + $output .= '
        '; + $output .= ''; + $output .= html_print_image('/images/minimap_open_arrow.png', true, ['id' => 'arrow_minimap_'.$networkmap['id']]); + $output .= '
        '; + + $output .= '
        '; + $output .= html_print_image('/images/icono_borrar.png', true, ['id' => 'image_hide_show_labels']); + $output .= '
        '; + + $output .= '
        'image_hide_show_labels']); + $output .= '
        '; + + // Close networkconsole_id div. + $output .= "
        \n"; + } + + return $output; + } + + + /** + * Print all components required to visualizate a network map. + * + * @param boolean $return Return as string or not. + * + * @return string HTML code. + */ + public function printMap($return=false) + { + global $config; + + // ACL. + $networkmap_read = check_acl( + $config['id_user'], + $networkmap['id_group'], + 'MR' + ); + $networkmap_write = check_acl( + $config['id_user'], + $networkmap['id_group'], + 'MW' + ); + $networkmap_manage = check_acl( + $config['id_user'], + $networkmap['id_group'], + 'MM' + ); + + if (!$networkmap_read + && !$networkmap_write + && !$networkmap_manage + ) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access networkmap' + ); + include 'general/noaccess.php'; + return ''; + } + + $user_readonly = !$networkmap_write && !$networkmap_manage; + + if (isset($this->idMap) + && isset($this->map['__simulated']) === false + ) { + $output .= $this->loadMapSkel(); + $output .= $this->loadMapData(); + $output .= $this->loadController(); + if (!$this->noPopUp) { + $output .= $this->loadAdvancedInterface(); + } + } else { + // Simulated, no tmap entries. + $output .= $this->loadMapSkel(); + $output .= $this->loadMapData(); + $output .= $this->loadController(); + if (!$this->noPopUp) { + $output .= $this->loadSimpleInterface(); + } + } + + $output .= ' + + +'; + if ($return === false) { + echo $output; + } + + return $output; + } + + +} diff --git a/pandora_console/include/config_process.php b/pandora_console/include/config_process.php index 3478c10c9c..2d0b52acfe 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 = 'PC190319'; -$pandora_version = 'v7.0NG.732'; +$build_version = 'PC190610'; +$pandora_version = 'v7.0NG.735'; // Do not overwrite default timezone set if defined. $script_tz = @date_default_timezone_get(); @@ -306,3 +306,9 @@ switch ($config['dbtype']) { } // ====================================================================== +// Menu display mode. +if ($_SESSION['menu_type']) { + $config['menu_type'] = $_SESSION['menu_type']; +} else { + $config['menu_type'] = 'classic'; +} diff --git a/pandora_console/include/constants.php b/pandora_console/include/constants.php index b472f98ad8..7266e9eff4 100644 --- a/pandora_console/include/constants.php +++ b/pandora_console/include/constants.php @@ -195,6 +195,14 @@ define('AGENT_STATUS_UNKNOWN', 3); define('AGENT_STATUS_ALERT_FIRED', 4); define('AGENT_STATUS_WARNING', 2); +// Pseudo criticity analysis. +define('NO_CRIT', -1); +define('CRIT_0', 0); +define('CRIT_1', 1); +define('CRIT_2', 2); +define('CRIT_3', 3); +define('CRIT_4', 4); +define('CRIT_5', 5); // Visual maps contants. // The items kind. @@ -506,6 +514,18 @@ define('OPTION_COLOR_PICKER', 11); define('NODE_TYPE', 0); define('ARROW_TYPE', 1); +// Discovery task steps. +define('STEP_SCANNING', 1); +define('STEP_AFT', 2); +define('STEP_TRACEROUTE', 3); +define('STEP_GATEWAY', 4); + +// Networkmap node types. +define('NODE_AGENT', 0); +define('NODE_MODULE', 1); +define('NODE_PANDORA', 2); +define('NODE_GENERIC', 3); + // SAML attributes constants. define('SAML_ROLE_AND_TAG', 'eduPersonEntitlement'); define('SAML_USER_DESC', 'commonName'); @@ -535,12 +555,55 @@ define('MAP_GENERATION_RADIAL', 2); define('MAP_GENERATION_SPRING1', 3); define('MAP_GENERATION_SPRING2', 4); +// Algorithm: Circo. +define('LAYOUT_CIRCULAR', 0); +// Algorithm: Dot. +define('LAYOUT_FLAT', 1); +// Algorithm: Twopi. +define('LAYOUT_RADIAL', 2); +// Algorithm: Neato. +define('LAYOUT_SPRING1', 3); +// Algorithm: Fdp. +define('LAYOUT_SPRING2', 4); +// Extra: radial dynamic. +define('LAYOUT_RADIAL_DYNAMIC', 6); + +// Map sources. +define('SOURCE_GROUP', 0); +define('SOURCE_TASK', 1); +define('SOURCE_NETWORK', 2); + +// Backward compatibility ~ Migration. define('MAP_SOURCE_GROUP', 0); define('MAP_SOURCE_IP_MASK', 1); define('NETWORKMAP_DEFAULT_WIDTH', 800); define('NETWORKMAP_DEFAULT_HEIGHT', 800); +// Discovery task types. +define('DISCOVERY_HOSTDEVICES', 0); +define('DISCOVERY_HOSTDEVICES_CUSTOM', 1); +define('DISCOVERY_CLOUD_AWS', 2); +define('DISCOVERY_APP_VMWARE', 3); +define('DISCOVERY_APP_MYSQL', 4); +define('DISCOVERY_APP_ORACLE', 5); +define('DISCOVERY_CLOUD_AWS_EC2', 6); +define('DISCOVERY_CLOUD_AWS_RDS', 7); + + +// Discovery types matching definition. +define('DISCOVERY_SCRIPT_HOSTDEVICES_CUSTOM', 0); +// Standard applications. +define('DISCOVERY_SCRIPT_APP_VMWARE', 1); +// Cloud environments. +define('DISCOVERY_SCRIPT_CLOUD_AWS', 2); +define('DISCOVERY_SCRIPT_IPAM_RECON', 3); +define('DISCOVERY_SCRIPT_IPMI_RECON', 4); + +// Discovery task descriptions. +define('CLOUDWIZARD_AWS_DESCRIPTION', 'Discovery.Cloud.AWS.EC2'); +define('CLOUDWIZARD_VMWARE_DESCRIPTION', 'Discovery.App.VMware'); + // Background options. define('CENTER', 0); define('MOSAIC', 1); diff --git a/pandora_console/include/db/mysql.php b/pandora_console/include/db/mysql.php index 82b654e374..17f5712d2c 100644 --- a/pandora_console/include/db/mysql.php +++ b/pandora_console/include/db/mysql.php @@ -205,7 +205,7 @@ function mysql_db_get_value($field, $table, $field_search=1, $condition=1, $sear * * @return mixed The first row of a database query or false. */ -function mysql_db_get_row($table, $field_search, $condition, $fields=false) +function mysql_db_get_row($table, $field_search, $condition, $fields=false, $cache=true) { if (empty($fields)) { $fields = '*'; @@ -243,7 +243,7 @@ function mysql_db_get_row($table, $field_search, $condition, $fields=false) ); } - $result = db_get_all_rows_sql($sql); + $result = db_get_all_rows_sql($sql, false, $cache); if ($result === false) { return false; diff --git a/pandora_console/include/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 b/pandora_console/include/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 new file mode 100755 index 0000000000..2312604864 Binary files /dev/null and b/pandora_console/include/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 differ diff --git a/pandora_console/include/functions.php b/pandora_console/include/functions.php index e1861e70a3..f299153da6 100644 --- a/pandora_console/include/functions.php +++ b/pandora_console/include/functions.php @@ -243,19 +243,25 @@ function format_numeric($number, $decimals=1) /** * Render numeric data for a graph. It adds magnitude suffix to the number - * (M for millions, K for thousands...) base-10 + * (M for millions, K for thousands...). Base can be modified with divider. * - * TODO: base-2 multiplication - * - * @param float $number Number to be rendered - * @param integer $decimals Numbers after comma. Default value: 1 - * @param dec_point Decimal separator character. Default value: . - * @param thousands_sep Thousands separator character. Default value: , + * @param float $number Number to be rendered. + * @param integer $decimals Numbers after comma (default 1). + * @param string $dec_point Decimal separator character (default .). + * @param string $thousands_sep Thousands separator character (default ,). + * @param integer $divider Number to divide the rendered number. + * @param string $sufix Units of the multiple. * * @return string A string with the number and the multiplier */ -function format_for_graph($number, $decimals=1, $dec_point='.', $thousands_sep=',') -{ +function format_for_graph( + $number, + $decimals=1, + $dec_point='.', + $thousands_sep=',', + $divider=1000, + $sufix='' +) { $shorts = [ '', 'K', @@ -268,15 +274,15 @@ function format_for_graph($number, $decimals=1, $dec_point='.', $thousands_sep=' 'Y', ]; $pos = 0; - while ($number >= 1000) { - // as long as the number can be divided by 1000 + while ($number >= $divider) { + // As long as the number can be divided by divider. $pos++; - // Position in array starting with 0 - $number = ($number / 1000); + // Position in array starting with 0. + $number = ($number / $divider); } - return remove_right_zeros(format_numeric($number, $decimals)).$shorts[$pos]; - // This will actually do the rounding and the decimals + // This will actually do the rounding and the decimals. + return remove_right_zeros(format_numeric($number, $decimals)).$shorts[$pos].$sufix; } @@ -865,12 +871,13 @@ function get_parameter_switch($name, $default='') $data = get_parameter($name, null); if ($data === null) { - return 0; + return (isset($default) ? $default : 0); } else if ($data == 'on') { return 1; } - return 0; + // Return value assigned to switch. + return $data; } @@ -1438,7 +1445,13 @@ function enterprise_include($filename) global $config; // Load enterprise extensions - $filepath = realpath($config['homedir'].'/'.ENTERPRISE_DIR.'/'.$filename); + if (defined('DESTDIR')) { + $destdir = DESTDIR; + } else { + $destdir = ''; + } + + $filepath = realpath($destdir.$config['homedir'].'/'.ENTERPRISE_DIR.'/'.$filename); if ($filepath === false) { return ENTERPRISE_NOT_HOOK; @@ -1750,16 +1763,22 @@ function array_key_to_offset($array, $key) /** * Make a snmpwalk and return it. * - * @param string $ip_target The target address. - * @param string $snmp_version Version of the snmp: 1,2,2c or 3. - * @param string $snmp_community. - * @param string $snmp3_auth_user. - * @param string $snmp3_security_level. - * @param string $snmp3_auth_method. - * @param string $snmp3_auth_pass. - * @param string $snmp3_privacy_method. - * @param string $snmp3_privacy_pass. - * @param integer $quick_print 0 for all details, 1 for only value. + * @param string $ip_target The target address. + * @param string $snmp_version Version of the snmp: 1,2,2c or 3. + * @param string $snmp_community Snmp_community. + * @param string $snmp3_auth_user Snmp3_auth_user. + * @param string $snmp3_security_level Snmp3_security_level. + * @param string $snmp3_auth_method Snmp3_auth_method. + * @param string $snmp3_auth_pass Snmp3_auth_pass. + * @param string $snmp3_privacy_method Snmp3_privacy_method. + * @param string $snmp3_privacy_pass Snmp3_privacy_pass. + * @param integer $quick_print To get all details 0, 1: only value. + * @param string $base_oid Base_oid. + * @param string $snmp_port Snmp_port. + * @param integer $server_to_exec Server_to_exec. + * @param string $extra_arguments Extra_arguments. + * @param string $format Format to apply, for instance, to + * retrieve hex-dumps: --hexOutputLength. * * @return array SNMP result. */ @@ -1777,7 +1796,8 @@ function get_snmpwalk( $base_oid='', $snmp_port='', $server_to_exec=0, - $extra_arguments='' + $extra_arguments='', + $format='-Oa' ) { global $config; @@ -1828,15 +1848,15 @@ function get_snmpwalk( case '3': switch ($snmp3_security_level) { case 'authNoPriv': - $command_str = $snmpwalk_bin.' -m ALL -Oa '.$extra_arguments.' -v 3'.' -u '.escapeshellarg($snmp3_auth_user).' -A '.escapeshellarg($snmp3_auth_pass).' -l '.escapeshellarg($snmp3_security_level).' -a '.escapeshellarg($snmp3_auth_method).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; + $command_str = $snmpwalk_bin.' -m ALL '.$format.' '.$extra_arguments.' -v 3'.' -u '.escapeshellarg($snmp3_auth_user).' -A '.escapeshellarg($snmp3_auth_pass).' -l '.escapeshellarg($snmp3_security_level).' -a '.escapeshellarg($snmp3_auth_method).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; break; case 'noAuthNoPriv': - $command_str = $snmpwalk_bin.' -m ALL -Oa '.$extra_arguments.' -v 3'.' -u '.escapeshellarg($snmp3_auth_user).' -l '.escapeshellarg($snmp3_security_level).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; + $command_str = $snmpwalk_bin.' -m ALL '.$format.' '.$extra_arguments.' -v 3'.' -u '.escapeshellarg($snmp3_auth_user).' -l '.escapeshellarg($snmp3_security_level).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; break; default: - $command_str = $snmpwalk_bin.' -m ALL -Oa '.$extra_arguments.' -v 3'.' -u '.escapeshellarg($snmp3_auth_user).' -A '.escapeshellarg($snmp3_auth_pass).' -l '.escapeshellarg($snmp3_security_level).' -a '.escapeshellarg($snmp3_auth_method).' -x '.escapeshellarg($snmp3_privacy_method).' -X '.escapeshellarg($snmp3_privacy_pass).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; + $command_str = $snmpwalk_bin.' -m ALL '.$format.' '.$extra_arguments.' -v 3'.' -u '.escapeshellarg($snmp3_auth_user).' -A '.escapeshellarg($snmp3_auth_pass).' -l '.escapeshellarg($snmp3_security_level).' -a '.escapeshellarg($snmp3_auth_method).' -x '.escapeshellarg($snmp3_privacy_method).' -X '.escapeshellarg($snmp3_privacy_pass).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; break; } break; @@ -1845,7 +1865,7 @@ function get_snmpwalk( case '2c': case '1': default: - $command_str = $snmpwalk_bin.' -m ALL '.$extra_arguments.' -Oa -v '.escapeshellarg($snmp_version).' -c '.escapeshellarg(io_safe_output($snmp_community)).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; + $command_str = $snmpwalk_bin.' -m ALL '.$extra_arguments.' '.$format.' -v '.escapeshellarg($snmp_version).' -c '.escapeshellarg(io_safe_output($snmp_community)).' '.escapeshellarg($ip_target).' '.$base_oid.' 2> '.$error_redir_dir; break; } @@ -2656,7 +2676,7 @@ function get_news($arguments) case 'mysql': case 'postgresql': $sql = sprintf( - "SELECT subject,timestamp,text,author + "SELECT id_news,subject,timestamp,text,author FROM tnews WHERE id_group IN (%s) AND modal = %s AND (expire = 0 OR (expire = 1 AND expire_timestamp > '%s')) @@ -3529,25 +3549,27 @@ function series_type_graph_array($data, $show_elements_graph) break; } - if (isset($show_elements_graph['labels']) + if (isset($show_elements_graph['labels'][$value['agent_module_id']]) && is_array($show_elements_graph['labels']) && (count($show_elements_graph['labels']) > 0) ) { if ($show_elements_graph['unit']) { - $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].' / '.__('Unit ').' '.$show_elements_graph['unit'].': '; + $name_legend = $show_elements_graph['labels'][$value['agent_module_id']].' / '.__('Unit ').' '.$show_elements_graph['unit'].': '; + $data_return['legend'][$key] = $show_elements_graph['labels'][$value['agent_module_id']].' / '.__('Unit ').' '.$show_elements_graph['unit'].': '; } else { - $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].': '; + $name_legend = $show_elements_graph['labels'][$value['agent_module_id']].': '; + $data_return['legend'][$key] = $show_elements_graph['labels'][$value['agent_module_id']].': '; } } else { if (strpos($key, 'baseline') !== false) { - if ($show_elements_graph['unit']) { - $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].' / '.__('Unit ').' '.$show_elements_graph['unit'].'Baseline '; + if ($value['unit']) { + $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].' / '.__('Unit ').' '.$value['unit'].'Baseline '; } else { $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].'Baseline '; } } else { - if ($show_elements_graph['unit']) { - $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].' / '.__('Unit ').' '.$show_elements_graph['unit'].': '; + if ($value['unit']) { + $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].' / '.__('Unit ').' '.$value['unit'].': '; } else { $name_legend = $data_return['legend'][$key] = $value['agent_alias'].' / '.$value['module_name'].': '; } @@ -3841,9 +3863,10 @@ function pandora_xhprof_display_result($key='', $method='link') /** * From a network with a mask remove the smallest ip and the highest * - * @param string address to identify the network. - * @param string mask to identify the mask network - * @return array or false with smallest ip and highest ip + * @param string $address Identify the network. + * @param string $mask Identify the mask network. + * + * @return array or false with smallest ip and highest ip. */ function range_ips_for_network($address, $mask) { @@ -3851,15 +3874,15 @@ function range_ips_for_network($address, $mask) return false; } - // convert ip addresses to long form + // Convert ip addresses to long form. $address_long = ip2long($address); $mask_long = ip2long($mask); - // caculate first usable address + // Calculate first usable address. $ip_host_first = ((~$mask_long) & $address_long); - $ip_first = (($address_long ^ $ip_host_first) + 1); + $ip_first = (($address_long ^ $ip_host_first)); - // caculate last usable address + // Calculate last usable address. $ip_broadcast_invert = ~$mask_long; $ip_last = (($address_long | $ip_broadcast_invert) - 1); @@ -3875,9 +3898,10 @@ function range_ips_for_network($address, $mask) /** * from two ips find out if there is such an ip * - * @param string ip ip wont validate - * @param string ip_lower - * @param string ip_upper + * @param string ip ip wont validate + * @param string ip_lower + * @param string ip_upper + * * @return boolean true or false if the ip is between the two ips */ function is_in_network($ip, $ip_lower, $ip_upper) @@ -3928,3 +3952,1337 @@ function mask2cidr($mask) $base = ip2long('255.255.255.255'); return (32 - log((($long ^ $base) + 1), 2)); } + + +function get_help_info($section_name) +{ + global $config; + // hd($section_name); + $user_language = get_user_language($id_user); + + $es = false; + $result = 'https://wiki.pandorafms.com/index.php?title=Pandora:Documentation_en:'; + if ($user_language == 'es') { + $es = true; + $result = 'https://wiki.pandorafms.com/index.php?title=Pandora:Documentation_es:'; + } + + switch ($section_name) { + case 'tactical_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Vista_t.C3.A1ctica'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Tactical_view'; + } + break; + + case 'group_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Vista_de_Grupos'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Group_view'; + } + break; + + case 'tree_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Vista_de_.C3.A1rbol'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#The_Tree_View'; + } + break; + + case 'monitor_detail_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Detalles_Monitores'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Monitor_Details'; + } + break; + + case 'tag_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Vista_de_etiquetas'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Tag_view'; + } + break; + + case 'alert_validation': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Detalles_de_Alertas'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Alert_Details'; + } + break; + + case 'agents_alerts_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Vista_de_agente_.2F_alerta'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Agent.2F_Alert_View'; + } + break; + + case 'agents_module_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Vista_de_agente_.2F_modulo'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Agents_.2F_Modules_View'; + } + break; + + case 'module_groups_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Vista_de_grupos_de_m.C3.B3dulos'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Module_Groups_View'; + } + break; + + case 'snmp_browser_view': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Navegador_SNMP_de_Pandora_FMS'; + } else { + $result .= 'Remote_Monitoring&printable=yes#Pandora_FMS_SNMP_MIB_Browser'; + } + break; + + case 'snmp_trap_generator_view': + if ($es) { + $result .= 'Monitorizacion_traps_SNMP&printable=yes#Generador_de_Traps'; + } else { + $result .= 'SNMP_traps_Monitoring&printable=yes#Trap_Generator'; + } + break; + + case 'real_time_view': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Gr.C3.A1ficas_Real-time'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Real-time_Graphs'; + } + break; + + case 'agent_status': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Detalles_del_agente'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Agent_Details'; + } + break; + + case 'agent_main_tab': + if ($es) { + $result .= 'Intro_Monitorizacion&printable=yes#Visualizaci.C3.B3n_del_agente'; + } else { + $result .= 'Intro_Monitoring&printable=yes#Agent_configuration_in_the_console_2'; + } + break; + + case 'alert_config': + if ($es) { + $result .= 'Alertas&printable=yes#Creaci.C3.B3n_de_una_Acci.C3.B3n'; + } else { + $result .= 'Alerts&printable=yes#Creating_an_Action'; + } + break; + + case 'alert_macros': + if ($es) { + $result .= 'Alertas&printable=yes#Macros_sustituibles_en_los_campos_Field1.2C_Field2.2C_Field3..._Field10'; + } else { + $result .= 'Alerts&printable=yes#Replaceable_Macros_within_Field_1_through_Field_10'; + } + break; + + case 'alerts_config': + if ($es) { + $result .= 'Alertas&printable=yes#Configuraci.C3.B3n_de_alertas_en_Pandora_FMS'; + } else { + $result .= 'Alerts&printable=yes#Alert_Configuration_in_Pandora_FMS'; + } + break; + + case 'alert_special_days': + if ($es) { + $result .= 'Alertas&printable=yes#Lista_de_d.C3.ADas_especiales'; + } else { + $result .= 'Alerts&printable=yes#List_of_special_days'; + } + break; + + case 'alerts': + if ($es) { + $result .= 'Politicas&printable=yes#Alertas'; + } else { + $result .= 'Policy&printable=yes#Alerts'; + } + break; + + case 'collections': + if ($es) { + $result .= 'Politicas&printable=yes#Colecciones_de_ficheros'; + } else { + $result .= 'Policy&printable=yes#File_Collections'; + } + break; + + case 'component_groups': + if ($es) { + $result .= 'Plantillas_y_Componentes&printable=yes#Grupos_de_componentes'; + } else { + $result .= 'Templates_and_components&printable=yes#Component_Groups'; + } + break; + + case 'configure_gis_map': + if ($es) { + $result .= 'Pandora_GIS&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'GIS&printable=yes#Introduction'; + } + break; + + case 'configure_gis_map_edit': + if ($es) { + $result .= 'Pandora_GIS&printable=yes#GIS_Maps'; + } else { + $result .= 'GIS&printable=yes#GIS_Maps'; + } + break; + + case 'event_alert': + if ($es) { + $result .= 'Eventos&printable=yes#Introducci.C3.B3n_2'; + } else { + $result .= 'Events&printable=yes#Introduction_2'; + } + break; + + case 'eventview': + if ($es) { + $result .= 'Eventos&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Events&printable=yes#Introduction'; + } + break; + + case 'export_server': + if ($es) { + $result .= 'ExportServer&printable=yes#A.C3.B1adir_un_servidor_de_destino'; + } else { + $result .= 'Export_Server&printable=yes#Adding_a_Target_Server'; + } + break; + + case 'external_alert': + if ($es) { + $result .= 'Politicas&printable=yes#Alertas_Externas'; + } else { + $result .= 'Policy&printable=yes#External_Alerts'; + } + break; + + case 'gis_tab': + if ($es) { + $result .= 'Pandora_GIS&printable=yes#Configuraci.C3.B3n_del_Agent_GIS'; + } else { + $result .= 'GIS&printable=yes#The_Agent.27s_GIS_Setup'; + } + break; + + case 'graph_builder': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Crear_Gr.C3.A1ficas_combinadas'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Creating_combined_graphs'; + } + break; + + case 'graph_editor': + if ($es) { + $result .= 'Presentacion_datos/visualizacion&printable=yes#Agregar_elementos_a_gr.C3.A1ficas_combinadas'; + } else { + $result .= 'Data_Presentation/Visualization&printable=yes#Adding_elements_to_combined_graphs'; + } + break; + + case 'dashboards_tab': + if ($es) { + $result .= 'Dashboard&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Dashboard&printable=yes#Introduction'; + } + break; + + case 'history_database': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Base_de_datos_hist.C3.B3rica'; + } else { + $result .= 'Console_Setup&printable=yes#The_History_Database'; + } + break; + + case 'inventory_tab': + if ($es) { + $result .= 'Inventario&printable=yes#M.C3.B3dulos_de_inventario'; + } else { + $result .= 'Inventory&printable=yes#Inventory_Modules'; + } + break; + + case 'ipam_list_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'IPAM&printable=yes#Introduction'; + } + break; + + case 'ipam_calculator_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Calculadora_de_subredes'; + } else { + $result .= 'IPAM&printable=yes#Subnetwork_calculator'; + } + break; + + case 'ipam_vlan_config_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Vlan_IPAM'; + } else { + $result .= 'IPAM&printable=yes#VLAN_IPAM'; + } + break; + + case 'ipam_vlan_statistics_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Estad.C3.ADsticas_IPAM_Vlan'; + } else { + $result .= 'IPAM&printable=yes#IPAM_VLAN_Stats'; + } + break; + + case 'ipam_vlan_wizard_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Wizard_IPAM_Vlan'; + } else { + $result .= 'IPAM&printable=yes#IPAM_VLAN_Wizard:'; + } + break; + + case 'ipam_supernet_config_tab': + if ($es) { + $result .= 'IPAM&printable=yes#IPAM_Supernet'; + } else { + $result .= 'IPAM&printable=yes#IPAM_Supernet'; + } + break; + + case 'ipam_supernet_map_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Mapa_Superred_IPAM'; + } else { + $result .= 'IPAM&printable=yes#IPAM_Supernet_Map'; + } + break; + + case 'ipam_supernet_statistics_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Estad.C3.ADsticas_IPAM_Superred'; + } else { + $result .= 'IPAM&printable=yes#IPAM_Supernet_Stats'; + } + break; + + case 'ipam_new_tab': + case 'ipam_edit_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Vista_de_edici.C3.B3n'; + } else { + $result .= 'IPAM&printable=yes#Edit_view'; + } + break; + + case 'ipam_massive_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Vista_Operaciones_masivas'; + } else { + $result .= 'IPAM&printable=yes#Massive_operations_view'; + } + break; + + case 'ipam_network_tab': + case 'ipam_force_tab': + if ($es) { + $result .= 'IPAM&printable=yes#Vista_de_iconos'; + } else { + $result .= 'IPAM&printable=yes#Icon_view'; + } + break; + + case 'macros_visual_maps': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Macros_en_las_consolas_visuales'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Macros_in_Visual_Consoles'; + } + break; + + case 'linked_map_status_calc': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Mapa_asociado'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Associated_Map'; + } + break; + + case 'main_tab': + if ($es) { + $result .= 'Intro_Monitorizacion&printable=yes#Configuraci.C3.B3n_del_agente_en_consola'; + } else { + $result .= 'Intro_Monitoring&printable=yes#Agent_configuration_in_the_console'; + } + break; + + case 'manage_alert_list': + if ($es) { + $result .= 'Alertas&printable=yes#Gestionar_alertas_desde_el_agente'; + } else { + $result .= 'Alerts&printable=yes#Managing_Alerts_from_within_the_Agent'; + } + break; + + case 'alert_scalate': + if ($es) { + $result .= 'Alertas&printable=yes#Escalado_de_alertas'; + } else { + $result .= 'Alerts&printable=yes#Scaling_Alerts'; + } + break; + + case 'map_builder_intro': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Introduction'; + } + break; + + case 'map_builder_favorite': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Consolas_visuales_favoritas'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Favorite_visual_consoles'; + } + break; + + case 'map_builder_template': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Plantillas_de_consolas_visuales'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Visual_Console_Templates'; + } + break; + + case 'map_builder_wizard': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Asistente_de_consola_visuales'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Wizard_Visual_Console'; + } + break; + + case 'module_linking': + if ($es) { + $result .= 'Politicas&printable=yes#Tipos_de_m.C3.B3dulos'; + } else { + $result .= 'Policy&printable=yes#Types_of_Modules'; + } + break; + + case 'network_map_enterprise_edit': + if ($es) { + $result .= 'Presentacion_datos/Mapas_de_red&printable=yes#Mapa_de_red_no_vac.C3.ADo'; + } else { + $result .= 'Data_Presentation/Network_Maps&printable=yes#Non_empty_network_map'; + } + break; + + case 'network_map_enterprise_list': + if ($es) { + $result .= 'Presentacion_datos/Mapas_de_red&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Data_Presentation/Network_Maps&printable=yes#Introduction'; + } + break; + + case 'network_map_enterprise_empty': + if ($es) { + $result .= 'Presentacion_datos/Mapas_de_red&printable=yes#Mapa_de_red_vac.C3.ADo'; + } else { + $result .= 'Data_Presentation/Network_Maps&printable=yes#Empty_network_map'; + } + break; + + case 'network_map_enterprise_view': + if ($es) { + $result .= 'Presentacion_datos/Mapas_de_red&printable=yes#Vista_de_un_mapa_de_red'; + } else { + $result .= 'Data_Presentation/Network_Maps&printable=yes#Network_map_view'; + } + break; + + case 'transactional_view': + if ($es) { + $result .= 'Monitorizacion_transaccional&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Transactional_Monitoring&printable=yes#Introduction'; + } + break; + + case 'pcap_filter': + if ($es) { + $result .= 'Netflow&printable=yes#Creaci.C3.B3n_del_filtro'; + } else { + $result .= 'Netflow&printable=yes#Filter_creation'; + } + break; + + case 'planned_downtime': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Introducci.C3.B3n_4'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Introduction_4'; + } + break; + + case 'planned_downtime_editor': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Creaci.C3.B3n_parada_planificada'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Create_a_scheduled_downtime'; + } + break; + + case 'plugin_definition': + if ($es) { + $result .= 'Anexo_Server_Plugins&printable=yes#Registro_manual_de_un_plugin_en_la_consola'; + } else { + $result .= 'Anexo_Server_plugins_developement&printable=yes#Plugin_manual_registration'; + } + break; + + case 'plugin_macros': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Macros_internas'; + } else { + $result .= 'Remote_Monitoring&printable=yes#Internal_Macros'; + } + break; + + case 'plugin_policy': + if ($es) { + $result .= 'Politicas&printable=yes#Plugins_de_agente'; + } else { + $result .= 'Policy&printable=yes#Agent_Plug_Ins'; + } + break; + + case 'policy_queue': + if ($es) { + $result .= 'Politicas&printable=yes#Gesti.C3.B3n_de_la_cola_de_pol.C3.ADticas'; + } else { + $result .= 'Policy&printable=yes#Policy_Queues_Management'; + } + break; + + case 'prediction_source_module': + if ($es) { + $result .= 'Monitorizacion_otra&printable=yes#Tipos_de_monitorizaci.C3.B3n_predictiva'; + } else { + $result .= 'Other_Monitoring&printable=yes#Types_of_predictive_monitoring'; + } + break; + + case 'wmi_module_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Monitorizaci.C3.B3n_de_Windows_remotos_con_WMI'; + } else { + $result .= 'Remote_Monitoring&printable=yes#Windows_Remote_Monitoring_with_WMI'; + } + break; + + case 'template_reporting_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#Introduction'; + } + break; + + case 'reporting_template_list_item_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Pesta.C3.B1a_List_Items'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_.27List_Items.27_Tab'; + } + break; + + case 'reporting_template_item_editor_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Pesta.C3.B1a_Item_editor'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_.27Item_Editor.27_Tab'; + } + break; + + case 'reporting_template_advanced_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Opciones_avanzadas_de_informe'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_Advanced_Options_Tab'; + } + break; + + case 'reporting_advanced_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Opciones_avanzadas_de_informe'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_Advanced_Options_Tab'; + } + break; + + case 'reporting_global_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Global'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_Global_Tab'; + } + break; + + case 'reporting_item_editor_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Pesta.C3.B1a_Item_editor'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_.27Item_Editor.27_Tab'; + } + break; + + case 'reporting_list_items_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Pesta.C3.B1a_List_Items'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_.27List_Items.27_Tab'; + } + break; + + case 'reporting_wizard_sla_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Wizard_SLA'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_SLA_Wizard_Tab'; + } + break; + + case 'reporting_wizard_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Wizard_general'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#The_Wizard_Tab'; + } + break; + + case 'response_macros': + if ($es) { + $result .= 'Eventos&printable=yes#Event_Responses_macros'; + } else { + $result .= 'Events&printable=yes#Event_Responses_macros'; + } + break; + + case 'events_responses_tab': + if ($es) { + $result .= 'Eventos&printable=yes#Introducci.C3.B3n_3'; + } else { + $result .= 'Events&printable=yes#Introduction_3'; + } + break; + + case 'servers': + if ($es) { + $result .= 'Interfaz&printable=yes#Gesti.C3.B3n_de_servidores'; + } else { + $result .= 'Interface&printable=yes#Server_management'; + } + break; + + case 'snmpwalk': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Navegador_SNMP_de_Pandora_FMS'; + } else { + $result .= 'Remote_Monitoring&printable=yes#Pandora_FMS_SNMP_MIB_Browser'; + } + break; + + case 'tags_config': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Sistemas_de_permisos_ampliados_mediante_etiquetas_.28tags.29'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Permission_system_extended_by_tags'; + } + break; + + case 'transactional_map_phases': + if ($es) { + $result .= 'Monitorizacion_transaccional&printable=yes#Creaci.C3.B3n_del_.C3.A1rbol_de_fases'; + } else { + $result .= 'Transactional_Monitoring&printable=yes#Creating_the_phase_tree'; + } + break; + + case 'transactional_map_phases_data': + if ($es) { + $result .= 'Monitorizacion_transaccional&printable=yes#Configuraci.C3.B3n_de_los_scripts_de_control'; + } else { + $result .= 'Transactional_Monitoring&printable=yes#Control_scripts_configuration'; + } + break; + + case 'wizard_reporting_tab': + if ($es) { + $result .= 'Presentacion_datos/Informes&printable=yes#Asistente_de_plantillas'; + } else { + $result .= 'Data_Presentation/Reports&printable=yes#Template_Wizard'; + } + break; + + case 'user_edit_notifications': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Configuraci.C3.B3n_de_notificaciones'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Notification_configuration'; + } + break; + + case 'view_services': + if ($es) { + $result .= 'Servicios&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Services&printable=yes#Introduction'; + } + break; + + case 'visual_console_editor_data_tab': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Creaci.C3.B3n_-_Datos_generales'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Creation_-_General_data'; + } + break; + + case 'visual_console_editor_editor_tab': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Creaci.C3.B3n_y_edici.C3.B3n_de_consolas_visuales'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Creation_and_edition_of_Visual_Consoles'; + } + break; + + case 'visual_console_editor_list_elements_tab': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Creaci.C3.B3n_-_lista_de_elementos'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Creation_-_List_of_Elements'; + } + break; + + case 'visual_console_editor_wizard_tab': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Creaci.C3.B3n_-_Wizard'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Creation_-_Wizard'; + } + break; + + case 'visual_console_editor_wizard_services_tab': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Creaci.C3.B3n_-_Wizard_de_Servicios'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Creation_-_Service_Wizard'; + } + break; + + case 'visual_console_tab': + if ($es) { + $result .= 'Presentacion_datos/Mapas_visuales&printable=yes#Mapa_asociado'; + } else { + $result .= 'Data_Presentation/Visual_Maps&printable=yes#Associated_Map'; + } + break; + + case 'view_created_map_services_tab': + if ($es) { + $result .= 'Servicios&printable=yes#Vista_de_mapa_de_servicio'; + } else { + $result .= 'Services&printable=yes#Service_Map_View'; + } + break; + + case 'view_created_services_tab': + if ($es) { + $result .= 'Servicios&printable=yes#Lista_simple_de_un_servicio_y_todos_los_elementos_que_contiene'; + } else { + $result .= 'Services&printable=yes#List-based_view_of_a_Service_and_its_Elements'; + } + break; + + case 'config_service_element_tab': + if ($es) { + $result .= 'Servicios&printable=yes#Configuraci.C3.B3n_de_elementos'; + } else { + $result .= 'Services&printable=yes#Element_Configuration'; + } + break; + + case 'config_service_tab': + if ($es) { + $result .= 'Servicios&printable=yes#Configuraci.C3.B3n_inicial'; + } else { + $result .= 'Services&printable=yes#Initial_Configuration'; + } + break; + + case 'other_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Otra_configuraci.C3.B3n'; + } else { + $result .= 'Console_Setup&printable=yes#Other_configuration'; + } + break; + + case 'services_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Configuraci.C3.B3n_servicios'; + } else { + $result .= 'Console_Setup&printable=yes#Services_configuration'; + } + break; + + case 'visual_consoles_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Configuraci.C3.B3n_de_las_consolas_visuales'; + } else { + $result .= 'Console_Setup&printable=yes#Visual_console_configuration'; + } + break; + + case 'charts_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Configuraci.C3.B3n_de_gr.C3.A1ficas'; + } else { + $result .= 'Console_Setup&printable=yes#Chart_settings'; + } + break; + + case 'front_and_text_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Configuraci.C3.B3n_de_Fuente_y_texto'; + } else { + $result .= 'Console_Setup&printable=yes#Font_and_text_settings'; + } + break; + + case 'gis_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Configuraci.C3.B3n_GIS'; + } else { + $result .= 'Console_Setup&printable=yes#GIS_configuration'; + } + break; + + case 'style_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Configuraci.C3.B3n_de_estilo'; + } else { + $result .= 'Console_Setup&printable=yes#Style_configuration'; + } + break; + + case 'behavoir_conf_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Configuraci.C3.B3n_del_comportamiento'; + } else { + $result .= 'Console_Setup&printable=yes#Behaviour_configuration'; + } + break; + + case 'setup_ehorus_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#eHorus'; + } else { + $result .= 'Console_Setup&printable=yes#EHorus'; + } + break; + + case 'diagnostic_tool_tab': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Diagnostic_tool'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Diagnostic_tool'; + } + break; + + case 'performance_metrics_tab': + if ($es) { + $result .= 'Optimizacion&printable=yes#Comprobaci.C3.B3n_del_fichero_my.ini.2Fcnf'; + } else { + $result .= 'Optimization&printable=yes#Check_my.ini.2Fcnf_settings'; + } + break; + + case 'db_status_tab': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#DB_Schema_Check'; + } else { + $result .= 'Managing_and_Administration&printable=yes#DB_Schema_Check'; + } + break; + + case 'database_backup_utility_tab': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Backup'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Backup'; + } + break; + + case 'update_manager_offline_tab': + if ($es) { + $result .= 'Actualizacion&printable=yes#Actualizaciones_.22offline.22'; + } else { + $result .= 'Anexo_Upgrade&printable=yes#.22Offline.22_updates'; + } + break; + + case 'update_manager_online_tab': + if ($es) { + $result .= 'Actualizacion&printable=yes#Actualizaciones_.22online.22'; + } else { + $result .= 'Anexo_Upgrade&printable=yes#.22Online.22_updates'; + } + break; + + case 'others_database_maintenance_options_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Otros'; + } else { + $result .= 'Console_Setup&printable=yes#Others'; + } + break; + + case 'database_maintenance_options_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Opciones_de_mantenimiento_de_la_base_de_datos'; + } else { + $result .= 'Console_Setup&printable=yes#Database_maintenance_options'; + } + break; + + case 'database_maintenance_status_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Estado_del_mantenimiento_de_las_bases_de_datos'; + } else { + $result .= 'Console_Setup&printable=yes#Database_maintenance_status'; + } + break; + + case 'historical_database_maintenance_options_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Opciones_de_mantenimiento_de_la_base_de_datos_hist.C3.B3rica'; + } else { + $result .= 'Console_Setup&printable=yes#Historical_database_maintenance_options'; + } + break; + + case 'setup_enterprise_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#Enterprise'; + } else { + $result .= 'Console_Setup&printable=yes#Features_of_the_Enterprise_Version'; + } + break; + + case 'setup_general_tab': + if ($es) { + $result .= 'Configuracion_Consola&printable=yes#General_Setup'; + } else { + $result .= 'Console_Setup&printable=yes#General_Setup'; + } + break; + + case 'export_target_tab': + if ($es) { + $result .= 'ExportServer&printable=yes#A.C3.B1adir_un_servidor_de_destino'; + } else { + $result .= 'Export_Server&printable=yes#Adding_a_Target_Server'; + } + break; + + case 'servers_ha_clusters_tab': + if ($es) { + $result .= 'HA&printable=yes#Alta_disponibilidad_del_Servidor_de_Datos'; + } else { + $result .= 'HA&printable=yes#HA_of_Data_Server'; + } + break; + + case 'plugins_tab': + if ($es) { + $result .= 'Anexo_Agent_Plugins&printable=yes#Caracter.C3.ADsticas_b.C3.A1sicas_de_plugin_de_agente'; + } else { + $result .= 'Anexo_Agent_Plugins&printable=yes#Basic_Features_of_the_Agent_Plugin'; + } + break; + + case 'create_agent': + if ($es) { + $result .= 'Intro_Monitorizacion&printable=yes#Configuraci.C3.B3n_del_agente_en_consola'; + } else { + $result .= 'Intro_Monitoring&printable=yes#Agent_configuration_in_the_console'; + } + break; + + case 'agent_snmp_explorer_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Wizard_SNMP'; + } else { + $result .= 'Remote_Monitoring&printable=yes#SNMP_Wizard'; + } + break; + + case 'agent_snmp_interfaces_explorer_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#SNMP_Interfaces_wizard'; + } else { + $result .= 'Remote_Monitoring&printable=yes#SNMP_Interface_Wizard'; + } + break; + + case 'agent_snmp_wmi_explorer_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Wizard_WMI'; + } else { + $result .= 'Remote_Monitoring&printable=yes#WMI_Wizard'; + } + break; + + case 'group_list_tab': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Introducci.C3.B3n_2'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Introduction_2'; + } + break; + + case 'acl_setup_tab': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Introducci.C3.B3n_3'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Introduction_3'; + } + break; + + case 'profile_tab': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Perfiles_en_Pandora_FMS'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Profiles_in_Pandora_FMS'; + } + break; + + case 'configure_profiles_tab': + if ($es) { + $result .= 'Gestion_y_Administracion&printable=yes#Perfiles_en_Pandora_FMS'; + } else { + $result .= 'Managing_and_Administration&printable=yes#Profiles_in_Pandora_FMS'; + } + break; + + case 'network_component_tab': + if ($es) { + $result .= 'Plantillas_y_Componentes&printable=yes#Componentes_de_red'; + } else { + $result .= 'Templates_and_components&printable=yes#Network_Components'; + } + break; + + case 'local_component_tab': + if ($es) { + $result .= 'Plantillas_y_Componentes&printable=yes#Componentes_locales'; + } else { + $result .= 'Templates_and_components&printable=yes#Local_Components'; + } + break; + + case 'module_template_tab': + if ($es) { + $result .= 'Plantillas_y_Componentes&printable=yes#Plantillas_de_m.C3.B3dulos'; + } else { + $result .= 'Templates_and_components&printable=yes#Module_Templates'; + } + break; + + case 'agent_autoconf_tab': + if ($es) { + $result .= 'Configuracion_Agentes&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Configuration_Agents&printable=yes#Introduction'; + } + break; + + case 'policies_management_tab': + if ($es) { + $result .= 'Politicas&printable=yes#Introducci.C3.B3n'; + } else { + $result .= 'Policy&printable=yes#Introduction'; + } + break; + + case 'massive_agents_tab': + if ($es) { + $result .= 'Operaciones_Masivas&printable=yes#Edici.C3.B3n_masiva_de_agentes'; + } else { + $result .= 'Massive_Operations&printable=yes#Agent_massive_edition'; + } + break; + + case 'massive_modules_tab': + if ($es) { + $result .= 'Operaciones_Masivas&printable=yes#Edici.C3.B3n_masiva_de_m.C3.B3dulos'; + } else { + $result .= 'Massive_Operations&printable=yes#Modules_massive_edition'; + } + break; + + case 'massive_policies_tab': + if ($es) { + $result .= 'Operaciones_Masivas&printable=yes#Editar_m.C3.B3dulos_de_pol.C3.ADticas_masivamente'; + } else { + $result .= 'Massive_Operations&printable=yes#Edit_policy_modules_massively'; + } + break; + + case 'alert_templates_tab': + if ($es) { + $result .= 'Alertas&printable=yes#Introducci.C3.B3n_4'; + } else { + $result .= 'Alerts&printable=yes#Introduction_4'; + } + break; + + case 'configure_alert_template_step_1': + if ($es) { + $result .= 'Alertas&printable=yes#Paso_1:_General'; + } else { + $result .= 'Alerts&printable=yes#Step_1:_General'; + } + break; + + case 'configure_alert_template_step_2': + if ($es) { + $result .= 'Alertas&printable=yes#Paso_2:_Condiciones'; + } else { + $result .= 'Alerts&printable=yes#Step_2:_Conditions'; + } + break; + + case 'configure_alert_template_step_3': + if ($es) { + $result .= 'Alertas&printable=yes#Paso_3:_Campos_avanzados'; + } else { + $result .= 'Alerts&printable=yes#Step_3:_Advanced_fields'; + } + break; + + case 'alerts_action': + if ($es) { + $result .= 'Alertas&printable=yes#Introducci.C3.B3n_3'; + } else { + $result .= 'Alerts&printable=yes#Introduction_3'; + } + break; + + case 'alerts_command_tab': + if ($es) { + $result .= 'Alertas&printable=yes#Introducci.C3.B3n_2'; + } else { + $result .= 'Alerts&printable=yes#Introduction_2'; + } + break; + + case 'alerts_config_command_tab': + if ($es) { + $result .= 'Alertas&printable=yes#Creaci.C3.B3n_de_un_comando_para_una_alerta'; + } else { + $result .= 'Alerts&printable=yes#Command_Creation_for_an_Alert'; + } + break; + + case 'configure_alert_event_step_1': + if ($es) { + $result .= 'Eventos&printable=yes#Creaci.C3.B3n_alerta_de_evento'; + } else { + $result .= 'Events&printable=yes#Event_Alert_creation'; + } + break; + + case 'configure_event_rule_tab': + if ($es) { + $result .= 'Eventos&printable=yes#Creaci.C3.B3n_alerta_de_evento'; + } else { + $result .= 'Events&printable=yes#Event_Alert_creation'; + } + break; + + case 'snmp_alert_overview_tab': + if ($es) { + $result .= 'Monitorizacion_traps_SNMP&printable=yes#Introducci.C3.B3n_2'; + } else { + $result .= 'SNMP_traps_Monitoring&printable=yes#Introduction_2'; + } + break; + + case 'snmp_alert_update_tab': + if ($es) { + $result .= 'Monitorizacion_traps_SNMP&printable=yes#A.C3.B1adir_una_alerta'; + } else { + $result .= 'SNMP_traps_Monitoring&printable=yes#Alert_Creation'; + } + break; + + case 'sound_console_tab': + if ($es) { + $result .= 'Eventos&printable=yes#Uso'; + } else { + $result .= 'Events&printable=yes#Use'; + } + break; + + case 'local_module_tab': + if ($es) { + $result .= 'Intro_Monitorizacion&printable=yes#Par.C3.A1metros_comunes'; + } else { + $result .= 'Intro_Monitoring&printable=yes#Common_Parameters'; + } + break; + + case 'local_module': + if ($es) { + $result .= 'Operacion&printable=yes#Tipos_de_m.C3.B3dulos'; + } else { + $result .= 'Operations&printable=yes#Types_of_Modules'; + } + break; + + case 'data_server_module_tab': + if ($es) { + $result .= 'Operacion&printable=yes#Tipos_de_m.C3.B3dulos'; + } else { + $result .= 'Operations&printable=yes#Types_of_Modules'; + } + break; + + case 'network_module_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Monitorizaci.C3.B3n_ICMP'; + } else { + $result .= 'Remote_Monitoring&printable=yes#ICMP_Monitoring'; + } + break; + + case 'wux_console': + if ($es) { + $result .= 'Monitorizacion_Usuario&printable=yes#Crear_un_m.C3.B3dulo_de_an.C3.A1lisis_web_en_Pandora_FMS_Console'; + } else { + $result .= 'User_Monitorization&printable=yes#Creating_a_Web_Analytics_module_in_Pandora_FMS_Console'; + } + break; + + case 'gis_basic_configurations_tab': + if ($es) { + $result .= 'Pandora_GIS&printable=yes#Configuraci.C3.B3n_B.C3.A1sica'; + } else { + $result .= 'GIS&printable=yes#Basic_Configuration'; + } + break; + + case 'gis_map_connection_tab': + if ($es) { + $result .= 'Pandora_GIS&printable=yes#Mapas_Open_Street'; + } else { + $result .= 'GIS&printable=yes#Open_Street_Maps'; + } + break; + + case 'icmp_module_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Monitorizaci.C3.B3n_ICMP'; + } else { + $result .= 'Remote_Monitoring&printable=yes#ICMP_Monitoring'; + } + break; + + case 'snmp_module_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Monitorizando_con_m.C3.B3dulos_de_red_tipo_SNMP'; + } else { + $result .= 'Remote_Monitoring&printable=yes#Monitoring_by_Network_Modules_with_SNMP'; + } + break; + + case 'tcp_module_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Monitorizaci.C3.B3n_TCP'; + } else { + $result .= 'Remote_Monitoring&printable=yes#TCP_Monitoring'; + } + break; + + case 'webserver_module_tab': + if ($es) { + $result .= 'Monitorizacion_web&printable=yes#Creaci.C3.B3n_de_m.C3.B3dulos_web'; + } else { + $result .= 'Web_Monitoring&printable=yes#Creating_Web_Modules'; + } + break; + + case 'wmi_query_tab': + if ($es) { + $result .= 'Monitorizacion_remota&printable=yes#Monitorizaci.C3.B3n_de_Windows_remotos_con_WMI'; + } else { + $result .= 'Remote_Monitoring&printable=yes#Windows_Remote_Monitoring_with_WMI'; + } + break; + + case 'module_type_tab': + if ($es) { + $result .= 'Operacion&printable=yes#Tipos_de_m.C3.B3dulos'; + } else { + $result .= ''; + } + break; + + case 'render_view_tab': + if ($es) { + $result .= 'Pandora_GIS&printable=yes#Operaci.C3.B3n'; + } else { + $result .= 'GIS&printable=yes#Operation'; + } + break; + } + + // hd($result); + return $result; +} diff --git a/pandora_console/include/functions_agents.php b/pandora_console/include/functions_agents.php index 0ac9bb876f..fd5e922e32 100644 --- a/pandora_console/include/functions_agents.php +++ b/pandora_console/include/functions_agents.php @@ -328,14 +328,21 @@ function agents_get_alerts_simple($id_agent=false, $filter='', $options=false, $ * * By default, it will return all the agents where the user has reading access. * - * @param array filter options in an indexed array. See - * db_format_array_where_clause_sql() - * @param array Fields to get. - * @param string Access needed in the agents groups. - * @param array $order The order of agents, by default is upward for field nombre. - * @param boolean $return Whether to return array with agents or false, or sql string statement + * @param array $filter Filter options in an indexed array. + * See db_format_array_where_clause_sql(). + * @param array $fields DB fields to get. + * @param string $access ACL level needed in the agents groups. + * @param array $order The order of agents, by default is upward + * for field nombre. + * @param boolean $return Whether to return array with agents or + * the sql string statement. + * @param boolean $disabled_agent Whether to return only the enabled agents + * or not. + * @param boolean $use_meta_table Whether to use the regular or the meta table + * to retrieve the agents. * - * @return mixed An array with all alerts defined for an agent or false in case no allowed groups are specified. + * @return mixed An array with all alerts defined for an agent + * or false in case no allowed groups are specified. */ function agents_get_agents( $filter=false, @@ -346,7 +353,8 @@ function agents_get_agents( 'order' => 'ASC', ], $return=false, - $disabled_agent=0 + $disabled_agent=0, + $use_meta_table=false ) { global $config; @@ -563,11 +571,15 @@ function agents_get_agents( ); } + $table_name = ($use_meta_table === true) ? 'tmetaconsole_agent' : 'tagente'; $sql = sprintf( 'SELECT DISTINCT %s - FROM tagente LEFT JOIN tagent_secondary_group ON tagent_secondary_group.id_agent=tagente.id_agente + FROM `%s` tagente + LEFT JOIN tagent_secondary_group + ON tagent_secondary_group.id_agent=tagente.id_agente WHERE %s %s', implode(',', $fields), + $table_name, $where, $order ); @@ -578,6 +590,7 @@ function agents_get_agents( } $sql = sprintf('%s %s', $sql, $limit_sql); + if ($return) { return $sql; } else { @@ -2202,7 +2215,7 @@ function agents_delete_agent($id_agents, $disableACL=false) // Delete agent in networkmap enterprise if (enterprise_installed()) { - enterprise_include_once('include/functions_pandora_networkmap.php'); + enterprise_include_once('include/functions_networkmap.php'); networkmap_delete_nodes_by_agent([$id_agent]); } diff --git a/pandora_console/include/functions_alerts.php b/pandora_console/include/functions_alerts.php index 6bfe709c32..011acafae9 100644 --- a/pandora_console/include/functions_alerts.php +++ b/pandora_console/include/functions_alerts.php @@ -664,6 +664,7 @@ function alerts_get_alert_templates_types() $types['unknown'] = __('Unknown status'); $types['onchange'] = __('On Change'); $types['always'] = __('Always'); + $types['not_normal'] = __('Not normal status'); return $types; } @@ -680,7 +681,7 @@ function alerts_get_alert_templates_type_name($type) { $types = alerts_get_alert_templates_types(); - if (! isset($type[$type])) { + if (!isset($types[$type])) { return __('Unknown'); } diff --git a/pandora_console/include/functions_api.php b/pandora_console/include/functions_api.php index e57dd31013..3c6cdbf595 100644 --- a/pandora_console/include/functions_api.php +++ b/pandora_console/include/functions_api.php @@ -4584,7 +4584,7 @@ function api_set_new_snmp_component($id, $thrash1, $other, $thrash2) return; } - $id = network_components_create_network_component($id, $other['data'][0], $other['data'][25], $values); + $id = network_components_create_network_component($id, $other['data'][0], $other['data'][26], $values); if (!$id) { returnError('error_set_new_snmp_component', 'Error creating SNMP component.'); @@ -11608,7 +11608,9 @@ function api_set_add_event_comment($id, $thrash2, $other, $thrash3) global $config; if (defined('METACONSOLE')) { - return; + $meta = true; + } else { + $meta = $other['data'][1]; } if (!check_acl($config['id_user'], 0, 'EW')) { @@ -11620,8 +11622,7 @@ function api_set_add_event_comment($id, $thrash2, $other, $thrash3) returnError('error_parameter', 'Error in the parameters.'); return; } else if ($other['type'] == 'array') { - $comment = io_safe_input($other['data'][0]); - $meta = $other['data'][1]; + $comment = $other['data'][0]; $history = $other['data'][2]; $status = events_comment( @@ -14684,3 +14685,365 @@ function api_set_reset_agent_counts($id, $thrash1, $thrash2, $thrash3) } } + + +/** + * Functions por get all user to new feature for Carrefour + * It depends of returnType, the method will return csv or json data + * + * @param string $thrash1 don't use + * @param string $thrash2 don't use + * @param array $other don't use + * *@param string $returnType + * Example: + * api.php?op=get&op2=list_all_user&return_type=json&apipass=1234&user=admin&pass=pandora + * @return + */ + + +function api_get_list_all_user($thrash1, $thrash2, $other, $returnType) +{ + global $config; + + if (!check_acl($config['id_user'], 0, 'AR')) { + returnError('forbidden', 'string'); + return; + } + + $sql = 'SELECT + tup.id_usuario AS user_id, + tu.fullname AS fullname, + tp.id_perfil AS profile_id, + tup.id_up AS id_up, + tp.name AS profile_name, + tup.id_grupo AS group_id, + tgp.nombre AS group_name + FROM tperfil tp + INNER JOIN tusuario_perfil tup + ON tp.id_perfil = tup.id_perfil + LEFT OUTER JOIN tgrupo tgp + ON tup.id_grupo = tgp.id_grupo + LEFT OUTER JOIN tusuario tu + ON tu.id_user = tup.id_usuario'; + + $users = db_get_all_rows_sql($sql); + + $i = 0; + + foreach ($users as $up) { + $group_name = $up['group_name']; + if ($up['group_name'] === null) { + $group_name = 'All'; + } + + $values[$i] = [ + 'id_usuario' => $up['user_id'], + 'fullname' => $up['fullname'], + 'id_up' => $up['id_up'], + 'id_perfil' => $up['profile_id'], + 'perfil_name' => $up['profile_name'], + 'id_grupo' => $up['group_id'], + 'group_name' => $group_name, + ]; + $i += 1; + } + + if ($values === false) { + returnError('Error_user', __('Users could not be found.')); + return; + } + + $data = [ + 'type' => 'array', + 'data' => $values, + ]; + + returnData($returnType, $data, ';'); +} + + +/** + * Funtion for get all info user to new feature for Carrefour + * It depends of returnType, the method will return csv or json data + * + * @param string $thrash1 don't use + * @param string $thrash2 don't use + * @param array $other other[0] = user database + * @param string $returnType + * Example + * api.php?op=get&op2=info_user_name&return_type=json&other=admin&other_mode=url_encode_separator_|&apipass=1234&user=admin&pass=pandora + * + * @return + */ + + +function api_get_info_user_name($thrash1, $thrash2, $other, $returnType) +{ + global $config; + + if (!check_acl($config['id_user'], 0, 'AR')) { + returnError('forbidden', 'string'); + return; + } + + $sql = sprintf( + 'SELECT tup.id_usuario AS user_id, + tu.fullname AS fullname, + tup.id_up AS id_up, + tp.id_perfil AS profile_id, + tp.name AS profile_name, + tup.id_grupo AS group_id, + tg.nombre AS group_name + FROM tperfil tp + INNER JOIN tusuario_perfil tup + ON tp.id_perfil = tup.id_perfil + LEFT OUTER JOIN tgrupo tg + ON tup.id_grupo = tg.id_grupo + LEFT OUTER JOIN tusuario tu + ON tu.id_user = tup.id_usuario + WHERE tup.id_usuario = "%s"', + io_safe_output($other['data'][0]) + ); + + $user_profile = db_get_all_rows_sql($sql); + + $i = 0; + + foreach ($user_profile as $up) { + $group_name = $up['group_name']; + if ($up['group_name'] === null) { + $group_name = 'All'; + } + + $values[$i] = [ + 'id_usuario' => $up['user_id'], + 'fullname' => $up['fullname'], + 'id_up' => $up['id_up'], + 'id_perfil' => $up['profile_id'], + 'perfil_name' => $up['profile_name'], + 'id_grupo' => $up['group_id'], + 'group_name' => $group_name, + ]; + $i += 1; + } + + $data = [ + 'type' => 'array', + 'data' => $values, + ]; + + returnData($returnType, $data, ';'); +} + + +/** + * Function for get user from a group to new feature for Carrefour. + * It depends of returnType, the method will return csv or json data. + * + * @param string $thrash1 don't use + * @param string $thrash2 don't use + * @param array $other + * $other[0] = id group + * $other[1] = is disabled or not + * @param string $returnType + * Example + * api.php?op=get&op2=filter_user_group&return_type=json&other=0|0&other_mode=url_encode_separator_|&apipass=1234&user=admin&pass=pandora + * + * @return + */ + + +function api_get_filter_user_group($thrash1, $thrash2, $other, $returnType) +{ + global $config; + + if (!check_acl($config['id_user'], 0, 'AR')) { + returnError('forbidden', 'string'); + return; + } + + $filter = ''; + + if ($other['data'][0] !== '' && $other['data'][1] !== '') { + $filter = 'WHERE tup.id_grupo = '.$other['data'][0].' AND tu.disabled = '.$other['data'][1].''; + } else if ($other['data'][0] !== '') { + $filter = 'WHERE tup.id_grupo = '.$other['data'][0].''; + } else if ($other['data'][1] !== '') { + $filter = 'WHERE tu.disabled = '.$other['data'][1].''; + } + + $sql = sprintf( + 'SELECT DISTINCT + tup.id_usuario AS user_id, + tu.fullname AS fullname, + tup.id_up AS id_up, + tp.id_perfil AS profile_id, + tp.name AS profile_name, + tup.id_grupo AS group_id, + tg.nombre AS group_name + FROM tperfil tp + INNER JOIN tusuario_perfil tup + ON tp.id_perfil = tup.id_perfil + LEFT OUTER JOIN tgrupo tg + ON tup.id_grupo = tg.id_grupo + LEFT OUTER JOIN tusuario tu + ON tu.id_user = tup.id_usuario + '.$filter.'' + ); + + $filter_user = db_get_all_rows_sql($sql); + + $i = 0; + + foreach ($filter_user as $up) { + $group_name = $up['group_name']; + if ($up['group_name'] === null) { + $group_name = 'All'; + } + + $values[$i] = [ + 'id_usuario' => $up['user_id'], + 'fullname' => $up['fullname'], + 'id_up' => $up['id_up'], + 'id_perfil' => $up['profile_id'], + 'perfil_name' => $up['profile_name'], + 'id_grupo' => $up['group_id'], + 'group_name' => $group_name, + ]; + $i += 1; + } + + $data = [ + 'type' => 'array', + 'data' => $values, + ]; + + returnData($returnType, $data, ';'); + +} + + +/** + * Function for delete an user permission for Carrefour new feature + * The return of this function its only a message + * + * @param string $thrash1 don't use + * @param string $thrash2 don't use + * @param array $other + * $other[0] = id up + * @param string $returnType + * Example + * api.php?op=set&op2=delete_user_permission&return_type=json&other=user|2&other_mode=url_encode_separator_|&apipass=1234&user=admin&pass=pandora + * + * @return void + */ + + +function api_set_delete_user_permission($thrash1, $thrash2, $other, $returnType) +{ + global $config; + + if (!check_acl($config['id_user'], 0, 'AW')) { + returnError('forbidden', 'string'); + return; + } + + if ($other['data'][0] != '') { + $values = [ + 'id_up' => io_safe_output($other['data'][0]), + ]; + } else { + returnError('Error_delete', __('User profile could not be deleted.')); + return; + } + + $deleted_permission = db_process_sql_delete('tusuario_perfil', $values); + + if ($deleted_permission == false) { + returnError('Error_delete', __('User profile could not be deleted.')); + return; + } + + $data = [ + 'type' => 'string', + 'data' => $deleted_permission, + ]; + + returnData('string', ['type' => 'string', 'data' => $data]); +} + + +/** + * Function for add permission a user to a group for Carrefour new feature + * It depends of returnType, the method will return csv or json data + * + * @param string $thrash1 don't use + * @param string $thrash2 don't use + * @param array $other other[0] = user database + * other[1] = id group + * other[2] = id profile + * other[3] = no_hierarchy ( 0 or 1, if empty = 0) + * other[4] = id from tusuario_perfil table (optional) + * * @param string $returnType + * Example + * api.php?op=set&op2=add_permission_user_to_group&return_type=json&other=admin|0|1|1|20&other_mode=url_encode_separator_|&apipass=1234&user=admin&pass=pandora + * + * @return void + */ + + +function api_set_add_permission_user_to_group($thrash1, $thrash2, $other, $returnType) +{ + global $config; + + if (!check_acl($config['id_user'], 0, 'AW')) { + returnError('forbidden', 'string'); + return; + } + + $sql = 'SELECT id_up + FROM tusuario_perfil + WHERE id_up = '.$other['data'][4].''; + + $exist_profile = db_get_value_sql($sql); + + if ($other['data'][3] < 0 || $other['data'][3] > 1) { + returnError('Error_insert', __('User profile could not be available.')); + return; + } + + if ($other['data'][3] == null) { + $other['data'][3] = 0; + } + + $values = [ + 'id_usuario' => $other['data'][0], + 'id_perfil' => $other['data'][2], + 'id_grupo' => $other['data'][1], + 'no_hierarchy' => $other['data'][3], + 'assigned_by' => $config['id_user'], + 'id_policy' => 0, + 'tags' => '', + + ]; + + $where_id_up = ['id_up' => $other['data'][4]]; + if ($exist_profile === $other['data'][4] && $where_id_up !== null) { + $sucessfull_insert = db_process_sql_update('tusuario_perfil', $values, $where_id_up); + } else { + $sucessfull_insert = db_process_sql_insert('tusuario_perfil', $values); + } + + if ($sucessfull_insert == false) { + returnError('Error_insert', __('User profile could not be available.')); + return; + } + + $data = [ + 'type' => 'array', + 'data' => $values, + ]; + + returnData($returnType, $data, ';'); + +} diff --git a/pandora_console/include/functions_clippy.php b/pandora_console/include/functions_clippy.php index f3bc6a5967..67999a75e0 100644 --- a/pandora_console/include/functions_clippy.php +++ b/pandora_console/include/functions_clippy.php @@ -296,7 +296,7 @@ function clippy_context_help($help=null) $code = str_replace('{clippy}', '#'.$id, $code); $code = str_replace('{clippy_obj}', 'intro_'.$id, $code); - $return = $code.'
        '.html_print_image( + $return = $code.' diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php index 9ba9ace3ff..488d6a28cd 100644 --- a/pandora_console/include/functions_config.php +++ b/pandora_console/include/functions_config.php @@ -224,6 +224,10 @@ function config_update_config() $error_update[] = __('Enable Netflow'); } + if (!config_update_value('activate_nta', (bool) get_parameter_switch('activate_nta'))) { + $error_update[] = __('Enable Network Traffic Analyzer'); + } + $timezone = (string) get_parameter('timezone'); if ($timezone != '') { if (!config_update_value('timezone', $timezone)) { @@ -266,7 +270,7 @@ function config_update_config() $error_update[] = __('Referer security'); } - if (!config_update_value('event_storm_protection', get_parameter('event_storm_protection'))) { + if (!config_update_value('event_storm_protection', get_parameter('event_storm_protection', 0))) { $error_update[] = __('Event storm protection'); } @@ -600,6 +604,10 @@ function config_update_config() config_update_value('ldap_save_password', 1); } + if (!config_update_value('ldap_save_profile', get_parameter('ldap_save_profile'))) { + $error_update[] = __('Save profile'); + } + if (!config_update_value('rpandora_server', get_parameter('rpandora_server'))) { $error_update[] = __('MySQL host'); } @@ -756,6 +764,10 @@ function config_update_config() $error_update[] = __('Max. days before delete old messages'); } + if (!config_update_value('delete_old_network_matrix', get_parameter('delete_old_network_matrix'))) { + $error_update[] = __('Max. days before delete old network matrix data'); + } + if (!config_update_value('max_graph_container', get_parameter('max_graph_container'))) { $error_update[] = __('Graph container - Max. Items'); } @@ -896,6 +908,14 @@ function config_update_config() $error_update[] = __('Custom networkmap center logo'); } + if (!config_update_value('custom_title_header', (string) get_parameter('custom_title_header'))) { + $error_update[] = __('Custom title header'); + } + + if (!config_update_value('custom_subtitle_header', (string) get_parameter('custom_subtitle_header'))) { + $error_update[] = __('Custom subtitle header'); + } + if (!config_update_value('custom_title1_login', (string) get_parameter('custom_title1_login'))) { $error_update[] = __('Custom title1 login'); } @@ -960,7 +980,15 @@ function config_update_config() $error_update[] = __('Custom support url'); } - if (!config_update_value('vc_refr', get_parameter('vc_refr'))) { + if (!config_update_value('legacy_vc', (int) get_parameter('legacy_vc'))) { + $error_update[] = __('Use the legacy Visual Console'); + } + + if (!config_update_value('vc_default_cache_expiration', (int) get_parameter('vc_default_cache_expiration'))) { + $error_update[] = __("Default expiration of the Visual Console item's cache"); + } + + if (!config_update_value('vc_refr', (int) get_parameter('vc_refr'))) { $error_update[] = __('Default interval for refresh on Visual Console'); } @@ -1325,10 +1353,14 @@ function config_update_config() break; case 'ehorus': - if (!config_update_value('ehorus_enabled', (int) get_parameter('ehorus_enabled', $config['ehorus_enabled']))) { + if (!config_update_value('ehorus_enabled', (int) get_parameter('ehorus_enabled', 0))) { $error_update[] = __('Enable eHorus'); } + if (!config_update_value('ehorus_user_level_conf', (int) get_parameter('ehorus_user_level_conf', 0))) { + $error_update[] = __('eHorus user login'); + } + if (!config_update_value('ehorus_user', (string) get_parameter('ehorus_user', $config['ehorus_user']))) { $error_update[] = __('eHorus user'); } @@ -1547,6 +1579,10 @@ function config_process_config() config_update_value('delete_old_messages', 21); } + if (!isset($config['delete_old_network_matrix'])) { + config_update_value('delete_old_network_matrix', 10); + } + if (!isset($config['max_graph_container'])) { config_update_value('max_graph_container', 10); } @@ -1792,7 +1828,7 @@ function config_process_config() } if (!isset($config['custom_logo'])) { - config_update_value('custom_logo', 'pandora_logo_head_green.png'); + config_update_value('custom_logo', 'pandora_logo_head_4.png'); } if (!isset($config['custom_logo_collapsed'])) { @@ -1827,6 +1863,14 @@ function config_process_config() config_update_value('custom_mobile_console_logo', ''); } + if (!isset($config['custom_title_header'])) { + config_update_value('custom_title_header', __('Pandora FMS')); + } + + if (!isset($config['custom_subtitle_header'])) { + config_update_value('custom_subtitle_header', __('the Flexible Monitoring System')); + } + if (!isset($config['custom_title1_login'])) { config_update_value('custom_title1_login', __('PANDORA FMS')); } @@ -1975,6 +2019,10 @@ function config_process_config() config_update_value('activate_netflow', 0); } + if (!isset($config['activate_nta'])) { + config_update_value('activate_nta', 0); + } + if (!isset($config['netflow_path'])) { if ($is_windows) { $default = 'C:\\PandoraFMS\\Pandora_Server\\data_in\\netflow'; @@ -2379,10 +2427,22 @@ function config_process_config() config_update_value('dbtype', 'mysql'); } + if (!isset($config['legacy_vc'])) { + config_update_value('legacy_vc', 1); + } + + if (!isset($config['vc_default_cache_expiration'])) { + config_update_value('vc_default_cache_expiration', 60); + } + if (!isset($config['vc_refr'])) { config_update_value('vc_refr', 300); } + if (!isset($config['vc_line_thickness'])) { + config_update_value('vc_line_thickness', 2); + } + if (!isset($config['agent_size_text_small'])) { config_update_value('agent_size_text_small', 18); } @@ -2684,9 +2744,7 @@ function config_check() if (enterprise_installed() === false) { $supervisor = new ConsoleSupervisor(false); $supervisor->run(); - } else if ($config['cron_last_run'] == 0 - || (get_system_time() - $config['cron_last_run']) > 3600 - ) { + } else { $supervisor = new ConsoleSupervisor(false); $supervisor->runBasic(); } @@ -2694,6 +2752,31 @@ function config_check() } +/** + * Retrieves base url stored for Update Manager. + * + * @return string URL. + */ +function get_um_url() +{ + global $config; + + if (isset($config['url_update_manager'])) { + $url = $config['url_update_manager']; + $url = substr($url, 0, (strlen($url) - strpos(strrev($url), '/'))); + } else { + $url = 'https://licensing.artica.es/pandoraupdate7/'; + config_update_value( + 'url_update_manager', + 'https://licensing.artica.es/pandoraupdate7/server.php' + ); + } + + return $url; + +} + + /** * Return in bytes * diff --git a/pandora_console/include/functions_cron.php b/pandora_console/include/functions_cron.php index e56a3d4a52..6a79096175 100644 --- a/pandora_console/include/functions_cron.php +++ b/pandora_console/include/functions_cron.php @@ -1,34 +1,51 @@ $module_id] - ); + $module_interval = db_get_value( + 'module_interval', + 'tagente_modulo', + 'id_agente_modulo', + $module_id + ); + + if ($cron === '* * * * *') { return db_process_sql( 'UPDATE tagente_estado SET current_interval = '.$module_interval.' WHERE id_agente_modulo = '.(int) $module_id ); @@ -41,15 +58,19 @@ function cron_update_module_interval($module_id, $cron) } -// Get the number of seconds left to the next execution of the given cron entry. +/** + * Get the number of seconds left to the next execution of the given cron entry. + * + * @param string $cron String with the Linux cron configuration. + * @param integer $module_interval Module interval. Minimum increased time. + * @param integer $module_id Module id. + * + * @return integer Time to next execution time. + */ function cron_next_execution($cron, $module_interval, $module_id) { // Get day of the week and month from cron config. $cron_array = explode(' ', $cron); - $minute = $cron_array[0]; - $hour = $cron_array[1]; - $mday = $cron_array[2]; - $month = $cron_array[3]; $wday = $cron_array[4]; // Get last execution time. @@ -60,55 +81,35 @@ function cron_next_execution($cron, $module_interval, $module_id) $module_id ); $cur_time = ($last_execution !== false) ? $last_execution : time(); - - // Any day of the way. - if ($wday == '*') { - $nex_time = cron_next_execution_date( - $cron, - $cur_time, - $module_interval - ); - return ($nex_time - $cur_time); + $nex_time = cron_next_execution_date($cron, $cur_time, $module_interval); + $nex_wday = (int) date('w', $nex_time); + // Check the wday values to avoid infinite loop. + $wday_int = cron_get_interval($wday); + if ($wday_int['down'] !== '*' && ($wday_int['down'] > 6 || ($wday_int['up'] !== false && $wday_int['up'] > 6))) { + $wday = '*'; } - // A specific day of the week. - $count = 0; - $nex_time = $cur_time; - do { - $nex_time = cron_next_execution_date( - $cron, - $nex_time, - $module_interval - ); - $nex_time_wd = $nex_time; + // Check day of the way. + while (!cron_check_interval($nex_wday, $wday)) { + // If it does not acomplish the day of the week, go to the next day. + $nex_time += SECONDS_1DAY; + $nex_time = cron_next_execution_date($cron, $nex_time, 0); + $nex_wday = (int) date('w', $nex_time); + } - $array_nex = explode(' ', date('m w', $nex_time_wd)); - $nex_mon = $array_nex[0]; - $nex_wday = $array_nex[1]; - - do { - // Check the day of the week. - if ($nex_wday == $wday) { - return ($nex_time_wd - $cur_time); - } - - // Move to the next day of the month. - $nex_time_wd += SECONDS_1DAY; - - $array_nex_w = explode(' ', date('m w', $nex_time_wd)); - $nex_mon_wd = $array_nex_w[0]; - $nex_wday = $array_nex_w[1]; - } while ($mday == '*' && $nex_mon_wd == $nex_mon); - - $count++; - } while ($count < SECONDS_1MINUTE); - - // Something went wrong, default to 5 minutes. - return SECONDS_5MINUTES; + return ($nex_time - $cur_time); } -// Get the next execution date for the given cron entry in seconds since epoch. +/** + * Get the next execution date for the given cron entry in seconds since epoch. + * + * @param string $cron String with the Linux cron configuration. + * @param integer $cur_time Current time in utimestamp. + * @param integer $module_interval Module interval. Minimum increased time. + * + * @return integer Next execution timestamp seing the cron configuration. + */ function cron_next_execution_date($cron, $cur_time=false, $module_interval=300) { // Get cron configuration. @@ -127,8 +128,7 @@ function cron_next_execution_date($cron, $cur_time=false, $module_interval=300) } // Update minutes. - $min_s = cron_get_interval($cron_array[0]); - $nex_time_array[0] = ($min_s['down'] == '*') ? 0 : $min_s['down']; + $nex_time_array[0] = cron_get_next_time_element($cron_array[0]); $nex_time = cron_valid_date($nex_time_array); if ($nex_time >= $cur_time) { @@ -166,8 +166,7 @@ function cron_next_execution_date($cron, $cur_time=false, $module_interval=300) } // Update the hour if fails. - $hour_s = cron_get_interval($cron_array[1]); - $nex_time_array[1] = ($hour_s['down'] == '*') ? 0 : $hour_s['down']; + $nex_time_array[1] = cron_get_next_time_element($cron_array[1]); // When an overflow is passed check the hour update again. $nex_time = cron_valid_date($nex_time_array); @@ -199,8 +198,7 @@ function cron_next_execution_date($cron, $cur_time=false, $module_interval=300) } // Update the day if fails. - $mday_s = cron_get_interval($cron_array[2]); - $nex_time_array[2] = ($mday_s['down'] == '*') ? 1 : $mday_s['down']; + $nex_time_array[2] = cron_get_next_time_element($cron_array[2]); // When an overflow is passed check the hour update in the next execution. $nex_time = cron_valid_date($nex_time_array); @@ -226,8 +224,7 @@ function cron_next_execution_date($cron, $cur_time=false, $module_interval=300) } // Update the month if fails. - $mon_s = cron_get_interval($cron_array[3]); - $nex_time_array[3] = ($mon_s['down'] == '*') ? 1 : $mon_s['down']; + $nex_time_array[3] = cron_get_next_time_element($cron_array[3]); // When an overflow is passed check the hour update in the next execution. $nex_time = cron_valid_date($nex_time_array); @@ -245,7 +242,33 @@ function cron_next_execution_date($cron, $cur_time=false, $module_interval=300) } -// Get an array with the cron interval. +/** + * Get the next tentative time for a cron value or interval in case of overflow. + * + * @param string $cron_array_elem Cron element. + * + * @return integer The tentative time. Ex: + * * shold returns 0. + * 5 should returns 5. + * 10-55 should returns 10. + * 55-10 should retunrs 0. + */ +function cron_get_next_time_element($cron_array_elem) +{ + $interval = cron_get_interval($cron_array_elem); + $value = ($interval['down'] == '*' || ($interval['up'] !== false && $interval['down'] > $interval['up'] )) ? 0 : $interval['down']; + return $value; +} + + +/** + * Get an array with the cron interval. + * + * @param string $element String with the elemen cron configuration. + * + * @return array With up and down elements. + * If there is not an interval, up element will be false. + */ function cron_get_interval($element) { // Not a range. @@ -263,7 +286,14 @@ function cron_get_interval($element) } -// Returns if a date is in a cron. Recursive. +/** + * Returns if a date is in a cron. Recursive. + * + * @param array $elems_cron Cron configuration in array format. + * @param integer $elems_curr_time Time to check if is in cron. + * + * @return boolean Returns true if is in cron. False if it is outside. + */ function cron_is_in_cron($elems_cron, $elems_curr_time) { $elem_cron = array_shift($elems_cron); @@ -275,31 +305,62 @@ function cron_is_in_cron($elems_cron, $elems_curr_time) } // Go to last element if current is a wild card. - if ($elem_cron != '*') { - $elem_s = cron_get_interval($elem_cron); - // Check if there is no a range - if (($elem_s['up'] === false) && ($elem_s['down'] != $elem_curr_time)) { - return false; - } - - // Check if there is on the range. - if ($elem_s['up'] !== false) { - if ($elem_s['down'] < $elem_s['up']) { - if ($elem_curr_time < $elem_s['down'] || $elem_curr_time > $elem_s['up']) { - return false; - } - } else { - if ($elem_curr_time > $elem_s['down'] || $elem_curr_time < $elem_s['up']) { - return false; - } - } - } + if (cron_check_interval($elem_curr_time, $elem_cron) === false) { + return false; } return cron_is_in_cron($elems_cron, $elems_curr_time); } +/** + * Check if an element is inside the cron interval or not. + * + * @param integer $elem_curr_time Integer that represents the time to check. + * @param string $elem_cron Cron interval (splitted by hypen) + * or cron single value (a number). + * + * @return boolean True if is in interval. + */ +function cron_check_interval($elem_curr_time, $elem_cron) +{ + // Go to last element if current is a wild card. + if ($elem_cron === '*') { + return true; + } + + $elem_s = cron_get_interval($elem_cron); + // Check if there is no a range. + if (($elem_s['up'] === false) && ($elem_s['down'] != $elem_curr_time)) { + return false; + } + + // Check if there is on the range. + if ($elem_s['up'] !== false && (int) $elem_s['up'] === (int) $elem_curr_time) { + return true; + } + + if ($elem_s['down'] < $elem_s['up']) { + if ($elem_curr_time < $elem_s['down'] || $elem_curr_time > $elem_s['up']) { + return false; + } + } else { + if ($elem_curr_time > $elem_s['down'] || $elem_curr_time < $elem_s['up']) { + return false; + } + } + + return true; +} + + +/** + * Check if a date is correct or not. + * + * @param array $da Date in array format [year, month, day, hour, minutes]. + * + * @return integer Utimestamp. False if date is incorrect. + */ function cron_valid_date($da) { $st = sprintf( @@ -315,7 +376,13 @@ function cron_valid_date($da) } -// Check if cron is properly constructed. +/** + * Check if cron is properly constructed. + * + * @param string $cron String with the Linux cron configuration. + * + * @return boolean True if is well formed. False otherwise. + */ function cron_check_syntax($cron) { return preg_match( @@ -325,6 +392,11 @@ function cron_check_syntax($cron) } +/** + * Cron list table. + * + * @return void It prints the HTML table. + */ function cron_list_table() { global $config; @@ -429,8 +501,10 @@ function cron_list_table() } $email = $args[1]; + $report_type = $args[4]; $data[2] .= '
        - '.__('Report').": "; $data[2] .= $report['name'].''; + $data[2] .= '
        - '.__('Report type').': '.$report_type; $data[2] .= '
        - '.__('Email').": "; $data[2] .= ui_print_truncate_text($email, 60, false).''; break; diff --git a/pandora_console/include/functions_custom_fields.php b/pandora_console/include/functions_custom_fields.php index 7e28b1dcd2..9570098766 100644 --- a/pandora_console/include/functions_custom_fields.php +++ b/pandora_console/include/functions_custom_fields.php @@ -1,16 +1,31 @@ 0 AND tae.estado <> 300 '; $empty_agents_count = ''; } } } - // filters module + // Filters module. if ($filters['module_search']) { $and_module_search = 'AND tam.nombre LIKE "%'.$filters['module_search'].'%"'; $empty_agents_count = ''; } - // filter group and check ACL groups + // Filter group and check ACL groups. $groups_and = ''; if (!users_can_manage_group_all('AR')) { if ($filters['group']) { @@ -338,7 +359,7 @@ function agent_counters_custom_fields($filters) } if ($filters['group']) { - // recursion check acl + // Recursion check acl. if ($filters['recursion']) { $recursion_groups = groups_get_id_recursive($filters['group'], true); if (!users_can_manage_group_all('AR')) { @@ -362,26 +383,26 @@ function agent_counters_custom_fields($filters) } } - // filter custom data + // Filter custom data. $custom_data_and = ''; if (!in_array(-1, $filters['id_custom_fields_data'])) { $custom_data_array = implode("', '", $filters['id_custom_fields_data']); $custom_data_and = "AND tcd.description IN ('".$custom_data_array."')"; } - // filter custom name + // Filter custom name. $custom_field_name = $filters['id_custom_fields']; if (is_metaconsole()) { $metaconsole_connections = metaconsole_get_connection_names(); - // For all nodes + // For all nodes. if (isset($metaconsole_connections) && is_array($metaconsole_connections)) { $result_meta = []; $data = []; foreach ($metaconsole_connections as $metaconsole) { - // Get server connection data + // Get server connection data. $server_data = metaconsole_get_connection($metaconsole); - // Establishes connection + // Establishes connection. if (metaconsole_load_external_db($server_data) !== NOERR) { continue; } @@ -470,7 +491,13 @@ function agent_counters_custom_fields($filters) WHEN ta.total_count = ta.notinit_count THEN 5 ELSE 0 - END) AS `status` + END) AS `status`, + ta.critical_count, + ta.warning_count, + ta.unknown_count, + ta.notinit_count, + ta.normal_count, + ta.total_count FROM tagente ta LEFT JOIN tagent_secondary_group tasg ON ta.id_agente = tasg.id_agent @@ -510,7 +537,7 @@ function agent_counters_custom_fields($filters) } $data = array_merge($data, $node_result); - // Restore connection to root node + // Restore connection to root node. metaconsole_restore_db(); } } @@ -519,7 +546,7 @@ function agent_counters_custom_fields($filters) $array_data = []; if (isset($result_meta) && is_array($result_meta)) { - // initialize counters + // Initialize counters. $final_result['counters_total'] = [ 't_m_normal' => 0, 't_m_critical' => 0, @@ -538,7 +565,7 @@ function agent_counters_custom_fields($filters) foreach ($result_meta as $k => $nodo) { if (isset($nodo) && is_array($nodo)) { foreach ($nodo as $key => $value) { - // Sum counters total + // Sum counters total. $final_result['counters_total']['t_m_normal'] += $value['m_normal']; $final_result['counters_total']['t_m_critical'] += $value['m_critical']; $final_result['counters_total']['t_m_warning'] += $value['m_warning']; @@ -553,7 +580,7 @@ function agent_counters_custom_fields($filters) $final_result['counters_total']['t_a_not_init'] += $value['a_not_init']; $final_result['counters_total']['t_a_agents'] += $value['a_agents']; - // Sum counters for data + // Sum counters for data. $array_data[$value['name_data']]['m_normal'] += $value['m_normal']; $array_data[$value['name_data']]['m_critical'] += $value['m_critical']; $array_data[$value['name_data']]['m_warning'] += $value['m_warning']; @@ -576,7 +603,7 @@ function agent_counters_custom_fields($filters) $final_result['indexed_descriptions'] = $data; } else { - // TODO + // TODO. $final_result = false; } @@ -586,7 +613,7 @@ function agent_counters_custom_fields($filters) function get_filters_custom_fields_view($id=0, $for_select=false, $name='') { - // filter group and check ACL groups + // Filter group and check ACL groups. $groups_and = ''; if (!users_can_manage_group_all()) { $user_groups = array_keys(users_get_groups(false, 'AR', false)); @@ -631,3 +658,38 @@ function get_group_filter_custom_field_view($id) return false; } + + +/** + * Function for print counters agents or modules. + * + * @param array $status_array Array need value, image, title, color, counter. + * @param string $id_form Id form default value ''. + * @param string $id_input Id input default value ''. + * + * @return array Return html print div container counters. + */ +function print_counters_cfv( + array $status_array, + string $id_form='', + string $id_input='' +) { + $html_result = ''; + foreach ($status_array as $key => $value) { + $checked = ($value['checked'] === 1) ? 'checked=true' : ''; + $disabled = ($value['counter'] === 0) ? 'disabled=true' : ''; + + $html_result .= ''; + $html_result .= ''; + } + + $html_result .= ''; + return $html_result; +} diff --git a/pandora_console/include/functions_custom_graphs.php b/pandora_console/include/functions_custom_graphs.php index 4859f4f378..f98084f808 100644 --- a/pandora_console/include/functions_custom_graphs.php +++ b/pandora_console/include/functions_custom_graphs.php @@ -161,3 +161,35 @@ function custom_graphs_get_user($id_user=0, $only_names=false, $returnAllGroup=t return $graphs; } + + +function custom_graphs_search($id_group, $search) +{ + if ($id_group != '' && $search != '') { + $all_graphs = db_get_all_rows_sql('select * from tgraph where id_group = '.$id_group.' AND name LIKE "%'.$search.'%"'); + } else if ($id_group != '') { + $all_graphs = db_get_all_rows_sql('select * from tgraph where id_group = '.$id_group.''); + } else { + $all_graphs = db_get_all_rows_sql('select * from tgraph where name LIKE "%'.$search.'%"'); + } + + if ($all_graphs === false) { + return []; + } + + $graphs = []; + foreach ($all_graphs as $graph) { + $graphsCount = db_get_value_sql( + 'SELECT COUNT(id_gs) + FROM tgraph_source + WHERE id_graph = '.$graph['id_graph'].'' + ); + + $graphs[$graph['id_graph']]['graphs_count'] = $graphsCount; + $graphs[$graph['id_graph']]['name'] = $graph['name']; + $graphs[$graph['id_graph']]['description'] = $graph['description']; + $graphs[$graph['id_graph']]['id_group'] = $graph['id_group']; + } + + return $graphs; +} diff --git a/pandora_console/include/functions_db.php b/pandora_console/include/functions_db.php index 9018a1044d..2061ab215c 100644 --- a/pandora_console/include/functions_db.php +++ b/pandora_console/include/functions_db.php @@ -421,13 +421,13 @@ function db_get_row_sql($sql, $search_history_db=false) * * @return mixed The first row of a database query or false. */ -function db_get_row($table, $field_search, $condition, $fields=false) +function db_get_row($table, $field_search, $condition, $fields=false, $cache=true) { global $config; switch ($config['dbtype']) { case 'mysql': - return mysql_db_get_row($table, $field_search, $condition, $fields); + return mysql_db_get_row($table, $field_search, $condition, $fields, $cache); break; case 'postgresql': diff --git a/pandora_console/include/functions_events.php b/pandora_console/include/functions_events.php index dc69af7baf..3feceb847b 100644 --- a/pandora_console/include/functions_events.php +++ b/pandora_console/include/functions_events.php @@ -959,6 +959,8 @@ function events_print_event_table( ) { global $config; + ui_require_css_file('events'); + if ($agent_id == 0) { $agent_condition = ''; } else { @@ -997,7 +999,7 @@ function events_print_event_table( $table->cellpadding = 0; $table->cellspacing = 0; $table->width = $width; - $table->class = 'databox data'; + $table->class = 'info_table no-td-padding'; if (!$tactical_view) { $table->title = __('Latest events'); } @@ -1010,36 +1012,38 @@ function events_print_event_table( $table->cellclass = []; $table->data = []; $table->align = []; - $table->style[0] = 'width:25px;'; - $table->style[1] = 'width:25px;'; - $table->style[2] = 'width:25px;'; - if ($agent_id == 0) { - $table->style[3] = 'word-break: break-all;'; - } + $table->style = []; - $table->style[4] = 'width:120px; word-break: break-all;'; + $i = 0; + $table->head[$i] = "".__('S.').''; + $table->headstyle[$i] = 'width: 1%;text-align: center;'; + $table->style[$i++] = 'text-align: center;'; - $table->head[0] = "".__('V.').''; - $table->align[0] = 'center'; + $table->head[$i] = __('Type'); + $table->headstyle[$i] = 'width: 3%;text-align: center;'; + $table->style[$i++] = 'text-align: center;'; - $table->head[1] = "".__('S.').''; - $table->align[1] = 'center'; - - $table->head[2] = __('Type'); - $table->headclass[2] = 'datos3 f9'; - $table->align[2] = 'center'; - - $table->head[3] = __('Event name'); + $table->head[$i] = __('Event name'); + $table->headstyle[$i] = ''; + $table->style[$i++] = 'word-break: break-word;'; if ($agent_id == 0) { - $table->head[4] = __('Agent name'); - $table->size[4] = '15%'; + $table->head[$i] = __('Agent name'); + $table->headstyle[$i] = ''; + $table->style[$i++] = 'word-break: break-all;'; } - $table->head[5] = __('Timestamp'); - $table->headclass[5] = 'datos3 f9'; - $table->align[5] = 'left'; - $table->size[5] = '15%'; + $table->head[$i] = __('Timestamp'); + $table->headstyle[$i] = 'width: 120px;'; + $table->style[$i++] = 'word-break: break-word;'; + + $table->head[$i] = __('Status'); + $table->headstyle[$i] = 'width: 150px;text-align: center;'; + $table->style[$i++] = 'text-align: center;'; + + $table->head[$i] = "".__('V.').''; + $table->headstyle[$i] = 'width: 1%;text-align: center;'; + $table->style[$i++] = 'text-align: center;'; $all_groups = []; if ($agent_id != 0) { @@ -1078,53 +1082,15 @@ function events_print_event_table( break; } - $data[0] = html_print_image( - $img, - true, - [ - 'class' => 'image_status', - 'title' => $title, - ] - ); - - switch ($event['criticity']) { - default: - case EVENT_CRIT_MAINTENANCE: - $img = 'images/status_sets/default/severity_maintenance.png'; - break; - case EVENT_CRIT_INFORMATIONAL: - $img = 'images/status_sets/default/severity_informational.png'; - break; - - case EVENT_CRIT_NORMAL: - $img = 'images/status_sets/default/severity_normal.png'; - break; - - case EVENT_CRIT_WARNING: - $img = 'images/status_sets/default/severity_warning.png'; - break; - - case EVENT_CRIT_CRITICAL: - $img = 'images/status_sets/default/severity_critical.png'; - break; - } - - $data[1] = html_print_image( - $img, - true, - [ - 'class' => 'image_status', - 'width' => 12, - 'height' => 12, - 'title' => get_priority_name($event['criticity']), - ] - ); + $i = 0; + // Criticity. + $data[$i++] = ui_print_event_priority($event['criticity'], true, true); // Event type. - $data[2] = events_print_type_img($event['event_type'], true); + $data[$i++] = events_print_type_img($event['event_type'], true); // Event text. - $data[3] = ui_print_string_substr( + $data[$i++] = ui_print_string_substr( strip_tags(io_safe_output($event['evento'])), 75, true, @@ -1135,33 +1101,32 @@ function events_print_event_table( if ($event['id_agente'] > 0) { // Agent name. // Get class name, for the link color, etc. - $myclass = get_priority_class($event['criticity']); - - $data[4] = "".agents_get_alias($event['id_agente']).''; + $data[$i] = "".agents_get_alias($event['id_agente']).''; // For System or SNMP generated alerts. } else if ($event['event_type'] == 'system') { - $data[4] = __('System'); + $data[$i] = __('System'); } else { - $data[4] = __('Alert').'SNMP'; + $data[$i] = __('Alert').'SNMP'; } + + $i++; } // Timestamp. - $data[5] = ui_print_timestamp($event['timestamp'], true, ['style' => 'font-size: 7.5pt; letter-spacing: 0.3pt;']); + $data[$i++] = ui_print_timestamp($event['timestamp'], true, ['style' => 'font-size: 7.5pt; letter-spacing: 0.3pt;']); - $class = get_priority_class($event['criticity']); - $cell_classes[3] = $class; - $cell_classes[4] = $class; - $cell_classes[5] = $class; + // Status. + $data[$i++] = ui_print_event_type($event['event_type'], true); - array_push($table->cellclass, $cell_classes); - - /* - Commented out (old). - // array_push ($table->rowclass, get_priority_class ($event["criticity"])); - */ - - array_push($table->data, $data); + $data[$i++] = html_print_image( + $img, + true, + [ + 'class' => 'image_status', + 'title' => $title, + ] + ); + $table->data[] = $data; } $events_table = html_print_table($table, true); @@ -1646,7 +1611,7 @@ function events_get_agent( $sql_where, 0, 1000, - is_metaconsole(), + (is_metaconsole() && $id_server) ? true : false, false, false, $history @@ -1987,9 +1952,9 @@ function events_page_responses($event, $childrens_ids=[]) $table_responses->width = '100%'; $table_responses->data = []; $table_responses->head = []; - $table_responses->style[0] = 'width:35%; font-weight: bold; text-align: left; height: 23px;'; - $table_responses->style[1] = 'text-align: left; height: 23px; text-align: right;'; - $table_responses->class = 'alternate rounded_cells'; + $table_responses->style[0] = 'height:30px'; + $table_responses->style[2] = 'text-align:right;'; + $table_responses->class = 'table_modal_alternate'; if (tags_checks_event_acl($config['id_user'], $event['id_grupo'], 'EM', $event['clean_tags'], $childrens_ids)) { // Owner. @@ -2056,7 +2021,7 @@ function events_page_responses($event, $childrens_ids=[]) -1, true ); - $data[1] .= html_print_button( + $data[2] .= html_print_button( __('Update'), 'owner_button', false, @@ -2136,7 +2101,7 @@ function events_page_responses($event, $childrens_ids=[]) ); if (!$status_blocked) { - $data[1] .= html_print_button( + $data[2] .= html_print_button( __('Update'), 'status_button', false, @@ -2151,7 +2116,8 @@ function events_page_responses($event, $childrens_ids=[]) // Comments. $data = []; $data[0] = __('Comment'); - $data[1] = html_print_button( + $data[1] = ''; + $data[2] = html_print_button( __('Add comment'), 'comment_button', false, @@ -2173,8 +2139,9 @@ function events_page_responses($event, $childrens_ids=[]) // Delete. $data = []; $data[0] = __('Delete event'); - $data[1] = '
        '; - $data[1] .= html_print_button( + $data[1] = ''; + $data[2] = ''; + $data[2] .= html_print_button( __('Delete event'), 'delete_button', false, @@ -2182,13 +2149,13 @@ function events_page_responses($event, $childrens_ids=[]) 'class="sub cancel"', true ); - $data[1] .= html_print_input_hidden('delete', 1, true); - $data[1] .= html_print_input_hidden( + $data[2] .= html_print_input_hidden('delete', 1, true); + $data[2] .= html_print_input_hidden( 'validate_ids', $event['id_evento'], true ); - $data[1] .= '
        '; + $data[2] .= ''; $table_responses->data[] = $data; } @@ -2229,7 +2196,7 @@ function events_page_responses($event, $childrens_ids=[]) $server_id = 0; } - $data[1] .= html_print_button( + $data[2] .= html_print_button( __('Execute'), 'custom_response_button', false, @@ -2249,14 +2216,14 @@ function events_page_responses($event, $childrens_ids=[]) $('.params_rows').remove(); $('#responses_table') - .append('".__('Description')."'+description+''); + .append('".__('Description')."'+description+''); if (params.length == 1 && params[0] == '') { return; } $('#responses_table') - .append('".__('Parameters')."'); + .append('".__('Parameters')."'); for (i = 0; i < params.length; i++) { add_row_param('responses_table',params[i]); @@ -2291,7 +2258,7 @@ function events_get_response_target( global $config; // If server_id > 0, it's a metaconsole query. - $meta = $server_id > 0; + $meta = $server_id > 0 || is_metaconsole(); $event_table = events_get_events_table($meta, $history); $event = db_get_row($event_table, 'id_evento', $event_id); @@ -2521,9 +2488,7 @@ function events_page_custom_fields($event) $table->width = '100%'; $table->data = []; $table->head = []; - $table->style[0] = 'width:35%; font-weight: bold; text-align: left; height: 23px;'; - $table->style[1] = 'text-align: left; height: 23px;'; - $table->class = 'alternate rounded_cells'; + $table->class = 'table_modal_alternate'; $all_customs_fields = (bool) check_acl( $config['id_user'], @@ -2671,11 +2636,9 @@ function events_page_details($event, $server='') $table_details->width = '100%'; $table_details->data = []; $table_details->head = []; - $table_details->cellspacing = 2; - $table_details->cellpadding = 2; - $table_details->style[0] = 'width:35%; font-weight: bold; text-align: left; height: 23px;'; - $table_details->style[1] = 'text-align: left; height: 23px;'; - $table_details->class = 'alternate rounded_cells'; + $table_details->cellspacing = 0; + $table_details->cellpadding = 0; + $table_details->class = 'table_modal_alternate'; /* * Useless switch. @@ -2919,9 +2882,10 @@ function events_page_details($event, $server='') true, [ 'class' => 'image_status', - 'width' => 12, - 'height' => 12, + 'width' => 61, + 'height' => 28, 'title' => $alert_priority, + 'style' => 'vertical-align:text-bottom', ] ); $data[1] .= ' '.$alert_priority; @@ -2985,9 +2949,7 @@ function events_page_custom_data($event) $table->width = '100%'; $table->data = []; $table->head = []; - $table->style[0] = 'width:35%; font-weight: bold; text-align: left;'; - $table->style[1] = 'text-align: left;'; - $table->class = 'alternate rounded_cells'; + $table->class = 'table_modal_alternate'; $json_custom_data = base64_decode($event['custom_data']); $custom_data = json_decode($json_custom_data); @@ -3137,14 +3099,12 @@ function events_page_general($event) // General. $table_general = new stdClass; - $table_general->cellspacing = 2; - $table_general->cellpadding = 2; + $table_general->cellspacing = 0; + $table_general->cellpadding = 0; $table_general->width = '100%'; $table_general->data = []; $table_general->head = []; - $table_general->style[0] = 'width:35%; font-weight: bold; text-align: left; height: 23px;'; - $table_general->style[1] = 'text-align: left; height: 23px;'; - $table_general->class = 'alternate rounded_cells'; + $table_general->class = 'table_modal_alternate'; $data = []; $data[0] = __('Event ID'); @@ -3167,8 +3127,7 @@ function events_page_general($event) $table_general->data[] = $data; - $event['owner_user'] = $event['id_usuario']; - + // $event['owner_user'] = $event['id_usuario']; $data = []; $data[0] = __('Owner'); if (empty($event['owner_user'])) { @@ -3186,10 +3145,12 @@ function events_page_general($event) $data = []; $data[0] = __('Type'); - $data[1] = events_print_type_img( + $data[1] = events_print_type_description($event['event_type'], true); + $data[2] = events_print_type_img( $event['event_type'], true - ).' '.events_print_type_description($event['event_type'], true); + ); + $table_general->data[] = $data; $data = []; @@ -3209,18 +3170,17 @@ function events_page_general($event) $data = []; $data[0] = __('Severity'); $event_criticity = get_priority_name($event['criticity']); - - $data[1] = html_print_image( + $data[1] = $event_criticity; + $data[2] = html_print_image( $img_sev, true, [ 'class' => 'image_status', - 'width' => 12, - 'height' => 12, + 'width' => 61, + 'height' => 28, 'title' => $event_criticity, ] ); - $data[1] .= ' '.$event_criticity; $table_general->data[] = $data; // Get Status. @@ -3228,7 +3188,8 @@ function events_page_general($event) $data = []; $data[0] = __('Status'); - $data[1] = html_print_image($event_st['img'], true).' '.$event_st['title']; + $data[1] = $event_st['title']; + $data[2] = html_print_image($event_st['img'], true); $table_general->data[] = $data; // If event is validated, show who and when acknowleded it. @@ -3251,12 +3212,23 @@ function events_page_general($event) $data = []; $data[0] = __('Group'); - $data[1] = ''; + $data[1] = groups_get_name($event['id_grupo']); if (!$config['show_group_name']) { - $data[1] = ui_print_group_icon($event['id_grupo'], true); + $data[2] = ui_print_group_icon($event['id_grupo'], true); + } + + $table_general->data[] = $data; + + $data = []; + $data[0] = __('Contact'); + $data[1] = ''; + $contact = db_get_value('contact', 'tgrupo', 'id_grupo', $event['id_grupo']); + if (empty($contact)) { + $data[1] = ''.__('N/A').''; + } else { + $data[1] = $contact; } - $data[1] .= groups_get_name($event['id_grupo']); $table_general->data[] = $data; $data = []; @@ -3282,6 +3254,16 @@ function events_page_general($event) $table_general->data[] = $data; + $table_data = $table_general->data; + $table_data_total = count($table_data); + + for ($i = 0; $i <= $table_data_total; $i++) { + if (count($table_data[$i]) == 2) { + $table_general->colspan[$i][1] = 2; + $table_general->style[2] = 'text-align:center; width:10%;'; + } + } + $general = '
        '.html_print_table($table_general, true).'
        '; return $general; @@ -3305,9 +3287,7 @@ function events_page_comments($event, $childrens_ids=[]) $table_comments->width = '100%'; $table_comments->data = []; $table_comments->head = []; - $table_comments->style[0] = 'width:35%; vertical-align: top; text-align: left;'; - $table_comments->style[1] = 'text-align: left;'; - $table_comments->class = 'alternate rounded_cells'; + $table_comments->class = 'table_modal_alternate'; $event_comments = $event['user_comment']; $event_comments = str_replace(["\n", ' '], '
        ', $event_comments); @@ -3412,9 +3392,9 @@ function events_page_comments($event, $childrens_ids=[]) $childrens_ids ))) && $config['show_events_in_local'] == false || $config['event_replication'] == false ) { - $comments_form = '
        '.html_print_textarea('comment', 3, 10, '', 'style="min-height: 15px; width: 100%; disabled"', true); + $comments_form = '
        '.html_print_textarea('comment', 3, 10, '', 'style="min-height: 15px; padding:0; width: 100%; disabled"', true); - $comments_form .= '
        '.html_print_button(__('Add comment'), 'comment_button', false, 'event_comment();', 'class="sub next"', true).'

        '; + $comments_form .= '
        '.html_print_button(__('Add comment'), 'comment_button', false, 'event_comment();', 'class="sub next"', true).'

        '; } else { $comments_form = ui_print_message( __('If event replication is ongoing, it won\'t be possible to enter comments here. This option is only to allow local pandora users to see comments, but not to operate with them. The operation, when event replication is enabled, must be done only in the Metaconsole.') diff --git a/pandora_console/include/functions_extensions.php b/pandora_console/include/functions_extensions.php index 8efb4c8f36..66e7ff9c3d 100755 --- a/pandora_console/include/functions_extensions.php +++ b/pandora_console/include/functions_extensions.php @@ -380,7 +380,18 @@ function extensions_load_extensions($process_login) // ~ } // ~ } // ~ else { + try { include_once $path_extension; + } + + // PHP 7 + catch (Throwable $e) { + } + + // PHP 5 + catch (Exception $e) { + } + // ~ } } } diff --git a/pandora_console/include/functions_filemanager.php b/pandora_console/include/functions_filemanager.php index 0cf2991630..b3aa490edf 100644 --- a/pandora_console/include/functions_filemanager.php +++ b/pandora_console/include/functions_filemanager.php @@ -120,18 +120,13 @@ if (!function_exists('mime_content_type')) { global $config; -if (isset($config['homedir_filemanager'])) { - $homedir_filemanager = trim(io_safe_output($config['homedir_filemanager'])); -} else { - $homedir_filemanager = trim($config['homedir']); -} +$homedir_filemanager = trim($config['homedir']); $sec2 = get_parameter('sec2'); if ($sec2 == 'enterprise/godmode/agentes/collections' || $sec2 == 'advanced/collections') { $homedir_filemanager .= '/attachment/collection/'; } - $upload_file_or_zip = (bool) get_parameter('upload_file_or_zip'); if ($upload_file_or_zip) { @@ -513,34 +508,81 @@ function filemanager_file_explorer( ?> width = '100%'; $table->id = 'table_filemanager'; if (!defined('METACONSOLE')) { - $table->class = 'databox data'; + $table->class = 'info_table'; $table->title = ''.__('Index of %s', $relative_directory).''; } if (defined('METACONSOLE')) { $table->class = 'databox_tactical'; - $table->title = ''.__('Index of images').''; + $table->title = ''.__('Index of %s', $relative_directory).''; } $table->colspan = []; @@ -582,13 +624,6 @@ function filemanager_file_explorer( $table->head[2] = __('Last modification'); $table->head[3] = __('Size'); $table->head[4] = __('Actions'); - if (!defined('METACONSOLE')) { - $table->headstyle[0] = 'background-color:#82B92E'; - $table->headstyle[1] = 'background-color:#82B92E'; - $table->headstyle[2] = 'background-color:#82B92E'; - $table->headstyle[3] = 'background-color:#82B92E'; - $table->headstyle[4] = 'background-color:#82B92E'; - } $prev_dir = explode('/', $relative_directory); $prev_dir_str = ''; @@ -608,85 +643,6 @@ function filemanager_file_explorer( $table->colspan[0][1] = 5; } - if (is_writable($real_directory)) { - $table->rowstyle[1] = 'display: none;'; - $table->data[1][0] = ''; - $table->data[1][1] = ''; - - $table->data[1][1] .= ''; - - $table->data[1][1] .= ''; - - $table->data[1][1] .= ''; - - $table->colspan[1][1] = 5; - } - foreach ($files as $fileinfo) { $fileinfo['realpath'] = str_replace('\\', '/', $fileinfo['realpath']); $relative_path = str_replace($_SERVER['DOCUMENT_ROOT'], '', $fileinfo['realpath']); @@ -777,6 +733,7 @@ function filemanager_file_explorer( $data[4] .= html_print_input_hidden('delete_file', 1, true); $relative_dir = str_replace($homedir_filemanager, '', str_replace('\\', '/', dirname($fileinfo['realpath']))); + if ($relative_dir[0] == '/') { $relative_dir = substr($relative_dir, 1); } @@ -812,13 +769,44 @@ function filemanager_file_explorer( if (!$readOnly) { if (is_writable($real_directory)) { // The buttons to make actions - if (defined('METACONSOLE')) { - echo "
        "; - } else { - echo "
        "; - } + $tabs_dialog = ''; - echo ""; + echo ''; + + echo ''; + + echo ' '; + + echo "'; } else { - echo "
        "; + echo "
        "; echo "".__('The directory is read-only'); echo '
        '; } diff --git a/pandora_console/include/functions_graph.php b/pandora_console/include/functions_graph.php index 71049d7804..cde59c52bb 100644 --- a/pandora_console/include/functions_graph.php +++ b/pandora_console/include/functions_graph.php @@ -1,16 +1,32 @@ 0) { - // Propagate the last known data to the end of the interval + // Propagate the last known data to the end of the interval. $nextData = [ 'datos' => $data[(count($data) - 1)]['datos'], 'utimestamp' => $date_array['final_date'], @@ -114,9 +144,8 @@ function grafico_modulo_sparse_data_chart( array_push($data, $nextData); } - // Check available data + // Check available data. if (count($data) < 1) { - // return fs_error_image (); return false; } @@ -126,7 +155,7 @@ function grafico_modulo_sparse_data_chart( $array_percentil = []; foreach ($data as $k => $v) { - // convert array + // Convert array. if ($params['flag_overlapped']) { $array_data['sum'.$series_suffix]['data'][$k] = [ (($v['utimestamp'] + $date_array['period'] ) * 1000), @@ -139,40 +168,43 @@ function grafico_modulo_sparse_data_chart( ]; } - // min + // Min. if ($min_value > $v['datos']) { $min_value = $v['datos']; } - // max + // Max. if ($max_value < $v['datos']) { $max_value = $v['datos']; } - // avg + // Avg. $sum_data += $v['datos']; $count_data++; - // percentil - if (!is_null($params['percentil']) && $params['percentil']) { + // Percentil. + if (!isset($params['percentil']) && $params['percentil']) { $array_percentil[] = $v['datos']; } } - $array_data['sum'.$series_suffix]['min'] = $min_value; - $array_data['sum'.$series_suffix]['max'] = $max_value; - $array_data['sum'.$series_suffix]['avg'] = ($sum_data / $count_data); + $array_data['sum'.$series_suffix]['min'] = $min_value; + $array_data['sum'.$series_suffix]['max'] = $max_value; + $array_data['sum'.$series_suffix]['avg'] = ($sum_data / $count_data); $array_data['sum'.$series_suffix]['agent_module_id'] = $agent_module_id; $array_data['sum'.$series_suffix]['id_module_type'] = $data_module_graph['id_module_type']; - $array_data['sum'.$series_suffix]['agent_name'] = $data_module_graph['agent_name']; - $array_data['sum'.$series_suffix]['module_name'] = $data_module_graph['module_name']; - $array_data['sum'.$series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; + $array_data['sum'.$series_suffix]['agent_name'] = $data_module_graph['agent_name']; + $array_data['sum'.$series_suffix]['module_name'] = $data_module_graph['module_name']; + $array_data['sum'.$series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; - if (!is_null($params['percentil']) + if (!isset($params['percentil']) && $params['percentil'] && !$params['flag_overlapped'] ) { - $percentil_result = get_percentile($params['percentil'], $array_percentil); + $percentil_result = get_percentile( + $params['percentil'], + $array_percentil + ); $array_data['percentil'.$series_suffix]['data'][0] = [ ($date_array['start_date'] * 1000), $percentil_result, @@ -188,6 +220,17 @@ function grafico_modulo_sparse_data_chart( } +/** + * Prepare data for send to function js paint charts. + * + * @param integer $agent_module_id ID. + * @param array $date_array Date stasrt finish and period. + * @param array $data_module_graph Data module. + * @param array $params Params graphs. + * @param integer $series_suffix Int. + * + * @return array Prepare data to paint js. + */ function grafico_modulo_sparse_data( $agent_module_id, $date_array, @@ -210,7 +253,7 @@ function grafico_modulo_sparse_data( $params['type_mode_graph'] ); } else { - // uncompress data except boolean and string. + // Uncompress data except boolean and string. if ($data_module_graph['id_module_type'] == 23 || $data_module_graph['id_module_type'] == 3 || $data_module_graph['id_module_type'] == 17 @@ -232,6 +275,7 @@ function grafico_modulo_sparse_data( $series_suffix ); } else { + $data_slice = ($date_array['period'] / (250 * $params['zoom']) + 100); $array_data = fullscale_data( $agent_module_id, $date_array, @@ -239,13 +283,13 @@ function grafico_modulo_sparse_data( $params['percentil'], $series_suffix, $params['flag_overlapped'], - $data_slice = ($date_array['period'] / (250 * $params['zoom']) + 100), + $data_slice, $params['type_mode_graph'] ); } } - if ($array_data === false || (!$params['graph_combined'] + if ($array_data === false && (!$params['graph_combined'] && !isset($array_data['sum1']['data'][0][1]) && !$params['baseline']) ) { return false; @@ -253,11 +297,13 @@ function grafico_modulo_sparse_data( $array_data['sum'.$series_suffix]['agent_module_id'] = $agent_module_id; $array_data['sum'.$series_suffix]['id_module_type'] = $data_module_graph['id_module_type']; - $array_data['sum'.$series_suffix]['agent_name'] = $data_module_graph['agent_name']; - $array_data['sum'.$series_suffix]['module_name'] = $data_module_graph['module_name']; - $array_data['sum'.$series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; + $array_data['sum'.$series_suffix]['agent_name'] = $data_module_graph['agent_name']; + $array_data['sum'.$series_suffix]['module_name'] = $data_module_graph['module_name']; + $array_data['sum'.$series_suffix]['agent_alias'] = $data_module_graph['agent_alias']; + $array_data['sum'.$series_suffix]['unit'] = $data_module_graph['unit']; - // This is for a specific type of report that consists in passing an interval and doing the average sum and avg. + // This is for a specific type of report that consists in passing + // an interval and doing the average sum and avg. if ($params['force_interval'] != '') { $period_time_interval = ($date_array['period'] * 1000); $start_period = ($date_array['start_date'] * 1000); @@ -269,7 +315,9 @@ function grafico_modulo_sparse_data( while ($period_time_interval > 0) { foreach ($array_data['sum1']['data'] as $key => $value) { - if ($value[0] >= $start_period && $value[0] < ($start_period + $params['time_interval'] * 1000)) { + if ($value[0] >= $start_period + && $value[0] < ($start_period + $params['time_interval'] * 1000) + ) { $sum_data = $value[1]; $array_data_only[] = $value[1]; $count_data++; @@ -277,7 +325,9 @@ function grafico_modulo_sparse_data( } else { if ($params['force_interval'] == 'max_only') { $acum_array_data[$i][0] = $start_period; - if (is_array($array_data_only) && count($array_data_only) > 0) { + if (is_array($array_data_only) + && count($array_data_only) > 0 + ) { $acum_array_data[$i][1] = max($array_data_only); $data_last_acum = $array_data_only[(count($array_data_only) - 1)]; } else { @@ -287,7 +337,9 @@ function grafico_modulo_sparse_data( if ($params['force_interval'] == 'min_only') { $acum_array_data[$i][0] = $start_period; - if (is_array($array_data_only) && count($array_data_only) > 0) { + if (is_array($array_data_only) + && count($array_data_only) > 0 + ) { $acum_array_data[$i][1] = min($array_data_only); $data_last_acum = $array_data_only[(count($array_data_only) - 1)]; } else { @@ -297,7 +349,9 @@ function grafico_modulo_sparse_data( if ($params['force_interval'] == 'avg_only') { $acum_array_data[$i][0] = $start_period; - if (is_array($array_data_only) && count($array_data_only) > 0) { + if (is_array($array_data_only) + && count($array_data_only) > 0 + ) { $acum_array_data[$i][1] = ($sum_data / $count_data); } else { $acum_array_data[$i][1] = $data_last_acum; @@ -316,7 +370,7 @@ function grafico_modulo_sparse_data( $period_time_interval = ($period_time_interval - $params['time_interval']); } - // drag the last value to paint the graph correctly + // Drag the last value to paint the graph correctly. $acum_array_data[] = [ 0 => $start_period, 1 => $acum_array_data[($i - 1)][1], @@ -360,7 +414,6 @@ function grafico_modulo_sparse_data( $date_array['final_date'], $data_module_graph['history_db'], 1 - // fix the time ranges to start_date - final_date ); if ($unknown_events !== false) { @@ -495,56 +548,55 @@ function grafico_modulo_sparse_data( } -/* - $params =array( - 'agent_module_id' => $agent_module_id, - 'period' => $period, - 'show_events' => false, - 'width' => $width, - 'height' => $height, - 'title' => '', - 'unit_name' => null, - 'show_alerts' => false, - 'date' => 0, - 'unit' => '', - 'baseline' => 0, - 'return_data' => 0, - 'show_title' => true, - 'only_image' => false, - 'homeurl' => $config['homeurl'], - 'ttl' => 1, - 'adapt_key' => '', - 'compare' => false, - 'show_unknown' => false, - 'menu' => true, - 'backgroundColor' => 'white', - 'percentil' => null, - 'dashboard' => false, - 'vconsole' => false, - 'type_graph' => 'area', - 'fullscale' => false, - 'id_widget_dashboard' => false, - 'force_interval' => '', - 'time_interval' => 300, - 'array_data_create' => 0, - 'show_legend' => true, - 'show_overview' => true, - 'return_img_base_64' => false, - 'image_treshold' => false, - 'graph_combined' => false, - 'zoom' => 1, - 'server_id' => null - ); -*/ +/** + * Functions tu create graphs. + * + * @param array $params Details builds graphs. For example: + * 'agent_module_id' => $agent_module_id, + * 'period' => $period, + * 'show_events' => false, + * 'width' => $width, + * 'height' => $height, + * 'title' => '', + * 'unit_name' => null, + * 'show_alerts' => false, + * 'date' => 0, + * 'unit' => '', + * 'baseline' => 0, + * 'return_data' => 0, + * 'show_title' => true, + * 'only_image' => false, + * 'homeurl' => $config['homeurl'], + * 'ttl' => 1, + * 'adapt_key' => '', + * 'compare' => false, + * 'show_unknown' => false, + * 'menu' => true, + * 'backgroundColor' => 'white', + * 'percentil' => null, + * 'dashboard' => false, + * 'vconsole' => false, + * 'type_graph' => 'area', + * 'fullscale' => false, + * 'id_widget_dashboard' => false, + * 'force_interval' => '', + * 'time_interval' => 300, + * 'array_data_create' => 0, + * 'show_legend' => true, + * 'show_overview' => true, + * 'return_img_base_64' => false, + * 'image_treshold' => false, + * 'graph_combined' => false, + * 'zoom' => 1, + * 'server_id' => null, + * 'stacked' => 0. + * + * @return string html Content graphs. + */ function grafico_modulo_sparse($params) { global $config; - /* - XXXXXXXXXXXX Documnetar - *Set all variable - */ - if (!isset($params) || !is_array($params)) { return false; } @@ -692,11 +744,10 @@ function grafico_modulo_sparse($params) } if (!isset($params['zoom'])) { - $params['zoom'] = $config['zoom_graph'] ? $config['zoom_graph'] : 1; + $params['zoom'] = ($config['zoom_graph']) ? $config['zoom_graph'] : 1; } if (!isset($params['type_mode_graph'])) { - // $config['type_mode_graph'] $params['type_mode_graph'] = $config['type_mode_graph']; } @@ -710,10 +761,15 @@ function grafico_modulo_sparse($params) $agent_module_id = $params['agent_module_id']; } - // XXXX Configurable - $params['grid_color'] = '#C1C1C1'; + if (!isset($params['stacked'])) { + $params['stacked'] = 0; + } + + // TODO: Configurable. + $params['grid_color'] = '#C1C1C1'; $params['legend_color'] = '#636363'; - $params['font'] = $config['fontpath']; + + $params['font'] = $config['fontpath']; $params['font_size'] = $config['font_size']; $params['short_data'] = $config['short_module_graph_data']; @@ -724,8 +780,8 @@ function grafico_modulo_sparse($params) global $graphic_type; global $array_events_alerts; - $array_data = []; - $legend = []; + $array_data = []; + $legend = []; $array_events_alerts = []; $date_array = []; @@ -740,25 +796,36 @@ function grafico_modulo_sparse($params) ); $data_module_graph = []; - $data_module_graph['history_db'] = db_search_in_history_db($date_array['start_date']); - $data_module_graph['agent_name'] = modules_get_agentmodule_agent_name($agent_module_id); - $data_module_graph['agent_alias'] = modules_get_agentmodule_agent_alias($agent_module_id); - $data_module_graph['agent_id'] = $module_data['id_agente']; - $data_module_graph['module_name'] = $module_data['nombre']; + $data_module_graph['history_db'] = db_search_in_history_db( + $date_array['start_date'] + ); + $data_module_graph['agent_name'] = modules_get_agentmodule_agent_name( + $agent_module_id + ); + $data_module_graph['agent_alias'] = modules_get_agentmodule_agent_alias( + $agent_module_id + ); + $data_module_graph['agent_id'] = $module_data['id_agente']; + $data_module_graph['module_name'] = $module_data['nombre']; $data_module_graph['id_module_type'] = $module_data['id_tipo_modulo']; - $data_module_graph['module_type'] = modules_get_moduletype_name($data_module_graph['id_module_type']); - $data_module_graph['uncompressed'] = is_module_uncompressed($data_module_graph['module_type']); - $data_module_graph['w_min'] = $module_data['min_warning']; - $data_module_graph['w_max'] = $module_data['max_warning']; - $data_module_graph['w_inv'] = $module_data['warning_inverse']; - $data_module_graph['c_min'] = $module_data['min_critical']; - $data_module_graph['c_max'] = $module_data['max_critical']; - $data_module_graph['c_inv'] = $module_data['critical_inverse']; + $data_module_graph['module_type'] = modules_get_moduletype_name( + $data_module_graph['id_module_type'] + ); + $data_module_graph['uncompressed'] = is_module_uncompressed( + $data_module_graph['module_type'] + ); + $data_module_graph['w_min'] = $module_data['min_warning']; + $data_module_graph['w_max'] = $module_data['max_warning']; + $data_module_graph['w_inv'] = $module_data['warning_inverse']; + $data_module_graph['c_min'] = $module_data['min_critical']; + $data_module_graph['c_max'] = $module_data['max_critical']; + $data_module_graph['c_inv'] = $module_data['critical_inverse']; + $data_module_graph['unit'] = $module_data['unit']; } else { $data_module_graph = false; } - // format of the graph + // Format of the graph. if (empty($params['unit'])) { $params['unit'] = $module_data['unit']; if (modules_is_unit_macro($params['unit'])) { @@ -768,7 +835,12 @@ function grafico_modulo_sparse($params) if (!$params['array_data_create']) { if ($params['baseline']) { - $array_data = get_baseline_data($agent_module_id, $date_array, $data_module_graph, $params); + $array_data = get_baseline_data( + $agent_module_id, + $date_array, + $data_module_graph, + $params + ); } else { if ($params['compare'] !== false) { $series_suffix = 2; @@ -794,10 +866,14 @@ function grafico_modulo_sparse($params) switch ($params['compare']) { case 'separated': case 'overlapped': - // Store the chart calculated + // Store the chart calculated. $array_data_prev = $array_data; $legend_prev = $legend; break; + + default: + // Not defined. + break; } } @@ -850,7 +926,7 @@ function grafico_modulo_sparse($params) $data_module_graph['series_suffix'] = $series_suffix; - // Check available data + // Check available data. if ($params['compare'] === 'separated') { if (!empty($array_data)) { $return = area_graph( @@ -924,67 +1000,41 @@ function grafico_modulo_sparse($params) /** - * Produces a combined/user defined graph + * Functions tu create graphs. * - * @param array List of source modules - * @param array List of weighs for each module - * @param int Period (in seconds) - * @param int Width, in pixels - * @param int Height, in pixels - * @param string Title for graph - * @param string Unit name, for render in legend - * @param int Show events in graph (set to 1) - * @param int Show alerts in graph (set to 1) - * @param int Pure mode (without titles) (set to 1) - * @param int Date to start of getting info. - * @param mixed If is a projection graph this parameter will be module data with prediction data (the projection) - * or false in other case. - * @param array List of names for the items. Should have the same size as the module list. - * @param array List of units for the items. Should have the same size as the module list. - * @param bool Show the last value of the item on the list. - * @param bool Show the max value of the item on the list. - * @param bool Show the min value of the item on the list. - * @param bool Show the average value of the item on the list. + * @param array $module_list Array modules. + * @param array $params Details builds graphs. For example: + * 'period' => $period, + * 'show_events' => false, + * 'width' => $width, + * 'height' => $height, + * 'title' => '', + * 'unit_name' => null, + * 'show_alerts' => false, + * 'date' => 0, + * 'unit' => '', + * 'only_image' => false, + * 'homeurl' => '', + * 'ttl' => 1, + * 'percentil' => null, + * 'dashboard' => false, + * 'vconsole' => false, + * 'fullscale' => false, + * 'id_widget_dashboard' => false. + * @param array $params_combined Details builds graphs. For example: + * 'weight_list' => array(), + * 'stacked' => 0, + * 'projection' => false, + * 'labels' => array(), + * 'from_interface' => false, + * 'summatory' => 0, + * 'average' => 0, + * 'modules_series' => 0, + * 'id_graph' => 0, + * 'return' => 1. * - * @return Mixed + * @return string html Content graphs. */ - - -/* - $params =array( - 'period' => $period, - 'show_events' => false, - 'width' => $width, - 'height' => $height, - 'title' => '', - 'unit_name' => null, - 'show_alerts' => false, - 'date' => 0, - 'unit' => '', - 'only_image' => false, - 'homeurl' => '', - 'ttl' => 1, - 'percentil' => null, - 'dashboard' => false, - 'vconsole' => false, - 'fullscale' => false, - 'id_widget_dashboard' => false, - ); - - $params_combined = array( - 'weight_list' => array(), - 'stacked' => 0, - 'projection' => false, - 'labels' => array(), - 'from_interface' => false, - 'summatory' => 0, - 'average' => 0, - 'modules_series' => 0, - 'id_graph' => 0, - 'return' => 1 - ); -*/ - function graphic_combined_module( $module_list, $params, @@ -1164,18 +1214,24 @@ function graphic_combined_module( $params_combined['graph_combined'] = true; if ($params['only_image']) { - return generator_chart_to_pdf('combined', $params, $params_combined, $module_list); + return generator_chart_to_pdf( + 'combined', + $params, + $params_combined, + $module_list + ); } if (!isset($params['zoom'])) { $params['zoom'] = 1; } - // XXXX Configurable + // TODO: Configurable. $params['grid_color'] = '#C1C1C1'; $params['legend_color'] = '#636363'; - $params['font'] = $config['fontpath']; - $params['font_size'] = $config['font_size']; + + $params['font'] = $config['fontpath']; + $params['font_size'] = $config['font_size']; $params['short_data'] = $config['short_module_graph_data']; @@ -1220,11 +1276,13 @@ function graphic_combined_module( foreach ($sources as $source) { array_push($modules, $source['id_agent_module']); array_push($weights, $source['weight']); - if ($source['label'] != '') { - $item['type'] = 'custom_graph'; - $item['id_agent'] = agents_get_module_id($source['id_agent_module']); + if ($source['label'] != '' || $params_combined['labels']) { + $item['type'] = 'custom_graph'; + $item['id_agent'] = agents_get_module_id( + $source['id_agent_module'] + ); $item['id_agent_module'] = $source['id_agent_module']; - $labels[$source['id_agent_module']] = reporting_label_macro($item, $source['label']); + $labels[$source['id_agent_module']] = ($source['label'] != '') ? reporting_label_macro($item, $source['label']) : reporting_label_macro($item, $params_combined['labels']); } } } @@ -1274,13 +1332,13 @@ function graphic_combined_module( } } - $width = $params['width']; - $height = $params['height']; - $homeurl = $params['homeurl']; - $ttl = $params['ttl']; + $width = $params['width']; + $height = $params['height']; + $homeurl = $params['homeurl']; + $ttl = $params['ttl']; $background_color = $params['backgroundColor']; - $datelimit = $date_array['start_date']; - $fixed_font_size = $config['font_size']; + $datelimit = $date_array['start_date']; + $fixed_font_size = $config['font_size']; if ($config['fixed_graph'] == false) { $water_mark = [ @@ -1337,23 +1395,34 @@ function graphic_combined_module( ); $data_module_graph = []; - $data_module_graph['history_db'] = db_search_in_history_db($date_array['start_date']); - $data_module_graph['agent_name'] = modules_get_agentmodule_agent_name($agent_module_id); - $data_module_graph['agent_alias'] = modules_get_agentmodule_agent_alias($agent_module_id); - $data_module_graph['agent_id'] = $module_data['id_agente']; - $data_module_graph['module_name'] = $module_data['nombre']; + $data_module_graph['history_db'] = db_search_in_history_db( + $date_array['start_date'] + ); + $data_module_graph['agent_name'] = modules_get_agentmodule_agent_name( + $agent_module_id + ); + $data_module_graph['agent_alias'] = modules_get_agentmodule_agent_alias( + $agent_module_id + ); + $data_module_graph['agent_id'] = $module_data['id_agente']; + $data_module_graph['module_name'] = $module_data['nombre']; $data_module_graph['id_module_type'] = $module_data['id_tipo_modulo']; - $data_module_graph['module_type'] = modules_get_moduletype_name($data_module_graph['id_module_type']); - $data_module_graph['uncompressed'] = is_module_uncompressed($data_module_graph['module_type']); - $data_module_graph['w_min'] = $module_data['min_warning']; - $data_module_graph['w_max'] = $module_data['max_warning']; - $data_module_graph['w_inv'] = $module_data['warning_inverse']; - $data_module_graph['c_min'] = $module_data['min_critical']; - $data_module_graph['c_max'] = $module_data['max_critical']; - $data_module_graph['c_inv'] = $module_data['critical_inverse']; - $data_module_graph['module_id'] = $agent_module_id; + $data_module_graph['module_type'] = modules_get_moduletype_name( + $data_module_graph['id_module_type'] + ); + $data_module_graph['uncompressed'] = is_module_uncompressed( + $data_module_graph['module_type'] + ); + $data_module_graph['w_min'] = $module_data['min_warning']; + $data_module_graph['w_max'] = $module_data['max_warning']; + $data_module_graph['w_inv'] = $module_data['warning_inverse']; + $data_module_graph['c_min'] = $module_data['min_critical']; + $data_module_graph['c_max'] = $module_data['max_critical']; + $data_module_graph['c_inv'] = $module_data['critical_inverse']; + $data_module_graph['module_id'] = $agent_module_id; + $data_module_graph['unit'] = $module_data['unit']; - // stract data + // Stract data. $array_data_module = grafico_modulo_sparse_data( $agent_module_id, $date_array, @@ -1364,12 +1433,14 @@ function graphic_combined_module( $series_suffix = $i; - // convert to array graph and weight + // Convert to array graph and weight. foreach ($array_data_module as $key => $value) { $array_data[$key] = $value; - if ($params_combined['weight_list'][$i] > 1) { + if ($params_combined['weight_list'][$i] != 1) { foreach ($value['data'] as $k => $v) { - $array_data[$key]['data'][$k][1] = ($v[1] * $params_combined['weight_list'][$i]); + if ($v[1] != false) { + $array_data[$key]['data'][$k][1] = ($v[1] * $params_combined['weight_list'][$i]); + } } } } @@ -1390,10 +1461,11 @@ function graphic_combined_module( // Work around for fixed the agents name with huge size chars. $fixed_font_size = $config['font_size']; - // $array_events_alerts[$series_suffix] = $events; $i++; - if (is_metaconsole() && $params_combined['type_report'] == 'automatic_graph') { + if (is_metaconsole() + && $params_combined['type_report'] == 'automatic_graph' + ) { metaconsole_restore_db(); } } @@ -1408,7 +1480,7 @@ function graphic_combined_module( } if ($params_combined['projection']) { - // If projection doesn't have data then don't draw graph + // If projection doesn't have data then don't draw graph. if ($output_projection != null) { $date_array_projection = max($output_projection); $date_array['final_date'] = ($date_array_projection[0] / 1000); @@ -1416,9 +1488,13 @@ function graphic_combined_module( } } - // summatory and average series - if ($params_combined['stacked'] == CUSTOM_GRAPH_AREA || $params_combined['stacked'] == CUSTOM_GRAPH_LINE) { - if ($params_combined['summatory'] || $params_combined['average']) { + // Summatory and average series. + if ($params_combined['stacked'] == CUSTOM_GRAPH_AREA + || $params_combined['stacked'] == CUSTOM_GRAPH_LINE + ) { + if ($params_combined['summatory'] + || $params_combined['average'] + ) { $array_data = combined_graph_summatory_average( $array_data, $params_combined['average'], @@ -1461,9 +1537,19 @@ function graphic_combined_module( $do_it_critical_inverse = true; foreach ($module_list as $index => $id_module) { - // Get module warning_min and critical_min - $warning_min = db_get_value('min_warning', 'tagente_modulo', 'id_agente_modulo', $id_module); - $critical_min = db_get_value('min_critical', 'tagente_modulo', 'id_agente_modulo', $id_module); + // Get module warning_min and critical_min. + $warning_min = db_get_value( + 'min_warning', + 'tagente_modulo', + 'id_agente_modulo', + $id_module + ); + $critical_min = db_get_value( + 'min_critical', + 'tagente_modulo', + 'id_agente_modulo', + $id_module + ); if ($index == 0) { $compare_warning = $warning_min; @@ -1484,8 +1570,18 @@ function graphic_combined_module( if ($do_it_warning_min || $do_it_critical_min) { foreach ($module_list as $index => $id_module) { - $warning_max = db_get_value('max_warning', 'tagente_modulo', 'id_agente_modulo', $id_module); - $critical_max = db_get_value('max_critical', 'tagente_modulo', 'id_agente_modulo', $id_module); + $warning_max = db_get_value( + 'max_warning', + 'tagente_modulo', + 'id_agente_modulo', + $id_module + ); + $critical_max = db_get_value( + 'max_critical', + 'tagente_modulo', + 'id_agente_modulo', + $id_module + ); if ($index == 0) { $yellow_up = $warning_max; @@ -1507,8 +1603,18 @@ function graphic_combined_module( if ($do_it_warning_min || $do_it_critical_min) { foreach ($module_list as $index => $id_module) { - $warning_inverse = db_get_value('warning_inverse', 'tagente_modulo', 'id_agente_modulo', $id_module); - $critical_inverse = db_get_value('critical_inverse', 'tagente_modulo', 'id_agente_modulo', $id_module); + $warning_inverse = db_get_value( + 'warning_inverse', + 'tagente_modulo', + 'id_agente_modulo', + $id_module + ); + $critical_inverse = db_get_value( + 'critical_inverse', + 'tagente_modulo', + 'id_agente_modulo', + $id_module + ); if ($index == 0) { $yellow_inverse = $warning_inverse; @@ -1528,18 +1634,24 @@ function graphic_combined_module( } } - if ($do_it_warning_min && $do_it_warning_max && $do_it_warning_inverse) { + if ($do_it_warning_min + && $do_it_warning_max + && $do_it_warning_inverse + ) { $yellow_threshold = $compare_warning; $threshold_data['yellow_threshold'] = $compare_warning; - $threshold_data['yellow_up'] = $yellow_up; - $threshold_data['yellow_inverse'] = (bool) $yellow_inverse; + $threshold_data['yellow_up'] = $yellow_up; + $threshold_data['yellow_inverse'] = (bool) $yellow_inverse; } - if ($do_it_critical_min && $do_it_critical_max && $do_it_critical_inverse) { + if ($do_it_critical_min + && $do_it_critical_max + && $do_it_critical_inverse + ) { $red_threshold = $compare_critical; $threshold_data['red_threshold'] = $compare_critical; - $threshold_data['red_up'] = $red_up; - $threshold_data['red_inverse'] = (bool) $red_inverse; + $threshold_data['red_up'] = $red_up; + $threshold_data['red_inverse'] = (bool) $red_inverse; } $params['threshold_data'] = $threshold_data; @@ -1586,9 +1698,12 @@ function graphic_combined_module( foreach ($module_list as $module_item) { $automatic_custom_graph_meta = false; if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from the report + // template in metaconsole. if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id($module_item['server']); + $server = metaconsole_get_connection_by_id( + $module_item['server'] + ); metaconsole_connect($server); $automatic_custom_graph_meta = true; } @@ -1625,16 +1740,27 @@ function graphic_combined_module( $value = false; } - if (!empty($params_combined['labels']) && isset($params_combined['labels'][$module])) { + if (!empty($params_combined['labels']) + && isset($params_combined['labels'][$module]) + ) { $label = io_safe_input($params_combined['labels'][$module]); } else { - $alias = db_get_value('alias', 'tagente', 'id_agente', $temp[$module]['id_agente']); + $alias = db_get_value( + 'alias', + 'tagente', + 'id_agente', + $temp[$module]['id_agente'] + ); $label = $alias.': '.$temp[$module]['nombre']; } $temp[$module]['label'] = $label; $temp[$module]['value'] = $value; - $temp_max = reporting_get_agentmodule_data_max($module, $params['period'], $params['date']); + $temp_max = reporting_get_agentmodule_data_max( + $module, + $params['period'], + $params['date'] + ); if ($temp_max < 0) { $temp_max = 0; } @@ -1645,7 +1771,11 @@ function graphic_combined_module( $temp[$module]['max'] = ($temp_max === false) ? 0 : $temp_max; } - $temp_min = reporting_get_agentmodule_data_min($module, $params['period'], $params['date']); + $temp_min = reporting_get_agentmodule_data_min( + $module, + $params['period'], + $params['date'] + ); if ($temp_min < 0) { $temp_min = 0; } @@ -1653,7 +1783,8 @@ function graphic_combined_module( $temp[$module]['min'] = ($temp_min === false) ? 0 : $temp_min; if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from the + // report template in metaconsole. if (is_array($module_list[0])) { metaconsole_restore_db(); } @@ -1678,7 +1809,12 @@ function graphic_combined_module( $color, $module_name_list, $long_index, - ui_get_full_url('images/image_problem_area_small.png', false, false, false), + ui_get_full_url( + 'images/image_problem_area_small.png', + false, + false, + false + ), '', '', $water_mark, @@ -1689,7 +1825,6 @@ function graphic_combined_module( $homeurl, $background_color ); - break; case CUSTOM_GRAPH_GAUGE: @@ -1698,9 +1833,12 @@ function graphic_combined_module( foreach ($module_list as $module_item) { $automatic_custom_graph_meta = false; if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from + // the report template in metaconsole. if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id($module_item['server']); + $server = metaconsole_get_connection_by_id( + $module_item['server'] + ); metaconsole_connect($server); $automatic_custom_graph_meta = true; } @@ -1737,7 +1875,14 @@ function graphic_combined_module( $temp[$module]['label'] = ($params_combined['labels'][$module] != '') ? $params_combined['labels'][$module] : $temp[$module]['nombre']; $temp[$module]['value'] = $value; - $temp[$module]['label'] = ui_print_truncate_text($temp[$module]['label'], 'module_small', false, true, false, '..'); + $temp[$module]['label'] = ui_print_truncate_text( + $temp[$module]['label'], + 'module_small', + false, + true, + false, + '..' + ); if ($temp[$module]['unit'] == '%') { $temp[$module]['min'] = 0; @@ -1745,7 +1890,11 @@ function graphic_combined_module( } else { $min = $temp[$module]['min']; if ($temp[$module]['max'] == 0) { - $max = reporting_get_agentmodule_data_max($module, $params['period'], $params['date']); + $max = reporting_get_agentmodule_data_max( + $module, + $params['period'], + $params['date'] + ); } else { $max = $temp[$module]['max']; } @@ -1757,7 +1906,8 @@ function graphic_combined_module( $temp[$module]['gauge'] = uniqid('gauge_'); if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from the report + // template in metaconsole. if (is_array($module_list[0])) { metaconsole_restore_db(); } @@ -1784,13 +1934,17 @@ function graphic_combined_module( $height, $color, $module_name_list, - ui_get_full_url('images/image_problem_area_small.png', false, false, false), + ui_get_full_url( + 'images/image_problem_area_small.png', + false, + false, + false + ), $config['fontpath'], $fixed_font_size, '', $homeurl ); - break; case CUSTOM_GRAPH_HBARS: @@ -1799,9 +1953,12 @@ function graphic_combined_module( foreach ($module_list as $module_item) { $automatic_custom_graph_meta = false; if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from the report + // template in metaconsole. if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id($module_item['server']); + $server = metaconsole_get_connection_by_id( + $module_item['server'] + ); metaconsole_connect($server); $automatic_custom_graph_meta = true; } @@ -1830,17 +1987,25 @@ function graphic_combined_module( modules_get_agentmodule_agent_name($module) ); - if (!empty($params_combined['labels']) && isset($params_combined['labels'][$module])) { + if (!empty($params_combined['labels']) + && isset($params_combined['labels'][$module]) + ) { $label = $params_combined['labels'][$module]; } else { - $alias = db_get_value('alias', 'tagente', 'id_agente', $module_data['id_agente']); + $alias = db_get_value( + 'alias', + 'tagente', + 'id_agente', + $module_data['id_agente'] + ); $label = $alias.' - '.$module_data['nombre']; } $temp[$label]['g'] = round($temp_data, 4); if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from the report + // template in metaconsole. if (is_array($module_list[0])) { metaconsole_restore_db(); } @@ -1864,7 +2029,12 @@ function graphic_combined_module( $color, $module_name_list, $long_index, - ui_get_full_url('images/image_problem_area_small.png', false, false, false), + ui_get_full_url( + 'images/image_problem_area_small.png', + false, + false, + false + ), '', '', $water_mark, @@ -1886,7 +2056,12 @@ function graphic_combined_module( $color, $module_name_list, $long_index, - ui_get_full_url('images/image_problem_area_small.png', false, false, false), + ui_get_full_url( + 'images/image_problem_area_small.png', + false, + false, + false + ), '', '', $water_mark, @@ -1908,9 +2083,12 @@ function graphic_combined_module( foreach ($module_list as $module_item) { $automatic_custom_graph_meta = false; if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from the report + // template in metaconsole. if (is_array($module_list[$i])) { - $server = metaconsole_get_connection_by_id($module_item['server']); + $server = metaconsole_get_connection_by_id( + $module_item['server'] + ); metaconsole_connect($server); $automatic_custom_graph_meta = true; } @@ -1949,10 +2127,19 @@ function graphic_combined_module( $total_modules += $value; - if (!empty($params_combined['labels']) && isset($params_combined['labels'][$module])) { - $label = io_safe_output($params_combined['labels'][$module]); + if (!empty($params_combined['labels']) + && isset($params_combined['labels'][$module]) + ) { + $label = io_safe_output( + $params_combined['labels'][$module] + ); } else { - $alias = db_get_value('alias', 'tagente', 'id_agente', $data_module['id_agente']); + $alias = db_get_value( + 'alias', + 'tagente', + 'id_agente', + $data_module['id_agente'] + ); $label = io_safe_output($alias.': '.$data_module['nombre']); } @@ -1961,7 +2148,8 @@ function graphic_combined_module( 'unit' => $data_module['unit'], ]; if ($config['metaconsole']) { - // Automatic custom graph from the report template in metaconsole + // Automatic custom graph from the report + // template in metaconsole. if (is_array($module_list[0])) { metaconsole_restore_db(); } @@ -1994,7 +2182,6 @@ function graphic_combined_module( false, $background_color ); - break; } @@ -2006,8 +2193,24 @@ function graphic_combined_module( } -function combined_graph_summatory_average($array_data, $average=false, $summatory=false, $modules_series=false, $baseline=false) -{ +/** + * Function for convert data summatory. + * + * @param array $array_data Data array. + * @param boolean $average Average. + * @param boolean $summatory Summatory. + * @param boolean $modules_series Series module. + * @param boolean $baseline Baseline data. + * + * @return array Data. + */ +function combined_graph_summatory_average( + $array_data, + $average=false, + $summatory=false, + $modules_series=false, + $baseline=false +) { if (isset($array_data) && is_array($array_data)) { foreach ($array_data as $key => $value) { if (strpos($key, 'sum') !== false) { @@ -2025,10 +2228,13 @@ function combined_graph_summatory_average($array_data, $average=false, $summator $data_array_pop = []; $count = 0; - while (count($data_array_reverse['sum0']) > 0) { + $count_data_array_reverse = count($data_array_reverse['sum0']); + while ($count_data_array_reverse > 0) { foreach ($data_array_reverse as $key_reverse => $value_reverse) { if (is_array($value_reverse) && count($value_reverse) > 0) { - $data_array_pop[$key_reverse] = array_pop($data_array_reverse[$key_reverse]); + $data_array_pop[$key_reverse] = array_pop( + $data_array_reverse[$key_reverse] + ); } } @@ -2082,12 +2288,18 @@ function combined_graph_summatory_average($array_data, $average=false, $summator $count++; } - if ($summatory && isset($array_sum_reverse) && is_array($array_sum_reverse) && count($array_sum_reverse) > 0) { + if ($summatory && isset($array_sum_reverse) + && is_array($array_sum_reverse) + && count($array_sum_reverse) > 0 + ) { $array_data['summatory']['data'] = $array_sum_reverse; $array_data['summatory']['color'] = 'purple'; } - if ($average && isset($array_avg_reverse) && is_array($array_avg_reverse) && count($array_avg_reverse) > 0) { + if ($average && isset($array_avg_reverse) + && is_array($array_avg_reverse) + && count($array_avg_reverse) > 0 + ) { if ($baseline) { $array_data['baseline']['data'] = $array_avg_reverse; $array_data['baseline']['color'] = 'green'; @@ -2108,19 +2320,28 @@ function combined_graph_summatory_average($array_data, $average=false, $summator /** * Print a graph with access data of agents * - * @param integer id_agent Agent ID - * @param integer width pie graph width - * @param integer height pie graph height - * @param integer period time period - * @param bool return or echo the result flag + * @param integer $id_agent Agent ID. + * @param integer $width Pie graph width. + * @param integer $height Pie graph height. + * @param integer $period Time period. + * @param boolean $return Return. + * @param boolean $tree View tree. + * + * @return string Return or echo the result flag. */ -function graphic_agentaccess($id_agent, $width, $height, $period=0, $return=false, $tree=false) -{ +function graphic_agentaccess( + $id_agent, + $width, + $height, + $period=0, + $return=false, + $tree=false +) { global $config; global $graphic_type; - $date = get_system_time(); - $datelimit = ($date - $period); + $date = get_system_time(); + $datelimit = ($date - $period); $data_array = []; $data = db_get_all_rows_sql( @@ -2150,7 +2371,12 @@ function graphic_agentaccess($id_agent, $width, $height, $period=0, $return=fals if ($config['fixed_graph'] == false) { $water_mark = [ 'file' => $config['homedir'].'/images/logo_vertical_water.png', - 'url' => ui_get_full_url('images/logo_vertical_water.png', false, false, false), + 'url' => ui_get_full_url( + 'images/logo_vertical_water.png', + false, + false, + false + ), ]; } @@ -2253,7 +2479,7 @@ function truncate_negatives(&$element) * @param bool return or echo flag * @param bool show_not_init flag */ -function graph_agent_status($id_agent=false, $width=300, $height=200, $return=false, $show_not_init=false, $data_agents=false) +function graph_agent_status($id_agent=false, $width=300, $height=200, $return=false, $show_not_init=false, $data_agents=false, $donut_narrow_graph=false) { global $config; @@ -2319,25 +2545,37 @@ function graph_agent_status($id_agent=false, $width=300, $height=200, $return=fa $data = []; } - $out = pie_graph( - $data, - $width, - $height, - __('other'), - ui_get_full_url(false, false, false, false), - '', - $config['fontpath'], - $config['font_size'], - 1, - 'hidden', - $colors, - 0 - ); - - if ($return) { + if ($donut_narrow_graph == true) { + $data_total = array_sum($data); + $out = print_donut_narrow_graph( + $colors, + $width, + $height, + $data, + $data_total + ); return $out; } else { - echo $out; + $out = pie_graph( + $data, + $width, + $height, + __('other'), + ui_get_full_url(false, false, false, false), + '', + $config['fontpath'], + $config['font_size'], + 1, + 'hidden', + $colors, + 0 + ); + + if ($return) { + return $out; + } else { + echo $out; + } } } @@ -3694,7 +3932,14 @@ function graph_graphic_moduleevents($id_agent, $id_module, $width, $height, $per } -// Prints an error image +/** + * Function for retun image no data. + * + * @param integer $width Width. + * @param integer $height Height. + * + * @return string Image. + */ function fs_error_image($width=300, $height=110) { global $config; @@ -3702,15 +3947,29 @@ function fs_error_image($width=300, $height=110) } +/** + * Function for uncompressed data module for cherts. + * + * @param integer $agent_module_id Id modulo. + * @param array $date_array Date start, finish, period. + * @param integer $show_unknown Show Unknown. + * @param integer $show_percentil Show Percentil. + * @param integer $series_suffix Series. + * @param boolean $compare Type compare. + * @param boolean $data_slice Size slice. + * @param string $type_mode_graph Type. + * + * @return array Return array data uncompresess. + */ function fullscale_data( $agent_module_id, $date_array, $show_unknown=0, $show_percentil=0, - $series_suffix, + $series_suffix=0, $compare=false, $data_slice=false, - $type_mode_graph + $type_mode_graph='' ) { global $config; $data_uncompress = db_uncompress_module_data( @@ -3722,218 +3981,234 @@ function fullscale_data( $data = []; $previous_data = 0; - // normal + // Normal. $min_value_total = PHP_INT_MAX; - $max_value_total = -PHP_INT_MAX; - // max + $max_value_total = (-PHP_INT_MAX); + // Max. $max_value_min = PHP_INT_MAX; - $max_value_max = -PHP_INT_MAX; - // min + $max_value_max = (-PHP_INT_MAX); + // Min. $min_value_min = PHP_INT_MAX; - $min_value_max = -PHP_INT_MAX; - // avg + $min_value_max = (-PHP_INT_MAX); + // Avg. $avg_value_min = PHP_INT_MAX; - $avg_value_max = -PHP_INT_MAX; + $avg_value_max = (-PHP_INT_MAX); $flag_unknown = 0; $array_percentil = []; if ($data_slice) { - foreach ($data_uncompress as $k) { - $sum_data = 0; - $count_data = 0; - $min_value = PHP_INT_MAX; - $max_value = -PHP_INT_MAX; - $flag_virtual_data = 0; - foreach ($k['data'] as $v) { - if (isset($v['type']) && $v['type'] == 1) { - // skip unnecesary virtual data - continue; - $flag_virtual_data = 1; - } + if (isset($data_uncompress) === true + && is_array($data_uncompress) === true + ) { + foreach ($data_uncompress as $k) { + $sum_data = 0; + $count_data = 0; + $min_value = PHP_INT_MAX; + $max_value = (-PHP_INT_MAX); + $flag_virtual_data = 0; + foreach ($k['data'] as $v) { + if (isset($v['type']) && $v['type'] == 1) { + // Skip unnecesary virtual data. + continue; + $flag_virtual_data = 1; + } - if ($compare) { - // * 1000 need js utimestam mlsecond - $real_date = (($v['utimestamp'] + $date_array['period']) * 1000); - } else { - $real_date = ($v['utimestamp'] * 1000); - } + if ($compare) { + // Data * 1000 need js utimestam mlsecond. + $real_date = (($v['utimestamp'] + $date_array['period']) * 1000); + } else { + $real_date = ($v['utimestamp'] * 1000); + } - if ($v['datos'] === null) { - // Unknown - if ($show_unknown) { - if (!$compare) { - if ($flag_unknown) { - $data['unknown'.$series_suffix]['data'][] = [ - $real_date, - 1, - ]; - } else { - $data['unknown'.$series_suffix]['data'][] = [ - ($real_date - 1), - 0, - ]; - $data['unknown'.$series_suffix]['data'][] = [ - $real_date, - 1, - ]; - $flag_unknown = 1; + if ($v['datos'] === null) { + // Unknown. + if ($show_unknown) { + if (!$compare) { + if ($flag_unknown) { + $data['unknown'.$series_suffix]['data'][] = [ + $real_date, + 1, + ]; + } else { + $data['unknown'.$series_suffix]['data'][] = [ + ($real_date - 1), + 0, + ]; + $data['unknown'.$series_suffix]['data'][] = [ + $real_date, + 1, + ]; + $flag_unknown = 1; + } + } + } + + $v['datos'] = $previous_data; + } else { + // Normal. + $previous_data = $v['datos']; + if ($show_unknown) { + if (!$compare) { + if ($flag_unknown) { + $data['unknown'.$series_suffix]['data'][] = [ + $real_date, + 0, + ]; + $flag_unknown = 0; + } } } } - $v['datos'] = $previous_data; - } else { - // normal - $previous_data = $v['datos']; - if ($show_unknown) { - if (!$compare) { - if ($flag_unknown) { - $data['unknown'.$series_suffix]['data'][] = [ - $real_date, - 0, - ]; - $flag_unknown = 0; - } + if (isset($v['datos']) && $v['datos']) { + // Max. + if ($v['datos'] >= $max_value) { + $max_value = $v['datos']; + } + + // Min. + if ($v['datos'] <= $min_value) { + $min_value = $v['datos']; + } + + // Avg sum. + $sum_data += $v['datos']; + } + + // Avg count. + $count_data++; + + if ($show_percentil && !$compare) { + $array_percentil[] = $v['datos']; + } + + $last_data = $v['datos']; + } + + if (!$flag_virtual_data) { + if ($compare) { + // Data * 1000 need js utimestam mlsecond. + $real_date = (($k['data'][0]['utimestamp'] + $date_array['period']) * 1000); + } else { + $real_date = ($k['data'][0]['utimestamp'] * 1000); + } + + $data['sum'.$series_suffix]['data'][] = [ + $real_date, + ($sum_data / $count_data), + ]; + if ($type_mode_graph && !$params['baseline']) { + if ($min_value != PHP_INT_MAX) { + $data['min'.$series_suffix]['data'][] = [ + $real_date, + $min_value, + ]; + } + + if ($max_value != (-PHP_INT_MAX)) { + $data['max'.$series_suffix]['data'][] = [ + $real_date, + $max_value, + ]; + } + } else { + if ($min_value != PHP_INT_MAX) { + $data['sum'.$series_suffix]['slice_data'][$real_date]['min'] = $min_value; + } + + $data['sum'.$series_suffix]['slice_data'][$real_date]['avg'] = ($sum_data / $count_data); + + if ($max_value != (-PHP_INT_MAX)) { + $data['sum'.$series_suffix]['slice_data'][$real_date]['max'] = $max_value; } } - } - if (isset($v['datos']) && $v['datos']) { - // max - if ($v['datos'] >= $max_value) { - $max_value = $v['datos']; + // Max total. + if ($max_value >= $max_value_total + && $max_value != (-PHP_INT_MAX) + ) { + $max_value_total = $max_value; } - // min - if ($v['datos'] <= $min_value) { - $min_value = $v['datos']; + // Min total. + if ($min_value <= $min_value_total + && $min_value != PHP_INT_MAX + ) { + $min_value_total = $min_value; } - // avg sum - $sum_data += $v['datos']; - } + // Avg sum total. + $sum_data_total += ($sum_data / $count_data); - // avg count - $count_data++; + // Avg count total. + $count_data_total++; - if ($show_percentil && !$compare) { - $array_percentil[] = $v['datos']; - } + if ($type_mode_graph && !$params['baseline']) { + // MIN. + // max min. + if ($min_value >= $min_value_max + && $min_value != PHP_INT_MAX + ) { + $min_value_max = $min_value; + } - $last_data = $v['datos']; - } + // Min min. + if ($min_value <= $min_value_min + && $min_value != PHP_INT_MAX + ) { + $min_value_min = $min_value; + } - if (!$flag_virtual_data) { - if ($compare) { - // * 1000 need js utimestam mlsecond - $real_date = (($k['data'][0]['utimestamp'] + $date_array['period']) * 1000); - } else { - $real_date = ($k['data'][0]['utimestamp'] * 1000); - } + // Avg sum min. + if ($min_value != PHP_INT_MAX) { + $sum_data_min += $min_value; + } - $data['sum'.$series_suffix]['data'][] = [ - $real_date, - ($sum_data / $count_data), - ]; - if ($type_mode_graph && !$params['baseline']) { - if ($min_value != PHP_INT_MAX) { - $data['min'.$series_suffix]['data'][] = [ - $real_date, - $min_value, - ]; + // MAX. + // Max max. + if ($max_value >= $max_value_max + && $max_value != (-PHP_INT_MAX) + ) { + $max_value_max = $max_value; + } + + // Min max. + if ($max_value <= $max_value_min + && $max_value != (-PHP_INT_MAX) + ) { + $max_value_min = $max_value; + } + + // Avg Sum max. + if ($max_value != (-PHP_INT_MAX)) { + $sum_data_max += $max_value; + } + + // AVG. + // Max max. + if (($sum_data / $count_data) >= $avg_value_max) { + $avg_value_max = ($sum_data / $count_data); + } + + // Min max. + if (($sum_data / $count_data) <= $avg_value_min) { + $avg_value_min = ($sum_data / $count_data); + } + + // Avg sum max. + $sum_data_avg += ($sum_data / $count_data); } - - if ($max_value != -PHP_INT_MAX) { - $data['max'.$series_suffix]['data'][] = [ - $real_date, - $max_value, - ]; - } - } else { - if ($min_value != PHP_INT_MAX) { - $data['sum'.$series_suffix]['slice_data'][$real_date]['min'] = $min_value; - } - - $data['sum'.$series_suffix]['slice_data'][$real_date]['avg'] = ($sum_data / $count_data); - - if ($max_value != -PHP_INT_MAX) { - $data['sum'.$series_suffix]['slice_data'][$real_date]['max'] = $max_value; - } - } - - // max_total - if ($max_value >= $max_value_total && $max_value != -PHP_INT_MAX) { - $max_value_total = $max_value; - } - - // min_total - if ($min_value <= $min_value_total && $min_value != PHP_INT_MAX) { - $min_value_total = $min_value; - } - - // avg sum_total - $sum_data_total += ($sum_data / $count_data); - - // avg count_total - $count_data_total++; - - if ($type_mode_graph && !$params['baseline']) { - /* - MIN*/ - // max_min - if ($min_value >= $min_value_max && $min_value != PHP_INT_MAX) { - $min_value_max = $min_value; - } - - // min_min - if ($min_value <= $min_value_min && $min_value != PHP_INT_MAX) { - $min_value_min = $min_value; - } - - // avg sum_min - if ($min_value != PHP_INT_MAX) { - $sum_data_min += $min_value; - } - - /* - MAX*/ - // max_max - if ($max_value >= $max_value_max && $max_value != -PHP_INT_MAX) { - $max_value_max = $max_value; - } - - // min_max - if ($max_value <= $max_value_min && $max_value != -PHP_INT_MAX) { - $max_value_min = $max_value; - } - - // avg sum_max - if ($max_value != -PHP_INT_MAX) { - $sum_data_max += $max_value; - } - - /* - AVG*/ - // max_max - if (($sum_data / $count_data) >= $avg_value_max) { - $avg_value_max = ($sum_data / $count_data); - } - - // min_max - if (($sum_data / $count_data) <= $avg_value_min) { - $avg_value_min = ($sum_data / $count_data); - } - - // avg sum_max - $sum_data_avg += ($sum_data / $count_data); } } } $data['sum'.$series_suffix]['min'] = $min_value_total; $data['sum'.$series_suffix]['max'] = $max_value_total; - $data['sum'.$series_suffix]['avg'] = ($sum_data_total / $count_data_total); + $data['sum'.$series_suffix]['avg'] = 0; + if (isset($count_data_total) === true) { + $data['sum'.$series_suffix]['avg'] = ($sum_data_total / $count_data_total); + } if ($type_mode_graph && !$params['baseline']) { $data['min'.$series_suffix]['min'] = $min_value_min; @@ -3956,19 +4231,19 @@ function fullscale_data( foreach ($data_uncompress as $k) { foreach ($k['data'] as $v) { if (isset($v['type']) && $v['type'] == 1) { - // skip unnecesary virtual data + // Skip unnecesary virtual data. continue; } if ($compare) { - // * 1000 need js utimestam mlsecond + // Data * 1000 need js utimestam mlsecond. $real_date = (($v['utimestamp'] + $date_array['period']) * 1000); } else { $real_date = ($v['utimestamp'] * 1000); } if ($v['datos'] === null) { - // Unknown + // Unknown. if ($show_unknown) { if (!$compare) { if ($flag_unknown) { @@ -3995,7 +4270,7 @@ function fullscale_data( $previous_data, ]; } else { - // normal + // Normal. $previous_data = $v['datos']; $data['sum'.$series_suffix]['data'][] = [ $real_date, @@ -4015,21 +4290,21 @@ function fullscale_data( } if (isset($v['datos']) && $v['datos']) { - // max + // Max. if ((float) $v['datos'] >= $max_value_max) { $max_value_max = $v['datos']; } - // min + // Min. if ((float) $v['datos'] <= $min_value_min) { $min_value_min = $v['datos']; } - // avg sum + // Avg sum. $sum_data += $v['datos']; } - // avg count + // Avg count. $count_data++; if ($show_percentil && !$compare) { @@ -4042,7 +4317,7 @@ function fullscale_data( $data['sum'.$series_suffix]['min'] = $min_value_min; $data['sum'.$series_suffix]['max'] = $max_value_max; - $data['sum'.$series_suffix]['avg'] = $count_data == 0 ? 0 : ($sum_data / $count_data); + $data['sum'.$series_suffix]['avg'] = ($count_data == 0) ? 0 : ($sum_data / $count_data); } if ($show_percentil && !$compare) { @@ -4068,7 +4343,7 @@ function fullscale_data( } } - // Add missed last data + // Add missed last data. if ($compare) { $data['sum'.$series_suffix]['data'][] = [ (($date_array['final_date'] + $date_array['period']) * 1000), @@ -4091,7 +4366,11 @@ function fullscale_data( ]; } else { $data['sum'.$series_suffix]['slice_data'][($date_array['final_date'] * 1000)]['min'] = $min_value; - $data['sum'.$series_suffix]['slice_data'][($date_array['final_date'] * 1000)]['avg'] = ($sum_data / $count_data); + $data['sum'.$series_suffix]['slice_data'][($date_array['final_date'] * 1000)]['avg'] = 0; + if (isset($count_data) === true) { + $data['sum'.$series_suffix]['slice_data'][($date_array['final_date'] * 1000)]['avg'] = ($sum_data / $count_data); + } + $data['sum'.$series_suffix]['slice_data'][($date_array['final_date'] * 1000)]['max'] = $max_value; } } @@ -4104,7 +4383,7 @@ function fullscale_data( /** * Print an area graph with netflow aggregated */ -function graph_netflow_aggregate_area($data, $period, $width, $height, $unit='', $ttl=1, $only_image=false) +function graph_netflow_aggregate_area($data, $period, $width, $height, $ttl=1, $only_image=false, $date=null) { global $config; global $graphic_type; @@ -4114,7 +4393,7 @@ function graph_netflow_aggregate_area($data, $period, $width, $height, $unit='', return; } - // Calculate source indexes + // Calculate source indexes. foreach ($data['sources'] as $key => $value) { $i = 0; foreach ($data['data'] as $k => $v) { @@ -4133,7 +4412,12 @@ function graph_netflow_aggregate_area($data, $period, $width, $height, $unit='', if ($config['fixed_graph'] == false) { $water_mark = [ 'file' => $config['homedir'].'/images/logo_vertical_water.png', - 'url' => ui_get_full_url('images/logo_vertical_water.png', false, false, false), + 'url' => ui_get_full_url( + 'images/logo_vertical_water.png', + false, + false, + false + ), ]; $water_mark = $config['homedir'].'/images/logo_vertical_water.png'; @@ -4150,7 +4434,7 @@ function graph_netflow_aggregate_area($data, $period, $width, $height, $unit='', 'period' => $period, 'width' => '90%', 'height' => 450, - 'unit' => $unit, + 'unit' => 'bytes', 'only_image' => $only_image, 'homeurl' => $homeurl, 'menu' => true, @@ -4159,6 +4443,10 @@ function graph_netflow_aggregate_area($data, $period, $width, $height, $unit='', 'font' => $config['fontpath'], 'font_size' => $config['font_size'], 'array_data_create' => $chart, + 'stacked' => 1, + 'date' => $date, + 'show_export_csv' => false, + 'show_overview' => false, ]; return grafico_modulo_sparse($params); @@ -4280,9 +4568,16 @@ function graph_netflow_aggregate_pie($data, $aggregate, $ttl=1, $only_image=fals /** - * Print a circular graph with the data transmitted between IPs + * Print a circular mesh array. + * + * @param array $data Array with properly data structure. Array with two + * elements required: + * 'elements': Non-associative array with all the relationships. + * 'matrix': Array of arrays with value of the relationship. + * + * @return string HTML data. */ -function graph_netflow_circular_mesh($data, $unit, $radius=700) +function graph_netflow_circular_mesh($data) { global $config; @@ -4292,14 +4587,14 @@ function graph_netflow_circular_mesh($data, $unit, $radius=700) include_once $config['homedir'].'/include/graphs/functions_d3.php'; - return d3_relationship_graph($data['elements'], $data['matrix'], $unit, $radius, true); + return d3_relationship_graph($data['elements'], $data['matrix'], 700, true); } /** * Print a rectangular graph with the traffic of the ports for each IP */ -function graph_netflow_host_traffic($data, $unit, $width=700, $height=700) +function graph_netflow_host_traffic($data, $width=700, $height=700) { global $config; @@ -4928,8 +5223,22 @@ function graph_monitor_wheel($width=550, $height=600, $filter=false) } -function get_baseline_data($agent_module_id, $date_array, $data_module_graph, $params) -{ +/** + * Function that on a date requests 3 times that period and takes an average. + * + * @param integer $agent_module_id ID module. + * @param array $date_array Date array start finish period. + * @param array $data_module_graph Data module. + * @param array $params Params. + * + * @return array Data baseline graph. + */ +function get_baseline_data( + $agent_module_id, + $date_array, + $data_module_graph, + $params +) { $period = $date_array['period']; $date = $date_array['final_date']; $array_data = []; @@ -4968,8 +5277,18 @@ function get_baseline_data($agent_module_id, $date_array, $data_module_graph, $p } $result['avg'] = (($array_data[0]['sum0']['avg'] + $array_data[1]['sum1']['avg'] + $array_data[2]['sum2']['avg'] + $array_data[3]['sum3']['avg']) / 4); - $result['max'] = max($array_data[0]['sum0']['max'], $array_data[1]['sum1']['max'], $array_data[2]['sum2']['max'], $array_data[3]['sum3']['max']); - $result['min'] = min($array_data[0]['sum0']['min'], $array_data[1]['sum1']['min'], $array_data[2]['sum2']['min'], $array_data[3]['sum3']['min']); + $result['max'] = max( + $array_data[0]['sum0']['max'], + $array_data[1]['sum1']['max'], + $array_data[2]['sum2']['max'], + $array_data[3]['sum3']['max'] + ); + $result['min'] = min( + $array_data[0]['sum0']['min'], + $array_data[1]['sum1']['min'], + $array_data[2]['sum2']['min'], + $array_data[3]['sum3']['min'] + ); $result['agent_module_id'] = $array_data[0]['sum0']['agent_module_id']; $result['id_module_type'] = $array_data[0]['sum0']['id_module_type']; diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index 6d461350e9..010845caf6 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -436,7 +436,9 @@ function html_print_select_groups( $keys_field='id_grupo', $strict_user=false, $delete_groups=false, - $include_groups=false + $include_groups=false, + $size=false, + $simple_multiple_options=false ) { global $config; @@ -481,7 +483,12 @@ function html_print_select_groups( $class, $disabled, $style, - $option_style + $option_style, + $size, + false, + '', + false, + $simple_multiple_options ); if ($return) { @@ -529,7 +536,8 @@ function html_print_select( $size=false, $modal=false, $message='', - $select_all=false + $select_all=false, + $simple_multiple_options=false ) { $output = "\n"; @@ -557,6 +565,14 @@ function html_print_select( } } + if ($simple_multiple_options === true) { + if ($size !== false) { + $attributes .= ' size="'.$size.'"'; + } else { + $attributes .= ' size="10"'; + } + } + if (!empty($class)) { $attributes .= ' class="'.$class.'"'; } @@ -644,7 +660,7 @@ function html_print_select( } if ($optlabel === '') { - $output .= '>'.$value.''; + $output .= '>None'; } else { $output .= '>'.$optlabel.''; } @@ -1060,10 +1076,10 @@ function html_print_extended_select_for_time( 'images/pencil.png', true, [ - 'class' => $uniq_name.'_toggler', + 'class' => $uniq_name.'_toggler '.$class, 'alt' => __('Custom'), 'title' => __('Custom'), - 'style' => 'width: 18px;'.$style_icon, + 'style' => 'width: 18px; margin-bottom: -5px;'.$style_icon, ], false, false, @@ -1098,7 +1114,7 @@ function html_print_extended_select_for_time( 'class' => $uniq_name.'_toggler', 'alt' => __('List'), 'title' => __('List'), - 'style' => 'width: 18px;'.$style_icon, + 'style' => 'width: 18px;margin-bottom: -5px;'.$style_icon, ] ).'
        '; echo '
        '; @@ -1774,7 +1790,7 @@ function html_print_button($label='OK', $name='', $disabled=false, $script='', $ */ function html_print_textarea($name, $rows, $columns, $value='', $attributes='', $return=false, $class='') { - $output = ''; @@ -2392,12 +2408,21 @@ function html_print_checkbox_switch($name, $value, $checked=false, $return=false /** * Prints an image HTML element. * - * @param string $src Image source filename. - * @param boolean $return Whether to return or print - * @param array $options Array with optional HTML options to set. At this moment, the - * following options are supported: alt, style, title, width, height, class, pos_tree. - * @param boolean $return_src Whether to return src field of image ('images/*.*') or complete html img tag ('...'). - * @param boolean $relative Whether to use relative path to image or not (i.e. $relative= true : /pandora/). + * @param string $src Image source filename. + * @param boolean $return Whether to return or print. + * @param array $options Array with optional HTML options to set. + * At this moment, the following options are supported: + * align, border, hspace, ismap, vspace, style, title, height, + * longdesc, usemap, width, id, class, lang, xml:lang, onclick, + * ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, + * onmouseout, onkeypress, onkeydown, onkeyup, pos_tree, alt. + * @param boolean $return_src Whether to return src field of image + * ('images/*.*') or complete html img tag ('...'). + * @param boolean $relative Whether to use relative path to image or not + * (i.e. $relative= true : /pandora/). + * @param boolean $no_in_meta Do not show on metaconsole folder at first. Go + * directly to the node. + * @param boolean $isExternalLink Do not shearch for images in Pandora. * * @return string HTML code if return parameter is true. */ @@ -2412,9 +2437,9 @@ function html_print_image( ) { global $config; - // If metaconsole is in use then don't use skins + // If metaconsole is in use then don't use skins. if (!is_metaconsole()) { - // Checks if user's skin is available + // Checks if user's skin is available. $isFunctionSkins = enterprise_include_once('include/functions_skins.php'); if ($isFunctionSkins !== ENTERPRISE_NOT_HOOK) { @@ -2426,11 +2451,11 @@ function html_print_image( } } - // If metaconsole is activated and image doesn't exists try to search on normal console + // If metaconsole is activated and image doesn't exists try to search on normal console. if (is_metaconsole()) { if (!$relative) { $working_dir = str_replace('\\', '/', getcwd()); - // Windows compatibility + // Windows compatibility. if ($no_in_meta) { $src = '../../'.$src; } else if (strstr($working_dir, 'enterprise/meta') === false) { @@ -2468,22 +2493,22 @@ function html_print_image( } } - // Only return src field of image + // Only return src field of image. if ($return_src) { if (!$return) { echo io_safe_input($src); - return; + return null; } return io_safe_input($src); } $output = ' '(GMT-11:00) '.__('Midway Island'), - 'US/Samoa' => '(GMT-11:00) '.__('Samoa'), - 'US/Hawaii' => '(GMT-10:00) '.__('Hawaii'), - 'US/Alaska' => '(GMT-09:00) '.__('Alaska'), - 'US/Pacific' => '(GMT-08:00) '.__('Pacific Time (US & Canada)'), - 'America/Tijuana' => '(GMT-08:00) '.__('Tijuana'), - 'US/Arizona' => '(GMT-07:00) '.__('Arizona'), - 'US/Mountain' => '(GMT-07:00) '.__('Mountain Time (US & Canada)'), - 'America/Chihuahua' => '(GMT-07:00) '.__('Chihuahua'), - 'America/Mazatlan' => '(GMT-07:00) '.__('Mazatlan'), - 'America/Mexico_City' => '(GMT-06:00) '.__('Mexico City'), - 'America/Monterrey' => '(GMT-06:00) '.__('Monterrey'), - 'Canada/Saskatchewan' => '(GMT-06:00) '.__('Saskatchewan'), - 'US/Central' => '(GMT-06:00) '.__('Central Time (US & Canada)'), - 'US/Eastern' => '(GMT-05:00) '.__('Eastern Time (US & Canada)'), - 'US/East-Indiana' => '(GMT-05:00) '.__('Indiana (East)'), - 'America/Bogota' => '(GMT-05:00) '.__('Bogota'), - 'America/Lima' => '(GMT-05:00) '.__('Lima'), - 'America/Caracas' => '(GMT-04:30) '.__('Caracas'), - 'Canada/Atlantic' => '(GMT-04:00) '.__('Atlantic Time (Canada)'), - 'America/La_Paz' => '(GMT-04:00) '.__('La Paz'), - 'America/Santiago' => '(GMT-04:00) '.__('Santiago'), - 'Canada/Newfoundland' => '(GMT-03:30) '.__('Newfoundland'), - 'America/Buenos_Aires' => '(GMT-03:00) '.__('Buenos Aires'), - "Greenland'" => '(GMT-03:00) '.__('Greenland'), - 'Atlantic/Stanley' => '(GMT-02:00) '.__('Stanley'), - 'Atlantic/Azores' => '(GMT-01:00) '.__('Azores'), - 'Atlantic/Cape_Verde' => '(GMT-01:00) '.__('Cape Verde Is.'), - 'Africa/Casablanca' => '(GMT+00:00) '.__('Casablanca'), - 'Europe/Dublin' => '(GMT+00:00) '.__('Dublin'), - 'Europe/Lisbon' => '(GMT+00:00) '.__('Lisbon'), - 'Europe/London' => '(GMT+00:00) '.__('London'), - 'Africa/Monrovia' => '(GMT+00:00) '.__('Monrovia'), - 'Europe/Amsterdam' => '(GMT+01:00) '.__('Amsterdam'), - 'Europe/Belgrade' => '(GMT+01:00) '.__('Belgrade'), - 'Europe/Berlin' => '(GMT+01:00) '.__('Berlin'), - 'Europe/Bratislava' => '(GMT+01:00) '.__('Bratislava'), - 'Europe/Brussels' => '(GMT+01:00) '.__('Brussels'), - 'Europe/Budapest' => '(GMT+01:00) '.__('Budapest'), - 'Europe/Copenhagen' => '(GMT+01:00) '.__('Copenhagen'), - 'Europe/Ljubljana' => '(GMT+01:00) '.__('Ljubljana'), - 'Europe/Madrid' => '(GMT+01:00) '.__('Madrid'), - 'Europe/Paris' => '(GMT+01:00) '.__('Paris'), - 'Europe/Prague' => '(GMT+01:00) '.__('Prague'), - 'Europe/Rome' => '(GMT+01:00) '.__('Rome'), - 'Europe/Sarajevo' => '(GMT+01:00) '.__('Sarajevo'), - 'Europe/Skopje' => '(GMT+01:00) '.__('Skopje'), - 'Europe/Stockholm' => '(GMT+01:00) '.__('Stockholm'), - 'Europe/Vienna' => '(GMT+01:00) '.__('Vienna'), - 'Europe/Warsaw' => '(GMT+01:00) '.__('Warsaw'), - 'Europe/Zagreb' => '(GMT+01:00) '.__('Zagreb'), - 'Europe/Athens' => '(GMT+02:00) '.__('Athens'), - 'Europe/Bucharest' => '(GMT+02:00) '.__('Bucharest'), - 'Africa/Cairo' => '(GMT+02:00) '.__('Cairo'), - 'Africa/Harare' => '(GMT+02:00) '.__('Harare'), - 'Europe/Helsinki' => '(GMT+02:00) '.__('Helsinki'), - 'Europe/Istanbul' => '(GMT+02:00) '.__('Istanbul'), - 'Asia/Jerusalem' => '(GMT+02:00) '.__('Jerusalem'), - 'Europe/Kiev' => '(GMT+02:00) '.__('Kyiv'), - 'Europe/Minsk' => '(GMT+02:00) '.__('Minsk'), - 'Europe/Riga' => '(GMT+02:00) '.__('Riga'), - 'Europe/Sofia' => '(GMT+02:00) '.__('Sofia'), - 'Europe/Tallinn' => '(GMT+02:00) '.__('Tallinn'), - 'Europe/Vilnius' => '(GMT+02:00) '.__('Vilnius'), - 'Asia/Baghdad' => '(GMT+03:00) '.__('Baghdad'), - 'Asia/Kuwait' => '(GMT+03:00) '.__('Kuwait'), - 'Africa/Nairobi' => '(GMT+03:00) '.__('Nairobi'), - 'Asia/Riyadh' => '(GMT+03:00) '.__('Riyadh'), - 'Europe/Moscow' => '(GMT+03:00) '.__('Moscow'), - 'Asia/Tehran' => '(GMT+03:30) '.__('Tehran'), - 'Asia/Baku' => '(GMT+04:00) '.__('Baku'), - 'Europe/Volgograd' => '(GMT+04:00) '.__('Volgograd'), - 'Asia/Muscat' => '(GMT+04:00) '.__('Muscat'), - 'Asia/Tbilisi' => '(GMT+04:00) '.__('Tbilisi'), - 'Asia/Yerevan' => '(GMT+04:00) '.__('Yerevan'), - 'Asia/Kabul' => '(GMT+04:30) '.__('Kabul'), - 'Asia/Karachi' => '(GMT+05:00) '.__('Karachi'), - 'Asia/Tashkent' => '(GMT+05:00) '.__('Tashkent'), - 'Asia/Kolkata' => '(GMT+05:30) '.__('Kolkata'), - 'Asia/Kathmandu' => '(GMT+05:45) '.__('Kathmandu'), - 'Asia/Yekaterinburg' => '(GMT+06:00) '.__('Ekaterinburg'), - 'Asia/Almaty' => '(GMT+06:00) '.__('Almaty'), - 'Asia/Dhaka' => '(GMT+06:00) '.__('Dhaka'), - 'Asia/Novosibirsk' => '(GMT+07:00) '.__('Novosibirsk'), - 'Asia/Bangkok' => '(GMT+07:00) '.__('Bangkok'), - 'Asia/Jakarta' => '(GMT+07:00) '.__('Jakarta'), - 'Asia/Krasnoyarsk' => '(GMT+08:00) '.__('Krasnoyarsk'), - 'Asia/Chongqing' => '(GMT+08:00) '.__('Chongqing'), - 'Asia/Hong_Kong' => '(GMT+08:00) '.__('Hong Kong'), - 'Asia/Kuala_Lumpur' => '(GMT+08:00) '.__('Kuala Lumpur'), - 'Australia/Perth' => '(GMT+08:00) '.__('Perth'), - 'Asia/Singapore' => '(GMT+08:00) '.__('Singapore'), - 'Asia/Taipei' => '(GMT+08:00) '.__('Taipei'), - 'Asia/Ulaanbaatar' => '(GMT+08:00) '.__('Ulaan Bataar'), - 'Asia/Urumqi' => '(GMT+08:00) '.__('Urumqi'), - 'Asia/Irkutsk' => '(GMT+09:00) '.__('Irkutsk'), - 'Asia/Seoul' => '(GMT+09:00) '.__('Seoul'), - 'Asia/Tokyo' => '(GMT+09:00) '.__('Tokyo'), - 'Australia/Adelaide' => '(GMT+09:30) '.__('Adelaide'), - 'Australia/Darwin' => '(GMT+09:30) '.__('Darwin'), - 'Asia/Yakutsk' => '(GMT+10:00) '.__('Yakutsk'), - 'Australia/Brisbane' => '(GMT+10:00) '.__('Brisbane'), - 'Australia/Canberra' => '(GMT+10:00) '.__('Canberra'), - 'Pacific/Guam' => '(GMT+10:00) '.__('Guam'), - 'Australia/Hobart' => '(GMT+10:00) '.__('Hobart'), - 'Australia/Melbourne' => '(GMT+10:00) '.__('Melbourne'), - 'Pacific/Port_Moresby' => '(GMT+10:00) '.__('Port Moresby'), - 'Australia/Sydney' => '(GMT+10:00) '.__('Sydney'), - 'Asia/Vladivostok' => '(GMT+11:00) '.__('Vladivostok'), - 'Asia/Magadan' => '(GMT+12:00) '.__('Magadan'), - 'Pacific/Auckland' => '(GMT+12:00) '.__('Auckland'), - 'Pacific/Fiji' => '(GMT+12:00) '.__('Fiji'), - ]; - + $timezones_index = timezone_identifiers_list(); + $timezones = timezone_identifiers_list(); + $timezones = array_combine($timezones_index, $timezones); return html_print_select($timezones, $name, $selected, '', __('None'), '', true, false, false); } @@ -3100,6 +2989,7 @@ function html_print_switch($attributes=[]) 'class', 'name', 'onclick', + 'onchange', ]; foreach ($valid_attrs as $va) { if (!isset($attributes[$va])) { @@ -3109,8 +2999,57 @@ function html_print_switch($attributes=[]) $html_expand .= ' '.$va.'="'.$attributes[$va].'"'; } - return "
      '; - // Invisible UL for adding border-top + // Invisible UL for adding border-top. echo '
    '; } @@ -506,7 +541,7 @@ function menu_add_extras(&$menu) $menu_extra['workspace']['sub']['operation/incidents/incident_detail']['text'] = __('Manage incident'); - // Duplicate extensions as sec=extension to check it from url + // Duplicate extensions as sec=extension to check it from url. foreach ($menu as $k => $m) { if (!isset($m['sub'])) { continue; @@ -550,7 +585,7 @@ function menu_get_sec($with_categories=false) if ($with_categories) { if (!$in_godmode && $k[0] == 'g') { // Hack to dont confuse with gis activated because godmode - // sec starts with g (like gismaps) + // sec starts with g (like gismaps). if ($k != 'gismaps') { $in_godmode = true; } @@ -619,17 +654,17 @@ function menu_get_sec_pages($sec, $menu_hash=false) $sec2_array = []; if (isset($sec)) { - // Get the sec2 of the main section + // Get the sec2 of the main section. $sec2_array[$menu[$sec]['sec2']] = $menu[$sec]['text']; - // Get the sec2 of the subsections + // Get the sec2 of the subsections. foreach ($menu[$sec]['sub'] as $k => $v) { - // Avoid special cases of standalone windows + // Avoid special cases of standalone windows. if (preg_match('/^javascript:/', $k) || preg_match('/\.php/', $k)) { continue; } - // If this value has various parameters, we only get the first + // If this value has various parameters, we only get the first. $k = explode('&', $k); $k = $k[0]; @@ -637,6 +672,7 @@ function menu_get_sec_pages($sec, $menu_hash=false) } } + $sec2_array = array_unique($sec2_array); return $sec2_array; } @@ -663,8 +699,8 @@ function menu_get_sec2_pages($sec, $sec2, $menu_hash=false) $sec3_array = []; - if (isset($menu[$sec]['sub']) and isset($menu[$sec]['sub'][$sec2]['sub2'])) { - // Get the sec2 of the subsections + if (isset($menu[$sec]['sub']) && isset($menu[$sec]['sub'][$sec2]['sub2'])) { + // Get the sec2 of the subsections. foreach ($menu[$sec]['sub'][$sec2]['sub2'] as $k => $v) { $sec3_array[$k] = $v['text']; } @@ -686,7 +722,7 @@ function menu_sec2_in_sec($sec, $sec2) { $sec2_array = menu_get_sec_pages($sec); - // If this value has various parameters, we only get the first + // If this value has various parameters, we only get the first. $sec2 = explode('&', $sec2); $sec2 = $sec2[0]; @@ -702,7 +738,7 @@ function menu_sec3_in_sec2($sec, $sec2, $sec3) { $sec3_array = menu_get_sec2_pages($sec, $sec2, $menu_hash = false); - // If this value has various parameters, we only get the first + // If this value has various parameters, we only get the first. $sec3 = explode('&', $sec3); $sec3 = $sec3[0]; diff --git a/pandora_console/include/functions_messages.php b/pandora_console/include/functions_messages.php index 5374fb7b1e..1bc54c9c89 100644 --- a/pandora_console/include/functions_messages.php +++ b/pandora_console/include/functions_messages.php @@ -442,14 +442,15 @@ function messages_get_count_sent(string $user='') /** * Get message overview in array * - * @param string $order How to order them valid: - * (status (default), subject, timestamp, sender). - * @param string $order_dir Direction of order - * (ASC = Ascending, DESC = Descending). - * @param boolean $incl_read Include read messages in return. - * @param boolean $incl_source_info Include source info. - * @param integer $limit Maximum number of result in the query. - * @param array $other_filter Add a filter on main query. + * @param string $order How to order them valid: + * (status (default), subject, timestamp, sender). + * @param string $order_dir Direction of order + * (ASC = Ascending, DESC = Descending). + * @param boolean $incl_read Include read messages in return. + * @param boolean $incl_source_info Include source info. + * @param integer $limit Maximum number of result in the query. + * @param array $other_filter Add a filter on main query. + * @param string $join_other_filter How to join filter on main query. * * @return integer The number of messages this user has */ @@ -459,7 +460,8 @@ function messages_get_overview( bool $incl_read=true, bool $incl_source_info=false, int $limit=0, - array $other_filter=[] + array $other_filter=[], + string $join_other_filter='AND' ) { global $config; @@ -529,7 +531,11 @@ function messages_get_overview( $config['id_user'], $config['id_user'], $read, - db_format_array_where_clause_sql($other_filter, 'AND', ' AND '), + db_format_array_where_clause_sql( + $other_filter, + $join_other_filter, + ' AND ' + ), $order, ($limit !== 0) ? ' LIMIT '.$limit : '' ); @@ -582,6 +588,56 @@ function messages_get_overview_sent( } +/** + * Get a message interpreted as a conversation. + * + * @param mixed $data Complete message or message id. + * + * @return mixed False if fails. A string array with the conversation. + */ +function messages_get_conversation($data) +{ + if (is_array($data)) { + $message = $data; + } else { + $message = messages_get_message($data); + } + + if (!isset($message) || !is_array($message)) { + return []; + } + + $conversation = []; + $target_str = $message['mensaje']; + + while (preg_match_all( + '/(.*)On(.*)wrote:(.*)/', + $target_str, + $decoded, + PREG_PATTERN_ORDER + ) !== false && empty($target_str) !== true) { + if (empty($decoded[2]) !== true) { + array_push( + $conversation, + [ + 'message' => array_pop($decoded)[0], + 'date' => array_pop($decoded)[0], + ] + ); + } else { + array_push( + $conversation, + ['message' => $target_str] + ); + } + + $target_str = $decoded[1][0]; + } + + return $conversation; +} + + /** * Get the URL of a message. If field in db is null, it returs a link to * messages view. diff --git a/pandora_console/include/functions_modules.php b/pandora_console/include/functions_modules.php index ed19c6d2c1..b9ed8e41d5 100755 --- a/pandora_console/include/functions_modules.php +++ b/pandora_console/include/functions_modules.php @@ -663,10 +663,11 @@ function modules_create_agent_module( 'estado' => $status, 'known_status' => $status, 'id_agente' => (int) $id_agent, - 'utimestamp' => 0, + 'utimestamp' => (time() - (int) $values['interval']), 'status_changes' => 0, 'last_status' => $status, 'last_known_status' => $status, + 'current_interval' => (int) $values['interval'], ] ); @@ -2293,6 +2294,54 @@ function modules_get_modulegroup_name($modulegroup_id) } +/** + * Returns target color to be used based on the status received. + * + * @param integer $status Source information. + * + * @return string HTML tag for color. + */ +function modules_get_color_status($status) +{ + if (isset($status) === false) { + return COL_UNKNOWN; + } + + switch ($status) { + case AGENT_MODULE_STATUS_NORMAL: + case AGENT_STATUS_NORMAL: + return COL_NORMAL; + + case AGENT_MODULE_STATUS_NOT_INIT: + case AGENT_STATUS_NOT_INIT: + return COL_NOTINIT; + + case AGENT_MODULE_STATUS_CRITICAL_BAD: + case AGENT_STATUS_CRITICAL: + return COL_CRITICAL; + + case AGENT_MODULE_STATUS_WARNING: + case AGENT_STATUS_WARNING: + return COL_WARNING; + + case AGENT_MODULE_STATUS_CRITICAL_ALERT: + case AGENT_MODULE_STATUS_WARNING_ALERT: + case AGENT_STATUS_ALERT_FIRED: + return COL_ALERTFIRED; + + case AGENT_MODULE_STATUS_UNKNOWN: + case AGENT_STATUS_UNKNOWN: + return COL_UNKNOWN; + + default: + // Ignored. + break; + } + + return COL_IGNORED; +} + + /** * Gets a module status an modify the status and title reference variables * @@ -2321,7 +2370,7 @@ function modules_get_status($id_agent_module, $db_status, $data, &$status, &$tit $status = STATUS_MODULE_OK; $title = __('NORMAL'); } else if ($db_status == AGENT_MODULE_STATUS_UNKNOWN) { - $status = STATUS_AGENT_DOWN; + $status = STATUS_MODULE_UNKNOWN; $last_status = modules_get_agentmodule_last_status($id_agent_module); switch ($last_status) { case AGENT_STATUS_NORMAL: diff --git a/pandora_console/include/functions_netflow.php b/pandora_console/include/functions_netflow.php index 84f3c26169..69aa0a1608 100644 --- a/pandora_console/include/functions_netflow.php +++ b/pandora_console/include/functions_netflow.php @@ -1,36 +1,60 @@ id_name)) or filters filtered * - * @param mixed Array with filter conditions to retrieve filters or false. + * @param mixed $filter Array with filter conditions to retrieve filters or + * false. * - * @return array List of all filters + * @return array List of all filters. */ function netflow_get_filters($filter=false) { @@ -56,9 +80,10 @@ function netflow_get_filters($filter=false) /** * Selects all netflow reports (array (id_name => id_name)) or filters filtered * - * @param mixed Array with filter conditions to retrieve filters or false. + * @param mixed $filter Array with filter conditions to retrieve filters or + * false. * - * @return array List of all filters + * @return array List of all filters. */ function netflow_get_reports($filter=false) { @@ -81,14 +106,20 @@ function netflow_get_reports($filter=false) } -// permite validar si un filtro pertenece a un grupo permitido para el usuario +/** + * Check if a filter owns to a certain group. + * + * @param integer $id_sg Id group to check. + * + * @return boolean True if user manages that group. + */ function netflow_check_filter_group($id_sg) { global $config; $id_group = db_get_value('id_group', 'tnetflow_filter', 'id_sg', $id_sg); $own_info = get_user_info($config['id_user']); - // Get group list that user has access + // Get group list that user has access. $groups_user = users_get_groups($config['id_user'], 'IW', $own_info['is_admin'], true); $groups_id = []; $has_permission = false; @@ -103,44 +134,12 @@ function netflow_check_filter_group($id_sg) } -/* - Permite validar si un informe pertenece a un grupo permitido para el usuario. - * Si mode = false entonces es modo godmode y solo puede ver el grupo All el admin - * Si es modo operation (mode = true) entonces todos pueden ver el grupo All - */ - -function netflow_check_report_group($id_report, $mode=false) -{ - global $config; - - if (!$mode) { - $own_info = get_user_info($config['id_user']); - $mode = $own_info['is_admin']; - } - - $id_group = db_get_value('id_group', 'tnetflow_report', 'id_report', $id_report); - - // Get group list that user has access - $groups_user = users_get_groups($config['id_user'], 'IW', $mode, true); - $groups_id = []; - $has_permission = false; - - foreach ($groups_user as $key => $groups) { - if ($groups['id_grupo'] == $id_group) { - return true; - } - } - - return false; -} - - /** * Get a filter. * - * @param int filter id to be fetched. - * @param array Extra filter. - * @param array Fields to be fetched. + * @param integer $id_sg Filter id to be fetched. + * @param mixed $filter Extra filter. + * @param mixed $fields Fields to be fetched. * * @return array A netflow filter matching id and filter. */ @@ -156,52 +155,11 @@ function netflow_filter_get_filter($id_sg, $filter=false, $fields=false) } -/** - * Get options. - * - * @param int filter id to be fetched. - * @param array Extra filter. - * @param array Fields to be fetched. - * - * @return array A netflow filter matching id and filter. - */ -function netflow_reports_get_reports($id_report, $filter=false, $fields=false) -{ - if (empty($id_report)) { - return false; - } - - if (! is_array($filter)) { - $filter = []; - } - - $filter['id_report'] = (int) $id_report; - - return db_get_row_filter('tnetflow_report', $filter, $fields); -} - - -function netflow_reports_get_content($id_rc, $filter=false, $fields=false) -{ - if (empty($id_rc)) { - return false; - } - - if (! is_array($filter)) { - $filter = []; - } - - $filter['id_rc'] = (int) $id_rc; - - return db_get_row_filter('tnetflow_report_content', $filter, $fields); -} - - /** * Compare two flows according to the 'data' column. * - * @param array a First flow. - * @param array b Second flow. + * @param array $a First flow. + * @param array $b Second flow. * * @return Result of the comparison. */ @@ -214,7 +172,9 @@ function compare_flows($a, $b) /** * Sort netflow data according to the 'data' column. * - * @param array netflow_data Netflow data array. + * @param array $netflow_data Netflow data array. + * + * @return void (Array passed by reference) */ function sort_netflow_data(&$netflow_data) { @@ -225,22 +185,22 @@ function sort_netflow_data(&$netflow_data) /** * Show a table with netflow statistics. * - * @param array data Statistic data. - * @param string start_date Start date. - * @param string end_date End date. - * @param string aggregate Aggregate field. - * @param string unit Unit to show. + * @param array $data Statistic data. + * @param string $start_date Start date. + * @param string $end_date End date. + * @param string $aggregate Aggregate field. * - * @return The statistics table. + * @return string HTML statistics table. */ -function netflow_stat_table($data, $start_date, $end_date, $aggregate, $unit) +function netflow_stat_table($data, $start_date, $end_date, $aggregate) { global $nfdump_date_format; $start_date = date($nfdump_date_format, $start_date); $end_date = date($nfdump_date_format, $end_date); $values = []; - $table->width = '40%'; + $table = new stdClass(); + $table->width = '100%'; $table->cellspacing = 0; $table->class = 'databox'; $table->data = []; @@ -249,7 +209,7 @@ function netflow_stat_table($data, $start_date, $end_date, $aggregate, $unit) $table->head = []; $table->head[0] = ''.netflow_format_aggregate($aggregate).''; - $table->head[1] = ''.netflow_format_unit($unit).''; + $table->head[1] = ''.__('Value').''; $table->style[0] = 'padding: 6px;'; $table->style[1] = 'padding: 6px;'; @@ -257,14 +217,13 @@ function netflow_stat_table($data, $start_date, $end_date, $aggregate, $unit) $agg = $data[$j]['agg']; if (!isset($values[$agg])) { $values[$agg] = $data[$j]['data']; - $table->data[$x][0] = $agg; - $table->data[$x][1] = format_numeric($data[$j]['data']).' '.netflow_format_unit($unit); } else { $values[$agg] += $data[$j]['data']; - $table->data[$x][0] = $agg; - $table->data[$x][1] = format_numeric($data[$j]['data']).' '.netflow_format_unit($unit); } + $table->data[$x][0] = $agg; + $table->data[$x][1] = network_format_bytes($data[$j]['data']); + $j++; $x++; } @@ -276,14 +235,14 @@ function netflow_stat_table($data, $start_date, $end_date, $aggregate, $unit) /** * Show a table with netflow data. * - * @param array data Netflow data. - * @param string start_date Start date. - * @param string end_date End date. - * @param string aggregate Aggregate field. + * @param array $data Netflow data. + * @param string $start_date Start date. + * @param string $end_date End date. + * @param string $aggregate Aggregate field. * - * @return The statistics table. + * @return string HTML data table. */ -function netflow_data_table($data, $start_date, $end_date, $aggregate, $unit) +function netflow_data_table($data, $start_date, $end_date, $aggregate) { global $nfdump_date_format; @@ -291,7 +250,7 @@ function netflow_data_table($data, $start_date, $end_date, $aggregate, $unit) $start_date = date($nfdump_date_format, $start_date); $end_date = date($nfdump_date_format, $end_date); - // Set the format + // Set the format. if ($period <= SECONDS_6HOURS) { $time_format = 'H:i:s'; } else if ($period < SECONDS_1DAY) { @@ -305,6 +264,7 @@ function netflow_data_table($data, $start_date, $end_date, $aggregate, $unit) } $values = []; + $table = new stdClass(); $table->size = ['100%']; $table->class = 'databox'; $table->cellspacing = 0; @@ -332,7 +292,7 @@ function netflow_data_table($data, $start_date, $end_date, $aggregate, $unit) $table->style[1] = 'padding: 4px;'; } - // No aggregates + // No aggregates. if ($source_count == 0) { $table->head[1] = __('Data'); $table->align[1] = 'right'; @@ -340,21 +300,17 @@ function netflow_data_table($data, $start_date, $end_date, $aggregate, $unit) foreach ($data as $timestamp => $value) { $table->data[$i][0] = date($time_format, $timestamp); - $table->data[$i][1] = format_numeric($value['data']).' '.netflow_format_unit($unit); + $table->data[$i][1] = network_format_bytes($value['data']); $i++; } - } - // Aggregates - else { + } else { $i = 0; foreach ($data['data'] as $timestamp => $values) { $table->data[$i][0] = date($time_format, $timestamp); for ($j = 0; $j < $source_count; $j++) { - if (isset($values[$source_index[$j]])) { - $table->data[$i][($j + 1)] = format_numeric($values[$source_index[$j]]).' '.netflow_format_unit($unit); - } else { - $table->data[$i][($j + 1)] = (0).' '.netflow_format_unit($unit); - } + $table->data[$i][($j + 1)] = network_format_bytes( + $values[$source_index[$j]] + ); } $i++; @@ -368,18 +324,19 @@ function netflow_data_table($data, $start_date, $end_date, $aggregate, $unit) /** * Show a table with a traffic summary. * - * @param array data Summary data. + * @param array $data Summary data. * - * @return The statistics table. + * @return string HTML summary table. */ function netflow_summary_table($data) { global $nfdump_date_format; $values = []; - $table->size = ['50%']; + $table = new stdClass(); $table->cellspacing = 0; $table->class = 'databox'; + $table->styleTable = 'width: 100%'; $table->data = []; $table->style[0] = 'font-weight: bold; padding: 6px'; @@ -387,32 +344,32 @@ function netflow_summary_table($data) $row = []; $row[] = __('Total flows'); - $row[] = format_numeric($data['totalflows']); + $row[] = format_for_graph($data['totalflows'], 2); $table->data[] = $row; $row = []; $row[] = __('Total bytes'); - $row[] = format_numeric($data['totalbytes']); + $row[] = network_format_bytes($data['totalbytes']); $table->data[] = $row; $row = []; $row[] = __('Total packets'); - $row[] = format_numeric($data['totalpackets']); + $row[] = format_for_graph($data['totalpackets'], 2); $table->data[] = $row; $row = []; $row[] = __('Average bits per second'); - $row[] = format_numeric($data['avgbps']); + $row[] = network_format_bytes($data['avgbps']); $table->data[] = $row; $row = []; $row[] = __('Average packets per second'); - $row[] = format_numeric($data['avgpps']); + $row[] = format_for_graph($data['avgpps'], 2); $table->data[] = $row; $row = []; $row[] = __('Average bytes per packet'); - $row[] = format_numeric($data['avgbpp']); + $row[] = format_for_graph($data['avgbpp'], 2); $table->data[] = $row; $html = html_print_table($table, true); @@ -424,7 +381,7 @@ function netflow_summary_table($data) /** * Returns 1 if the given address is a network address. * - * @param string address Host or network address. + * @param string $address Host or network address. * * @return 1 if the address is a network address, 0 otherwise. */ @@ -441,225 +398,173 @@ function netflow_is_net($address) /** * Returns netflow data for the given period in an array. * - * @param string start_date Period start date. - * @param string end_date Period end date. - * @param string filter Netflow filter. - * @param string aggregate Aggregate field. - * @param int max Maximum number of aggregates. - * @param string unit Unit to show. + * @param string $start_date Period start date. + * @param string $end_date Period end date. + * @param mixed $interval_length Resolution points or hourly or daily. + * @param string $filter Netflow filter. + * @param string $aggregate Aggregate field. + * @param integer $max Maximum number of aggregates. + * @param boolean $absolute True to give the absolute data and false + * 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. * - * @return An array with netflow stats. + * @return array An array with netflow stats. */ -function netflow_get_data($start_date, $end_date, $interval_length, $filter, $aggregate, $max, $unit, $connection_name='', $address_resolution=false) -{ +function netflow_get_data( + $start_date, + $end_date, + $interval_length, + $filter, + $aggregate, + $max, + $absolute, + $connection_name='', + $address_resolution=false +) { global $nfdump_date_format; global $config; - // Requesting remote data + // Requesting remote data. if (defined('METACONSOLE') && $connection_name != '') { - $data = metaconsole_call_remote_api($connection_name, 'netflow_get_data', "$start_date|$end_date|$interval_length|".base64_encode(json_encode($filter))."|$aggregate|$max|$unit".(int) $address_resolution); + $data = metaconsole_call_remote_api( + $connection_name, + 'netflow_get_data', + "$start_date|$end_date|$interval_length|".base64_encode(json_encode($filter))."|$aggregate|$max|1".(int) $address_resolution + ); return json_decode($data, true); } - // Calculate the number of intervals - if ($interval_length <= 0) { - // $num_intervals = $config['graph_res'] * 50; - $num_intervals = 250; - $period = ($end_date - $start_date); - $interval_length = (int) ($period / $num_intervals); - } else { - $period = ($end_date - $start_date); - $num_intervals = (int) ($period / $interval_length); + if ($start_date > $end_date) { + return []; } - // Set a max number of intervals - if ($num_intervals > $config['netflow_max_resolution']) { - $num_intervals = $config['netflow_max_resolution']; - $interval_length = (int) ($period / $num_intervals); + // Calculate the number of intervals. + $multiplier_time = ($end_date - $start_date); + switch ($interval_length) { + case NETFLOW_RES_LOWD: + case NETFLOW_RES_MEDD: + case NETFLOW_RES_HID: + case NETFLOW_RES_ULTRAD: + $multiplier_time = ceil(($end_date - $start_date) / $interval_length); + break; + + case NETFLOW_RES_HOURLY: + $multiplier_time = SECONDS_1HOUR; + break; + + case NETFLOW_RES_DAILY: + $multiplier_time = SECONDS_1DAY; + break; + + default: + $multiplier_time = ($end_date - $start_date); + break; } - // If there is aggregation calculate the top n - if ($aggregate != 'none') { - $values['data'] = []; - $values['sources'] = []; - - // Get the command to call nfdump - $command = netflow_get_command($filter); - - // Suppress the header line and the statistics at the bottom and configure piped output - $command .= ' -q -o csv'; - - // Call nfdump - $agg_command = $command." -n $max -s $aggregate/bytes -t ".date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); - exec($agg_command, $string); - - // Remove the first line - $string[0] = ''; - - // Parse aggregates - foreach ($string as $line) { - if ($line == '') { - continue; - } - - $val = explode(',', $line); - if ($aggregate == 'proto') { - $values['sources'][$val[3]] = 1; - } else { - $values['sources'][$val[4]] = 1; - } - } - - // Update the filter - switch ($aggregate) { - case 'proto': - $extra_filter = 'proto'; - break; - - default: - case 'srcip': - $extra_filter = 'ip_src'; - break; - case 'srcport': - $extra_filter = 'src_port'; - break; - - case 'dstip': - $extra_filter = 'ip_dst'; - break; - - case 'dstport': - $extra_filter = 'dst_port'; - break; - } - - if (isset($filter[$extra_filter]) && $filter[$extra_filter] != '') { - $filter[$extra_filter] .= ','; - } - - $filter[$extra_filter] = implode( - ',', - array_keys($values['sources']) + // Recalculate to not pass of netflow_max_resolution. + if ($config['netflow_max_resolution'] > 0 + && (($end_date - $start_date) / $multiplier_time) > 50 + ) { + $multiplier_time = ceil( + (($end_date - $start_date) / $config['netflow_max_resolution']) ); - } else { - $values = []; } - // Address resolution start - $get_hostnames = false; - if ($address_resolution && ($aggregate == 'srcip' || $aggregate == 'dstip')) { - $get_hostnames = true; - global $hostnames; + // Put all points into an array. + $intervals = [($start_date - $multiplier_time)]; + while ((end($intervals) < $end_date) === true) { + $intervals[] = (end($intervals) + $multiplier_time); + } - $sources = []; - foreach ($values['sources'] as $source => $value) { - if (!isset($hostnames[$source])) { - $hostname = gethostbyaddr($source); - if ($hostname !== false) { - $hostnames[$source] = $hostname; - $source = $hostname; + if (end($intervals) != $end_date) { + $intervals[] = $end_date; + } + + // Calculate the top values. + $values = netflow_get_top_data( + $start_date, + $end_date, + $filter, + $aggregate, + $max + ); + + // Update the filter to get properly next data. + netflow_update_second_level_filter( + $filter, + $aggregate, + array_keys($values['sources']) + ); + + // Resolve addresses if required. + $get_hostnames = false; + if ($address_resolution === true) { + global $hostnames; + netflow_address_resolution($values, $get_hostnames, $aggregate); + } + + foreach ($intervals as $k => $time) { + $interval_start = $time; + if (!isset($intervals[($k + 1)])) { + continue; + } + + $interval_end = $intervals[($k + 1)]; + + // Set default values. + foreach ($values['sources'] as $source => $discard) { + $values['data'][$interval_end][$source] = 0; + } + + $data = netflow_get_stats( + $interval_start, + $interval_end, + $filter, + $aggregate, + $max, + $absolute, + $connection_name + ); + + foreach ($data as $line) { + // Address resolution start. + if ($get_hostnames) { + if (!isset($hostnames[$line['agg']])) { + $hostname = false; + // Trying to get something like an IP from the description. + if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $line['agg'], $matches) + || preg_match( + "/(((?=(?>.*?(::))(?!.+\3)))\3?|([\dA-F]{1,4}(\3|:?)|\2))(?4){5}((?4){2}|(25[0-5]| + (2[0-4]|1\d|[1-9])?\d)(\.(?7)){3})/i", + $line['agg'], + $matches + ) + ) { + if ($matches[0]) { + $hostname = gethostbyaddr($line['agg']); + } + } + + if ($hostname !== false) { + $hostnames[$line['agg']] = $hostname; + $line['agg'] = $hostname; + } + } else { + $line['agg'] = $hostnames[$line['agg']]; } - } else { - $source = $hostnames[$source]; } - $sources[$source] = $value; - } - - $values['sources'] = $sources; - } - - // Address resolution end - $interval_start = $start_date; - for ($i = 0; $i < $num_intervals; $i++, $interval_start += ($interval_length + 1)) { - $interval_end = ($interval_start + $interval_length); - if ($interval_end > $end_date) { - $interval_end = $end_date; - } - - if ($aggregate == 'none') { - $data = netflow_get_summary($interval_start, $interval_end, $filter, $connection_name); - if (! isset($data['totalbytes'])) { - $values[$interval_start]['data'] = 0; + // Address resolution end. + if (! isset($values['sources'][$line['agg']])) { continue; } - switch ($unit) { - case 'megabytes': - $values[$interval_start]['data'] = ($data['totalbytes'] / 1048576); - break; - - case 'megabytespersecond': - $values[$interval_start]['data'] = ($data['avgbps'] / 1048576 / 8); - break; - - case 'kilobytes': - $values[$interval_start]['data'] = ($data['totalbytes'] / 1024); - break; - - case 'kilobytespersecond': - $values[$interval_start]['data'] = ($data['avgbps'] / 1024 / 8); - break; - - default: - $values[$interval_start]['data'] = $data['totalbytes']; - break; - } - } else { - // Set default values - foreach ($values['sources'] as $source => $discard) { - $values['data'][$interval_start][$source] = 0; - } - - $data = netflow_get_stats( - $interval_start, - $interval_end, - $filter, - $aggregate, - $max, - $unit, - $connection_name - ); - - foreach ($data as $line) { - // Address resolution start - if ($get_hostnames) { - if (!isset($hostnames[$line['agg']])) { - $hostname = false; - // Trying to get something like an IP from the description - if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $line['agg'], $matches) - || preg_match( - "/(((?=(?>.*?(::))(?!.+\3)))\3?|([\dA-F]{1,4}(\3|:?)|\2))(?4){5}((?4){2}|(25[0-5]| - (2[0-4]|1\d|[1-9])?\d)(\.(?7)){3})/i", - $line['agg'], - $matches - ) - ) { - if ($matches[0]) { - $hostname = gethostbyaddr($line['agg']); - } - } - - if ($hostname !== false) { - $hostnames[$line['agg']] = $hostname; - $line['agg'] = $hostname; - } - } else { - $line['agg'] = $hostnames[$line['agg']]; - } - } - - // Address resolution end - if (! isset($values['sources'][$line['agg']])) { - continue; - } - - $values['data'][$interval_start][$line['agg']] = $line['data']; - } + $values['data'][$interval_end][$line['agg']] = $line['data']; } } - if (($aggregate != 'none') && (empty($values['data']))) { + if (empty($values['data'])) { return []; } @@ -670,29 +575,40 @@ function netflow_get_data($start_date, $end_date, $interval_length, $filter, $ag /** * Returns netflow stats for the given period in an array. * - * @param string start_date Period start date. - * @param string end_date Period end date. - * @param string filter Netflow filter. - * @param string aggregate Aggregate field. - * @param int max Maximum number of aggregates. - * @param string unit Unit to show. + * @param string $start_date Period start date. + * @param string $end_date Period end date. + * @param string $filter Netflow filter. + * @param string $aggregate Aggregate field. + * @param integer $max Maximum number of aggregates. + * @param boolean $absolute True to give the absolute data and false + * 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. * - * @return An array with netflow stats. + * @return array With netflow stats. */ -function netflow_get_stats($start_date, $end_date, $filter, $aggregate, $max, $unit, $connection_name='', $address_resolution=false) -{ +function netflow_get_stats( + $start_date, + $end_date, + $filter, + $aggregate, + $max, + $absolute=true, + $connection_name='', + $address_resolution=false +) { global $config, $nfdump_date_format; - // Requesting remote data + // Requesting remote data. if (defined('METACONSOLE') && $connection_name != '') { - $data = metaconsole_call_remote_api($connection_name, 'netflow_get_stats', "$start_date|$end_date|".base64_encode(json_encode($filter))."|$aggregate|$max|$unit|".(int) $address_resolution); + $data = metaconsole_call_remote_api($connection_name, 'netflow_get_stats', "$start_date|$end_date|".base64_encode(json_encode($filter))."|$aggregate|$max|$absolute|".(int) $address_resolution); return json_decode($data, true); } - // Get the command to call nfdump + // Get the command to call nfdump. $command = netflow_get_command($filter); - // Execute nfdump + // Execute nfdump. $command .= " -o csv -q -n $max -s $aggregate/bytes -t ".date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); exec($command, $string); @@ -700,7 +616,7 @@ function netflow_get_stats($start_date, $end_date, $filter, $aggregate, $max, $u return []; } - // Remove the first line + // Remove the first line. $string[0] = ''; $i = 0; @@ -716,60 +632,35 @@ function netflow_get_stats($start_date, $end_date, $filter, $aggregate, $max, $u $values[$i]['date'] = $val[0]; $values[$i]['time'] = $val[1]; - // create field to sort array + // Create field to sort array. $datetime = $val[0]; $end_date = strtotime($datetime); $values[$i]['datetime'] = $end_date; - if ($aggregate == 'proto') { - $values[$i]['agg'] = $val[3]; - } else { - // Address resolution start - if ($address_resolution && ($aggregate == 'srcip' || $aggregate == 'dstip')) { - global $hostnames; + // Address resolution start. + if ($address_resolution && ($aggregate == 'srcip' || $aggregate == 'dstip')) { + global $hostnames; - if (!isset($hostnames[$val[4]])) { - $hostname = gethostbyaddr($val[4]); - if ($hostname !== false) { - $hostnames[$val[4]] = $hostname; - $val[4] = $hostname; - } - } else { - $val[4] = $hostnames[$val[4]]; + if (!isset($hostnames[$val[4]])) { + $hostname = gethostbyaddr($val[4]); + if ($hostname !== false) { + $hostnames[$val[4]] = $hostname; + $val[4] = $hostname; } + } else { + $val[4] = $hostnames[$val[4]]; } - - // Address resolution end - $values[$i]['agg'] = $val[4]; } + // Address resolution end. + $values[$i]['agg'] = $val[4]; + if (! isset($val[9])) { return []; } - switch ($unit) { - case 'megabytes': - $values[$i]['data'] = ($val[9] / 1048576); - break; - - case 'megabytespersecond': - $values[$i]['data'] = ($val[9] / 1048576 / $interval_length); - break; - - case 'kilobytes': - $values[$i]['data'] = ($val[9] / 1024); - break; - - case 'kilobytespersecond': - $values[$i]['data'] = ($val[9] / 1024 / $interval_length); - break; - - default: - case 'bytes': - $values[$i]['data'] = $val[9]; - break; - case 'bytespersecond': - $values[$i]['data'] = ($val[9] / $interval_length); - break; + $values[$i]['data'] = $val[9]; + if (!$absolute) { + $values[$i]['data'] = ($values[$i]['data'] / $interval_length); } $i++; @@ -784,27 +675,28 @@ function netflow_get_stats($start_date, $end_date, $filter, $aggregate, $max, $u /** * Returns a traffic summary for the given period in an array. * - * @param string start_date Period start date. - * @param string end_date Period end date. - * @param string filter Netflow filter. + * @param string $start_date Period start date. + * @param string $end_date Period end date. + * @param string $filter Netflow filter. + * @param string $connection_name Node name when data is get in meta. * - * @return An array with netflow stats. + * @return array With netflow summary data. */ function netflow_get_summary($start_date, $end_date, $filter, $connection_name='') { global $nfdump_date_format; global $config; - // Requesting remote data + // Requesting remote data. if (defined('METACONSOLE') && $connection_name != '') { $data = metaconsole_call_remote_api($connection_name, 'netflow_get_summary', "$start_date|$end_date|".base64_encode(json_encode($filter))); return json_decode($data, true); } - // Get the command to call nfdump + // Get the command to call nfdump. $command = netflow_get_command($filter); - // Execute nfdump + // Execute nfdump. $command .= ' -o csv -n 1 -s srcip/bytes -t '.date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); exec($command, $string); @@ -812,7 +704,7 @@ function netflow_get_summary($start_date, $end_date, $filter, $connection_name=' return []; } - // Read the summary + // Read the summary. $summary = explode(',', $string[5]); if (! isset($summary[5])) { return []; @@ -830,137 +722,174 @@ function netflow_get_summary($start_date, $end_date, $filter, $connection_name=' /** - * Returns a traffic record for the given period in an array. + * Returns a relationships data for the given period in an array. * - * @param string start_date Period start date. - * @param string end_date Period end date. - * @param string filter Netflow filter. - * @param int max Maximum number of elements. - * @param string unit to show. + * @param string $start_date Period start date. + * @param string $end_date Period end date. + * @param string $filter Netflow filter. + * @param integer $max Maximum number of elements. + * @param string $aggregate One of srcip, srcport, dstip, dstport. * - * @return An array with netflow stats. + * @return array With raw relationship data. */ -function netflow_get_record($start_date, $end_date, $filter, $max, $unit, $address_resolution=false) -{ +function netflow_get_relationships_raw_data( + $start_date, + $end_date, + $filter, + $max, + $aggregate +) { global $nfdump_date_format; global $config; - // TIME_START = 0; - // TIME_END = 1; - // DURATION = 2; - // SOURCE_ADDRESS = 3; - // DESTINATION_ADDRESS = 4; - // SOURCE_PORT = 5; - // DESTINATION_PORT = 6; - // PROTOCOL = 7; - // INPUT_BYTES = 12; - // Get the command to call nfdump + $max_data = netflow_get_top_data( + $start_date, + $end_date, + $filter, + $aggregate, + $max + ); + + // Update src and dst filter (both). + $sources_array = array_keys($max_data['sources']); + $is_ip = netflow_aggregate_is_ip($aggregate); + netflow_update_second_level_filter( + $filter, + ($is_ip === true) ? 'dstip' : 'dstport', + $sources_array + ); + netflow_update_second_level_filter( + $filter, + ($is_ip === true) ? 'srcip' : 'srcport', + $sources_array + ); + + // Get the command to call nfdump. + $command = sprintf( + '%s -q -o csv -n %s -s %s/bytes -t %s-%s', + netflow_get_command($filter), + NETFLOW_MAX_DATA_CIRCULAR_MESH, + 'record', + date($nfdump_date_format, $start_date), + date($nfdump_date_format, $end_date) + ); + + // Get the command to call nfdump. $command = netflow_get_command($filter); - // Execute nfdump - $command .= " -q -o csv -n $max -s record/bytes -t ".date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $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); exec($command, $result); if (! is_array($result)) { + return [ + 'lines' => [], + 'sources' => [], + ]; + } + + return [ + 'lines' => $result, + 'sources' => $sources_array, + ]; +} + + +/** + * Parse the raw relationships data to be painted by circular mesh chart. + * + * @param array $result Lines gotten from nfdump call. + * @param array $sources_array Array with sources involved in the chart. + * @param boolean $is_ip Is ip or port. + * + * @return array With data to be parsed on circular mesh chart. + */ +function netflow_parse_relationships_for_circular_mesh( + $result, + $sources_array, + $is_ip +) { + if (empty($result)) { return []; } - $values = []; - foreach ($result as $key => $line) { - $data = []; + // Initialize some data structures. + $data = [ + 'elements' => [], + 'matrix' => [], + ]; + $initial_data = []; + // This array has the ips or port like keys and the array position as value. + $inverse_sources_array = array_flip($sources_array); + foreach ($sources_array as $sdata) { + $data['elements'][$inverse_sources_array[$sdata]] = $sdata; + $initial_data[$inverse_sources_array[$sdata]] = 0; + } + foreach ($sources_array as $sdata) { + $data['matrix'][$inverse_sources_array[$sdata]] = $initial_data; + } + + // Port are situated in a different places from addreses. + $src_key = ($is_ip === true) ? 3 : 5; + $dst_key = ($is_ip === true) ? 4 : 6; + // Store a footprint of initial data to be compared at the end. + $freeze_data = md5(serialize($data)); + foreach ($result as $line) { + if (empty($line) === true) { + continue; + } + + // Parse the line. $items = explode(',', $line); - $data['time_start'] = $items[0]; - $data['time_end'] = $items[1]; - $data['duration'] = ($items[2] / 1000); - $data['source_address'] = $items[3]; - $data['destination_address'] = $items[4]; - $data['source_port'] = $items[5]; - $data['destination_port'] = $items[6]; - $data['protocol'] = $items[7]; + // Get the required data. + $src_item = $inverse_sources_array[$items[$src_key]]; + $dst_item = $inverse_sources_array[$items[$dst_key]]; + $value = $items[12]; - switch ($unit) { - case 'megabytes': - $data['data'] = ($items[12] / 1048576); - break; - - case 'megabytespersecond': - $data['data'] = ($items[12] / 1048576 / $data['duration']); - break; - - case 'kilobytes': - $data['data'] = ($items[12] / 1024); - break; - - case 'kilobytespersecond': - $data['data'] = ($items[12] / 1024 / $data['duration']); - break; - - default: - case 'bytes': - $data['data'] = $items[12]; - break; - case 'bytespersecond': - $data['data'] = ($items[12] / $data['duration']); - break; + // Check if valid data. + if (!isset($value) + || !isset($data['matrix'][$dst_item][$src_item]) + || !isset($data['matrix'][$src_item][$dst_item]) + ) { + continue; } - $values[] = $data; + // Update the value. + $data['matrix'][$src_item][$dst_item] += (int) $value; } - // Address resolution start - if ($address_resolution) { - global $hostnames; - - for ($i = 0; $i < count($values); $i++) { - if (!isset($hostnames[$values[$i]['source_address']])) { - $hostname = gethostbyaddr($values[$i]['source_address']); - if ($hostname !== false) { - $hostnames[$values[$i]['source_address']] = $hostname; - $values[$i]['source_address'] = $hostname; - } - } else { - $values[$i]['source_address'] = $hostnames[$values[$i]['source_address']]; - } - - if (!isset($hostnames[$values[$i]['destination_address']])) { - $hostname = gethostbyaddr($values[$i]['destination_address']); - if ($hostname !== false) { - $hostnames[$values[$i]['destination_address']] = $hostname; - $values[$i]['destination_address'] = $hostname; - } - } else { - $values[$i]['destination_address'] = $hostnames[$values[$i]['destination_address']]; - } - } + // Comparte footprints. + if ($freeze_data === md5(serialize($data))) { + // Taht means that all relationships are 0. + return []; } - // Address resolution end - return $values; + return $data; } /** * Returns the command needed to run nfdump for the given filter. * - * @param array filter Netflow filter. + * @param array $filter Netflow filter. * - * @return Command to run. + * @return string Command to run. */ function netflow_get_command($filter) { global $config; - // Build command + // Build command. $command = io_safe_output($config['netflow_nfdump']).' -N'; - // Netflow data path + // Netflow data path. if (isset($config['netflow_path']) && $config['netflow_path'] != '') { $command .= ' -R. -M '.$config['netflow_path']; } - // Filter options + // Filter options. $command .= netflow_get_filter_arguments($filter); return $command; @@ -970,13 +899,13 @@ function netflow_get_command($filter) /** * Returns the nfdump command line arguments that match the given filter. * - * @param array filter Netflow filter. + * @param array $filter Netflow filter. * - * @return Command line argument string. + * @return string Command line argument string. */ function netflow_get_filter_arguments($filter) { - // Advanced filter + // Advanced filter. $filter_args = ''; if ($filter['advanced_filter'] != '') { $filter_args = preg_replace('/["\r\n]/', '', io_safe_output($filter['advanced_filter'])); @@ -987,7 +916,7 @@ function netflow_get_filter_arguments($filter) $filter_args .= ' "(router ip '.$filter['router_ip'].')'; } - // Normal filter + // Normal filter. if ($filter['ip_dst'] != '') { $filter_args .= ' "('; $val_ipdst = explode(',', io_safe_output($filter['ip_dst'])); @@ -1102,74 +1031,11 @@ function netflow_get_filter_arguments($filter) function netflow_get_chart_types() { return [ - 'netflow_area' => __('Area graph'), - 'netflow_pie_summatory' => __('Pie graph and Summary table'), - 'netflow_statistics' => __('Statistics table'), - 'netflow_data' => __('Data table'), - 'netflow_mesh' => __('Circular mesh'), - 'netflow_host_treemap' => __('Host detailed traffic'), - ]; -} - - -/** - * Gets valid intervals for a netflow chart in the format: - * - * interval_length => interval_description - * - * @return array of valid intervals. - */ -function netflow_get_valid_intervals() -{ - return [ - (string) SECONDS_10MINUTES => __('10 mins'), - (string) SECONDS_15MINUTES => __('15 mins'), - (string) SECONDS_30MINUTES => __('30 mins'), - (string) SECONDS_1HOUR => __('1 hour'), - (string) SECONDS_2HOUR => __('2 hours'), - (string) SECONDS_5HOUR => __('5 hours'), - (string) SECONDS_12HOURS => __('12 hours'), - (string) SECONDS_1DAY => __('1 day'), - (string) SECONDS_2DAY => __('2 days'), - (string) SECONDS_5DAY => __('5 days'), - (string) SECONDS_15DAYS => __('15 days'), - (string) SECONDS_1WEEK => __('Last week'), - (string) SECONDS_1MONTH => __('Last month'), - (string) SECONDS_2MONTHS => __('2 months'), - (string) SECONDS_3MONTHS => __('3 months'), - (string) SECONDS_6MONTHS => __('6 months'), - (string) SECONDS_1YEAR => __('Last year'), - (string) SECONDS_2YEARS => __('2 years'), - ]; -} - - -/** - * Gets valid intervals for a netflow chart in the format: - * - * interval_length => interval_description - * - * @return array of valid intervals. - */ -function netflow_get_valid_subintervals() -{ - return [ - (string) SECONDS_1MINUTE => __('1 min'), - (string) SECONDS_2MINUTES => __('2 mins'), - (string) SECONDS_5MINUTES => __('5 mins'), - (string) SECONDS_10MINUTES => __('10 mins'), - (string) SECONDS_15MINUTES => __('15 mins'), - (string) SECONDS_30MINUTES => __('30 mins'), - (string) SECONDS_1HOUR => __('1 hour'), - (string) SECONDS_2HOUR => __('2 hours'), - (string) SECONDS_5HOUR => __('5 hours'), - (string) SECONDS_12HOURS => __('12 hours'), - (string) SECONDS_1DAY => __('1 day'), - (string) SECONDS_2DAY => __('2 days'), - (string) SECONDS_5DAY => __('5 days'), - (string) SECONDS_15DAYS => __('15 days'), - (string) SECONDS_1WEEK => __('1 week'), - (string) SECONDS_1MONTH => __('1 month'), + 'netflow_area' => __('Area graph'), + 'netflow_summary' => __('Summary'), + 'netflow_data' => __('Data table'), + 'netflow_mesh' => __('Circular mesh'), + 'netflow_host_treemap' => __('Host detailed traffic'), ]; } @@ -1177,20 +1043,31 @@ function netflow_get_valid_subintervals() /** * Draw a netflow report item. * - * @param string start_date Period start date. - * @param string end_date Period end date. - * @param string interval_length Interval length in seconds (num_intervals * interval_length = start_date - end_date). - * @param string type Chart type. - * @param array filter Netflow filter. - * @param int max_aggregates Maximum number of aggregates. - * @param string output Output format. Only HTML and XML are supported. + * @param string $start_date Period start date. + * @param string $end_date Period end date. + * @param mixed $interval_length Resolution points or hourly or daily. + * @param string $type Chart type. + * @param array $filter Netflow filter. + * @param integer $max_aggregates Maximum number of aggregates. + * @param string $connection_name Node name when data is get in meta. + * @param string $output Output format. Only HTML, PDF and XML + * are supported. + * @param boolean $address_resolution True to resolve ips to hostnames. * - * @return The netflow report in the appropriate format. + * @return string The netflow report in the appropriate format. */ -function netflow_draw_item($start_date, $end_date, $interval_length, $type, $filter, $max_aggregates, $connection_name='', $output='HTML', $address_resolution=false) -{ +function netflow_draw_item( + $start_date, + $end_date, + $interval_length, + $type, + $filter, + $max_aggregates, + $connection_name='', + $output='HTML', + $address_resolution=false +) { $aggregate = $filter['aggregate']; - $unit = $filter['output']; $interval = ($end_date - $start_date); if (defined('METACONSOLE')) { $width = 950; @@ -1200,106 +1077,17 @@ function netflow_draw_item($start_date, $end_date, $interval_length, $type, $fil $height = 320; - // Process item + // Process item. switch ($type) { - case '0': case 'netflow_area': - $data = netflow_get_data($start_date, $end_date, $interval_length, $filter, $aggregate, $max_aggregates, $unit, $connection_name, $address_resolution); - if (empty($data)) { - break; - } - - if ($aggregate != 'none') { - if ($output == 'HTML') { - $html = ''.__('Unit').': '.netflow_format_unit($unit); - $html .= ' '.__('Aggregate').': '.netflow_format_aggregate($aggregate); - if ($interval_length != 0) { - $html .= ' '._('Resolution').": $interval_length ".__('seconds'); - } - - $html .= graph_netflow_aggregate_area($data, $interval, $width, $height, netflow_format_unit($unit)); - return $html; - } else if ($output == 'PDF') { - $html = ''.__('Unit').': '.netflow_format_unit($unit); - $html .= ' '.__('Aggregate').': '.netflow_format_aggregate($aggregate); - if ($interval_length != 0) { - $html .= ' '._('Resolution').": $interval_length ".__('seconds'); - } - - $html .= graph_netflow_aggregate_area($data, $interval, $width, $height, netflow_format_unit($unit), 2, true); - return $html; - } else if ($output == 'XML') { - $xml = "$unit\n"; - $xml .= "$aggregate\n"; - $xml .= "$interval_length\n"; - $xml .= netflow_aggregate_area_xml($data); - return $xml; - } - } else { - if ($output == 'HTML') { - $html = ''.__('Unit').': '.netflow_format_unit($unit); - if ($interval_length != 0) { - $html .= ' '._('Resolution').": $interval_length ".__('seconds'); - } - - $html .= graph_netflow_total_area($data, $interval, 660, 320, netflow_format_unit($unit)); - return $html; - } else if ($output == 'PDF') { - $html = ''.__('Unit').': '.netflow_format_unit($unit); - if ($interval_length != 0) { - $html .= ' '._('Resolution').": $interval_length ".__('seconds'); - } - - $html .= graph_netflow_total_area($data, $interval, 660, 320, netflow_format_unit($unit), 2, true); - return $html; - } else if ($output == 'XML') { - $xml = "$unit\n"; - $xml .= "$interval_length\n"; - $xml .= netflow_total_area_xml($data); - return $xml; - } - } - break; - - case '2': - case 'netflow_data': - $data = netflow_get_data($start_date, $end_date, $interval_length, $filter, $aggregate, $max_aggregates, $unit, $connection_name, $address_resolution); - - if (empty($data)) { - break; - } - - if ($output == 'HTML' || $output == 'PDF') { - $html = ''.__('Unit').': '.netflow_format_unit($unit); - $html .= ' '.__('Aggregate').': '.netflow_format_aggregate($aggregate); - if ($interval_length != 0) { - $html .= ' '._('Resolution').": $interval_length ".__('seconds'); - } - - $html .= "
    "; - $html .= netflow_data_table($data, $start_date, $end_date, $aggregate, $unit); - $html .= '
    '; - - return $html; - } else if ($output == 'XML') { - $xml = "$unit\n"; - $xml .= "$aggregate\n"; - $xml .= "$interval_length\n"; - // Same as netflow_aggregate_area_xml - $xml .= netflow_aggregate_area_xml($data); - return $xml; - } - break; - - case '3': - case 'netflow_statistics': - $data = netflow_get_stats( + $data = netflow_get_data( $start_date, $end_date, + $interval_length, $filter, $aggregate, $max_aggregates, - $unit, + false, $connection_name, $address_resolution ); @@ -1308,14 +1096,55 @@ function netflow_draw_item($start_date, $end_date, $interval_length, $type, $fil } if ($output == 'HTML' || $output == 'PDF') { - $html = netflow_stat_table($data, $start_date, $end_date, $aggregate, $unit); + $html .= graph_netflow_aggregate_area( + $data, + $interval, + $width, + $height, + ($output === 'HTML') ? 1 : 2, + ($output === 'HTML'), + $end_date + ); return $html; } else if ($output == 'XML') { - return netflow_stat_xml($data); + $xml .= ''.$aggregate."\n"; + $xml .= ''.$interval_length."\n"; + $xml .= netflow_aggregate_area_xml($data); + return $xml; + } + break; + + case 'netflow_data': + $data = netflow_get_data( + $start_date, + $end_date, + $interval_length, + $filter, + $aggregate, + $max_aggregates, + true, + $connection_name, + $address_resolution + ); + if (empty($data)) { + break; + } + + if ($output == 'HTML' || $output == 'PDF') { + $html .= "
    "; + $html .= netflow_data_table($data, $start_date, $end_date, $aggregate); + $html .= '
    '; + + return $html; + } else if ($output == 'XML') { + $xml .= ''.$aggregate."\n"; + $xml .= ''.$interval_length."\n"; + // Same as netflow_aggregate_area_xml. + $xml .= netflow_aggregate_area_xml($data); + return $xml; } break; - case '4': case 'netflow_summary': $data_summary = netflow_get_summary( $start_date, @@ -1327,22 +1156,13 @@ function netflow_draw_item($start_date, $end_date, $interval_length, $type, $fil break; } - if ($output == 'HTML' || $output == 'PDF') { - return netflow_summary_table($data_summary); - } else if ($output == 'XML') { - return netflow_summary_xml($data_summary); - } - break; - - case '1': - case 'netflow_pie': $data_pie = netflow_get_stats( $start_date, $end_date, $filter, $aggregate, $max_aggregates, - $unit, + true, $connection_name, $address_resolution ); @@ -1350,131 +1170,65 @@ function netflow_draw_item($start_date, $end_date, $interval_length, $type, $fil break; } - if ($output == 'HTML') { - $html = ''.__('Unit').': '.netflow_format_unit($unit); - $html .= ' '.__('Aggregate').': '.netflow_format_aggregate($aggregate); - $html .= graph_netflow_aggregate_pie($data_pie, netflow_format_aggregate($aggregate)); + if ($output === 'HTML' || $output === 'PDF') { + $html = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= '
    '; + $html .= netflow_summary_table($data_summary); + $html .= ''; + $html .= graph_netflow_aggregate_pie( + $data_pie, + netflow_format_aggregate($aggregate), + ($output === 'HTML') ? 1 : 2, + ($output === 'HTML') + ); + $html .= '
    '; + $html .= netflow_stat_table( + $data_pie, + $start_date, + $end_date, + $aggregate + ); return $html; - } else if ($output == 'PDF') { - $html = ''.__('Unit').': '.netflow_format_unit($unit); - $html .= ' '.__('Aggregate').": $aggregate"; - $html .= graph_netflow_aggregate_pie($data_pie, netflow_format_aggregate($aggregate), 2, true); - return $html; - } else if ($output == 'XML') { - $xml = "$unit\n"; - $xml .= "$aggregate\n"; - $xml .= netflow_aggregate_pie_xml($data_pie); - return $xml; - } - break; - - case 'netflow_pie_summatory': - $data_summary = netflow_get_summary( - $start_date, - $end_date, - $filter, - $connection_name - ); - if (empty($data_summary)) { - break; - } - - $data_pie = netflow_get_stats( - $start_date, - $end_date, - $filter, - $aggregate, - $max_aggregates, - $unit, - $connection_name, - $address_resolution - ); - if (empty($data_pie)) { - break; - } - - switch ($output) { - case 'HTML': - $html = ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= '
    '; - $html .= netflow_summary_table($data_summary); - $html .= ''.__('Unit').': '.netflow_format_unit($unit); - $html .= ' '.__('Aggregate').': '.netflow_format_aggregate($aggregate); - $html .= ''; - $html .= graph_netflow_aggregate_pie($data_pie, netflow_format_aggregate($aggregate)); - $html .= '
    '; - return $html; - - break; - case 'PDF': - break; - - case 'XML': - return netflow_summary_xml($data_summary); - - break; + } else if ($output === 'XML') { + return netflow_summary_xml($data_summary, $data_pie); } break; case 'netflow_mesh': - $netflow_data = netflow_get_record($start_date, $end_date, $filter, $max_aggregates, $unit, $address_resolution); - - switch ($aggregate) { - case 'srcport': - case 'dstport': - $source_type = 'source_port'; - $destination_type = 'destination_port'; - break; - - default: - case 'dstip': - case 'srcip': - $source_type = 'source_address'; - $destination_type = 'destination_address'; - break; - } - - $data = []; - $data['elements'] = []; - $data['matrix'] = []; - foreach ($netflow_data as $record) { - if (!in_array($record[$source_type], $data['elements'])) { - $data['elements'][] = $record[$source_type]; - $data['matrix'][] = []; - } - - if (!in_array($record[$destination_type], $data['elements'])) { - $data['elements'][] = $record[$destination_type]; - $data['matrix'][] = []; - } - } - - for ($i = 0; $i < count($data['matrix']); $i++) { - $data['matrix'][$i] = array_fill(0, count($data['matrix']), 0); - } - - foreach ($netflow_data as $record) { - $source_key = array_search($record[$source_type], $data['elements']); - $destination_key = array_search($record[$destination_type], $data['elements']); - if ($source_key !== false && $destination_key !== false) { - $data['matrix'][$source_key][$destination_key] += $record['data']; - } - } + $data = netflow_get_relationships_raw_data( + $start_date, + $end_date, + $filter, + $max_aggregates, + $aggregate, + $address_resolution + ); + $data_circular = netflow_parse_relationships_for_circular_mesh( + $data['lines'], + $data['sources'], + netflow_aggregate_is_ip($aggregate) + ); $html = '
    '; - $html .= graph_netflow_circular_mesh($data, netflow_format_unit($unit), 700); + $html .= graph_netflow_circular_mesh($data_circular); $html .= '
    '; - return $html; - break; case 'netflow_host_treemap': - $netflow_data = netflow_get_record($start_date, $end_date, $filter, $max_aggregates, $unit, $address_resolution); - + $data_stats = netflow_get_stats( + $start_date, + $end_date, + $filter, + $aggregate, + $max_aggregates, + true, + $connection_name, + $address_resolution + ); switch ($aggregate) { case 'srcip': case 'srcport': @@ -1492,51 +1246,30 @@ function netflow_draw_item($start_date, $end_date, $interval_length, $type, $fil break; } - $data_aux = []; - foreach ($netflow_data as $record) { - $address = $record[$address_type]; - $port = $record[$port_type]; - - if (!isset($data_aux[$address])) { - $data_aux[$address] = []; - } - - if (!isset($data_aux[$address][$port])) { - $data_aux[$address][$port] = 0; - } - - $data_aux[$address][$port] += $record['data']; - } - + $data_graph = [ + 'name' => __('Host detailed traffic').': '.$type, + 'children' => [], + ]; $id = -1; - $data = []; - - if (! empty($netflow_data)) { - $data['name'] = __('Host detailed traffic').': '.$type; - $data['children'] = []; - - foreach ($data_aux as $address => $ports) { - $children = []; - $children['id'] = $id++; - $children['name'] = $address; - $children['children'] = []; - foreach ($ports as $port => $value) { - $children_data = []; - $children_data['id'] = $id++; - $children_data['name'] = $port; - $children_data['value'] = $value; - $children_data['tooltip_content'] = "$port: ".format_numeric($value).' '.netflow_format_unit($unit).''; - $children['children'][] = $children_data; - } - - $data['children'][] = $children; - } + foreach ($data_stats as $sdata) { + $data_graph['children'][] = [ + 'id' => $i++, + 'name' => $sdata['agg'], + 'children' => [ + [ + 'id' => $i++, + 'name' => $sdata['agg'], + 'value' => $sdata['data'], + 'tooltip_content' => network_format_bytes($sdata['data']), + ], + ], + ]; } - return graph_netflow_host_traffic($data, netflow_format_unit($unit), 'auto', 400); + return graph_netflow_host_traffic($data_graph, 'auto', 400); - break; default: + // Nothing to do. break; } @@ -1546,107 +1279,32 @@ function netflow_draw_item($start_date, $end_date, $interval_length, $type, $fil } -/** - * Render a netflow report as an XML. - * - * @param int ID of the netflow report. - * @param string end_date Period start date. - * @param string end_date Period end date. - * @param string interval_length Interval length in seconds (num_intervals * interval_length = start_date - end_date). - */ -function netflow_xml_report($id, $start_date, $end_date, $interval_length=0) -{ - // Get report data - $report = db_get_row_sql('SELECT * FROM tnetflow_report WHERE id_report ='.(int) $id); - if ($report === false) { - echo ''.__('Error generating report')."\n"; - return; - } - - // Print report header - $time = get_system_time(); - echo ''; - echo "\n"; - echo " \n"; - echo ' '.$time."\n"; - echo ' '.date('r', $time)."\n"; - echo " \n"; - echo ' '.io_safe_output($report['id_name'])."\n"; - echo ' '.io_safe_output($report['description'])."\n"; - echo ' '.date('r', $start_date)."\n"; - echo ' '.date('r', $end_date)."\n"; - - // Get netflow item types - $item_types = netflow_get_chart_types(); - - // Print report items - $report_contents = db_get_all_rows_sql( - "SELECT * - FROM tnetflow_report_content - WHERE id_report='".$report['id_report']."' - ORDER BY `order`" - ); - foreach ($report_contents as $content) { - // Get item filters - $filter = db_get_row_sql( - "SELECT * - FROM tnetflow_filter - WHERE id_sg = '".io_safe_input($content['id_filter'])."'", - false, - true - ); - if ($filter === false) { - continue; - } - - echo " \n"; - echo ' '.io_safe_output($content['description'])."\n"; - echo ' '.io_safe_output($item_types[$content['show_graph']])."\n"; - echo ' '.$content['max']."\n"; - echo " \n"; - echo ' '.io_safe_output($filter['id_name'])."\n"; - echo ' '.io_safe_output($filter['ip_src'])."\n"; - echo ' '.io_safe_output($filter['ip_dst'])."\n"; - echo ' '.io_safe_output($filter['src_port'])."\n"; - echo ' '.io_safe_output($filter['src_port'])."\n"; - echo ' '.io_safe_output($filter['advanced_filter'])."\n"; - echo ' '.io_safe_output($filter['aggregate'])."\n"; - echo ' '.io_safe_output($filter['output'])."\n"; - echo " \n"; - - echo netflow_draw_item($start_date, $end_date, $interval_length, $content['show_graph'], $filter, $content['max'], $report['server_name'], 'XML'); - - echo " \n"; - } - - echo "\n"; -} - - /** * Render an aggregated area chart as an XML. * - * @param array Netflow data. + * @param array $data Netflow data. + * + * @return void XML is echoed. */ function netflow_aggregate_area_xml($data) { - // Print source information + // Print source information. if (isset($data['sources'])) { echo "\n"; foreach ($data['sources'] as $source => $discard) { - echo "$source\n"; + echo ''.$source."\n"; } echo "\n"; - // Print flow information + // Print flow information. echo "\n"; foreach ($data['data'] as $timestamp => $flow) { echo "\n"; echo ' '.$timestamp."\n"; echo " \n"; foreach ($flow as $source => $data) { - echo " $source\n"; + echo ' '.$source."\n"; echo ' '.$data."\n"; } @@ -1669,133 +1327,49 @@ function netflow_aggregate_area_xml($data) } -/** - * Render an area chart as an XML. - * - * @param array Netflow data. - */ -function netflow_total_area_xml($data) -{ - // Print flow information - $xml = "\n"; - foreach ($data as $timestamp => $flow) { - $xml .= "\n"; - $xml .= ' '.$timestamp."\n"; - $xml .= ' '.$flow['data']."\n"; - $xml .= "\n"; - } - - $xml .= "\n"; - - return $xml; -} - - -/** - * Render a pie chart as an XML. - * - * @param array Netflow data. - */ -function netflow_aggregate_pie_xml($data) -{ - // Calculate total - $total = 0; - foreach ($data as $flow) { - $total += $flow['data']; - } - - if ($total == 0) { - return; - } - - // Print percentages - echo "\n"; - foreach ($data as $flow) { - echo ''.$flow['agg']."\n"; - echo ''.format_numeric((100 * $flow['data'] / $total), 2)."%\n"; - } - - echo "\n"; -} - - -/** - * Render a stats table as an XML. - * - * @param array Netflow data. - */ -function netflow_stat_xml($data) -{ - // Print stats - $xml .= "\n"; - foreach ($data as $flow) { - $xml .= ''.$flow['agg']."\n"; - $xml .= ''.$flow['data']."\n"; - } - - $xml .= "\n"; - - return $xml; -} - - /** * Render a summary table as an XML. * - * @param array Netflow data. + * @param array $data Netflow data. + * @param array $rows_data Table info (top N hosts). + * + * @return string Wiht XML data. */ -function netflow_summary_xml($data) +function netflow_summary_xml($data, $rows_data) { - // Print summary + // Print summary. $xml = "\n"; - $xml .= ' '.$data['totalflows']."\n"; - $xml .= ' '.$data['totalbytes']."\n"; - $xml .= ' '.$data['totalbytes']."\n"; - $xml .= ' '.$data['avgbps']."\n"; - $xml .= ' '.$data['avgpps']."\n"; - $xml .= ' '.$data['avgpps']."\n"; + $xml = " \n"; + $xml .= ' '.$data['totalflows']."\n"; + $xml .= ' '.$data['totalbytes']."\n"; + $xml .= ' '.$data['totalbytes']."\n"; + $xml .= ' '.$data['avgbps']."\n"; + $xml .= ' '.$data['avgpps']."\n"; + $xml .= ' '.$data['avgpps']."\n"; + $xml .= " \n"; + + // Add the data table. + $xml .= " \n"; + foreach ($rows_data as $d) { + $xml .= "\n"; + $xml .= ''.$d['agg']."\n"; + $xml .= ''.$d['data']."\n"; + $xml .= "\n"; + } + + $xml .= " \n"; $xml .= "\n"; return $xml; } -/** - * Return a string describing the given unit. - * - * @param string Netflow unit. - */ -function netflow_format_unit($unit) -{ - switch ($unit) { - case 'megabytes': - return __('MB'); - - case 'megabytespersecond': - return __('MB/s'); - - case 'kilobytes': - return __('kB'); - - case 'kilobytespersecond': - return __('kB/s'); - - case 'bytes': - return __('Bytes'); - - case 'bytespersecond': - return __('B/s'); - - default: - return ''; - } -} - - /** * Return a string describing the given aggregate. * - * @param string Netflow aggregate. + * @param string $aggregate Netflow aggregate. + * + * @return string With formatted aggregate. */ function netflow_format_aggregate($aggregate) { @@ -1806,9 +1380,6 @@ function netflow_format_aggregate($aggregate) case 'dstip': return __('Dst IP'); - case 'proto': - return __('Protocol'); - case 'srcip': return __('Src IP'); @@ -1824,20 +1395,20 @@ function netflow_format_aggregate($aggregate) /** * Check the nfdump binary for compatibility. * - * @param string nfdump binary full path. + * @param string $nfdump_binary Nfdump binary full path. * - * @return 1 if the binary does not exist or is not executable, 2 if a + * @return integer 1 if the binary does not exist or is not executable, 2 if a * version older than 1.6.8 is installed or the version cannot be * determined, 0 otherwise. */ function netflow_check_nfdump_binary($nfdump_binary) { - // Check that the binary exists and is executable + // Check that the binary exists and is executable. if (! is_executable($nfdump_binary)) { return 1; } - // Check at least version 1.6.8 + // Check at least version 1.6.8. $output = ''; $rc = -1; exec($nfdump_binary.' -V', $output, $rc); @@ -1866,3 +1437,462 @@ function netflow_check_nfdump_binary($nfdump_binary) return 2; } + + +/** + * Get the netflow datas to build a netflow explorer data structure. + * + * @param integer $max Number of result displayed. + * @param string $top_action Action to do (listeners,talkers,tcp or udp). + * @param integer $start_date In utimestamp. + * @param integer $end_date In utimestamp. + * @param string $filter Ip to filter. + * @param string $order Select one of bytes,pkts,flow. + * + * @return array With data (host, sum_bytes, sum_pkts and sum_flows). + */ +function netflow_get_top_summary( + $max, + $top_action, + $start_date, + $end_date, + $filter='', + $order='bytes' +) { + global $nfdump_date_format; + $netflow_filter = []; + $sort = ''; + switch ($top_action) { + case 'listeners': + if (empty(!$filter)) { + $netflow_filter['ip_src'] = $filter; + } + + $sort = 'dstip'; + break; + + case 'talkers': + if (empty(!$filter)) { + $netflow_filter['ip_dst'] = $filter; + } + + $sort = 'srcip'; + break; + + case 'tcp': + case 'udp': + $netflow_filter['proto'] = $top_action; + $sort = 'port'; + if (empty(!$filter)) { + $netflow_filter['advanced_filter'] = sprintf( + '((dst port %s) or (src port %s)) and (proto %s)', + $filter, + $filter, + $top_action + ); + // Display ips when filter is set in port. + $sort = 'ip'; + } + break; + + default: + return []; + } + + $command = netflow_get_command($netflow_filter); + + // Execute nfdump. + $order_text = ''; + switch ($order) { + case 'flows': + $order_text = 'flows'; + break; + + case 'pkts': + $order_text = 'packets'; + break; + + case 'bytes': + default: + $order_text = 'bytes'; + break; + } + + $command .= " -q -o csv -n $max -s $sort/$order_text -t ".date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date); + exec($command, $result); + + if (! is_array($result)) { + return []; + } + + // Remove first line (avoiding slow array_shift). + $result = array_reverse($result); + array_pop($result); + $result = array_reverse($result); + + $top_info = []; + foreach ($result as $line) { + if (empty($line)) { + continue; + } + + $data = explode(',', $line); + if (!isset($data[9])) { + continue; + } + + $top_info[(string) $data[4]] = [ + 'host' => $data[4], + 'sum_bytes' => $data[9], + 'sum_pkts' => $data[7], + 'sum_flows' => $data[5], + 'pct_bytes' => $data[10], + 'pct_pkts' => $data[8], + 'pct_flows' => $data[6], + ]; + } + + return $top_info; +} + + +/** + * Check the netflow version and print an error message if there is not correct. + * + * @return boolean True if version check is correct. + */ +function netflow_print_check_version_error() +{ + global $config; + + switch (netflow_check_nfdump_binary($config['netflow_nfdump'])) { + case 0: + return true; + + case 1: + ui_print_error_message( + __('nfdump binary (%s) not found!', $config['netflow_nfdump']) + ); + return false; + + case 2: + default: + ui_print_error_message( + __('Make sure nfdump version 1.6.8 or newer is installed!') + ); + return false; + } +} + + +/** + * Returns the array for netflow resolution select. + * + * @return array With all values. + */ +function netflow_resolution_select_params() +{ + return [ + NETFLOW_RES_LOWD => __('Low'), + NETFLOW_RES_MEDD => __('Medium'), + NETFLOW_RES_HID => __('High'), + NETFLOW_RES_ULTRAD => __('Ultra High'), + NETFLOW_RES_HOURLY => __('Hourly'), + NETFLOW_RES_DAILY => __('Daily'), + ]; +} + + +/** + * Get the resolution name. + * + * @param mixed $value Type. + * + * @return string Translated name. Unknown for unrecognized resolution names. + */ +function netflow_get_resolution_name($value) +{ + $resolutions = netflow_resolution_select_params(); + return (isset($resolutions[$value])) ? $resolutions[$value] : __('Unknown'); +} + + +/** + * Report formatted subtitle. + * + * @param string $aggregate Aggregate by param. + * @param string $resolution Netfow live view resolution. + * @param string $type Type of view. + * + * @return string HTML with formatted subtitle. + */ +function netflow_generate_subtitle_report($aggregate, $resolution, $type) +{ + $subt = __( + 'Agregate by %s', + netflow_format_aggregate($aggregate) + ); + + // Display the resolution only in required reports. + if (in_array($type, ['netflow_area', 'netflow_data']) === true) { + $subt .= ' - '; + $subt .= __( + 'Resolution %s', + netflow_get_resolution_name($resolution) + ); + } + + return $subt; +} + + +/** + * Returns netflow stats for the given period in an array. + * + * @param string $start_date Period start date. + * @param string $end_date Period end date. + * @param string $filter Netflow filter. + * @param string $aggregate Aggregate field. + * @param integer $max Maximum number of aggregates. + * + * @return array With netflow stats. + */ +function netflow_get_top_data( + $start_date, + $end_date, + $filter, + $aggregate, + $max +) { + global $nfdump_date_format; + + $values = [ + 'data' => [], + 'sources' => [], + ]; + + // Get the command to call nfdump. + $agg_command = sprintf( + '%s -q -o csv -n %s -s %s/bytes -t %s-%s', + netflow_get_command($filter), + $max, + $aggregate, + date($nfdump_date_format, $start_date), + date($nfdump_date_format, $end_date) + ); + + // Call nfdump. + exec($agg_command, $string); + + // Remove the first line. + $string[0] = ''; + + // Parse aggregates. + foreach ($string as $line) { + if (empty($line) === true) { + continue; + } + + $val = explode(',', $line); + $values['sources'][$val[4]] = 1; + } + + return $values; +} + + +/** + * Returns netflow stats for the given period in an array. + * + * @param string $filter Netflow filter (passed by reference). + * @param string $aggregate Aggregate field. + * @param array $sources Sources to aggregate to filter. + * + * @return void $filter is passed by reference. + */ +function netflow_update_second_level_filter(&$filter, $aggregate, $sources) +{ + // Update the filter. + switch ($aggregate) { + default: + case 'srcip': + $extra_filter = 'ip_src'; + break; + case 'srcport': + $extra_filter = 'src_port'; + break; + + case 'dstip': + $extra_filter = 'ip_dst'; + break; + + case 'dstport': + $extra_filter = 'dst_port'; + break; + } + + if (isset($filter[$extra_filter]) && $filter[$extra_filter] != '') { + $filter[$extra_filter] .= ','; + } + + $filter[$extra_filter] = implode(',', $sources); +} + + +/** + * Change some values on address resolve. + * + * @param array $values Where data will be overwritten (ref). + * @param boolean $get_hostnames Change it if address resolution es done (ref). + * @param string $aggregate One of srcip, srcport, dstip, dstport. + * + * @return void Referenced passed params will be changed. + */ +function netflow_address_resolution(&$values, &$get_hostnames, $aggregate) +{ + if ($aggregate !== 'srcip' && $aggregate !== 'dstip') { + return; + } + + $get_hostnames = true; + global $hostnames; + + $sources = []; + foreach ($values['sources'] as $source => $value) { + if (!isset($hostnames[$source])) { + $hostname = gethostbyaddr($source); + if ($hostname !== false) { + $hostnames[$source] = $hostname; + $source = $hostname; + } + } else { + $source = $hostnames[$source]; + } + + $sources[$source] = $value; + } + + $values['sources'] = $sources; +} + + +/** + * Check if is aggregate by IP or by port + * + * @param string $aggregate Aggregate tag. + * + * @return boolean True if is IP. False for port. + */ +function netflow_aggregate_is_ip($aggregate) +{ + return in_array($aggregate, ['dstip', 'srcip']); +} + + +/** + * Build netflow data structure to network map. + * + * @param integer $start_date Time in timestamp format. + * @param integer $end_date Time in timestamp format. + * @param integer $top Max data to show. + * @param integer $aggregate One of dstip or srcip. + * + * @return array With map structure. + */ +function netflow_build_map_data($start_date, $end_date, $top, $aggregate) +{ + // Pass an empty filter data structure. + $data = netflow_get_relationships_raw_data( + $start_date, + $end_date, + [ + 'id_name' => '', + 'id_group' => 0, + 'aggregate' => $aggregate, + 'id_dst' => '', + 'ip_src' => '', + 'dst_port' => '', + 'src_port' => '', + 'advanced_filter' => '', + 'router_ip' => '', + ], + $top, + $aggregate + ); + + $nodes = array_map( + function ($elem) { + return network_init_node_map($elem); + }, + array_merge($data['sources'], [__('Others')]) + ); + + $relations = []; + $inverse_nodes = array_flip($data['sources']); + + // Port are situated in a different places from addreses. + $is_ip = true; + $src_key = ($is_ip === true) ? 3 : 5; + $dst_key = ($is_ip === true) ? 4 : 6; + $retrieved_data = array_fill_keys($inverse_nodes, false); + + foreach ($data['lines'] as $line) { + if (empty($line) === true) { + continue; + } + + // Parse the line. + $items = explode(',', $line); + + // Get the required data. + $src_item = $inverse_nodes[$items[$src_key]]; + $dst_item = $inverse_nodes[$items[$dst_key]]; + $value = $items[12]; + $index_rel = $src_item.'-'.$dst_item; + + // Check if valid data. + if (!isset($value) || (!isset($src_item) && !isset($dst_item))) { + continue; + } + + // Mark as connected source and destination. + $retrieved_data[$src_item] = true; + $retrieved_data[$dst_item] = true; + + if (isset($relations[$index_rel])) { + $relations[$index_rel]['text_start'] += $value; + } else { + // Update the value. + network_init_relation_map($relations, $src_item, $dst_item, $value); + } + } + + // Format the data in edges. + array_walk( + $relations, + function (&$elem) { + $elem['text_start'] = network_format_bytes($elem['text_start']); + } + ); + + // Search for orphan nodes. + $orphan_hosts = []; + $orphan_index = (end($inverse_nodes) + 1); + foreach ($retrieved_data as $position => $rd) { + if ($rd === true) { + continue; + } + + network_init_relation_map($orphan_hosts, $position, $orphan_index); + } + + // If there is not any orphan node, delete it. + if (empty($orphan_hosts)) { + array_pop($nodes); + } + + return network_general_map_configuration( + $nodes, + array_merge($relations, $orphan_hosts) + ); +} diff --git a/pandora_console/include/functions_network.php b/pandora_console/include/functions_network.php new file mode 100644 index 0000000000..9c9e1456b4 --- /dev/null +++ b/pandora_console/include/functions_network.php @@ -0,0 +1,320 @@ + %d AND utimestamp < %d + %s + %s + GROUP BY %s + ORDER BY %s DESC + LIMIT %d', + $field_to_group, + $start, + $end, + $filter_sql, + $host_filter_sql, + $field_to_group, + $field_to_order, + $top + ); + + $data = db_get_all_rows_sql($sql); + + return ($data !== false) ? $data : []; +} + + +/** + * Get the possible actions on networking. + * + * @param boolean $network True if network. False if netflow. + * + * @return array With the actions to print in a select. + */ +function network_get_report_actions($network=true) +{ + $common_actions = [ + 'listeners' => __('Top listeners'), + 'talkers' => __('Top talkers'), + ]; + + if ($network) { + return $common_actions; + } + + return array_merge( + $common_actions, + [ + 'tcp' => __('Top TCP protocols'), + 'udp' => __('Top UDP protocols'), + ] + ); +} + + +/** + * Print the header of the network + * + * @param string $title Title of header. + * @param string $order Current ordering. + * @param string $selected Selected order. + * @param array $hidden_data All the data to hide into the button. + * + * @return string With HTML data. + */ +function network_print_explorer_header( + $title, + $order, + $selected, + $hidden_data +) { + $cell = '
    '; + $cell .= $title; + $cell .= html_print_link_with_params( + 'images/arrow-down-white.png', + array_merge($hidden_data, ['order_by' => $order]), + 'image', + ($selected === $order) ? 'opacity: 0.5' : '' + ); + $cell .= '
    '; + + return $cell; +} + + +/** + * Alias for format_for_graph to print bytes. + * + * @param integer $value Value to parse like bytes. + * + * @return string Number parsed. + */ +function network_format_bytes($value) +{ + if (!isset($value)) { + $value = 0; + } + + $value = (int) $value; + + return format_for_graph( + $value, + 2, + '.', + ',', + 1024, + 'B' + ); +} + + +/** + * Build netflow data structure to network map. + * + * @param integer $start Time in timestamp format. + * @param integer $end Time in timestamp format. + * @param integer $top Max data to show. + * @param boolean $talker True to get top tolkers. False for listeners. + * + * @return array With map structure. + */ +function network_build_map_data($start, $end, $top, $talker) +{ + $data = network_matrix_get_top($top, $talker, $start, $end); + + $hosts = array_map( + function ($elem) { + return $elem['host']; + }, + $data + ); + $inverse_hosts = array_flip($hosts); + + $nodes = array_map( + function ($elem) { + return network_init_node_map($elem); + }, + $hosts + ); + + $relations = []; + $orphan_relations = []; + foreach ($hosts as $host) { + $host_top = network_matrix_get_top( + $top, + !$talker, + $start, + $end, + $host, + true, + $hosts + ); + foreach ($host_top as $sd) { + $src_index = $inverse_hosts[$host]; + $dst_index = $inverse_hosts[$sd['host']]; + if (isset($src_index) === false || isset($dst_index) === false) { + continue; + } + + network_init_relation_map( + $relations, + $src_index, + $dst_index, + network_format_bytes($sd['sum_bytes']) + ); + } + + // Put the orphans on Other node. + if (empty($host_top)) { + $other_id = (end($inverse_hosts) + 1); + // TODOS: Add the data. + network_init_relation_map( + $orphan_relations, + $other_id, + $inverse_hosts[$host] + ); + } + } + + // Put the Others node and their relations. + if (empty($orphan_relations) === false) { + $nodes[] = network_init_node_map(__('Others')); + $relations = array_merge($relations, $orphan_relations); + } + + return network_general_map_configuration($nodes, $relations); +} + + +/** + * Return the array to pass to constructor to NetworkMap. + * + * @param array $nodes Nodes data structure. + * @param array $relations Relations data structure. + * + * @return array To be passed to NetworMap class. + */ +function network_general_map_configuration($nodes, $relations) +{ + return [ + 'nodes' => $nodes, + 'relations' => $relations, + 'pure' => 1, + 'no_pandora_node' => 1, + 'no_popup' => 1, + 'map_options' => [ + 'generation_method' => LAYOUT_SPRING1, + 'map_filter' => [ + 'node_radius' => 40, + 'node_sep' => 7, + ], + ], + ]; +} + + +/** + * Added a relation to relations array + * + * @param array $relations Relations array (passed by reference). + * @param integer $parent Parent id (numeric). + * @param integer $child Child id (numeric). + * @param string $text Text to show at the end of edge (optional). + * + * @return void Relations will be modified (passed by reference). + */ +function network_init_relation_map(&$relations, $parent, $child, $text='') +{ + $index = $parent.'-'.$child; + $relations[$index] = [ + 'id_parent' => $parent, + 'parent_type' => NODE_GENERIC, + 'child_type' => NODE_GENERIC, + 'id_child' => $child, + 'link_color' => '#82B92E', + ]; + + if (empty($text) === false) { + $relations[$index]['text_start'] = $text; + } +} + + +/** + * Initialize a node structure to NetworkMap class. + * + * @param string $name Node name. + * + * @return array Node data structure. + */ +function network_init_node_map($name) +{ + return [ + 'name' => $name, + 'type' => NODE_GENERIC, + 'width' => 20, + 'height' => 20, + 'status' => '#82B92E', + ]; +} diff --git a/pandora_console/include/functions_networkmap.php b/pandora_console/include/functions_networkmap.php index adbaa00922..af11deb541 100644 --- a/pandora_console/include/functions_networkmap.php +++ b/pandora_console/include/functions_networkmap.php @@ -1,32 +1,36 @@ = 1) { + /* + * Select data origin. + * group + * discovery task + * - Cloud + * - Application + * - Standar or custom + * network/mask + */ + + if ($group >= 0 && empty($ip_mask)) { if ($dont_show_subgroups) { $filter['id_grupo'] = $group; } else { @@ -372,22 +358,8 @@ function networkmap_generate_dot( } else if ($group == -666) { $agents = false; } else if (!empty($ip_mask)) { - $agents = networkmap_get_new_nodes_from_ip_mask( - $ip_mask, - [ - 'id_grupo', - 'nombre', - 'id_os', - 'id_parent', - 'id_agente', - 'normal_count', - 'warning_count', - 'critical_count', - 'unknown_count', - 'total_count', - 'notinit_count', - ], - $strict_user + $agents = networkmap_get_nodes_from_ip_mask( + $ip_mask ); } else { $agents = agents_get_agents( @@ -455,7 +427,6 @@ function networkmap_generate_dot( // Get agent modules data $modules = agents_get_modules($agent['id_agente'], '*', $filter, true, true); - if ($modules === false) { $modules = []; } @@ -518,11 +489,11 @@ function networkmap_generate_dot( // Create void statistics array $stats = []; - - $count = 0; - $group_nodes = 10; - $graph .= networkmap_create_transparent_node($count); - foreach (array_keys($orphans) as $node) { + /* + $count = 0; + $group_nodes = 10; + $graph .= networkmap_create_transparent_node($count); + foreach (array_keys($orphans) as $node) { if ($group_nodes == 0) { $count++; $graph .= networkmap_create_transparent_node($count); @@ -536,7 +507,8 @@ function networkmap_generate_dot( ); $group_nodes--; - } + } + */ // Create nodes foreach ($nodes as $node_id => $node) { @@ -694,17 +666,57 @@ function networkmap_generate_dot( } -// Returns an edge definition -function networkmap_create_edge($head, $tail, $layout, $nooverlap, $pure, $zoom, $ranksep, $simple, $regen, $font_size, $group, $sec2='operation/agentes/networkmap', $tab='topology', $id_networkmap=0) -{ +/** + * Returns an edge definition. + * + * @param mixed $head Head. + * @param mixed $tail Tail. + * @param string $layout Layout. + * @param string $nooverlap Nooverlap. + * @param integer $pure Pure. + * @param float $zoom Zoom. + * @param float $ranksep Ranksep. + * @param integer $simple Simple. + * @param integer $regen Regen. + * @param integer $font_size Font_size. + * @param integer $group Group. + * @param string $sec2 Sec2. + * @param string $tab Tab. + * @param integer $id_networkmap Id_networkmap. + * + * @return string Dot string. + */ +function networkmap_create_edge( + $head, + $tail, + $layout, + $nooverlap, + $pure, + $zoom, + $ranksep, + $simple, + $regen, + $font_size, + $group, + $sec2='operation/agentes/networkmap', + $tab='topology', + $id_networkmap=0 +) { if (defined('METACONSOLE')) { $url = ''; } else { - $url = 'index.php?sec=estado&'.'sec2='.$sec2.'&'.'tab='.$tab.'&'.'recenter_networkmap=1&'.'center='.$head.'&'.'layout='.$layout.'&'.'nooverlap='.$nooverlap.'&'.'pure='.$pure.'&'.'zoom='.$zoom.'&'.'ranksep='.$ranksep.'&'.'simple='.$simple.'&'.'regen=1'.'&'.'font_size='.$font_size.'&'.'group='.$group.'&'.'id_networkmap='.$id_networkmap; + $url = 'index.php?sec=estado&sec2='.$sec2.'&tab='.$tab.'&'; + $url .= 'recenter_networkmap=1¢er='.$head.'&'; + $url .= 'layout='.$layout.'&nooverlap='.$nooverlap.'&'; + $url .= 'pure='.$pure.'&zoom='.$zoom.'&ranksep='.$ranksep.'&'; + $url .= 'simple='.$simple.'®en=1&font_size='.$font_size.'&'; + $url .= 'group='.$group.'&id_networkmap='.$id_networkmap; } - // edgeURL allows node navigation - $edge = "\n".$head.' -- '.$tail.'[color="#BDBDBD", headclip=false, tailclip=false, edgeURL=""];'."\n"; + // Option edgeURL allows node navigation. + $edge = "\n".$head.' -- '.$tail; + $edge .= '[len='.$ranksep.', color="#BDBDBD", headclip=false, tailclip=false, edgeURL=""];'; + $edge .= "\n"; return $edge; } @@ -719,96 +731,6 @@ function networkmap_create_transparent_edge($head, $tail) } -// Returns a group node definition -function networkmap_create_group_node($group, $simple=0, $font_size=10, $metaconsole=false, $id_server=null, $strict_user=false) -{ - global $config; - global $hack_networkmap_mobile; - - $status = groups_get_status($group['id_grupo'], $strict_user); - - // Set node status - switch ($status) { - case 0: - $status_color = COL_NORMAL; - // Normal monitor - break; - - case 1: - $status_color = COL_CRITICAL; - // Critical monitor - break; - - case 2: - $status_color = COL_WARNING; - // Warning monitor - break; - - case 4: - $status_color = COL_ALERTFIRED; - // Alert fired - break; - - default: - $status_color = COL_UNKNOWN; - // Unknown monitor - break; - } - - $icon = groups_get_icon($group['id_grupo']); - - if ($simple == 0) { - // Set node icon - if ($hack_networkmap_mobile) { - $img_node = $config['homedir'].'/images/groups_small/'.$icon.'.png'; - - if (!file_exists($img_node)) { - $img_node = '-'; - } - - $img_node = ''; - } else if (file_exists(html_print_image('images/groups_small/'.$icon.'.png', true, false, true, true))) { - $img_node = html_print_image('images/groups_small/'.$icon.'.png', true, false, false, true); - } else { - $img_node = '-'; - } - - if (strlen(groups_get_name($group['id_grupo'])) > 40) { - $name = substr(groups_get_name($group['id_grupo']), 0, 40).'...'; - } else { - $name = groups_get_name($group['id_grupo']); - } - - if (defined('METACONSOLE')) { - $url = ''; - $url_tooltip = ''; - } else { - $url = 'index.php?sec=estado&sec2=operation/agentes/estado_agente&refr=60&group_id='.$group['id_grupo']; - $url_tooltip = 'ajax.php?page=operation/agentes/ver_agente&get_group_status_tooltip=1&id_group='.$group['id_grupo']; - } - - $node = "\n".$group['id_node'].' [ color="'.$status_color.'", fontsize='.$font_size.', style="filled", fixedsize=true, width=0.30, height=0.30, '.'label=< -
    '.$img_node.'
    '.io_safe_output($name).'
    >, - shape="invtrapezium", URL="'.$url.'", - tooltip="'.$url_tooltip.'"];'."\n"; - } else { - if (defined('METACONSOLE')) { - $url = ''; - $url_tooltip = ''; - } else { - $url = 'index.php?sec=estado&sec2=operation/agentes/estado_agente&refr=60&group_id='.$group['id_grupo']; - $url_tooltip = 'ajax.php?page=operation/agentes/ver_agente&get_group_status_tooltip=1&id_group='.$group['id_grupo']; - } - - $node = "\n".$group['id_node'].' [ color="'.$status_color.'", fontsize='.$font_size.', shape="invtrapezium", - URL="'.$url.'", style="filled", fixedsize=true, width=0.20, height=0.20, label="", - tooltip="'.$url_tooltip.'"];'."\n"; - } - - return $node; -} - - // Returns a node definition function networkmap_create_agent_node($agent, $simple=0, $font_size=10, $cut_names=true, $relative=false, $metaconsole=false, $id_server=null, $strict_user=false) { @@ -952,76 +874,6 @@ function networkmap_create_agent_node($agent, $simple=0, $font_size=10, $cut_nam } -function networkmap_create_module_group_node($module_group, $simple=0, $font_size=10, $metaconsole=false, $id_server=null) -{ - global $config; - global $hack_networkmap_mobile; - - // Set node status - switch ($module_group['status']) { - case 0: - $status_color = COL_NORMAL; - // Normal monitor - break; - - case 1: - $status_color = COL_CRITICAL; - // Critical monitor - break; - - case 2: - $status_color = COL_WARNING; - // Warning monitor - break; - - case 4: - $status_color = COL_ALERTFIRED; - // Alert fired - break; - - default: - $status_color = COL_UNKNOWN; - // Unknown monitor - break; - } - - if ($simple == 0) { - if (defined('METACONSOLE')) { - $url = ''; - $url_tooltip = ''; - } else { - $url = ''; - $url_tooltip = ''; - } - - $node = $module_group['id_node'].' [ color="'.$status_color.'", fontsize='.$font_size.', style="filled", '.'fixedsize=true, width=0.30, height=0.30, '.'label=<
    '.io_safe_output($module_group['name']).'
    >, - shape="square", URL="'.$url.'", - tooltip="'.$url_tooltip.'"];'; - } else { - if ($hack_networkmap_mobile) { - $img_node = ui_print_moduletype_icon($module['id_tipo_modulo'], true, true, false, true); - - $img_node = $config['homedir'].'/'.$img_node; - $img_node = ''; - } else { - $img_node = ui_print_moduletype_icon($module['id_tipo_modulo'], true, true, false); - } - - if (defined('METACONSOLE')) { - $url = ''; - $url_tooltip = ''; - } else { - $url = ''; - $url_tooltip = ''; - } - - $node = $module_group['id_node'].' [ color="'.$status_color.'", fontsize='.$font_size.', shape="square", URL="'.$url.'", '.'style="filled", fixedsize=true, width=0.20, '.'height=0.20, label="", tooltip="'.$url_tooltip.'"];'; - } - - return $node; -} - - // Returns a module node definition function networkmap_create_module_node($module, $simple=0, $font_size=10, $metaconsole=false, $id_server=null) { @@ -1175,35 +1027,13 @@ function networkmap_create_transparent_node($count=0) } -// Opens a group definition -function networkmap_open_group($id) -{ - $img = 'images/'.groups_get_icon($id).'.png'; - $name = groups_get_name($id); - - $group = 'subgraph cluster_'.$id.' { style=filled; color=darkolivegreen3; label=< - -
    '.html_print_image($img, true).''.$name.'
    >; tooltip="'.$name.'"; - URL="index.php?sec=estado&sec2=operation/agentes/estado_agente&group_id='.$id.'";'; - - return $group; -} - - -// Closes a group definition -function networkmap_close_group() -{ - return '}'; -} - - // Opens a graph definition function networkmap_open_graph( $layout, $nooverlap, $pure, $zoom, - $ranksep, + $rank_sep, $font_size, $size_canvas, $map_filter=[] @@ -1236,7 +1066,7 @@ function networkmap_open_graph( if (isset($map_filter['node_sep'])) { $node_sep = $map_filter['node_sep']; } else { - $node_sep = 0.25; + $node_sep = 0.1; } if (isset($map_filter['rank_sep'])) { @@ -1258,14 +1088,14 @@ function networkmap_open_graph( if (isset($map_filter['kval'])) { $kval = $map_filter['kval']; } else { - $kval = 0.3; + $kval = 0.1; } // BEWARE: graphwiz DONT use single ('), you need double (") $head = 'graph networkmap { dpi=100; bgcolor="transparent"; labeljust=l; margin=0; pad="0.75,0.75";'; if ($nooverlap != '') { $head .= 'overlap="false";'; - $head .= 'outputorder=edgesfirst;'; + $head .= 'outputorder=first;'; } if ($layout == 'flat' || $layout == 'spring1' || $layout == 'spring2') { @@ -1290,12 +1120,13 @@ function networkmap_open_graph( $head .= "mindist=\"$mindist\";"; } - $head .= 'ratio=fill;'; + $head .= 'ratio="fill";'; $head .= 'root=0;'; $head .= "nodesep=\"$node_sep\";"; $head .= "size=\"$size\";"; $head .= "\n"; + return $head; } @@ -1338,67 +1169,6 @@ function networkmap_get_filter($layout) } -/** - * Creates a networkmap. - * - * @param string Network map name. - * @param string Network map type (topology, groups or policies). - * @param layout Network map layout (circular, flat, radial, spring1 or spring2). - * @param bool overlapping activate flag. - * @param bool simple view activate flag. - * @param bool regenerate file activate flag. - * @param int font size. - * @param int group id filter (0 for all). - * @param int module group id filter (0 for all). - * @param int policy id filter (0 for all). - * @param string depth level. - * @param bool only modules with alerts flag. - * @param bool hide policy modules flag - * @param float zoom factor - * - * @return mixed New networkmap id if created. False if it could not be created. - */ -function networkmap_create_networkmap($values) -{ - global $config; - - // The name is required - if (! isset($values['name'])) { - return false; - } - - // Set defaults for the empty values - set_unless_defined($values['type'], 'topology'); - set_unless_defined($values['layout'], 'radial'); - set_unless_defined($values['nooverlap'], true); - set_unless_defined($values['simple'], false); - set_unless_defined($values['regenerate'], true); - set_unless_defined($values['font_size'], 12); - set_unless_defined($values['store_group'], 0); - set_unless_defined($values['id_group'], 0); - set_unless_defined($values['regenerate'], true); - set_unless_defined($values['id_module_group'], 0); - set_unless_defined($values['depth'], 'all'); - set_unless_defined($values['only_modules_with_alerts'], false); - set_unless_defined($values['hide_policy_modules'], false); - set_unless_defined($values['zoom'], 1); - set_unless_defined($values['distance_nodes'], 2.5); - set_unless_defined($values['center'], 0); - set_unless_defined($values['id_user'], $config['id_user']); - set_unless_defined($values['text_filter'], ''); - set_unless_defined($values['regenerate'], true); - set_unless_defined($values['dont_show_subgroups'], 0); - set_unless_defined($values['show_groups'], false); - set_unless_defined($values['pandoras_children'], false); - set_unless_defined($values['show_modules'], false); - set_unless_defined($values['show_snmp_modules'], 0); - set_unless_defined($values['l2_network'], 0); - set_unless_defined($values['server_name'], ''); - - return @db_process_sql_insert('tnetwork_map', $values); -} - - /** * Get a network map report. * @@ -1517,57 +1287,6 @@ function networkmap_type_to_str_type($type) } -/** - * Deletes a network map if the property is that user. - * - * @param string User id that call this funtion. - * @param int Map id to be deleted. - * - * @return boolean True if the map was deleted, false the map is not yours. - */ -function networkmap_delete_user_networkmap($id_user='', $id_networkmap) -{ - if ($id_user == '') { - $id_user = $config['id_user']; - } - - $id_networkmap = safe_int($id_networkmap); - if (empty($id_networkmap)) { - return false; - } - - $networkmap = networkmap_get_networkmap($id_networkmap); - if ($networkmap === false) { - return false; - } - - return @db_process_sql_delete('tnetwork_map', ['id_networkmap' => $id_networkmap, 'id_user' => $id_user]); -} - - -/** - * Updates a network map. - * - * @param int Map id. - * @param array Extra values to be set. - * - * @return boolean True if the map was updated. False otherwise. - */ -function networkmap_update_networkmap($id_networkmap, $values) -{ - $networkmap = networkmap_get_networkmap($id_networkmap); - if ($networkmap === false) { - return false; - } - - return (db_process_sql_update( - 'tnetwork_map', - $values, - ['id_networkmap' => $id_networkmap] - )) !== false; -} - - /** * Get different networkmaps types for creation. * @@ -1597,108 +1316,78 @@ function networkmap_get_types($strict_user=false) /** - * Get networkmaps types. + * Retrieve agent list matching desired network. * - * @return array Networkmap diferent types. + * @param string $ip_mask Networks. + * @param array $fields Extra fields. + * + * @return array Of agents. */ -function networkmap_get_filter_types($strict_user=false) -{ - $networkmap_types = []; - - $is_enterprise = enterprise_include_once('include/functions_policies.php'); - - $networkmap_types['topology'] = __('Topology'); - $networkmap_types['groups'] = __('Group'); - $networkmap_types['dynamic'] = __('Dynamic'); - if (!$strict_user) { - $networkmap_types['radial_dynamic'] = __('Radial dynamic'); - } - - if (($is_enterprise !== ENTERPRISE_NOT_HOOK) && (!$strict_user)) { - $enterprise_types = enterprise_hook('policies_get_networkmap_filter_types'); - - $networkmap_types = array_merge($networkmap_types, $enterprise_types); - } - - return $networkmap_types; -} - - -function networkmap_cidr_match($ip, $cidr_mask) -{ - // copy from open source code - // https://gist.github.com/linickx/1309388 - $chunks = explode('/', $cidr_mask); - $subnet = $chunks[0]; - $bits = $chunks[1]; - - $ip = ip2long($ip); - $subnet = ip2long($subnet); - $mask = (-1 << (32 - $bits)); - $subnet &= $mask; - // nb: in case the supplied subnet wasn't correctly aligned - return ($ip & $mask) == $subnet; -} - - -function networkmap_get_new_nodes_from_ip_mask( +function networkmap_get_nodes_from_ip_mask( $ip_mask, - $fields=[], - $strict_user=false + $return_ids_only=false ) { $list_ip_masks = explode(',', $ip_mask); - $list_address = db_get_all_rows_in_table('taddress'); - if (empty($address)) { - $address = []; - } - - if ($strict_user) { - $filter['group_by'] = 'tagente.id_agente'; - $fields = ['tagente.id_agente']; - $acltags = tags_get_user_groups_and_tags($config['id_user'], 'AR', $strict_user); - $user_agents = tags_get_all_user_agents(false, $config['id_user'], $acltags, $filter, $fields, false, $strict_user, true); - - foreach ($all_user_agents as $agent) { - $user_agents[$agent['id_agente']] = $agent['id_agente']; - } + if (empty($list_ip_masks)) { + return []; } $agents = []; - foreach ($list_address as $address) { - foreach ($list_ip_masks as $ip_mask) { - if (networkmap_cidr_match($address['ip'], $ip_mask)) { - $id_agent = db_get_value_filter( - 'id_agent', - 'taddress_agent', - ['id_a' => $address['id_a']] - ); + foreach ($list_ip_masks as $subnet) { + $net = explode('/', $subnet); - if (empty($id_agent)) { - continue; - } + // Calculate real network address. Avoid user bad input. + $mask = ~((1 << (32 - $net[1])) - 1); + $network = long2ip(ip2long($net[0]) & $mask); - if (empty($fields)) { - if ($strict_user) { - if (array_key_exists($id_agent, $user_agents)) { - $agents[] = db_get_value_filter('id_agent', 'taddress_agent', ['id_a' => $address['id_a']]); - } - } else { - $agents[] = db_get_value_filter('id_agent', 'taddress_agent', ['id_a' => $address['id_a']]); - } - } else { - if ($strict_user) { - if (array_key_exists($id_agent, $user_agents)) { - $agents[] = db_get_row('tagente', 'id_agente', $id_agent, $fields); - } - } else { - $agents[] = db_get_row('tagente', 'id_agente', $id_agent, $fields); - } - } - } + $sql = sprintf( + 'SELECT * + FROM `tagente` + INNER JOIN + (SELECT DISTINCT `id_agent` FROM + (SELECT `id_agente` AS "id_agent", `direccion` AS "ip" + FROM `tagente` + UNION + SELECT ag.`id_agent`, a.`ip` + FROM `taddress_agent` ag + INNER JOIN `taddress` a + ON ag.id_a=a.id_a + ) t_tmp + WHERE (-1 << %d) & INET_ATON(t_tmp.ip) = INET_ATON("%s") + ) t_res + ON t_res.`id_agent` = `tagente`.`id_agente`', + (32 - $net[1]), + $network + ); + + $subnet_agents = db_get_all_rows_sql($sql); + + if ($subnet_agents !== false) { + $agents = array_merge($agents, $subnet_agents); } } + if ($return_ids_only === false) { + $agents = array_reduce( + $agents, + function ($carry, $item) { + $carry[$item['id_agente']] = $item; + return $carry; + }, + [] + ); + } else { + $agents = array_reduce( + $agents, + function ($carry, $item) { + $carry[$item['id_agente']] = $item['id_agente']; + return $carry; + }, + [] + ); + } + return $agents; } @@ -1729,7 +1418,944 @@ function modules_get_all_interfaces($id_agent) } -?> +function networkmap_delete_networkmap($id=0) +{ + if (enterprise_installed()) { + // Relations + $result = delete_relations($id); + + // Nodes + $result = delete_nodes($id); + } + + // Map + $result = db_process_sql_delete('tmap', ['id' => $id]); + + return $result; +} + + +function networkmap_delete_nodes($id_map) +{ + return db_process_sql_delete('titem', ['id_map' => $id_map]); +} + + +function get_networkmaps($id) +{ + $groups = array_keys(users_get_groups(null, 'IW')); + + $filter = []; + $filter['id_group'] = $groups; + $filter['id'] = '<>'.$id; + $networkmaps = db_get_all_rows_filter('tmap', $filter); + if ($networkmaps === false) { + $networkmaps = []; + } + + $return = []; + $return[0] = __('None'); + foreach ($networkmaps as $networkmap) { + $return[$networkmap['id']] = $networkmap['name']; + } + + return $return; +} + + +/** + * Translates node (nodes_and_relations) into JS node. + * + * @param array $node Node. + * @param integer $count Count. + * @param integer $count_item_holding_area Count_item_holding_area. + * @param boolean $simulated Simulated. + * + * @return array JS nodes. + */ +function networkmap_db_node_to_js_node( + $node, + &$count, + &$count_item_holding_area, + $simulated=false +) { + global $config; + + $networkmap = db_get_row('tmap', 'id', $node['id_map']); + + $networkmap['filter'] = json_decode($networkmap['filter'], true); + + // Hardcoded + $networkmap['filter']['holding_area'] = [ + 500, + 500, + ]; + + // 40 = DEFAULT NODE RADIUS + // 30 = for to align + $holding_area_max_y = ($networkmap['height'] + 30 + 40 * 2 - $networkmap['filter']['holding_area'][1] + 10 * 40); + + $item = []; + $item['id'] = $count; + + if (enterprise_installed() && $simulated === false) { + enterprise_include_once('include/functions_networkmap.php'); + $item['id_db'] = $node['id_in_db']; + } else { + $item['id_db'] = (int) $node['id']; + } + + if ((int) $node['type'] == 0) { + $item['type'] = 0; + $item['id_agent'] = (int) $node['source_data']; + $item['id_module'] = ''; + } else if ((int) $node['type'] == 1) { + $item['type'] = 1; + $item['id_agent'] = (int) $node['style']['id_agent']; + $item['id_module'] = (int) $node['source_data']; + } else { + $item['type'] = 3; + } + + $item['fixed'] = true; + $item['x'] = (int) $node['x']; + $item['y'] = (int) $node['y']; + $item['px'] = (int) $node['x']; + $item['py'] = (int) $node['y']; + $item['z'] = (int) $node['z']; + $item['state'] = $node['state']; + $item['deleted'] = $node['deleted']; + if ($item['state'] == 'holding_area') { + // 40 = DEFAULT NODE RADIUS + // 30 = for to align + $holding_area_x = ($networkmap['width'] + 30 + 40 * 2 - $networkmap['filter']['holding_area'][0] + ($count_item_holding_area % 11) * 40); + $holding_area_y = ($networkmap['height'] + 30 + 40 * 2 - $networkmap['filter']['holding_area'][1] + (int) (($count_item_holding_area / 11)) * 40); + + if ($holding_area_max_y <= $holding_area_y) { + $holding_area_y = $holding_area_max_y; + } + + $item['x'] = $holding_area_x; + $item['y'] = $holding_area_y; + + // Increment for the next node in holding area + $count_item_holding_area++; + } + + $item['image_url'] = ''; + $item['image_width'] = 0; + $item['image_height'] = 0; + if (!empty($node['style']['image'])) { + $item['image_url'] = html_print_image( + $node['style']['image'], + true, + false, + true + ); + $image_size = getimagesize( + $config['homedir'].'/'.$node['style']['image'] + ); + $item['image_width'] = (int) $image_size[0]; + $item['image_height'] = (int) $image_size[1]; + } + + $item['raw_text'] = $node['style']['label']; + $item['text'] = io_safe_output($node['style']['label']); + $item['shape'] = $node['style']['shape']; + + switch ($node['type']) { + case 0: + $color = get_status_color_networkmap($node['source_data']); + break; + + default: + // Old code + if ($node['source_data'] == -1) { + $color = '#364D1F'; + } else if ($node['source_data'] == -2) { + $color = '#364D1F'; + } else { + $color = get_status_color_networkmap($node['source_data']); + } + break; + } + + $item['color'] = $color; + $item['map_id'] = 0; + if (isset($node['id_map'])) { + $item['map_id'] = $node['id_map']; + } + + if (!isset($node['style']['id_networkmap']) || $node['style']['id_networkmap'] == '' || $node['style']['id_networkmap'] == 0) { + $item['networkmap_id'] = 0; + } else { + $item['networkmap_id'] = $node['style']['id_networkmap']; + } + + $count++; + + return $item; +} + + +function get_status_color_networkmap($id, $color=true) +{ + // $status = agents_get_status($id); + $agent_data = db_get_row_sql('SELECT * FROM tagente WHERE id_agente = '.$id); + + if ($agent_data === false) { + return COL_UNKNOWN; + } + + $status = agents_get_status_from_counts($agent_data); + + if (!$color) { + return $status; + } + + if ($agent_data['fired_count'] > 0) { + return COL_ALERTFIRED; + } + + // Select node color by checking status. + switch ($status) { + case AGENT_MODULE_STATUS_NORMAL: + return COL_NORMAL; + + case AGENT_MODULE_STATUS_NOT_INIT: + return COL_NOTINIT; + + case AGENT_MODULE_STATUS_CRITICAL_BAD: + return COL_CRITICAL; + + case AGENT_MODULE_STATUS_WARNING: + return COL_WARNING; + + case AGENT_MODULE_STATUS_UNKNOWN: + default: + return COL_UNKNOWN; + } + + return COL_UNKNOWN; +} + + +function networkmap_clean_relations_for_js(&$relations) +{ + do { + $cleaned = true; + + foreach ($relations as $key => $relation) { + if ($relation['id_parent_source_data'] == $relation['id_child_source_data']) { + if (($relation['child_type'] != 3) && $relation['parent_type'] != 3) { + $cleaned = false; + + if ($relation['parent_type'] == 1) { + $to_find = $relation['id_parent_source_data']; + $to_replace = $relation['id_child_source_data']; + } else if ($relation['child_type'] == 1) { + $to_find = $relation['id_child_source_data']; + $to_replace = $relation['id_parent_source_data']; + } + + // Replace and erase the links + foreach ($relations as $key2 => $relation2) { + if ($relation2['id_parent_source_data'] == $to_find) { + $relations[$key2]['id_parent_source_data'] = $to_replace; + } else if ($relation2['id_child_source_data'] == $to_find) { + $relations[$key2]['id_child_source_data'] = $to_replace; + } + } + + unset($relations[$key]); + + break; + } + } + } + } while (!$cleaned); +} + + +/** + * Transform networkmap relations into js links. + * + * @param array $relations Relations. + * @param array $nodes_graph Nodes_graph. + * @param boolean $simulated Simulated. + * + * @return array JS relations. + */ +function networkmap_links_to_js_links( + $relations, + $nodes_graph, + $simulated=false +) { + $return = []; + + if (enterprise_installed() && $simulated === false) { + enterprise_include_once('include/functions_networkmap.php'); + } + + $count = 0; + foreach ($relations as $key => $relation) { + if (($relation['parent_type'] == NODE_MODULE) + && ($relation['child_type'] == NODE_MODULE) + ) { + $id_target_agent = agents_get_agent_id_by_module_id( + $relation['id_parent_source_data'] + ); + $id_source_agent = agents_get_agent_id_by_module_id( + $relation['id_child_source_data'] + ); + $id_target_module = $relation['id_parent_source_data']; + $id_source_module = $relation['id_child_source_data']; + } else if (($relation['parent_type'] == NODE_MODULE) + && ($relation['child_type'] == NODE_AGENT) + ) { + $id_target_agent = agents_get_agent_id_by_module_id( + $relation['id_parent_source_data'] + ); + $id_target_module = $relation['id_parent_source_data']; + $id_source_agent = $relation['id_child_source_data']; + } else if (($relation['parent_type'] == NODE_AGENT) + && ($relation['child_type'] == NODE_MODULE) + ) { + $id_target_agent = $relation['id_parent_source_data']; + $id_source_module = $relation['id_child_source_data']; + $id_source_agent = agents_get_agent_id_by_module_id( + $relation['id_child_source_data'] + ); + } else { + $id_target_agent = $relation['id_parent_source_data']; + $id_source_agent = $relation['id_child_source_data']; + } + + $item = []; + $item['id'] = $count; + $count++; + if (enterprise_installed() && $simulated === false) { + $item['id_db'] = get_relation_id($relation); + } else { + $item['id_db'] = $key; + } + + $item['arrow_start'] = ''; + $item['arrow_end'] = ''; + $item['status_start'] = ''; + $item['status_end'] = ''; + $item['id_module_start'] = 0; + $item['id_agent_start'] = (int) $id_source_agent; + $item['id_module_end'] = 0; + $item['id_agent_end'] = (int) $id_target_agent; + $item['link_color'] = '#999'; + $item['target'] = -1; + $item['source'] = -1; + $item['deleted'] = $relation['deleted']; + + if (enterprise_installed() && $simulated === false) { + $target_and_source = []; + $target_and_source = get_id_target_and_source_in_db($relation); + $item['target_id_db'] = (int) $target_and_source['target']; + $item['source_id_db'] = (int) $target_and_source['source']; + } else { + if (($relation['parent_type'] == NODE_MODULE) && ($relation['child_type'] == NODE_MODULE)) { + $item['target_id_db'] = $id_target_agent; + $item['source_id_db'] = $id_source_agent; + } else if (($relation['parent_type'] == NODE_AGENT) && ($relation['child_type'] == NODE_AGENT)) { + $item['target_id_db'] = (int) $relation['id_parent_source_data']; + $item['source_id_db'] = $id_source_agent; + } else { + $item['target_id_db'] = (int) $relation['id_parent_source_data']; + $item['source_id_db'] = (int) $relation['id_child_source_data']; + } + } + + $item['text_end'] = ''; + $item['text_start'] = ''; + + if ($relation['parent_type'] == 1) { + $item['arrow_end'] = 'module'; + $item['status_end'] = modules_get_agentmodule_status((int) $id_target_module, false, false, null); + $item['id_module_end'] = (int) $id_target_module; + $text_end = modules_get_agentmodule_name((int) $id_target_module); + if (preg_match('/(.+)_ifOperStatus$/', (string) $text_end, $matches)) { + if ($matches[1]) { + // It's ok to safe_output as it inlo goint to be user into the map line + $item['text_end'] = io_safe_output($matches[1]); + } + } + } + + if ($relation['child_type'] == NODE_MODULE) { + $item['arrow_start'] = 'module'; + $item['status_start'] = modules_get_agentmodule_status((int) $id_source_module, false, false, null); + $item['id_module_start'] = (int) $id_source_module; + $text_start = modules_get_agentmodule_name((int) $id_source_module); + if (preg_match('/(.+)_ifOperStatus$/', (string) $text_start, $matches)) { + if ($matches[1]) { + // It's ok to safe_output as it inlo goint to be user into the map line + $item['text_start'] = io_safe_output($matches[1]); + } + } + } + + $agent = 0; + $agent2 = 0; + $control1 = false; + $control2 = false; + + if (($relation['parent_type'] == NODE_MODULE) && ($relation['child_type'] == NODE_MODULE)) { + if (($item['status_start'] == AGENT_MODULE_STATUS_CRITICAL_BAD) || ($item['status_end'] == AGENT_MODULE_STATUS_CRITICAL_BAD)) { + $item['link_color'] = '#FC4444'; + } else if (($item['status_start'] == AGENT_MODULE_STATUS_WARNING) || ($item['status_end'] == AGENT_MODULE_STATUS_WARNING)) { + $item['link_color'] = '#FAD403'; + } + + $agent = agents_get_agent_id_by_module_id( + $relation['id_parent_source_data'] + ); + $agent2 = agents_get_agent_id_by_module_id( + $relation['id_child_source_data'] + ); + foreach ($nodes_graph as $key2 => $node) { + if (isset($node['id_agent'])) { + if ($node['id_agent'] == $agent) { + $agent = $node['id_db']; + $control1 = true; + } + + if ($node['id_agent'] == $agent2) { + $agent2 = $node['id_db']; + $control2 = true; + } + + if ($control1 && $control2) { + break; + } + } + } + } else if ($relation['child_type'] == NODE_MODULE) { + if ($item['status_start'] == AGENT_MODULE_STATUS_CRITICAL_BAD) { + $item['link_color'] = '#FC4444'; + } else if ($item['status_start'] == AGENT_MODULE_STATUS_WARNING) { + $item['link_color'] = '#FAD403'; + } + + $agent2 = agents_get_agent_id_by_module_id( + $relation['id_child_source_data'] + ); + foreach ($nodes_graph as $key2 => $node) { + if (isset($node['id_agent'])) { + if ($node['id_agent'] == $relation['id_parent_source_data']) { + $agent = $node['id_db']; + $control1 = true; + } + + if ($node['id_agent'] == $agent2) { + $agent2 = $node['id_db']; + $control2 = true; + } + + if ($control1 && $control2) { + break; + } + } + } + } else if ($relation['parent_type'] == NODE_MODULE) { + if ($item['status_end'] == AGENT_MODULE_STATUS_CRITICAL_BAD) { + $item['link_color'] = '#FC4444'; + } else if ($item['status_end'] == AGENT_MODULE_STATUS_WARNING) { + $item['link_color'] = '#FAD403'; + } + + $agent = agents_get_agent_id_by_module_id( + $relation['id_parent_source_data'] + ); + foreach ($nodes_graph as $key2 => $node) { + if (isset($node['id_agent'])) { + if ($node['id_agent'] == $agent) { + $agent = $node['id_db']; + $control1 = true; + } + + if ($node['id_agent'] == $relation['id_child_source_data']) { + $agent2 = $node['id_db']; + $control2 = true; + } + + if ($control1 && $control2) { + break; + } + } + } + } else if (($relation['parent_type'] == NODE_PANDORA) + && ($relation['child_type'] == NODE_PANDORA) + ) { + foreach ($nodes_graph as $key2 => $node) { + if ($relation['id_parent'] == $node['id_db']) { + $agent = $node['id_db']; + } + } + + foreach ($nodes_graph as $key2 => $node) { + if ($relation['id_child'] == $node['id_db']) { + $agent2 = $node['id_db']; + } + } + } else if (($relation['parent_type'] == NODE_PANDORA) + || ($relation['child_type'] == NODE_PANDORA) + ) { + if ($relation['parent_type'] == NODE_PANDORA) { + foreach ($nodes_graph as $key2 => $node) { + if ($relation['id_parent'] == $node['id_db']) { + $agent = $node['id_db']; + } else if ($node['id_agent'] == $relation['id_child_source_data']) { + $agent2 = $node['id_db']; + } + } + } else if ($relation['child_type'] == NODE_PANDORA) { + foreach ($nodes_graph as $key2 => $node) { + if ($relation['id_child'] == $node['id_db']) { + $agent2 = $node['id_db']; + } else if ($node['id_agent'] == $relation['id_parent_source_data']) { + $agent = $node['id_db']; + } + } + } + } else { + foreach ($nodes_graph as $key2 => $node) { + if (isset($node['id_agent'])) { + if ($node['id_agent'] == $relation['id_parent_source_data']) { + $agent = $node['id_db']; + } else if ($node['id_agent'] == $relation['id_child_source_data']) { + $agent2 = $node['id_db']; + } + } + } + } + + foreach ($nodes_graph as $node) { + if ($node['id_db'] == $agent) { + $item['target'] = $node['id']; + } else if ($node['id_db'] == $agent2) { + $item['source'] = $node['id']; + } + } + + if ((($item['target'] == -1) || ($item['source'] == -1)) + && $relation['parent_type'] == NODE_MODULE + && $relation['child_type'] == NODE_MODULE + ) { + continue; + } + + $return[] = $item; + } + + return $return; +} + + +function get_status_color_module_networkmap($id_agente_modulo) +{ + $status = modules_get_agentmodule_status($id_agente_modulo); + + // Set node status + switch ($status) { + case 0: + // At the moment the networkmap enterprise does not show the + // alerts. + case AGENT_MODULE_STATUS_NORMAL_ALERT: + $status_color = COL_NORMAL; + // Normal monitor + break; + + case 1: + $status_color = COL_CRITICAL; + // Critical monitor + break; + + case 2: + $status_color = COL_WARNING; + // Warning monitor + break; + + case 4: + $status_color = COL_ALERTFIRED; + // Alert fired + break; + + default: + $status_color = COL_UNKNOWN; + // Unknown monitor + break; + } + + return $status_color; +} + + +function duplicate_networkmap($id) +{ + $return = true; + + $values = db_get_row('tmap', 'id', $id); + unset($values['id']); + $free_name = false; + $values['name'] = io_safe_input(__('Copy of ')).$values['name']; + $count = 1; + while (!$free_name) { + $exist = db_get_row_filter('tmap', ['name' => $values['name']]); + if ($exist === false) { + $free_name = true; + } else { + $values['name'] = $values['name'].io_safe_input(' '.$count); + } + } + + $correct_or_id = db_process_sql_insert('tmap', $values); + if ($correct_or_id === false) { + $return = false; + } else { + if (enterprise_installed()) { + $new_id = $correct_or_id; + duplicate_map_insert_nodes_and_relations($id, $new_id); + } + } + + if ($return) { + return true; + } else { + // Clean DB. + if (enterprise_installed()) { + // Relations + delete_relations($new_id); + + // Nodes + delete_nodes($new_id); + } + + db_process_sql_delete('tmap', ['id' => $new_id]); + + return false; + } +} + + +function clean_duplicate_links($relations) +{ + if (enterprise_installed()) { + enterprise_include_once('include/functions_networkmap.php'); + } + + $segregation_links = []; + $index = 0; + $index2 = 0; + $index3 = 0; + $index4 = 0; + foreach ($relations as $rel) { + if (($rel['parent_type'] == 0) && ($rel['child_type'] == 0)) { + $segregation_links['aa'][$index] = $rel; + $index++; + } else if (($rel['parent_type'] == 1) && ($rel['child_type'] == 1)) { + $segregation_links['mm'][$index2] = $rel; + $index2++; + } else if (($rel['parent_type'] == 3) && ($rel['child_type'] == 3)) { + $segregation_links['ff'][$index4] = $rel; + $index4++; + } else { + $segregation_links['am'][$index3] = $rel; + $index3++; + } + } + + $final_links = []; + + // ---------------------------------------------------------------- + // --------------------- Clean duplicate links -------------------- + // ---------------------------------------------------------------- + $duplicated = false; + $index_to_del = 0; + $index = 0; + if (isset($segregation_links['aa']) === true + && is_array($segregation_links['aa']) === true + ) { + foreach ($segregation_links['aa'] as $link) { + foreach ($segregation_links['aa'] as $link2) { + if ($link['id_parent'] == $link2['id_child'] + && $link['id_child'] == $link2['id_parent'] + ) { + if (enterprise_installed()) { + delete_link($segregation_links['aa'][$index_to_del]); + } + + unset($segregation_links['aa'][$index_to_del]); + } + + $index_to_del++; + } + + $final_links['aa'][$index] = $link; + $index++; + + $duplicated = false; + $index_to_del = 0; + } + } + + $duplicated = false; + $index_to_del = 0; + $index2 = 0; + if (isset($segregation_links['mm']) === true + && is_array($segregation_links['mm']) === true + ) { + foreach ($segregation_links['mm'] as $link) { + foreach ($segregation_links['mm'] as $link2) { + if ($link['id_parent'] == $link2['id_child'] + && $link['id_child'] == $link2['id_parent'] + ) { + if (enterprise_installed()) { + delete_link($segregation_links['mm'][$index_to_del]); + } + } + + $index_to_del++; + } + + $final_links['mm'][$index2] = $link; + $index2++; + + $duplicated = false; + $index_to_del = 0; + } + } + + $duplicated = false; + $index_to_del = 0; + $index3 = 0; + + if (isset($segregation_links['ff']) === true + && is_array($segregation_links['ff']) === true + ) { + foreach ($segregation_links['ff'] as $link) { + foreach ($segregation_links['ff'] as $link2) { + if ($link['id_parent'] == $link2['id_child'] + && $link['id_child'] == $link2['id_parent'] + ) { + if (enterprise_installed()) { + delete_link($segregation_links['ff'][$index_to_del]); + } + + unset($segregation_links['ff'][$index_to_del]); + } + + $index_to_del++; + } + + $final_links['ff'][$index3] = $link; + $index3++; + + $duplicated = false; + $index_to_del = 0; + } + } + + $final_links['am'] = $segregation_links['am']; + + /* + ---------------------------------------------------------------- + ----------------- AA, AM and MM links management --------------- + ------------------ Priority: ----------------------------------- + -------------------- 1 -> MM (module - module) ----------------- + -------------------- 2 -> AM (agent - module) ------------------ + -------------------- 3 -> AA (agent - agent) ------------------- + ---------------------------------------------------------------- + */ + + $final_links2 = []; + $index = 0; + $l3_link = []; + $agent1 = 0; + $agent2 = 0; + + if (isset($final_links['mm']) === true + && is_array($final_links['mm']) === true + ) { + foreach ($final_links['mm'] as $rel_mm) { + $module_parent = $rel_mm['id_parent_source_data']; + $module_children = $rel_mm['id_child_source_data']; + $agent1 = (int) agents_get_agent_id_by_module_id($module_parent); + $agent2 = (int) agents_get_agent_id_by_module_id($module_children); + foreach ($final_links['aa'] as $key => $rel_aa) { + $l3_link = $rel_aa; + $id_p_source_data = (int) $rel_aa['id_parent_source_data']; + $id_c_source_data = (int) $rel_aa['id_child_source_data']; + if ((($id_p_source_data == $agent1) + && ($id_c_source_data == $agent2)) + || (($id_p_source_data == $agent2) + && ($id_c_source_data == $agent1)) + ) { + if (enterprise_installed()) { + delete_link($final_links['aa'][$key]); + } + + unset($final_links['aa'][$key]); + } + } + } + } + + $final_links2['aa'] = $final_links['aa']; + $final_links2['mm'] = $final_links['mm']; + $final_links2['am'] = $final_links['am']; + $final_links2['ff'] = $final_links['ff']; + + $same_m = []; + $index = 0; + if (isset($final_links2['am']) === true + && is_array($final_links2['am']) === true + ) { + foreach ($final_links2['am'] as $rel_am) { + foreach ($final_links2['am'] as $rel_am2) { + if (($rel_am['id_child_source_data'] == $rel_am2['id_child_source_data']) + && ($rel_am['id_parent_source_data'] != $rel_am2['id_parent_source_data']) + ) { + $same_m[$index]['rel'] = $rel_am2; + $same_m[$index]['agent_parent'] = $rel_am['id_parent_source_data']; + $index++; + } + } + } + } + + $final_links3 = []; + $index = 0; + $l3_link = []; + $have_l3 = false; + if (isset($final_links2['aa']) === true + && is_array($final_links2['aa']) === true + ) { + foreach ($final_links2['aa'] as $key => $rel_aa) { + $l3_link = $rel_aa; + foreach ($same_m as $rel_am) { + if ((($rel_aa['id_parent_source_data'] == $rel_am['parent']['id_parent_source_data']) + && ($rel_aa['id_child_source_data'] == $rel_am['rel']['id_parent_source_data'])) + || (($rel_aa['id_child_source_data'] == $rel_am['parent']['id_parent_source_data']) + && ($rel_aa['id_parent_source_data'] == $rel_am['rel']['id_parent_source_data'])) + ) { + if (enterprise_installed()) { + delete_link($final_links2['aa'][$key]); + } + + unset($final_links2['aa'][$key]); + } + } + } + } + + $final_links3['aa'] = $final_links2['aa']; + $final_links3['mm'] = $segregation_links['mm']; + $final_links3['am'] = $segregation_links['am']; + $final_links3['ff'] = $final_links2['ff']; + + $cleaned_links = []; + if (isset($final_links3['aa']) === true + && is_array($final_links3['aa']) === true + ) { + foreach ($final_links3['aa'] as $link) { + $cleaned_links[] = $link; + } + } + + if (isset($final_links3['am']) === true + && is_array($final_links3['am']) === true + ) { + foreach ($final_links3['am'] as $link) { + $cleaned_links[] = $link; + } + } + + if (isset($final_links3['mm']) === true + && is_array($final_links3['mm']) === true + ) { + foreach ($final_links3['mm'] as $link) { + $cleaned_links[] = $link; + } + } + + if (isset($final_links3['ff']) === true + && is_array($final_links3['ff']) === true + ) { + foreach ($final_links3['ff'] as $link) { + $cleaned_links[] = $link; + } + } + + return $cleaned_links; +} + + +function migrate_older_open_maps($id) +{ + global $config; + + $old_networkmap = db_get_row_filter( + 'tnetwork_map', + ['id_networkmap' => $id] + ); + + $map_values = []; + $map_values['id_group'] = $old_networkmap['id_group']; + $map_values['id_user'] = $old_networkmap['id_user']; + $map_values['type'] = 0; + $map_values['subtype'] = 0; + $map_values['name'] = $old_networkmap['name']; + + $new_map_filter = []; + $new_map_filter['dont_show_subgroups'] = $old_networkmap['dont_show_subgroups']; + $new_map_filter['node_radius'] = 40; + $new_map_filter['id_migrate_map'] = $id; + $map_values['filter'] = json_encode($new_map_filter); + + $map_values['description'] = 'Mapa open migrado'; + $map_values['width'] = 4000; + $map_values['height'] = 4000; + $map_values['center_x'] = 2000; + $map_values['center_y'] = 2000; + $map_values['background'] = ''; + $map_values['background_options'] = 0; + $map_values['source_period'] = 60; + $map_values['source'] = 0; + $map_values['source_data'] = $old_networkmap['id_group']; + if ($old_networkmap['type'] == 'radial_dinamic') { + $map_values['generation_method'] = 6; + } else { + $map_values['generation_method'] = 4; + } + + $map_values['generated'] = 0; + + $id_new_map = db_process_sql_insert('tmap', $map_values); + + if (!$id_new_map) { + return false; + } + + return true; +} + + +/** + * Load cluetip required files and JS. + * + * @return void + */ +function networkmap_load_cluetip() +{ + ui_require_css_file('cluetip', 'include/styles/js/'); + + ?> +
    '
    +            );
    +        break;
    +    }
    +
         return sprintf(
             "
                 %s
                 
    @@ -990,10 +1009,12 @@ function notifications_print_dropdown_element($message_info)

    ", + $action.';click_on_notification_toast(event)', $message_info['id_mensaje'], messages_get_url($message_info['id_mensaje']), + $target, html_print_image('images/'.$message_info['icon'], true), - $message_info['subject'], - str_replace([io_safe_input('
    ')], ' ', $message_info['mensaje']) + io_safe_output($message_info['subject']), + $body_preview ); } diff --git a/pandora_console/include/functions_pandora_networkmap.php b/pandora_console/include/functions_pandora_networkmap.php index 9870172de4..cad7fc7edf 100644 --- a/pandora_console/include/functions_pandora_networkmap.php +++ b/pandora_console/include/functions_pandora_networkmap.php @@ -1,2189 +1,29 @@ $id]); - - return $result; -} - - -function networkmap_delete_nodes($id_map) -{ - return db_process_sql_delete('titem', ['id_map' => $id_map]); -} - - -function networkmap_process_networkmap($id=0) -{ - global $config; - - include_once 'include/functions_os.php'; - - $numNodes = (int) db_get_num_rows( - ' - SELECT * - FROM titem - WHERE id_map = '.$id.' and deleted = 0' - ); - - $networkmap = db_get_row_filter( - 'tmap', - ['id' => $id] - ); - $map_filter = json_decode($networkmap['filter'], true); - - $pure = (int) get_parameter('pure', 0); - - switch ($networkmap['generation_method']) { - case 0: - $filter = 'circo'; - $layout = 'circular'; - break; - - case 1: - $filter = 'dot'; - $layout = 'flat'; - break; - - case 2: - $filter = 'twopi'; - $layout = 'radial'; - break; - - case 3: - $filter = 'neato'; - $layout = 'spring1'; - break; - - case 4: - $filter = 'fdp'; - $layout = 'spring2'; - break; - } - - $simple = 0; - $font_size = 12; - $nooverlap = false; - $zoom = 1; - $ranksep = 0.5; - $center = 0; - $regen = 1; - $show_snmp_modules = false; - $dont_show_subgroups = false; - - $id_group = $networkmap['id_group']; - $ip_mask = ''; - switch ($networkmap['source']) { - case 1: - $recon_task = db_get_row_filter( - 'trecon_task', - ['id_rt' => $networkmap['source_data']] - ); - - $ip_mask = $recon_task['subnet']; - break; - - case 2: - $ip_mask = $networkmap['source_data']; - break; - } - - $nodes_and_relations = []; - - if (enterprise_installed() && ($numNodes > 0)) { - $nodes_and_relations = get_structure_nodes($id); - } else { - if ($map_filter['empty_map']) { - // Open Graph - $graph = networkmap_open_graph( - $layout, - $nooverlap, - $pure, - $zoom, - $ranksep, - $font_size, - null - ); - $graph .= networkmap_create_pandora_node(get_product_name(), $font_size, $simple); - $graph .= networkmap_close_graph(); - - switch (PHP_OS) { - case 'WIN32': - case 'WINNT': - case 'Windows': - $filename_dot = sys_get_temp_dir()."\\networkmap_".$filter; - break; - - default: - $filename_dot = sys_get_temp_dir().'/networkmap_'.$filter; - break; - } - - if ($simple) { - $filename_dot .= '_simple'; - } - - if ($nooverlap) { - $filename_dot .= '_nooverlap'; - } - - $filename_dot .= '_'.$id.'.dot'; - - file_put_contents($filename_dot, $graph); - - switch (PHP_OS) { - case 'WIN32': - case 'WINNT': - case 'Windows': - $filename_plain = sys_get_temp_dir().'\\plain.txt'; - - $cmd = io_safe_output( - $config['graphviz_bin_dir']."\\$filter.exe -Tplain -o ".$filename_plain.' '.$filename_dot - ); - break; - - default: - $filename_plain = sys_get_temp_dir().'/plain.txt'; - - $cmd = "$filter -Tplain -o ".$filename_plain.' '.$filename_dot; - break; - } - - system($cmd); - - unlink($filename_dot); - - $nodes = networkmap_loadfile( - $id, - $filename_plain, - $relation_nodes, - $graph - ); - - unlink($filename_plain); - - // Set the position of modules - foreach ($nodes as $key => $node) { - if ($node['type'] == 'module') { - // Search the agent of this module for to get the - // position - foreach ($nodes as $key2 => $node2) { - if ($node2['id_agent'] != 0 && $node2['type'] == 'agent') { - if ($node2['id_agent'] == $node['id_agent']) { - $nodes[$key]['coords'][0] = ($nodes[$key2]['coords'][0] + $node['height'] / 2); - $nodes[$key]['coords'][1] = ($nodes[$key2]['coords'][1] + $node['width'] / 2); - } - } - } - } - } - - $nodes_and_relations['nodes'] = []; - $index = 0; - foreach ($nodes as $key => $node) { - $nodes_and_relations['nodes'][$index]['id'] = $node['id']; - $nodes_and_relations['nodes'][$index]['id_map'] = $id; - - $nodes_and_relations['nodes'][$index]['x'] = (int) $node['coords'][0]; - $nodes_and_relations['nodes'][$index]['y'] = (int) $node['coords'][1]; - - if (($node['type'] == 'agent') || ($node['type'] == '')) { - $nodes_and_relations['nodes'][$index]['source_data'] = $node['id_agent']; - $nodes_and_relations['nodes'][$index]['type'] = 0; - } else { - $nodes_and_relations['nodes'][$index]['source_data'] = $node['id_module']; - $nodes_and_relations['nodes'][$index]['id_agent'] = $node['id_agent']; - $nodes_and_relations['nodes'][$index]['type'] = 1; - } - - $style = []; - $style['shape'] = 'circle'; - $style['image'] = $node['image']; - $style['width'] = $node['width']; - $style['height'] = $node['height']; - $style['label'] = $node['text']; - $style['id_networkmap'] = $node['networkmap']; - $nodes_and_relations['nodes'][$index]['style'] = json_encode($style); - - $index++; - } - - $nodes_and_relations['relations'] = []; - - if (enterprise_installed()) { - enterprise_include_once('include/functions_pandora_networkmap.php'); - save_generate_nodes($id, $nodes_and_relations); - } - } else { - // Generate dot file - $graph = networkmap_generate_dot( - get_product_name(), - $id_group, - $simple, - $font_size, - $layout, - $nooverlap, - $zoom, - $ranksep, - $center, - $regen, - $pure, - $id, - $show_snmp_modules, - false, - // cut_names - true, - // relative - '', - $ip_mask, - $dont_show_subgroups, - false, - null, - $old_mode, - $map_filter - ); - - switch (PHP_OS) { - case 'WIN32': - case 'WINNT': - case 'Windows': - $filename_dot = sys_get_temp_dir()."\\networkmap_".$filter; - break; - - default: - $filename_dot = sys_get_temp_dir().'/networkmap_'.$filter; - break; - } - - if ($simple) { - $filename_dot .= '_simple'; - } - - if ($nooverlap) { - $filename_dot .= '_nooverlap'; - } - - $filename_dot .= '_'.$id.'.dot'; - - file_put_contents($filename_dot, $graph); - - switch (PHP_OS) { - case 'WIN32': - case 'WINNT': - case 'Windows': - $filename_plain = sys_get_temp_dir().'\\plain.txt'; - - $cmd = io_safe_output( - $config['graphviz_bin_dir']."\\$filter.exe -Tplain -o ".$filename_plain.' '.$filename_dot - ); - break; - - default: - $filename_plain = sys_get_temp_dir().'/plain.txt'; - - $cmd = "$filter -Tplain -o ".$filename_plain.' '.$filename_dot; - break; - } - - system($cmd); - - unlink($filename_dot); - - $nodes = networkmap_loadfile( - $id, - $filename_plain, - $relation_nodes, - $graph - ); - - unlink($filename_plain); - - // Set the position of modules - foreach ($nodes as $key => $node) { - if ($node['type'] == 'module') { - // Search the agent of this module for to get the - // position - foreach ($nodes as $key2 => $node2) { - if ($node2['id_agent'] != 0 && $node2['type'] == 'agent') { - if ($node2['id_agent'] == $node['id_agent']) { - $nodes[$key]['coords'][0] = ($nodes[$key2]['coords'][0] + $node['height'] / 2); - $nodes[$key]['coords'][1] = ($nodes[$key2]['coords'][1] + $node['width'] / 2); - } - } - } - } - } - - $nodes_and_relations['nodes'] = []; - $index = 0; - $node_center = []; - foreach ($nodes as $key => $node) { - $nodes_and_relations['nodes'][$index]['id'] = $node['id']; - $nodes_and_relations['nodes'][$index]['id_map'] = $id; - - $children_count = 0; - foreach ($relation_nodes as $relation) { - if (($relation['parent_type'] == 'agent') || ($relation['parent_type'] == '')) { - if ($nodes[$relation['id_parent']]['id_agent'] == $node['id_agent']) { - $children_count++; - } - } else if ($relation['parent_type'] == 'module') { - if ($nodes[$relation['id_parent']]['id_module'] == $node['id_module']) { - $children_count++; - } - } - } - - if (empty($node_center) || $node_center['counter'] < $children_count) { - $node_center['x'] = (int) $node['coords'][0]; - $node_center['y'] = (int) $node['coords'][1]; - $node_center['counter'] = $children_count; - } - - $nodes_and_relations['nodes'][$index]['x'] = (int) $node['coords'][0]; - $nodes_and_relations['nodes'][$index]['y'] = (int) $node['coords'][1]; - - if (($node['type'] == 'agent') || ($node['type'] == '')) { - $nodes_and_relations['nodes'][$index]['source_data'] = $node['id_agent']; - $nodes_and_relations['nodes'][$index]['type'] = 0; - } else { - $nodes_and_relations['nodes'][$index]['source_data'] = $node['id_module']; - $nodes_and_relations['nodes'][$index]['id_agent'] = $node['id_agent']; - $nodes_and_relations['nodes'][$index]['type'] = 1; - } - - $style = []; - $style['shape'] = 'circle'; - $style['image'] = $node['image']; - $style['width'] = $node['width']; - $style['height'] = $node['height']; - $style['label'] = $node['text']; - $nodes_and_relations['nodes'][$index]['style'] = json_encode($style); - - $index++; - } - - $nodes_and_relations['relations'] = []; - $index = 0; - foreach ($relation_nodes as $relation) { - $nodes_and_relations['relations'][$index]['id_map'] = $id; - - if (($relation['parent_type'] == 'agent') || ($relation['parent_type'] == '')) { - $nodes_and_relations['relations'][$index]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index]['id_parent_source_data'] = $nodes[$relation['id_parent']]['id_agent']; - $nodes_and_relations['relations'][$index]['parent_type'] = 0; - } else if ($relation['parent_type'] == 'module') { - $nodes_and_relations['relations'][$index]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index]['id_parent_source_data'] = $nodes[$relation['id_parent']]['id_module']; - $nodes_and_relations['relations'][$index]['parent_type'] = 1; - } else { - $nodes_and_relations['relations'][$index]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = -2; - $nodes_and_relations['relations'][$index]['parent_type'] = 3; - } - - if (($relation['child_type'] == 'agent') || ($relation['child_type'] == '')) { - $nodes_and_relations['relations'][$index]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = $nodes[$relation['id_child']]['id_agent']; - $nodes_and_relations['relations'][$index]['child_type'] = 0; - } else if ($relation['child_type'] == 'module') { - $nodes_and_relations['relations'][$index]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = $nodes[$relation['id_child']]['id_module']; - $nodes_and_relations['relations'][$index]['child_type'] = 1; - } else { - $nodes_and_relations['relations'][$index]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = -2; - $nodes_and_relations['relations'][$index]['child_type'] = 3; - } - - $index++; - } - - if (enterprise_installed()) { - enterprise_include_once('include/functions_pandora_networkmap.php'); - save_generate_nodes($id, $nodes_and_relations); - } - - $pandorafms_node = $nodes_and_relations['nodes'][0]; - $center = [ - 'x' => $node_center['x'], - 'y' => $node_center['y'], - ]; - - $networkmap['center_x'] = $center['x']; - $networkmap['center_y'] = $center['y']; - db_process_sql_update( - 'tmap', - [ - 'center_x' => $networkmap['center_x'], - 'center_y' => $networkmap['center_y'], - ], - ['id' => $id] - ); - } - } - - return $nodes_and_relations; -} - - -function get_networkmaps($id) -{ - $groups = array_keys(users_get_groups(null, 'IW')); - - $filter = []; - $filter['id_group'] = $groups; - $filter['id'] = '<>'.$id; - $networkmaps = db_get_all_rows_filter('tmap', $filter); - if ($networkmaps === false) { - $networkmaps = []; - } - - $return = []; - $return[0] = __('None'); - foreach ($networkmaps as $networkmap) { - $return[$networkmap['id']] = $networkmap['name']; - } - - return $return; -} - - -function networkmap_db_node_to_js_node($node, &$count, &$count_item_holding_area) -{ - global $config; - - $networkmap = db_get_row('tmap', 'id', $node['id_map']); - - $networkmap['filter'] = json_decode($networkmap['filter'], true); - - // Hardcoded - $networkmap['filter']['holding_area'] = [ - 500, - 500, - ]; - - // 40 = DEFAULT NODE RADIUS - // 30 = for to align - $holding_area_max_y = ($networkmap['height'] + 30 + 40 * 2 - $networkmap['filter']['holding_area'][1] + 10 * 40); - - $item = []; - $item['id'] = $count; - - if (enterprise_installed()) { - enterprise_include_once('include/functions_pandora_networkmap.php'); - $item['id_db'] = $node['id_in_db']; - } else { - $item['id_db'] = (int) $node['id']; - } - - if ((int) $node['type'] == 0) { - $item['type'] = 0; - $item['id_agent'] = (int) $node['source_data']; - $item['id_module'] = ''; - } else if ((int) $node['type'] == 1) { - $item['type'] = 1; - $item['id_agent'] = (int) $node['style']['id_agent']; - $item['id_module'] = (int) $node['source_data']; - } else { - $item['type'] = 3; - } - - $item['fixed'] = true; - $item['x'] = (int) $node['x']; - $item['y'] = (int) $node['y']; - $item['px'] = (int) $node['x']; - $item['py'] = (int) $node['y']; - $item['z'] = (int) $node['z']; - $item['state'] = $node['state']; - $item['deleted'] = $node['deleted']; - if ($item['state'] == 'holding_area') { - // 40 = DEFAULT NODE RADIUS - // 30 = for to align - $holding_area_x = ($networkmap['width'] + 30 + 40 * 2 - $networkmap['filter']['holding_area'][0] + ($count_item_holding_area % 11) * 40); - $holding_area_y = ($networkmap['height'] + 30 + 40 * 2 - $networkmap['filter']['holding_area'][1] + (int) (($count_item_holding_area / 11)) * 40); - - if ($holding_area_max_y <= $holding_area_y) { - $holding_area_y = $holding_area_max_y; - } - - $item['x'] = $holding_area_x; - $item['y'] = $holding_area_y; - - // Increment for the next node in holding area - $count_item_holding_area++; - } - - $item['image_url'] = ''; - $item['image_width'] = 0; - $item['image_height'] = 0; - if (!empty($node['style']['image'])) { - $item['image_url'] = html_print_image( - $node['style']['image'], - true, - false, - true - ); - $image_size = getimagesize( - $config['homedir'].'/'.$node['style']['image'] - ); - $item['image_width'] = (int) $image_size[0]; - $item['image_height'] = (int) $image_size[1]; - } - - $item['raw_text'] = $node['style']['label']; - $item['text'] = io_safe_output($node['style']['label']); - $item['shape'] = $node['style']['shape']; - switch ($node['type']) { - case 0: - $color = get_status_color_networkmap($node['source_data']); - break; - - default: - // Old code - if ($node['source_data'] == -1) { - $color = '#364D1F'; - } else if ($node['source_data'] == -2) { - $color = '#364D1F'; - } else { - $color = get_status_color_networkmap($node['source_data']); - } - break; - } - - $item['color'] = $color; - $item['map_id'] = 0; - if (isset($node['id_map'])) { - $item['map_id'] = $node['id_map']; - } - - if (!isset($node['style']['id_networkmap']) || $node['style']['id_networkmap'] == '' || $node['style']['id_networkmap'] == 0) { - $item['networkmap_id'] = 0; - } else { - $item['networkmap_id'] = $node['style']['id_networkmap']; - } - - $count++; - - return $item; -} - - -function get_status_color_networkmap($id, $color=true) -{ - $status = agents_get_status($id); - - if (!$color) { - return $status; - } - - // Set node status - switch ($status) { - case 0: - $status_color = COL_NORMAL; - // Normal monitor - break; - - case 1: - $status_color = COL_CRITICAL; - // Critical monitor - break; - - case 2: - $status_color = COL_WARNING; - // Warning monitor - break; - - case 4: - $status_color = COL_ALERTFIRED; - // Alert fired - break; - - default: - $status_color = COL_UNKNOWN; - // Unknown monitor - break; - } - - return $status_color; -} - - -function networkmap_clean_relations_for_js(&$relations) -{ - do { - $cleaned = true; - - foreach ($relations as $key => $relation) { - if ($relation['id_parent_source_data'] == $relation['id_child_source_data']) { - if (($relation['child_type'] != 3) && $relation['parent_type'] != 3) { - $cleaned = false; - - if ($relation['parent_type'] == 1) { - $to_find = $relation['id_parent_source_data']; - $to_replace = $relation['id_child_source_data']; - } else if ($relation['child_type'] == 1) { - $to_find = $relation['id_child_source_data']; - $to_replace = $relation['id_parent_source_data']; - } - - // Replace and erase the links - foreach ($relations as $key2 => $relation2) { - if ($relation2['id_parent_source_data'] == $to_find) { - $relations[$key2]['id_parent_source_data'] = $to_replace; - } else if ($relation2['id_child_source_data'] == $to_find) { - $relations[$key2]['id_child_source_data'] = $to_replace; - } - } - - unset($relations[$key]); - - break; - } - } - } - } while (!$cleaned); -} - - -function networkmap_links_to_js_links($relations, $nodes_graph) -{ - $return = []; - - if (enterprise_installed()) { - enterprise_include_once('include/functions_pandora_networkmap.php'); - } - - $count = 0; - foreach ($relations as $key => $relation) { - if (($relation['parent_type'] == 1) && ($relation['child_type'] == 1)) { - $id_target_agent = agents_get_agent_id_by_module_id($relation['id_parent_source_data']); - $id_source_agent = agents_get_agent_id_by_module_id($relation['id_child_source_data']); - $id_target_module = $relation['id_parent_source_data']; - $id_source_module = $relation['id_child_source_data']; - } else if (($relation['parent_type'] == 1) && ($relation['child_type'] == 0)) { - $id_target_agent = agents_get_agent_id_by_module_id($relation['id_parent_source_data']); - $id_target_module = $relation['id_parent_source_data']; - $id_source_agent = $relation['id_child_source_data']; - } else if (($relation['parent_type'] == 0) && ($relation['child_type'] == 1)) { - $id_target_agent = $relation['id_parent_source_data']; - $id_source_module = $relation['id_child_source_data']; - $id_source_agent = agents_get_agent_id_by_module_id($relation['id_child_source_data']); - } else { - $id_target_agent = $relation['id_parent_source_data']; - $id_source_agent = $relation['id_child_source_data']; - } - - $item = []; - $item['id'] = $count; - $count++; - if (enterprise_installed()) { - $item['id_db'] = get_relation_id($relation); - } else { - $item['id_db'] = $key; - } - - $item['arrow_start'] = ''; - $item['arrow_end'] = ''; - $item['status_start'] = ''; - $item['status_end'] = ''; - $item['id_module_start'] = 0; - $item['id_agent_start'] = (int) $id_source_agent; - $item['id_module_end'] = 0; - $item['id_agent_end'] = (int) $id_target_agent; - $item['link_color'] = '#999'; - $item['target'] = -1; - $item['source'] = -1; - $item['deleted'] = $relation['deleted']; - - if (enterprise_installed()) { - $target_and_source = []; - $target_and_source = get_id_target_and_source_in_db($relation); - $item['target_id_db'] = (int) $target_and_source['target']; - $item['source_id_db'] = (int) $target_and_source['source']; - } else { - if (($relation['parent_type'] == 1) && ($relation['child_type'] == 1)) { - $item['target_id_db'] = $id_target_agent; - $item['source_id_db'] = $id_source_agent; - } else if (($relation['parent_type'] == 0) && ($relation['child_type'] == 0)) { - $item['target_id_db'] = (int) $relation['id_parent_source_data']; - $item['source_id_db'] = $id_source_agent; - } else { - $item['target_id_db'] = (int) $relation['id_parent_source_data']; - $item['source_id_db'] = (int) $relation['id_child_source_data']; - } - } - - $item['text_end'] = ''; - $item['text_start'] = ''; - - if ($relation['parent_type'] == 1) { - $item['arrow_end'] = 'module'; - $item['status_end'] = modules_get_agentmodule_status((int) $id_target_module, false, false, null); - $item['id_module_end'] = (int) $id_target_module; - $text_end = modules_get_agentmodule_name((int) $id_target_module); - if (preg_match('/(.+)_ifOperStatus$/', (string) $text_end, $matches)) { - if ($matches[1]) { - // It's ok to safe_output as it inlo goint to be user into the map line - $item['text_end'] = io_safe_output($matches[1]); - } - } - } - - if ($relation['child_type'] == 1) { - $item['arrow_start'] = 'module'; - $item['status_start'] = modules_get_agentmodule_status((int) $id_source_module, false, false, null); - $item['id_module_start'] = (int) $id_source_module; - $text_start = modules_get_agentmodule_name((int) $id_source_module); - if (preg_match('/(.+)_ifOperStatus$/', (string) $text_start, $matches)) { - if ($matches[1]) { - // It's ok to safe_output as it inlo goint to be user into the map line - $item['text_start'] = io_safe_output($matches[1]); - } - } - } - - $agent = 0; - $agent2 = 0; - - if (($relation['parent_type'] == 1) && ($relation['child_type'] == 1)) { - $mod1_status = db_get_value_filter('estado', 'tagente_estado', ['id_agente_modulo' => $relation['id_parent_source_data']]); - $mod2_status = db_get_value_filter('estado', 'tagente_estado', ['id_agente_modulo' => $relation['id_child_source_data']]); - - if (($mod1_status == AGENT_MODULE_STATUS_CRITICAL_BAD) || ($mod2_status == AGENT_MODULE_STATUS_CRITICAL_BAD)) { - $item['link_color'] = '#FC4444'; - } else if (($mod1_status == AGENT_MODULE_STATUS_WARNING) || ($mod2_status == AGENT_MODULE_STATUS_WARNING)) { - $item['link_color'] = '#FAD403'; - } - - $agent = agents_get_agent_id_by_module_id($relation['id_parent_source_data']); - $agent2 = agents_get_agent_id_by_module_id($relation['id_child_source_data']); - foreach ($nodes_graph as $key2 => $node) { - if (isset($node['id_agent'])) { - if ($node['id_agent'] == $agent) { - $agent = $node['id_db']; - } else if ($node['id_agent'] == $agent2) { - $agent2 = $node['id_db']; - } - } - } - } else if ($relation['child_type'] == 1) { - $mod1_status = db_get_value_filter('estado', 'tagente_estado', ['id_agente_modulo' => $relation['id_child_source_data']]); - - if ($mod1_status == AGENT_MODULE_STATUS_CRITICAL_BAD) { - $item['link_color'] = '#FC4444'; - } else if ($mod1_status == AGENT_MODULE_STATUS_WARNING) { - $item['link_color'] = '#FAD403'; - } - - $agent2 = agents_get_agent_id_by_module_id($relation['id_child_source_data']); - foreach ($nodes_graph as $key2 => $node) { - if (isset($node['id_agent'])) { - if ($node['id_agent'] == $relation['id_parent_source_data']) { - $agent = $node['id_db']; - } else if ($node['id_agent'] == $agent2) { - $agent2 = $node['id_db']; - } - } - } - } else if ($relation['parent_type'] == 1) { - $mod1_status = db_get_value_filter('estado', 'tagente_estado', ['id_agente_modulo' => $relation['id_parent_source_data']]); - - if ($mod1_status == AGENT_MODULE_STATUS_CRITICAL_BAD) { - $item['link_color'] = '#FC4444'; - } else if ($mod1_status == AGENT_MODULE_STATUS_WARNING) { - $item['link_color'] = '#FAD403'; - } - - $agent = agents_get_agent_id_by_module_id($relation['id_parent_source_data']); - - foreach ($nodes_graph as $key2 => $node) { - if (isset($node['id_agent'])) { - if ($node['id_agent'] == $agent) { - $agent = $node['id_db']; - } else if ($node['id_agent'] == $relation['id_child_source_data']) { - $agent2 = $node['id_db']; - } - } - } - } else if (($relation['parent_type'] == 3) && ($relation['child_type'] == 3)) { - foreach ($nodes_graph as $key2 => $node) { - if ($relation['id_parent'] == $node['id_db']) { - $agent = $node['id_db']; - } - } - - foreach ($nodes_graph as $key2 => $node) { - if ($relation['id_child'] == $node['id_db']) { - $agent2 = $node['id_db']; - } - } - } else if (($relation['parent_type'] == 3) || ($relation['child_type'] == 3)) { - if ($relation['parent_type'] == 3) { - foreach ($nodes_graph as $key2 => $node) { - if ($relation['id_parent'] == $node['id_db']) { - $agent = $node['id_db']; - } else if ($node['id_agent'] == $relation['id_child_source_data']) { - $agent2 = $node['id_db']; - } - } - } else if ($relation['child_type'] == 3) { - foreach ($nodes_graph as $key2 => $node) { - if ($relation['id_child'] == $node['id_db']) { - $agent2 = $node['id_db']; - } else if ($node['id_agent'] == $relation['id_parent_source_data']) { - $agent = $node['id_db']; - } - } - } - } else { - foreach ($nodes_graph as $key2 => $node) { - if (isset($node['id_agent'])) { - if ($node['id_agent'] == $relation['id_parent_source_data']) { - $agent = $node['id_db']; - } else if ($node['id_agent'] == $relation['id_child_source_data']) { - $agent2 = $node['id_db']; - } - } - } - } - - foreach ($nodes_graph as $node) { - if ($node['id_db'] == $agent) { - $item['target'] = $node['id']; - } else if ($node['id_db'] == $agent2) { - $item['source'] = $node['id']; - } - } - - if ((($item['target'] == -1) || ($item['source'] == -1)) && $relation['parent_type'] == 1 && $relation['child_type'] == 1) { - continue; - } - - $return[] = $item; - } - - return $return; -} - - -function networkmap_write_js_array($id, $nodes_and_relations=[], $map_dash_details=[]) -{ - global $config; - - db_clean_cache(); - - $ent_installed = (int) enterprise_installed(); - - $networkmap = db_get_row('tmap', 'id', $id); - - $networkmap['filter'] = json_decode($networkmap['filter'], true); - - // Hardcoded - $networkmap['filter']['holding_area'] = [ - 500, - 500, - ]; - - echo "\n"; - echo "////////////////////////////////////////////////////////////////////\n"; - echo "// VARS FROM THE DB\n"; - echo "////////////////////////////////////////////////////////////////////\n"; - echo "\n"; - echo "var url_background_grid = '".ui_get_full_url( - 'images/background_grid.png' - )."'\n"; - echo 'var networkmap_id = '.$id.";\n"; - - if (!empty($map_dash_details)) { - echo 'var x_offs = '.$map_dash_details['x_offs'].";\n"; - echo 'var y_offs = '.$map_dash_details['y_offs'].";\n"; - echo 'var z_dash = '.$map_dash_details['z_dash'].";\n"; - } else { - echo "var x_offs = null;\n"; - echo "var y_offs = null;\n"; - echo "var z_dash = null;\n"; - } - - echo 'var networkmap_refresh_time = 1000 * '.$networkmap['source_period'].";\n"; - echo 'var networkmap_center = [ '.$networkmap['center_x'].', '.$networkmap['center_y']."];\n"; - echo 'var networkmap_dimensions = [ '.$networkmap['width'].', '.$networkmap['height']."];\n"; - - echo 'var enterprise_installed = '.$ent_installed.";\n"; - - echo 'var node_radius = '.$networkmap['filter']['node_radius'].";\n"; - - echo 'var networkmap_holding_area_dimensions = '.json_encode($networkmap['filter']['holding_area']).";\n"; - - echo "var networkmap = {'nodes': [], 'links': []};\n"; - - $nodes = $nodes_and_relations['nodes']; - - if (empty($nodes)) { - $nodes = []; - } - - $count_item_holding_area = 0; - $count = 0; - $nodes_graph = []; - - foreach ($nodes as $key => $node) { - $style = json_decode($node['style'], true); - $node['style'] = json_decode($node['style'], true); - - // Only agents can be show - if (isset($node['type'])) { - if ($node['type'] == 1) { - continue; - } - } else { - $node['type'] = ''; - } - - $item = networkmap_db_node_to_js_node( - $node, - $count, - $count_item_holding_area - ); - if ($item['deleted']) { - continue; - } - - echo 'networkmap.nodes.push('.json_encode($item).");\n"; - $nodes_graph[$item['id']] = $item; - } - - $relations = $nodes_and_relations['relations']; - - if ($relations === false) { - $relations = []; - } - - // Clean the relations and transform the module relations into - // interfaces - networkmap_clean_relations_for_js($relations); - - $links_js = networkmap_links_to_js_links($relations, $nodes_graph); - - $array_aux = []; - foreach ($links_js as $link_js) { - if ($link_js['deleted']) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['target'] == -1) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['source'] == -1) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['target'] == $link_js['source']) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['arrow_start'] == 'module' && $link_js['arrow_end'] == 'module') { - echo 'networkmap.links.push('.json_encode($link_js).");\n"; - $array_aux[$link_js['id_agent_start']] = 1; - unset($links_js[$link_js['id']]); - } - } - - foreach ($links_js as $link_js) { - if (($link_js['id_agent_end'] === 0) && $array_aux[$link_js['id_agent_start']] === 1) { - continue; - } else { - echo 'networkmap.links.push('.json_encode($link_js).");\n"; - } - } - - echo "\n"; - echo "\n"; - - echo "////////////////////////////////////////////////////////////////////\n"; - echo "// INTERFACE STATUS COLORS\n"; - echo "////////////////////////////////////////////////////////////////////\n"; - - $module_color_status = []; - $module_color_status[] = [ - 'status_code' => AGENT_MODULE_STATUS_NORMAL, - 'color' => COL_NORMAL, - ]; - $module_color_status[] = [ - 'status_code' => AGENT_MODULE_STATUS_CRITICAL_BAD, - 'color' => COL_CRITICAL, - ]; - $module_color_status[] = [ - 'status_code' => AGENT_MODULE_STATUS_WARNING, - 'color' => COL_WARNING, - ]; - $module_color_status[] = [ - 'status_code' => AGENT_STATUS_ALERT_FIRED, - 'color' => COL_ALERTFIRED, - ]; - $module_color_status_unknown = COL_UNKNOWN; - - echo 'var module_color_status = '.json_encode($module_color_status).";\n"; - echo "var module_color_status_unknown = '".$module_color_status_unknown."';\n"; - - echo "\n"; - echo "\n"; - - echo "////////////////////////////////////////////////////////////////////\n"; - echo "// Other vars\n"; - echo "////////////////////////////////////////////////////////////////////\n"; - - echo "var translation_none = '".__('None')."';\n"; - echo "var dialog_node_edit_title = '".__('Edit node %s')."';\n"; - echo "var holding_area_title = '".__('Holding Area')."';\n"; - echo "var edit_menu = '".__('Show details and options')."';\n"; - echo "var interface_link_add = '".__('Add a interface link')."';\n"; - echo "var set_parent_link = '".__('Set parent interface')."';\n"; - echo "var set_as_children_menu = '".__('Set as children')."';\n"; - echo "var set_parent_menu = '".__('Set parent')."';\n"; - echo "var abort_relationship_menu = '".__('Abort the action of set relationship')."';\n"; - echo "var delete_menu = '".__('Delete')."';\n"; - echo "var add_node_menu = '".__('Add node')."';\n"; - echo "var set_center_menu = '".__('Set center')."';\n"; - echo "var refresh_menu = '".__('Refresh')."';\n"; - echo "var refresh_holding_area_menu = '".__('Refresh Holding area')."';\n"; - echo "var ok_button = '".__('Proceed')."';\n"; - echo "var message_to_confirm = '".__('Resetting the map will delete all customizations you have done, including manual relationships between elements, new items, etc.')."';\n"; - echo "var warning_message = '".__('WARNING')."';\n"; - echo "var ok_button = '".__('Proceed')."';\n"; - echo "var cancel_button = '".__('Cancel')."';\n"; - echo "var restart_map_menu = '".__('Restart map')."';\n"; - echo "var abort_relationship_interface = '".__('Abort the interface relationship')."';\n"; - echo "var abort_relationship_menu = '".__('Abort the action of set relationship')."';\n"; - - echo "\n"; - echo "\n"; -} - - -function networkmap_loadfile( - $id=0, - $file='', - &$relations_param, - $graph -) { - global $config; - - $height_map = db_get_value('height', 'tmap', 'id', $id); - - $networkmap_nodes = []; - - $relations = []; - - $other_file = file($file); - - // Remove the graph head - $graph = preg_replace('/^graph .*/', '', $graph); - // Cut in nodes the graph - $graph = explode(']', $graph); - - $ids = []; - foreach ($graph as $node) { - $line = str_replace("\n", ' ', $node); - - if (preg_match('/([0-9]+) \[.*tooltip.*id_module=([0-9]+)/', $line, $match) != 0) { - $ids[$match[1]] = [ - 'type' => 'module', - 'id_module' => $match[2], - ]; - } else if (preg_match('/([0-9]+) \[.*tooltip.*id_agent=([0-9]+)/', $line, $match) != 0) { - $ids[$match[1]] = [ - 'type' => 'agent', - 'id_agent' => $match[2], - ]; - } - } - - foreach ($other_file as $key => $line) { - // clean line a long spaces for one space caracter - $line = preg_replace('/[ ]+/', ' ', $line); - - $data = []; - - if (preg_match('/^node.*$/', $line) != 0) { - $items = explode(' ', $line); - $node_id = $items[1]; - $node_x = ($items[2] * 100); - // 200 is for show more big - $node_y = ($height_map - $items[3] * 100); - // 200 is for show more big - $data['id'] = $node_id; - $data['text'] = ''; - $data['image'] = ''; - $data['width'] = 10; - $data['height'] = 10; - $data['id_agent'] = 0; - - if (preg_match('/ $line_orig, - 'dest' => $line_dest, - ]; - } - } - - $relations_param = []; - - foreach ($relations as $rel) { - if (strpos($rel['orig'], 'transp_') !== false) { - // removed the transparent nodes - continue; - } - - if (strpos($rel['dest'], 'transp_') !== false) { - // removed the transparent nodes - continue; - } - - $row = [ - 'id_child' => $rel['orig'], - 'child_type' => $networkmap_nodes[$rel['orig']]['type'], - 'id_parent' => $rel['dest'], - 'parent_type' => $networkmap_nodes[$rel['dest']]['type'], - ]; - $relations_param[] = $row; - } - - return $networkmap_nodes; -} - - -function get_status_color_module_networkmap($id_agente_modulo) -{ - $status = modules_get_agentmodule_status($id_agente_modulo); - - // Set node status - switch ($status) { - case 0: - // At the moment the networkmap enterprise does not show the - // alerts. - case AGENT_MODULE_STATUS_NORMAL_ALERT: - $status_color = COL_NORMAL; - // Normal monitor - break; - - case 1: - $status_color = COL_CRITICAL; - // Critical monitor - break; - - case 2: - $status_color = COL_WARNING; - // Warning monitor - break; - - case 4: - $status_color = COL_ALERTFIRED; - // Alert fired - break; - - default: - $status_color = COL_UNKNOWN; - // Unknown monitor - break; - } - - return $status_color; -} - - -function duplicate_networkmap($id) -{ - $return = true; - - $values = db_get_row('tmap', 'id', $id); - unset($values['id']); - $free_name = false; - $values['name'] = io_safe_input(__('Copy of ')).$values['name']; - $count = 1; - while (!$free_name) { - $exist = db_get_row_filter('tmap', ['name' => $values['name']]); - if ($exist === false) { - $free_name = true; - } else { - $values['name'] = $values['name'].io_safe_input(' '.$count); - } - } - - $correct_or_id = db_process_sql_insert('tmap', $values); - if ($correct_or_id === false) { - $return = false; - } else { - if (enterprise_installed()) { - $new_id = $correct_or_id; - duplicate_map_insert_nodes_and_relations($id, $new_id); - } - } - - if ($return) { - return true; - } else { - // Clean DB. - if (enterprise_installed()) { - // Relations - delete_relations($new_id); - - // Nodes - delete_nodes($new_id); - } - - db_process_sql_delete('tmap', ['id' => $new_id]); - - return false; - } -} - - -function clean_duplicate_links($relations) -{ - if (enterprise_installed()) { - enterprise_include_once('include/functions_pandora_networkmap.php'); - } - - $segregation_links = []; - $index = 0; - $index2 = 0; - $index3 = 0; - $index4 = 0; - foreach ($relations as $rel) { - if (($rel['parent_type'] == 0) && ($rel['child_type'] == 0)) { - $segregation_links['aa'][$index] = $rel; - $index++; - } else if (($rel['parent_type'] == 1) && ($rel['child_type'] == 1)) { - $segregation_links['mm'][$index2] = $rel; - $index2++; - } else if (($rel['parent_type'] == 3) && ($rel['child_type'] == 3)) { - $segregation_links['ff'][$index4] = $rel; - $index4++; - } else { - $segregation_links['am'][$index3] = $rel; - $index3++; - } - } - - $final_links = []; - - // ---------------------------------------------------------------- - // --------------------- Clean duplicate links -------------------- - // ---------------------------------------------------------------- - $duplicated = false; - $index_to_del = 0; - $index = 0; - if (isset($segregation_links['aa']) === true - && is_array($segregation_links['aa']) === true - ) { - foreach ($segregation_links['aa'] as $link) { - foreach ($segregation_links['aa'] as $link2) { - if ($link['id_parent'] == $link2['id_child'] - && $link['id_child'] == $link2['id_parent'] - ) { - if (enterprise_installed()) { - delete_link($segregation_links['aa'][$index_to_del]); - } - - unset($segregation_links['aa'][$index_to_del]); - } - - $index_to_del++; - } - - $final_links['aa'][$index] = $link; - $index++; - - $duplicated = false; - $index_to_del = 0; - } - } - - $duplicated = false; - $index_to_del = 0; - $index2 = 0; - if (isset($segregation_links['mm']) === true - && is_array($segregation_links['mm']) === true - ) { - foreach ($segregation_links['mm'] as $link) { - foreach ($segregation_links['mm'] as $link2) { - if ($link['id_parent'] == $link2['id_child'] - && $link['id_child'] == $link2['id_parent'] - ) { - if (enterprise_installed()) { - delete_link($segregation_links['mm'][$index_to_del]); - } - } - - $index_to_del++; - } - - $final_links['mm'][$index2] = $link; - $index2++; - - $duplicated = false; - $index_to_del = 0; - } - } - - $duplicated = false; - $index_to_del = 0; - $index3 = 0; - - if (isset($segregation_links['ff']) === true - && is_array($segregation_links['ff']) === true - ) { - foreach ($segregation_links['ff'] as $link) { - foreach ($segregation_links['ff'] as $link2) { - if ($link['id_parent'] == $link2['id_child'] - && $link['id_child'] == $link2['id_parent'] - ) { - if (enterprise_installed()) { - delete_link($segregation_links['ff'][$index_to_del]); - } - - unset($segregation_links['ff'][$index_to_del]); - } - - $index_to_del++; - } - - $final_links['ff'][$index3] = $link; - $index3++; - - $duplicated = false; - $index_to_del = 0; - } - } - - $final_links['am'] = $segregation_links['am']; - - /* - ---------------------------------------------------------------- - ----------------- AA, AM and MM links management --------------- - ------------------ Priority: ----------------------------------- - -------------------- 1 -> MM (module - module) ----------------- - -------------------- 2 -> AM (agent - module) ------------------ - -------------------- 3 -> AA (agent - agent) ------------------- - ---------------------------------------------------------------- - */ - - $final_links2 = []; - $index = 0; - $l3_link = []; - $agent1 = 0; - $agent2 = 0; - - if (isset($final_links['mm']) === true - && is_array($final_links['mm']) === true - ) { - foreach ($final_links['mm'] as $rel_mm) { - $module_parent = $rel_mm['id_parent_source_data']; - $module_children = $rel_mm['id_child_source_data']; - $agent1 = (int) agents_get_agent_id_by_module_id($module_parent); - $agent2 = (int) agents_get_agent_id_by_module_id($module_children); - foreach ($final_links['aa'] as $key => $rel_aa) { - $l3_link = $rel_aa; - $id_p_source_data = (int) $rel_aa['id_parent_source_data']; - $id_c_source_data = (int) $rel_aa['id_child_source_data']; - if ((($id_p_source_data == $agent1) - && ($id_c_source_data == $agent2)) - || (($id_p_source_data == $agent2) - && ($id_c_source_data == $agent1)) - ) { - if (enterprise_installed()) { - delete_link($final_links['aa'][$key]); - } - - unset($final_links['aa'][$key]); - } - } - } - } - - $final_links2['aa'] = $final_links['aa']; - $final_links2['mm'] = $final_links['mm']; - $final_links2['am'] = $final_links['am']; - $final_links2['ff'] = $final_links['ff']; - - $same_m = []; - $index = 0; - if (isset($final_links2['am']) === true - && is_array($final_links2['am']) === true - ) { - foreach ($final_links2['am'] as $rel_am) { - foreach ($final_links2['am'] as $rel_am2) { - if (($rel_am['id_child_source_data'] == $rel_am2['id_child_source_data']) - && ($rel_am['id_parent_source_data'] != $rel_am2['id_parent_source_data']) - ) { - $same_m[$index]['rel'] = $rel_am2; - $same_m[$index]['agent_parent'] = $rel_am['id_parent_source_data']; - $index++; - } - } - } - } - - $final_links3 = []; - $index = 0; - $l3_link = []; - $have_l3 = false; - if (isset($final_links2['aa']) === true - && is_array($final_links2['aa']) === true - ) { - foreach ($final_links2['aa'] as $key => $rel_aa) { - $l3_link = $rel_aa; - foreach ($same_m as $rel_am) { - if ((($rel_aa['id_parent_source_data'] == $rel_am['parent']['id_parent_source_data']) - && ($rel_aa['id_child_source_data'] == $rel_am['rel']['id_parent_source_data'])) - || (($rel_aa['id_child_source_data'] == $rel_am['parent']['id_parent_source_data']) - && ($rel_aa['id_parent_source_data'] == $rel_am['rel']['id_parent_source_data'])) - ) { - if (enterprise_installed()) { - delete_link($final_links2['aa'][$key]); - } - - unset($final_links2['aa'][$key]); - } - } - } - } - - $final_links3['aa'] = $final_links2['aa']; - $final_links3['mm'] = $segregation_links['mm']; - $final_links3['am'] = $segregation_links['am']; - $final_links3['ff'] = $final_links2['ff']; - - $cleaned_links = []; - if (isset($final_links3['aa']) === true - && is_array($final_links3['aa']) === true - ) { - foreach ($final_links3['aa'] as $link) { - $cleaned_links[] = $link; - } - } - - if (isset($final_links3['am']) === true - && is_array($final_links3['am']) === true - ) { - foreach ($final_links3['am'] as $link) { - $cleaned_links[] = $link; - } - } - - if (isset($final_links3['mm']) === true - && is_array($final_links3['mm']) === true - ) { - foreach ($final_links3['mm'] as $link) { - $cleaned_links[] = $link; - } - } - - if (isset($final_links3['ff']) === true - && is_array($final_links3['ff']) === true - ) { - foreach ($final_links3['ff'] as $link) { - $cleaned_links[] = $link; - } - } - - return $cleaned_links; -} - - -function is_in_rel_array($relations, $relation) -{ - $is_in_array = false; - foreach ($relations as $rel) { - if ($rel['id_parent_source_data'] == $relation['id_parent_source_data'] - && $rel['id_child_source_data'] == $relation['id_child_source_data'] - ) { - $is_in_array = true; - } - } - - return $is_in_array; -} - - -function map_migrated($id) -{ - $new_maps = db_get_all_rows_sql('SELECT filter FROM tmap'); - $new_map_filter = json_decode($new_maps, true); - - foreach ($new_map_filter as $filter) { - if ((isset($filter['id_migrate_map'])) && ($filter['id_migrate_map'] == $id)) { - return true; - } - } - - return false; -} - - -function migrate_older_open_maps($id) -{ - global $config; - - $old_networkmap = db_get_row_filter( - 'tnetwork_map', - ['id_networkmap' => $id] - ); - - $map_values = []; - $map_values['id_group'] = $old_networkmap['id_group']; - $map_values['id_user'] = $old_networkmap['id_user']; - $map_values['type'] = 0; - $map_values['subtype'] = 0; - $map_values['name'] = $old_networkmap['name']; - - $new_map_filter = []; - $new_map_filter['dont_show_subgroups'] = $old_networkmap['dont_show_subgroups']; - $new_map_filter['node_radius'] = 40; - $new_map_filter['id_migrate_map'] = $id; - $map_values['filter'] = json_encode($new_map_filter); - - $map_values['description'] = 'Mapa open migrado'; - $map_values['width'] = 4000; - $map_values['height'] = 4000; - $map_values['center_x'] = 2000; - $map_values['center_y'] = 2000; - $map_values['background'] = ''; - $map_values['background_options'] = 0; - $map_values['source_period'] = 60; - $map_values['source'] = 0; - $map_values['source_data'] = $old_networkmap['id_group']; - if ($old_networkmap['type'] == 'radial_dinamic') { - $map_values['generation_method'] = 6; - } else { - $map_values['generation_method'] = 4; - } - - $map_values['generated'] = 0; - - $id_new_map = db_process_sql_insert('tmap', $map_values); - - if (!$id_new_map) { - return false; - } - - return true; -} - - -function show_networkmap($id=0, $user_readonly=false, $nodes_and_relations=[], $dashboard_mode=false, $map_dash_details=[]) -{ - global $config; - $clean_relations = clean_duplicate_links($nodes_and_relations['relations']); - - $hide_minimap = ''; - - $nodes_and_relations['relations'] = $clean_relations; - - $networkmap = db_get_row('tmap', 'id', $id); - $networkmap['filter'] = json_decode($networkmap['filter'], true); - - $networkmap['filter']['l2_network_interfaces'] = 1; - - echo ''; - ui_require_css_file('jquery.contextMenu', 'include/styles/js/'); - echo ''; - echo ''; - echo '
    '; - if ($dashboard_mode) { - $hide_minimap = 'none'; - } - - echo '
    '; - echo ' - '; - - echo '
    - - - -
    '; - echo '
    '; - - echo '
    - - - -
    '; - echo ''; - - echo '
    '; - - ?> - - - - - - - - - - - - - $row) { - // Metaconsole connection + // Metaconsole connection. $server_name = $row['server_name']; if (($config['metaconsole'] == 1) && $server_name != '' && defined('METACONSOLE')) { $connection = metaconsole_get_connection($server_name); @@ -1414,7 +1411,7 @@ function reporting_event_top_n( case REPORT_TOP_N_AVG: default: - // If nothing is selected then it will be shown the average data + // If nothing is selected then it will be shown the average data. $value = reporting_get_agentmodule_data_average($row['id_agent_module'], $content['period']); break; } @@ -1428,7 +1425,7 @@ function reporting_event_top_n( $units[$key] = $unit; } - // Restore dbconnection + // Restore dbconnection. if (($config['metaconsole'] == 1) && $server_name != '' && defined('METACONSOLE')) { metaconsole_restore_db(); } @@ -1441,21 +1438,25 @@ function reporting_event_top_n( // Order to show. switch ($order_uptodown) { - // Descending + // Descending. case 1: array_multisort($data_top, SORT_DESC, $agent_name, SORT_ASC, $module_name, SORT_ASC, $id_agent_module, SORT_ASC, $units, SORT_ASC); break; - // Ascending + // Ascending. case 2: array_multisort($data_top, SORT_ASC, $agent_name, SORT_ASC, $module_name, SORT_ASC, $id_agent_module, SORT_ASC, $units, SORT_ASC); break; - // By agent name or without selection + // By agent name or without selection. case 0: case 3: array_multisort($agent_name, SORT_ASC, $data_top, SORT_ASC, $module_name, SORT_ASC, $id_agent_module, SORT_ASC, $units, SORT_ASC); break; + + default: + // Default. + break; } array_splice($data_top, $top_n_value); @@ -1471,7 +1472,7 @@ function reporting_event_top_n( $data_top_values['id_agent_module'] = $id_agent_module; $data_top_values['units'] = $units; - // Define truncate size depends the graph width + // Define truncate size depends the graph width. $truncate_size = ($width / (4 * ($config['font_size'])) - 1); if ($order_uptodown == 1 || $order_uptodown == 2) { @@ -1597,7 +1598,7 @@ function reporting_event_top_n( $ttl ); - // Display bars graph + // Display bars graph. $return['charts']['bars'] = hbar_graph( $data_hbar, $width, @@ -1622,7 +1623,7 @@ function reporting_event_top_n( $return['resume'] = null; if ($content['show_resume'] && count($data_top_values) > 0) { - // Get the very first not null value + // Get the very first not null value. $i = 0; do { $min = $data_top_values['data_top'][$i]; @@ -1697,18 +1698,19 @@ function reporting_event_report_group( } $return['description'] = $content['description']; + $return['show_extended_events'] = $content['show_extended_events']; $return['date'] = reporting_get_date_text($report, $content); $event_filter = $content['style']; $return['show_summary_group'] = $event_filter['show_summary_group']; - // filter + // Filter. $show_summary_group = $event_filter['show_summary_group']; $filter_event_severity = json_decode($event_filter['filter_event_severity'], true); $filter_event_type = json_decode($event_filter['filter_event_type'], true); $filter_event_status = json_decode($event_filter['filter_event_status'], true); $filter_event_filter_search = $event_filter['event_filter_search']; - // graphs + // Graphs. $event_graph_by_agent = $event_filter['event_graph_by_agent']; $event_graph_by_user_validator = $event_filter['event_graph_by_user_validator']; $event_graph_by_criticity = $event_filter['event_graph_by_criticity']; @@ -1870,7 +1872,7 @@ function reporting_event_report_group( metaconsole_restore_db(); } - // total_events + // total_events. if ($return['data'] != '') { $return['total_events'] = count($return['data']); } else { @@ -1912,24 +1914,29 @@ function reporting_event_report_module( $return['title'] = $content['name']; $return['subtitle'] = agents_get_alias($content['id_agent']).' - '.io_safe_output(modules_get_agentmodule_name($content['id_agent_module'])); + $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + if ($return['label'] != '') { + $return['label'] = reporting_label_macro($content, $return['label']); + } + if (is_metaconsole()) { metaconsole_restore_db(); } $return['description'] = $content['description']; + $return['show_extended_events'] = $content['show_extended_events']; $return['date'] = reporting_get_date_text($report, $content); - $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; $event_filter = $content['style']; $return['show_summary_group'] = $event_filter['show_summary_group']; - // filter + // Filter. $show_summary_group = $event_filter['show_summary_group']; $filter_event_severity = json_decode($event_filter['filter_event_severity'], true); $filter_event_type = json_decode($event_filter['filter_event_type'], true); $filter_event_status = json_decode($event_filter['filter_event_status'], true); $filter_event_filter_search = $event_filter['event_filter_search']; - // graphs + // Graphs. $event_graph_by_user_validator = $event_filter['event_graph_by_user_validator']; $event_graph_by_criticity = $event_filter['event_graph_by_criticity']; $event_graph_validated_vs_unvalidated = $event_filter['event_graph_validated_vs_unvalidated']; @@ -1941,7 +1948,7 @@ function reporting_event_report_module( $metaconsole_dbtable = false; } - // data events + // Data events. $data = reporting_get_module_detailed_event( $content['id_agent_module'], $content['period'], @@ -1970,7 +1977,7 @@ function reporting_event_report_module( metaconsole_restore_db(); } - // total_events + // Total_events. if ($return['data'][0]['data'] != '') { $return['total_events'] = count($return['data'][0]['data']); } else { @@ -2287,6 +2294,10 @@ function reporting_exception( $return['subtitle'] = __('Exception - Modules at critical or warning status'); $return['subtype'] = __('Modules at critical or warning status'); break; + + default: + // Default. + break; } $return['description'] = $content['description']; @@ -2297,7 +2308,7 @@ function reporting_exception( $return['resume'] = []; if (empty($content['subitems'])) { - // Get all the related data + // Get all the related data. $sql = sprintf( ' SELECT id_agent_module, server_name, operation @@ -2314,10 +2325,10 @@ function reporting_exception( if ($exceptions === false) { $return['failed'] = __('There are no Agent/Modules defined'); } else { - // Get the very first not null value + // Get the very first not null value. $i = 0; do { - // Metaconsole connection + // Metaconsole connection. $server_name = $exceptions[$i]['server_name']; if (($config['metaconsole'] == 1) && $server_name != '' && defined('METACONSOLE')) { $connection = metaconsole_get_connection($server_name); @@ -2351,12 +2362,16 @@ function reporting_exception( $content['period'] ); break; + + default: + // Default. + break; } } $i++; - // Restore dbconnection + // Restore dbconnection. if (($config['metaconsole'] == 1) && $server_name != '' && defined('METACONSOLE')) { metaconsole_restore_db(); } @@ -2368,7 +2383,7 @@ function reporting_exception( $i = 0; foreach ($exceptions as $exc) { - // Metaconsole connection + // Metaconsole connection. $server_name = $exc['server_name']; if (($config['metaconsole'] == 1) && $server_name != '' && defined('METACONSOLE')) { $connection = metaconsole_get_connection($server_name); @@ -2730,6 +2745,17 @@ function reporting_group_report($report, $content) } +/** + * Create data report event agent. + * + * @param array $report Data report. + * @param array $content Content report. + * @param string $type Type report. + * @param integer $force_width_chart Force width. + * @param integer $force_height_chart Force height. + * + * @return array Data. + */ function reporting_event_report_agent( $report, $content, @@ -2750,25 +2776,26 @@ function reporting_event_report_agent( $history = true; } - $return['title'] = $content['name']; - $return['subtitle'] = agents_get_alias($content['id_agent']); - $return['description'] = $content['description']; - $return['date'] = reporting_get_date_text($report, $content); - $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + $return['title'] = $content['name']; + $return['subtitle'] = agents_get_alias($content['id_agent']); + $return['description'] = $content['description']; + $return['date'] = reporting_get_date_text($report, $content); + $return['show_summary_group'] = $content['style']['show_summary_group']; + $return['show_extended_events'] = $content['show_extended_events']; $style = $content['style']; - // filter - $show_summary_group = $style['show_summary_group']; - $filter_event_severity = json_decode($style['filter_event_severity'], true); - $filter_event_type = json_decode($style['filter_event_type'], true); - $filter_event_status = json_decode($style['filter_event_status'], true); + // Filter. + $show_summary_group = $style['show_summary_group']; + $filter_event_severity = json_decode($style['filter_event_severity'], true); + $filter_event_type = json_decode($style['filter_event_type'], true); + $filter_event_status = json_decode($style['filter_event_status'], true); $filter_event_filter_search = $style['event_filter_search']; - // graph - $event_graph_by_user_validator = $style['event_graph_by_user_validator']; - $event_graph_by_criticity = $style['event_graph_by_criticity']; + // Graph. + $event_graph_by_user_validator = $style['event_graph_by_user_validator']; + $event_graph_by_criticity = $style['event_graph_by_criticity']; $event_graph_validated_vs_unvalidated = $style['event_graph_validated_vs_unvalidated']; $return['data'] = reporting_get_agents_detailed_event( @@ -2813,6 +2840,13 @@ function reporting_event_report_agent( $metaconsole_dbtable = false; } + $label = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + if ($label != '') { + $label = reporting_label_macro($content, $label); + } + + $return['label'] = $label; + if ($event_graph_by_user_validator) { $data_graph = events_get_count_events_validated_by_user( ['id_agent' => $content['id_agent']], @@ -2896,7 +2930,7 @@ function reporting_event_report_agent( metaconsole_restore_db(); } - // total_events + // Total events. if ($return['data'] != '') { $return['total_events'] = count($return['data']); } else { @@ -2929,7 +2963,11 @@ function reporting_historical_data($report, $content) $return['subtitle'] = $agent_name.' - '.$module_name; $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); + $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + if ($return['label'] != '') { + $return['label'] = reporting_label_macro($content, $return['label']); + } $return['keys'] = [ __('Date'), @@ -3005,7 +3043,6 @@ function reporting_database_serialized($report, $content) $return['subtitle'] = $agent_name.' - '.$module_name; $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); - $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; $keys = []; if (isset($content['header_definition']) && ($content['header_definition'] != '')) { @@ -3031,6 +3068,11 @@ function reporting_database_serialized($report, $content) metaconsole_connect($server); } + $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + if ($return['label'] != '') { + $return['label'] = reporting_label_macro($content, $return['label']); + } + $datelimit = ($report['datetime'] - $content['period']); $search_in_history_db = db_search_in_history_db($datelimit); @@ -3577,7 +3619,12 @@ function reporting_alert_report_agent($report, $content) $return['subtitle'] = $agent_name; $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); + $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + if ($return['label'] != '') { + $return['label'] = reporting_label_macro($content, $return['label']); + } + $module_list = agents_get_modules($content['id_agent']); $data = []; @@ -3709,6 +3756,9 @@ function reporting_alert_report_module($report, $content) $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + if ($return['label'] != '') { + $return['label'] = reporting_label_macro($content, $return['label']); + } $data_row = []; @@ -3911,7 +3961,6 @@ function reporting_monitor_report($report, $content) $return['subtitle'] = $agent_name.' - '.$module_name; $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); - $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; if ($config['metaconsole']) { $id_meta = metaconsole_get_id_server($content['server_name']); @@ -3920,6 +3969,11 @@ function reporting_monitor_report($report, $content) metaconsole_connect($server); } + $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + if ($return['label'] != '') { + $return['label'] = reporting_label_macro($content, $return['label']); + } + $module_name = io_safe_output( modules_get_agentmodule_name($content['id_agent_module']) ); @@ -3955,6 +4009,20 @@ function reporting_monitor_report($report, $content) } +/** + * Generates the data structure to build a netflow report. + * + * @param array $report Global report info. + * @param array $content Report item info. + * @param string $type Report type (static, dynamic, data). + * @param integer $force_width_chart Fixed width chart. + * @param integer $force_height_chart Fixed height chart. + * @param string $type_netflow One of netflow_area, netflow_data, + * netflow_summary. + * @param boolean $pdf True if a pdf report is generating. + * + * @return array Report item structure. + */ function reporting_netflow( $report, $content, @@ -3971,21 +4039,17 @@ function reporting_netflow( $return['type'] = 'netflow_area'; break; - case 'netflow_pie': - $return['type'] = 'netflow_pie'; - break; - case 'netflow_data': $return['type'] = 'netflow_data'; break; - case 'netflow_statistics': - $return['type'] = 'netflow_statistics'; - break; - case 'netflow_summary': $return['type'] = 'netflow_summary'; break; + + default: + $return['type'] = 'unknown'; + break; } if (empty($content['name'])) { @@ -3994,20 +4058,16 @@ function reporting_netflow( $content['name'] = __('Netflow Area'); break; - case 'netflow_pie': - $content['name'] = __('Netflow Pie'); + case 'netflow_summary': + $content['name'] = __('Netflow Summary'); break; case 'netflow_data': $content['name'] = __('Netflow Data'); break; - case 'netflow_statistics': - $content['name'] = __('Netflow Statistics'); - break; - - case 'netflow_summary': - $content['name'] = __('Netflow Summary'); + default: + $content['name'] = __('Unknown report'); break; } } @@ -4016,7 +4076,7 @@ function reporting_netflow( $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); - // Get chart + // Get chart. reporting_set_conf_charts( $width, $height, @@ -4034,7 +4094,7 @@ function reporting_netflow( $height = $force_height_chart; } - // Get item filters + // Get item filters. $filter = db_get_row_sql( "SELECT * FROM tnetflow_filter @@ -4059,9 +4119,17 @@ function reporting_netflow( break; case 'data': + default: + // Nothing to do. break; } + $return['subtitle'] = netflow_generate_subtitle_report( + $filter['aggregate'], + $content['top_n'], + $type_netflow + ); + return reporting_check_structure_content($return); } @@ -4413,6 +4481,9 @@ function reporting_value($report, $content, $type, $pdf=false) $return['description'] = $content['description']; $return['date'] = reporting_get_date_text($report, $content); $return['label'] = (isset($content['style']['label'])) ? $content['style']['label'] : ''; + $return['agents'] = [$content['id_agent']]; + $return['id_agent'] = $content['id_agent']; + $return['id_agent_module'] = $content['id_agent_module']; $return['agent_name'] = $agent_name; $return['module_name'] = $module_name; @@ -5486,6 +5557,9 @@ function reporting_advanced_sla( $max_value = 0; $min_value = 0; $inverse_interval = 1; + } else if ($agentmodule_info['id_tipo_modulo'] == '100') { + $max_value = 0.9; + $min_value = 0; } } } @@ -6266,6 +6340,19 @@ function reporting_availability($report, $content, $date=false, $time=false) $return['resume']['avg'] = $avg; $return['resume']['max_text'] = $max_text; $return['resume']['max'] = $max; + $return['fields'] = []; + $return['fields']['total_time'] = $content['total_time']; + $return['fields']['time_failed'] = $content['time_failed']; + $return['fields']['time_in_ok_status'] = $content['time_in_ok_status']; + $return['fields']['time_in_unknown_status'] = $content['time_in_unknown_status']; + $return['fields']['time_of_not_initialized_module'] = $content['time_of_not_initialized_module']; + $return['fields']['time_of_downtime'] = $content['time_of_downtime']; + $return['fields']['total_checks'] = $content['total_checks']; + $return['fields']['checks_failed'] = $content['checks_failed']; + $return['fields']['checks_in_ok_status'] = $content['checks_in_ok_status']; + $return['fields']['unknown_checks'] = $content['unknown_checks']; + $return['fields']['agent_max_value'] = $content['agent_max_value']; + $return['fields']['agent_min_value'] = $content['agent_min_value']; return reporting_check_structure_content($return); } @@ -7228,6 +7315,7 @@ function reporting_custom_graph( 'modules_series' => $graphs[0]['modules_series'], 'id_graph' => $id_graph, 'type_report' => $type_report, + 'labels' => $content['style']['label'], ]; $return['chart'] = graphic_combined_module( @@ -7288,6 +7376,7 @@ function reporting_simple_graph( $report, $content ); + $label = (isset($content['style']['label'])) ? $content['style']['label'] : ''; if ($label != '') { $label = reporting_label_macro($content, $label); @@ -7299,7 +7388,7 @@ function reporting_simple_graph( $return['chart'] = ''; - // Get chart + // Get chart. reporting_set_conf_charts($width, $height, $only_image, $type, $content, $ttl); if (!empty($force_width_chart)) { @@ -7439,6 +7528,21 @@ function reporting_check_structure_content($report) $report['date']['to'] = ''; } + if (!isset($report['fields'])) { + $return['fields']['total_time'] = ''; + $return['fields']['time_failed'] = ''; + $return['fields']['time_in_ok_status'] = ''; + $return['fields']['time_in_unknown_status'] = ''; + $return['fields']['time_of_not_initialized_module'] = ''; + $return['fields']['time_of_downtime'] = ''; + $return['fields']['total_checks'] = ''; + $return['fields']['checks_failed'] = ''; + $return['fields']['checks_in_ok_status'] = ''; + $return['fields']['unknown_checks'] = ''; + $return['fields']['agent_max_value'] = ''; + $return['fields']['agent_min_value'] = ''; + } + return $report; } @@ -7734,6 +7838,7 @@ function reporting_get_agents_detailed_event( 'criticity' => $e['criticity'], 'validated_by' => $e['id_usuario'], 'timestamp' => $e['timestamp_rep'], + 'id_evento' => $e['id_evento'], ]; } else { $return_data[] = [ @@ -7743,6 +7848,7 @@ function reporting_get_agents_detailed_event( 'criticity' => $e['criticity'], 'validated_by' => $e['id_usuario'], 'timestamp' => $e['timestamp'], + 'id_evento' => $e['id_evento'], ]; } } @@ -7765,11 +7871,11 @@ function reporting_get_agents_detailed_event( foreach ($events as $eventRow) { foreach ($eventRow as $k => $event) { - // First pass along the class of this row + // First pass along the class of this row. $table->cellclass[$k][1] = $table->cellclass[$k][2] = $table->cellclass[$k][4] = $table->cellclass[$k][5] = $table->cellclass[$k][6] = get_priority_class($event['criticity']); $data = []; - // Colored box + // Colored box. switch ($event['estado']) { case 0: $img_st = 'images/star.png'; @@ -9284,15 +9390,34 @@ function reporting_get_agent_module_info($id_agent) $return = []; $return['last_contact'] = 0; - // Last agent contact + // Last agent contact. $return['status'] = STATUS_AGENT_NO_DATA; - $return['status_img'] = ui_print_status_image(STATUS_AGENT_NO_DATA, __('Agent without data'), true); + $return['status_img'] = ui_print_status_image( + STATUS_AGENT_NO_DATA, + __('Agent without data'), + true + ); $return['alert_status'] = 'notfired'; $return['alert_value'] = STATUS_ALERT_NOT_FIRED; - $return['alert_img'] = ui_print_status_image(STATUS_ALERT_NOT_FIRED, __('Alert not fired'), true); - $return['agent_group'] = agents_get_agent_group($id_agent); + $return['alert_img'] = ui_print_status_image( + STATUS_ALERT_NOT_FIRED, + __('Alert not fired'), + true + ); - if (!check_acl($config['id_user'], $return['agent_group'], 'AR')) { + $return['agent_group'] = ''; + // Important agents_get_all_groups_agent check secondary groups. + $id_all_groups = agents_get_all_groups_agent($id_agent); + if (isset($id_all_groups) && is_array($id_all_groups)) { + foreach ($id_all_groups as $value) { + if (check_acl($config['id_user'], $value, 'AR')) { + $return['agent_group'] = $value; + } + } + } + + // If $return['agent_group'] is empty no access. + if ($return['agent_group'] == '') { return $return; } @@ -9305,7 +9430,7 @@ function reporting_get_agent_module_info($id_agent) $now = get_system_time(); - // Get modules status for this agent + // Get modules status for this agent. $agent = db_get_row('tagente', 'id_agente', $id_agent); $return['total_count'] = $agent['total_count']; @@ -9319,28 +9444,52 @@ function reporting_get_agent_module_info($id_agent) if ($return['total_count'] > 0) { if ($return['critical_count'] > 0) { $return['status'] = STATUS_AGENT_CRITICAL; - $return['status_img'] = ui_print_status_image(STATUS_AGENT_CRITICAL, __('At least one module in CRITICAL status'), true); + $return['status_img'] = ui_print_status_image( + STATUS_AGENT_CRITICAL, + __('At least one module in CRITICAL status'), + true + ); } else if ($return['warning_count'] > 0) { $return['status'] = STATUS_AGENT_WARNING; - $return['status_img'] = ui_print_status_image(STATUS_AGENT_WARNING, __('At least one module in WARNING status'), true); + $return['status_img'] = ui_print_status_image( + STATUS_AGENT_WARNING, + __('At least one module in WARNING status'), + true + ); } else if ($return['unknown_count'] > 0) { $return['status'] = STATUS_AGENT_DOWN; - $return['status_img'] = ui_print_status_image(STATUS_AGENT_DOWN, __('At least one module is in UKNOWN status'), true); + $return['status_img'] = ui_print_status_image( + STATUS_AGENT_DOWN, + __('At least one module is in UKNOWN status'), + true + ); } else { $return['status'] = STATUS_AGENT_OK; - $return['status_img'] = ui_print_status_image(STATUS_AGENT_OK, __('All Monitors OK'), true); + $return['status_img'] = ui_print_status_image( + STATUS_AGENT_OK, + __('All Monitors OK'), + true + ); } } - // Alert not fired is by default + // Alert not fired is by default. if ($return['fired_count'] > 0) { $return['alert_status'] = 'fired'; - $return['alert_img'] = ui_print_status_image(STATUS_ALERT_FIRED, __('Alert fired'), true); + $return['alert_img'] = ui_print_status_image( + STATUS_ALERT_FIRED, + __('Alert fired'), + true + ); $return['alert_value'] = STATUS_ALERT_FIRED; } else if (groups_give_disabled_group($return['agent_group'])) { $return['alert_status'] = 'disabled'; $return['alert_value'] = STATUS_ALERT_DISABLED; - $return['alert_img'] = ui_print_status_image(STATUS_ALERT_DISABLED, __('Alert disabled'), true); + $return['alert_img'] = ui_print_status_image( + STATUS_ALERT_DISABLED, + __('Alert disabled'), + true + ); } return $return; @@ -9350,13 +9499,23 @@ function reporting_get_agent_module_info($id_agent) /** * Print tiny statistics of the status of one agent, group, etc. * - * @param mixed Array with the counts of the total modules, normal modules, critical modules, warning modules, unknown modules and fired alerts - * @param bool return or echo flag + * @param mixed $counts_info Array with the counts of the total modules, + * normal modules, critical modules, warning modules, unknown modules and + * fired alerts. + * @param boolean $return Return or echo flag. + * @param string $type agent or modules or ??. + * @param string $separator Sepearator (classic view). + * @param boolean $modern Use modern interfaces or old one. * - * @return string html formatted tiny stats of modules/alerts of an agent + * @return string HTML formatted tiny stats of modules/alerts of an agent. */ -function reporting_tiny_stats($counts_info, $return=false, $type='agent', $separator=':', $strict_user=false) -{ +function reporting_tiny_stats( + $counts_info, + $return=false, + $type='agent', + $separator=':', + $modern=false +) { global $config; $out = ''; @@ -9473,37 +9632,74 @@ function reporting_tiny_stats($counts_info, $return=false, $type='agent', $separ $out .= html_print_div($params, true); } - // If total count is less than 0, is an error. Never show negative numbers + // If total count is less than 0, is an error. Never show negative numbers. if ($total_count < 0) { $total_count = 0; } - $out .= ''.''.$total_count.''; - if (isset($fired_count) && $fired_count > 0) { - $out .= ' '.$separator.' '.$fired_count.''; - } + if ($modern === true) { + $out .= '
    '; + // $out .=''.$total_count.$separator.''; + if (isset($fired_count) && $fired_count > 0) { + $out .= '
    '; + $out .= ''.$fired_count.'
    '; + } - if (isset($critical_count) && $critical_count > 0) { - $out .= ' '.$separator.' '.$critical_count.''; - } + if (isset($critical_count) && $critical_count > 0) { + $out .= '
    '; + $out .= ''.$critical_count.'
    '; + } - if (isset($warning_count) && $warning_count > 0) { - $out .= ' '.$separator.' '.$warning_count.''; - } + if (isset($warning_count) && $warning_count > 0) { + $out .= '
    '; + $out .= ''.$warning_count.'
    '; + } - if (isset($unknown_count) && $unknown_count > 0) { - $out .= ' '.$separator.' '.$unknown_count.''; - } + if (isset($unknown_count) && $unknown_count > 0) { + $out .= '
    '; + $out .= ''.$unknown_count.'
    '; + } - if (isset($not_init_count) && $not_init_count > 0) { - $out .= ' '.$separator.' '.$not_init_count.''; - } + if (isset($not_init_count) && $not_init_count > 0) { + $out .= '
    '; + $out .= ''.$not_init_count.'
    '; + } - if (isset($normal_count) && $normal_count > 0) { - $out .= ' '.$separator.' '.$normal_count.''; - } + if (isset($normal_count) && $normal_count > 0) { + $out .= '
    '; + $out .= ''.$normal_count.'
    '; + } - $out .= '
    '; + $out .= '
    '; + } else { + // Classic ones. + $out .= ''.$total_count.''; + if (isset($fired_count) && $fired_count > 0) { + $out .= ' '.$separator.' '.$fired_count.''; + } + + if (isset($critical_count) && $critical_count > 0) { + $out .= ' '.$separator.' '.$critical_count.''; + } + + if (isset($warning_count) && $warning_count > 0) { + $out .= ' '.$separator.' '.$warning_count.''; + } + + if (isset($unknown_count) && $unknown_count > 0) { + $out .= ' '.$separator.' '.$unknown_count.''; + } + + if (isset($not_init_count) && $not_init_count > 0) { + $out .= ' '.$separator.' '.$not_init_count.''; + } + + if (isset($normal_count) && $normal_count > 0) { + $out .= ' '.$separator.' '.$normal_count.''; + } + + $out .= ''; + } if ($return) { return $out; @@ -11436,33 +11632,95 @@ function reporting_sla_is_ignored_from_array($sla_array) * * @return integer Status */ -function reporting_sla_get_status_period($sla_times, $priority_mode=REPORT_PRIORITY_MODE_OK) -{ - if ($sla_times['time_error'] > 0) { +function reporting_sla_get_status_period( + $sla, + $priority_mode=REPORT_PRIORITY_MODE_OK +) { + if ($sla['time_error'] > 0) { return REPORT_STATUS_ERR; } - if ($priority_mode == REPORT_PRIORITY_MODE_OK && $sla_times['time_ok'] > 0) { + if ($priority_mode == REPORT_PRIORITY_MODE_OK && $sla['time_ok'] > 0) { return REPORT_STATUS_OK; } - if ($sla_times['time_out'] > 0) { + if ($sla['time_out'] > 0) { return REPORT_STATUS_IGNORED; } - if ($sla_times['time_downtime'] > 0) { + if ($sla['time_downtime'] > 0) { return REPORT_STATUS_DOWNTIME; } - if ($sla_times['time_unknown'] > 0) { + if ($sla['time_unknown'] > 0) { return REPORT_STATUS_UNKNOWN; } - if ($sla_times['time_not_init'] > 0) { + if ($sla['time_not_init'] > 0) { return REPORT_STATUS_NOT_INIT; } - if ($sla_times['time_ok'] > 0) { + if ($sla['time_ok'] > 0) { + return REPORT_STATUS_OK; + } + + return REPORT_STATUS_IGNORED; +} + + +/** + * @brief Given a period, get the SLA status + * of the period compare with sla_limit. + * + * @param Array An array with all times to calculate the SLA. + * @param int Limit SLA pass for user. + * Only used for monthly, weekly And hourly report. + * + * @return integer Status + */ +function reporting_sla_get_status_period_compliance( + $sla, + $sla_limit +) { + global $config; + + $time_compliance = ( + $sla['time_ok'] + $sla['time_unknown'] + $sla['time_downtime'] + ); + + $time_total_working = ( + $time_compliance + $sla['time_error'] + ); + + $time_compliance = ($time_compliance == 0) ? 0 : (($time_compliance / $time_total_working) * 100); + + if ($sla['time_error'] > 0 && ($time_compliance < $sla_limit)) { + return REPORT_STATUS_ERR; + } + + if ($priority_mode == REPORT_PRIORITY_MODE_OK + && $sla['time_ok'] > 0 && ($time_compliance >= $sla_limit) + ) { + return REPORT_STATUS_OK; + } + + if ($sla['time_out'] > 0 && ($time_compliance < $sla_limit)) { + return REPORT_STATUS_IGNORED; + } + + if ($sla['time_downtime'] > 0 && ($time_compliance < $sla_limit)) { + return REPORT_STATUS_DOWNTIME; + } + + if ($sla['time_unknown'] > 0 && ($time_compliance < $sla_limit)) { + return REPORT_STATUS_UNKNOWN; + } + + if ($sla['time_not_init'] > 0 && ($time_compliance < $sla_limit)) { + return REPORT_STATUS_NOT_INIT; + } + + if ($sla['time_ok'] > 0 && ($time_compliance >= $sla_limit)) { return REPORT_STATUS_OK; } @@ -11498,7 +11756,7 @@ function reporting_translate_sla_status_for_graph($status) * * @return html Return table of header. */ -function reporting_header_table_for_pdf(string $title='', string $description='') +function reporting_header_table_for_pdf($title='', $description='') { $result_pdf .= ''; $result_pdf .= ''; @@ -11512,3 +11770,38 @@ function reporting_header_table_for_pdf(string $title='', string $description='' return $result_pdf; } + + +/** + * Build the required data to build network traffic top N report + * + * @param int Period (time window). + * @param array Information about the item of report. + * @param bool Pdf or not + * + * @return array With report presentation info and report data. + */ +function reporting_nt_top_n_report($period, $content, $pdf) +{ + $return = []; + $return['type'] = 'nt_top_n'; + $return['title'] = $content['name']; + $return['description'] = $content['description']; + + // Get the data sent and received + $return['data'] = []; + $start_time = ($period['datetime'] - (int) $content['period']); + $return['data']['send'] = network_matrix_get_top( + $content['top_n_value'], + true, + $start_time, + $period['datetime'] + ); + $return['data']['recv'] = network_matrix_get_top( + $content['top_n_value'], + false, + $start_time, + $period['datetime'] + ); + return $return; +} diff --git a/pandora_console/include/functions_reporting_html.php b/pandora_console/include/functions_reporting_html.php index b469e930c3..10bef8631f 100644 --- a/pandora_console/include/functions_reporting_html.php +++ b/pandora_console/include/functions_reporting_html.php @@ -271,21 +271,7 @@ function reporting_html_print_report($report, $mini=false, $report_info=1) break; case 'netflow_area': - reporting_html_graph($table, $item); - break; - - case 'netflow_pie': - reporting_html_graph($table, $item); - break; - case 'netflow_data': - reporting_html_graph($table, $item); - break; - - case 'netflow_statistics': - reporting_html_graph($table, $item); - break; - case 'netflow_summary': reporting_html_graph($table, $item); break; @@ -367,6 +353,10 @@ function reporting_html_print_report($report, $mini=false, $report_info=1) reporting_enterprise_html_SLA_monthly($table, $item, $mini); break; + case 'nt_top_n': + reporting_html_nt_top_n($table, $item, $mini); + break; + case 'SLA_weekly': reporting_enterprise_html_SLA_weekly($table, $item, $mini); break; @@ -549,10 +539,6 @@ function reporting_html_SLA($table, $item, $mini, $pdf=0) foreach ($item['data'] as $sla) { if (isset($sla)) { - $the_first_men_time = get_agent_first_time( - io_safe_output($sla['agent']) - ); - // First_table. $row = []; $row[] = $sla['agent']; @@ -914,6 +900,9 @@ function reporting_html_top_n($table, $item, $pdf=0) function reporting_html_event_report_group($table, $item, $pdf=0) { global $config; + + $show_extended_events = $item['show_extended_events']; + if ($item['total_events']) { $table1 = new stdClass(); $table1->width = '99%'; @@ -1021,16 +1010,25 @@ function reporting_html_event_report_group($table, $item, $pdf=0) } array_push($table1->data, $data); + + if ($show_extended_events == 1 && events_has_extended_info($event['id_evento'])) { + $extended_events = events_get_extended_events($event['id_evento']); + + foreach ($extended_events as $extended_event) { + $extended_data = []; + + $extended_data[] = "'; + array_push($table1->data, $extended_data); + } + } } if ($pdf) { $table0 = new stdClass(); $table0->width = '99%'; - $table0->class = 'table-beauty'; $table0->data['count_row']['count'] = 'Total events: '.$item['total_events']; $pdf_export = html_print_table($table0, true); - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1046,7 +1044,6 @@ function reporting_html_event_report_group($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_agent']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1064,7 +1061,6 @@ function reporting_html_event_report_group($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_user_validator']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1082,7 +1078,6 @@ function reporting_html_event_report_group($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_criticity']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1100,7 +1095,6 @@ function reporting_html_event_report_group($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['validated_vs_unvalidated']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1117,7 +1111,6 @@ function reporting_html_event_report_group($table, $item, $pdf=0) if ($pdf) { $table0 = new stdClass(); $table0->width = '99%'; - $table0->class = 'table-beauty'; $table0->data['count_row']['count'] = 'Total events: '.$item['total_events']; $pdf_export = html_print_table($table0, true); @@ -1130,6 +1123,9 @@ function reporting_html_event_report_group($table, $item, $pdf=0) function reporting_html_event_report_module($table, $item, $pdf=0) { global $config; + + $show_extended_events = $item['show_extended_events']; + $show_summary_group = $item['show_summary_group']; if ($item['total_events']) { if (!empty($item['failed'])) { @@ -1213,17 +1209,26 @@ function reporting_html_event_report_module($table, $item, $pdf=0) } $table1->data[] = $data; + + if ($show_extended_events == 1 && events_has_extended_info($event['id_evento'])) { + $extended_events = events_get_extended_events($event['id_evento']); + + foreach ($extended_events as $extended_event) { + $extended_data = []; + + $extended_data[] = "'; + array_push($table1->data, $extended_data); + } + } } } if ($pdf) { $table0 = new stdClass(); $table0->width = '99%'; - $table0->class = 'table-beauty'; $table0->data['count_row']['count'] = 'Total events: '.$item['total_events']; $pdf_export = html_print_table($table0, true); - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1239,7 +1244,6 @@ function reporting_html_event_report_module($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_agent']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1257,7 +1261,6 @@ function reporting_html_event_report_module($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_user_validator']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1275,7 +1278,6 @@ function reporting_html_event_report_module($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_criticity']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1293,7 +1295,6 @@ function reporting_html_event_report_module($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['validated_vs_unvalidated']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -1312,7 +1313,6 @@ function reporting_html_event_report_module($table, $item, $pdf=0) if ($pdf) { $table0 = new stdClass(); $table0->width = '99%'; - $table0->class = 'table-beauty'; $table0->data['count_row']['count'] = 'Total events: '.$item['total_events']; $pdf_export = html_print_table($table0, true); @@ -1902,6 +1902,8 @@ function reporting_html_event_report_agent($table, $item, $pdf=0) { global $config; + $show_extended_events = $item['show_extended_events']; + if ($item['total_events'] != 0) { $table1 = new stdClass(); $table1->width = '99%'; @@ -1989,16 +1991,25 @@ function reporting_html_event_report_agent($table, $item, $pdf=0) } array_push($table1->data, $data); + + if ($show_extended_events == 1 && events_has_extended_info($event['id_evento'])) { + $extended_events = events_get_extended_events($event['id_evento']); + + foreach ($extended_events as $extended_event) { + $extended_data = []; + + $extended_data[] = "'; + array_push($table1->data, $extended_data); + } + } } if ($pdf) { $table0 = new stdClass(); $table0->width = '99%'; - $table0->class = 'table-beauty'; $table0->data['count_row']['count'] = 'Total events: '.$item['total_events']; $pdf_export = html_print_table($table0, true); - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -2015,7 +2026,6 @@ function reporting_html_event_report_agent($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_user_validator']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -2033,7 +2043,6 @@ function reporting_html_event_report_agent($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['by_criticity']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -2051,7 +2060,6 @@ function reporting_html_event_report_agent($table, $item, $pdf=0) $table1->data[0][0] = $item['chart']['validated_vs_unvalidated']; if ($pdf) { - $table1->class = 'table-beauty'; $pdf_export .= html_print_table($table1, true); $pdf_export .= '
    '; } else { @@ -2068,7 +2076,6 @@ function reporting_html_event_report_agent($table, $item, $pdf=0) if ($pdf) { $table0 = new stdClass(); $table0->width = '99%'; - $table0->class = 'table-beauty'; $table0->data['count_row']['count'] = 'Total events: '.$item['total_events']; $pdf_export = html_print_table($table0, true); @@ -2411,7 +2418,7 @@ function reporting_html_alert_report($table, $item, $pdf=0) $table->data['alerts']['cell'] = html_print_table($table1, true); if ($pdf) { - $table1->class = 'table-beauty pdf_alert_table'; + $table1->class = 'pdf_alert_table'; return html_print_table($table1, true); } } @@ -2880,7 +2887,6 @@ function reporting_html_availability($table, $item, $pdf=0) io_safe_output($style), true ); - $same_agent_in_resume = ''; global $config; @@ -2900,32 +2906,62 @@ function reporting_html_availability($table, $item, $pdf=0) $table1->head[1] = __('Module'); } - $table1->head[2] = __('Total time'); - $table1->head[3] = __('Time failed'); - $table1->head[4] = __('Time OK'); - $table1->head[5] = __('Time Uknown'); - $table1->head[6] = __('Time Not Init Module'); - $table1->head[7] = __('Time Downtime'); + if ($item['fields']['total_time']) { + $table1->head[2] = __('Total time'); + } else { + $table1->head[2] = __(''); + } + + if ($item['fields']['time_failed']) { + $table1->head[3] = __('Time failed'); + } else { + $table1->head[3] = __(''); + } + + if ($item['fields']['time_in_ok_status']) { + $table1->head[4] = __('Time OK'); + } else { + $table1->head[4] = __(''); + } + + if ($item['fields']['time_in_unknown_status']) { + $table1->head[5] = __('Time Unknown'); + } else { + $table1->head[5] = __(''); + } + + if ($item['fields']['time_of_not_initialized_module']) { + $table1->head[6] = __('Time Not Init Module'); + } else { + $table1->head[6] = __(''); + } + + if ($item['fields']['time_of_downtime']) { + $table1->head[7] = __('Time Downtime'); + } else { + $table1->head[7] = __(''); + } + $table1->head[8] = __('% Ok'); $table1->headstyle = []; $table1->headstyle[0] = 'text-align: left'; $table1->headstyle[1] = 'text-align: left'; - $table1->headstyle[2] = 'text-align: right'; - $table1->headstyle[3] = 'text-align: right'; - $table1->headstyle[4] = 'text-align: right'; - $table1->headstyle[5] = 'text-align: right'; - $table1->headstyle[6] = 'text-align: right'; + $table1->headstyle[2] = 'text-align: center'; + $table1->headstyle[3] = 'text-align: center'; + $table1->headstyle[4] = 'text-align: center'; + $table1->headstyle[5] = 'text-align: center'; + $table1->headstyle[6] = 'text-align: center'; $table1->headstyle[7] = 'text-align: right'; $table1->headstyle[8] = 'text-align: right'; $table1->style[0] = 'text-align: left'; $table1->style[1] = 'text-align: left'; - $table1->style[2] = 'text-align: right'; - $table1->style[3] = 'text-align: right'; - $table1->style[4] = 'text-align: right'; - $table1->style[5] = 'text-align: right'; - $table1->style[6] = 'text-align: right'; + $table1->style[2] = 'text-align: center'; + $table1->style[3] = 'text-align: center'; + $table1->style[4] = 'text-align: center'; + $table1->style[5] = 'text-align: center'; + $table1->style[6] = 'text-align: center'; $table1->style[7] = 'text-align: right'; $table1->style[8] = 'text-align: right'; @@ -2943,10 +2979,29 @@ function reporting_html_availability($table, $item, $pdf=0) $table2->head[1] = __('Module'); } - $table2->head[2] = __('Total checks'); - $table2->head[3] = __('Checks failed'); - $table2->head[4] = __('Checks OK'); - $table2->head[5] = __('Checks Uknown'); + if ($item['fields']['total_checks']) { + $table2->head[2] = __('Total checks'); + } else { + $table2->head[2] = __(''); + } + + if ($item['fields']['checks_failed']) { + $table2->head[3] = __('Checks failed'); + } else { + $table2->head[3] = __(''); + } + + if ($item['fields']['checks_in_ok_status']) { + $table2->head[4] = __('Checks OK'); + } else { + $table2->head[4] = __(''); + } + + if ($item['fields']['unknown_checks']) { + $table2->head[5] = __('Checks Uknown'); + } else { + $table2->head[5] = __(''); + } $table2->headstyle = []; $table2->headstyle[0] = 'text-align: left'; @@ -2964,77 +3019,101 @@ function reporting_html_availability($table, $item, $pdf=0) $table2->style[5] = 'text-align: right'; foreach ($item['data'] as $row) { - $the_first_men_time = get_agent_first_time( - io_safe_output($row['agent']) - ); - $table_row = []; $table_row[] = $row['agent']; $table_row[] = $row['availability_item']; - if ($row['time_total'] != 0) { + if ($row['time_total'] != 0 && $item['fields']['total_time']) { $table_row[] = human_time_description_raw( $row['time_total'], true ); - } else { + } else if ($row['time_total'] == 0 && $item['fields']['total_time']) { $table_row[] = '--'; - } + } else { + $table_row[] = ''; + }; - if ($row['time_error'] != 0) { + if ($row['time_error'] != 0 && $item['fields']['time_failed']) { $table_row[] = human_time_description_raw( $row['time_error'], true ); - } else { + } else if ($row['time_error'] == 0 && $item['fields']['time_failed']) { $table_row[] = '--'; - } + } else { + $table_row[] = ''; + }; - if ($row['time_ok'] != 0) { + if ($row['time_ok'] != 0 && $item['fields']['time_in_ok_status']) { $table_row[] = human_time_description_raw( $row['time_ok'], true ); - } else { + } else if ($row['time_ok'] == 0 && $item['fields']['time_in_ok_status']) { $table_row[] = '--'; - } + } else { + $table_row[] = ''; + }; - if ($row['time_unknown'] != 0) { + if ($row['time_unknown'] != 0 && $item['fields']['time_in_unknown_status']) { $table_row[] = human_time_description_raw( $row['time_unknown'], true ); - } else { + } else if ($row['time_unknown'] == 0 && $item['fields']['time_in_unknown_status']) { $table_row[] = '--'; - } + } else { + $table_row[] = ''; + }; - if ($row['time_not_init'] != 0) { + if ($row['time_not_init'] != 0 && $item['fields']['time_of_not_initialized_module']) { $table_row[] = human_time_description_raw( $row['time_not_init'], true ); - } else { + } else if ($row['time_not_init'] == 0 && $item['fields']['time_of_not_initialized_module']) { $table_row[] = '--'; - } + } else { + $table_row[] = ''; + }; - if ($row['time_downtime'] != 0) { + if ($row['time_downtime'] != 0 && $item['fields']['time_of_downtime']) { $table_row[] = human_time_description_raw( $row['time_downtime'], true ); - } else { + } else if ($row['time_downtime'] == 0 && $item['fields']['time_of_downtime']) { $table_row[] = '--'; - } + } else { + $table_row[] = ''; + }; $table_row[] = ''.sla_truncate($row['SLA'], $config['graph_precision']).'%'; $table_row2 = []; $table_row2[] = $row['agent']; $table_row2[] = $row['availability_item']; - $table_row2[] = $row['checks_total']; - $table_row2[] = $row['checks_error']; - $table_row2[] = $row['checks_ok']; - $table_row2[] = $row['checks_unknown']; + if ($item['fields']['total_checks']) { + $table_row2[] = $row['checks_total']; + } else { + $table_row2[] = ''; + }; + if ($item['fields']['checks_failed']) { + $table_row2[] = $row['checks_error']; + } else { + $table_row2[] = ''; + }; + if ($item['fields']['checks_in_ok_status']) { + $table_row2[] = $row['checks_ok']; + } else { + $table_row2[] = ''; + }; + if ($item['fields']['unknown_checks']) { + $table_row2[] = $row['checks_unknown']; + } else { + $table_row2[] = ''; + }; $table1->data[] = $table_row; $table2->data[] = $table_row2; @@ -3073,7 +3152,7 @@ function reporting_html_availability($table, $item, $pdf=0) if ($item['resume']['resume'] && !empty($item['data'])) { $table1->width = '99%'; $table1->data = []; - if ((strpos($item['resume']['min_text'], $same_agent_in_resume) === false)) { + if (empty($same_agent_in_resume) || (strpos($item['resume']['min_text'], $same_agent_in_resume) === false)) { $table1->head = []; $table1->head['max_text'] = __('Agent max value'); $table1->head['max'] = __('Max Value'); @@ -3108,6 +3187,19 @@ function reporting_html_availability($table, $item, $pdf=0) ).'%', 'avg' => ''.sla_truncate($item['resume']['avg'], $config['graph_precision']).'%', ]; + if ($item['fields']['agent_max_value'] == false) { + $table1->head['max_text'] = ''; + $table1->data[0]['max_text'] = ''; + $table1->head['max'] = ''; + $table1->data[0]['max'] = ''; + } + + if ($item['fields']['agent_min_value'] == false) { + $table1->head['min_text'] = ''; + $table1->data[0]['min_text'] = ''; + $table1->head['min'] = ''; + $table1->data[0]['min'] = ''; + } if ($pdf === 0) { $table->colspan[3][0] = 3; @@ -3276,9 +3368,8 @@ function get_agent_first_time($agent_name) $id = agents_get_agent_id($agent_name, true); $utimestamp = db_get_all_rows_sql( - 'SELECT utimestamp FROM tagente_datos WHERE id_agente_modulo IN - (SELECT id_agente_modulo FROM tagente_modulo WHERE id_agente = '.$id.') - ORDER BY utimestamp ASC LIMIT 1' + 'SELECT min(utimestamp) FROM tagente_datos WHERE id_agente_modulo IN + (SELECT id_agente_modulo FROM tagente_modulo WHERE id_agente = '.$id.')' ); $utimestamp = $utimestamp[0]['utimestamp']; @@ -4737,6 +4828,65 @@ function reporting_get_event_histogram_meta($width) } +/** + * Print network traffic data into top n tables + * (one for received data and another for sent) + * + * @param stdClass Table class to paint the report + * @param array Associative array with info about + * @param bool Unused + */ +function reporting_html_nt_top_n($table, $item, $mini) +{ + // Prepare the table + $table_top = new stdClass(); + $table_top->cellpadding = 0; + $table_top->cellspacing = 0; + $table_top->width = '100%'; + $table_top->class = 'databox data'; + $table_top->cellpadding = 0; + $table_top->cellspacing = 0; + $table_top->width = '100%'; + $table_top->class = 'databox data'; + $table_top->head['host'] = __('Agent'); + $table_top->head['bytes'] = __('Kilobytes'); + $table_top->head['pkts'] = __('Packages'); + + // Build the table for sent packages + if (empty($item['data']['send'])) { + $table->data['send_title'] = '

    '.__('No network traffic sent data').'

    '; + } else { + foreach ($item['data']['send'] as $s_item) { + $table_top->data[] = [ + 'host' => $s_item['host'], + 'bytes' => remove_right_zeros(number_format(($s_item['sum_bytes'] / 1024), $config['graph_precision'])), + 'pkts' => remove_right_zeros(number_format($s_item['sum_pkts'], $config['graph_precision'])), + ]; + } + + $table->data['send_title'] = '

    '.__('Network traffic sent').'

    '; + $table->data['send'] = html_print_table($table_top, true); + } + + // Reset the table and build the table for received packages + $table_top->data = []; + if (empty($item['data']['send'])) { + $table->data['recv_title'] = '

    '.__('No network traffic received data').'

    '; + } else { + foreach ($item['data']['recv'] as $s_item) { + $table_top->data[] = [ + 'host' => $s_item['host'], + 'bytes' => remove_right_zeros(number_format(($s_item['sum_bytes'] / 1024), $config['graph_precision'])), + 'pkts' => remove_right_zeros(number_format($s_item['sum_pkts'], $config['graph_precision'])), + ]; + } + + $table->data['recv_title'] = '

    '.__('Network traffic received').'

    '; + $table->data['recv'] = html_print_table($table_top, true); + } +} + + function reporting_html_planned_downtimes_table($planned_downtimes) { global $config; @@ -4767,7 +4917,7 @@ function reporting_html_planned_downtimes_table($planned_downtimes) if ($for_pdf) { $table->titlestyle = 'background: #373737; color: #FFF; display: table-cell; font-size: 12px; border: 1px solid grey'; - $table->class = 'table_sla table_beauty'; + $table->class = 'table_sla'; for ($i = 0; $i < count($table->head); $i++) { $table->headstyle[$i] = 'background: #666; color: #FFF; display: table-cell; font-size: 11px; border: 1px solid grey'; diff --git a/pandora_console/include/functions_reporting_xml.php b/pandora_console/include/functions_reporting_xml.php index d1b28ac2a0..91e3288d83 100644 --- a/pandora_console/include/functions_reporting_xml.php +++ b/pandora_console/include/functions_reporting_xml.php @@ -11,10 +11,10 @@ // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -require_once 'include/functions_modules.php'; -require_once 'include/functions_events.php'; -require_once 'include/functions_groups.php'; -require_once 'include/functions_netflow.php'; +require_once __DIR__.'/functions_modules.php'; +require_once __DIR__.'/functions_events.php'; +require_once __DIR__.'/functions_groups.php'; +require_once __DIR__.'/functions_netflow.php'; enterprise_include_once('include/functions_metaconsole.php'); @@ -31,7 +31,7 @@ function reporting_xml_get_report($report, $filename, $return=false) unset($report['private']); unset($report['custom_logo']); // ---------------------------------------------------------------------- - // change agent name + // change agent name. if (count($report['contents']) > 0) { for ($i = 0; $i < count($report['contents']); $i++) { $aux = explode('-', $report['contents'][$i]['subtitle']); @@ -44,16 +44,18 @@ function reporting_xml_get_report($report, $filename, $return=false) $xml = preg_replace('/(<[^>]+>)(<[^>]+>)(<[^>]+>)/', "$1\n$2\n$3", $xml); $xml = preg_replace('/(<[^>]+>)(<[^>]+>)/', "$1\n$2", $xml); - // Return if is marked to return + // Return if is marked to return. if ($return) { return $xml; } - // Download if marked to download - header('Content-Type: application/xml; charset=UTF-8'); - header('Content-Disposition: attachment; filename="'.$filename.'.xml"'); + // Download if marked to download. + if ($filename !== false) { + header('Content-Type: application/xml; charset=UTF-8'); + header('Content-Disposition: attachment; filename="'.$filename.'.xml"'); + } - // Clean the output buffer + // Clean the output buffer. ob_clean(); echo $xml; diff --git a/pandora_console/include/functions_reports.php b/pandora_console/include/functions_reports.php index fd68aa0da2..0bf1f8fe70 100755 --- a/pandora_console/include/functions_reports.php +++ b/pandora_console/include/functions_reports.php @@ -867,18 +867,10 @@ function reports_get_report_types($template=false, $not_editor=false) 'optgroup' => __('Netflow'), 'name' => __('Netflow area chart'), ]; - $types['netflow_pie'] = [ - 'optgroup' => __('Netflow'), - 'name' => __('Netflow pie chart'), - ]; $types['netflow_data'] = [ 'optgroup' => __('Netflow'), 'name' => __('Netflow data table'), ]; - $types['netflow_statistics'] = [ - 'optgroup' => __('Netflow'), - 'name' => __('Netflow statistics table'), - ]; $types['netflow_summary'] = [ 'optgroup' => __('Netflow'), 'name' => __('Netflow summary table'), @@ -892,5 +884,10 @@ function reports_get_report_types($template=false, $not_editor=false) ]; } + $types['nt_top_n'] = [ + 'optgroup' => __('Network traffic'), + 'name' => __('Network Traffic Top N'), + ]; + return $types; } diff --git a/pandora_console/include/functions_servers.php b/pandora_console/include/functions_servers.php index ba6659ddc1..0850dcbfac 100644 --- a/pandora_console/include/functions_servers.php +++ b/pandora_console/include/functions_servers.php @@ -88,6 +88,58 @@ function servers_force_recon_task($id_recon_task) } +/** + * Retrieves total number of modules per server. + * + * @return array Modules per server (total). + */ +function servers_get_total_modules() +{ + $modules = []; + + $modules_from_monitors = db_get_all_rows_sql( + 'SELECT + tserver.server_type, + count(tagente_estado.id_agente_modulo) as modules_assigned + FROM tserver, tagente_estado, tagente_modulo, tagente + WHERE tagente.disabled=0 + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente_modulo.disabled = 0 + AND tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo + AND tagente_estado.running_by = tserver.id_server + GROUP BY tserver.server_type;' + ); + + if ($modules_from_monitors !== false) { + $modules = array_reduce( + $modules_from_monitors, + function ($carry, $item) { + $carry[$item['server_type']] = $item['modules_assigned']; + return $carry; + } + ); + } + + $modules[SERVER_TYPE_INVENTORY] = db_get_sql( + 'SELECT COUNT(tagent_module_inventory.id_agent_module_inventory) + FROM tagente, tagent_module_inventory + WHERE tagente.disabled=0 + AND tagent_module_inventory.id_agente = tagente.id_agente' + ); + + $modules[SERVER_TYPE_EXPORT] = db_get_sql( + 'SELECT COUNT(tagente_modulo.id_agente_modulo) + FROM tagente, tagente_modulo + WHERE tagente.disabled=0 + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente_modulo.id_export != 0' + ); + + return $modules; + +} + + /** * This function will get several metrics from the database to get info about server performance * @@ -989,6 +1041,9 @@ function servers_get_server_string_name(int $server) case SERVER_TYPE_WUX: return __('WUX server'); + case SERVER_TYPE_ENTERPRISE_SATELLITE: + return __('Satellite'); + default: return __('N/A'); } diff --git a/pandora_console/include/functions_snmp_browser.php b/pandora_console/include/functions_snmp_browser.php index fd68b77e54..ca2b2d055b 100644 --- a/pandora_console/include/functions_snmp_browser.php +++ b/pandora_console/include/functions_snmp_browser.php @@ -41,23 +41,38 @@ $nfdump_date_format = 'Y/m/d.H:i:s'; /** - * Selects all netflow filters (array (id_name => id_name)) or filters filtered + * Generates a Tree with given $tree information. * - * @param tree string SNMP tree returned by snmp_broser_get_tree. - * @param id string Level ID. Do not set, used for recursion. - * @param depth string Branch depth. Do not set, used for recursion. + * Selects all netflow filters (array (id_name => id_name)) or filters filtered + * Used also in Cloud Wizard. + * + * @param string $tree SNMP tree returned by snmp_broser_get_tree. + * @param string $id Level ID. Do not set, used for recursion. + * @param string $depth Branch depth. Do not set, used for recursion. + * @param integer $last Last. + * @param array $last_array Last_array. + * @param string $sufix Sufix. + * @param array $checked Checked. + * @param boolean $descriptive_ids Descriptive_ids. + * @param string $previous_id Previous_id. + * + * @return string HTML code with complete tree. */ -function snmp_browser_print_tree( +function snmp_browser_get_html_tree( $tree, $id=0, $depth=0, $last=0, $last_array=[], $sufix=false, - $checked=[] + $checked=[], + $descriptive_ids=false, + $previous_id='' ) { static $url = false; + $output = ''; + // Get the base URL for images. if ($url === false) { $url = ui_get_full_url('operation/tree', false, false, false); @@ -65,17 +80,17 @@ function snmp_browser_print_tree( // Leaf. if (empty($tree['__LEAVES__'])) { - return; + return ''; } $count = 0; - $total = (sizeof(array_keys($tree['__LEAVES__'])) - 1); + $total = (count(array_keys($tree['__LEAVES__'])) - 1); $last_array[$depth] = $last; if ($depth > 0) { - echo " @@ -293,7 +314,8 @@ function ui_print_message($message, $class='', $attributes='', $return=false, $t + $form_items['percentile_item_row_6']['html'] = ''; - // Insert and modify before the buttons to create or update. + if (!$config['legacy_vc']) { + $intervals = [ + 10 => '10 '.__('seconds'), + 30 => '30 '.__('seconds'), + 60 => '1 '.__('minutes'), + 300 => '5 '.__('minutes'), + 900 => '15 '.__('minutes'), + 1800 => '30 '.__('minutes'), + 3600 => '1 '.__('hour'), + ]; + + $form_items_advance['cache_expiration_row'] = []; + $form_items_advance['cache_expiration_row']['items'] = [ + 'static_graph', + 'percentile_bar', + 'percentile_item', + 'module_graph', + 'simple_value', + 'datos', + 'auto_sla_graph', + 'group_item', + 'bars_graph', + 'donut_graph', + 'color_cloud', + 'service', + ]; + $form_items_advance['cache_expiration_row']['html'] = ''; + $form_items_advance['cache_expiration_row']['html'] .= ''; + } + + // Insert and modify before the buttons to create or update. if (enterprise_installed()) { - enterprise_visual_map_editor_modify_form_items_advance_palette($form_items_advance); + enterprise_visual_map_editor_modify_form_items_advance_palette( + $form_items_advance + ); } foreach ($form_items_advance as $item => $item_options) { @@ -1386,6 +1435,10 @@ function visual_map_editor_print_hack_translate_strings() echo "

    ".__('Could not be save.').'

    '; echo ''; + echo "'; + } @@ -1394,6 +1447,9 @@ function visual_map_editor_print_hack_translate_strings() "; if (!$return) { @@ -354,8 +354,19 @@ function ux_console_phases_donut($phases, $id, $return=false) } -function d3_progress_bar($id, $percentile, $width, $height, $color, $unit='%', $text='', $fill_color='#FFFFFF') -{ +function d3_progress_bar( + $id, + $percentile, + $width, + $height, + $color, + $unit='%', + $text='', + $fill_color='#FFFFFF', + $radiusx=10, + $radiusy=10, + $transition=1 +) { global $config; $recipient_name = 'progress_bar_'.$id; @@ -366,8 +377,20 @@ function d3_progress_bar($id, $percentile, $width, $height, $color, $unit='%', $ $output .= '
    "; $output .= include_javascript_d3(true); $output .= ""; + progress_bar_d3( + '".$recipient_name_to_js."', + ".(int) $percentile.', + '.(int) $width.', + '.(int) $height.", + '".$color."', + '".$unit."', + '".$text."', + '".$fill_color."', + ".(int) $radiusx.', + '.(int) $radiusy.', + '.(int) $transition.' + ); + '; return $output; } @@ -392,7 +415,7 @@ function d3_progress_bubble($id, $percentile, $width, $height, $color, $unit='%' } -function progress_circular_bar($id, $percentile, $width, $height, $color, $unit='%', $text='', $fill_color='#FFFFFF') +function progress_circular_bar($id, $percentile, $width, $height, $color, $unit='%', $text='', $fill_color='#FFFFFF', $transition=1) { global $config; @@ -404,7 +427,7 @@ function progress_circular_bar($id, $percentile, $width, $height, $color, $unit= $output .= '
    "; $output .= include_javascript_d3(true); $output .= ""; return $output; @@ -697,3 +720,48 @@ function print_clock_digital_1($time_format, $timezone, $clock_animation, $width return $output; } + + +/** + * Print dougnhnut. + * + * @param array $colors Colors. + * @param integer $width Width. + * @param integer $height Height. + * @param array $data Data. + * @param mixed $data_total Data_total. + * + * @return string HTML. + */ +function print_donut_narrow_graph( + array $colors, + $width, + $height, + array $data, + $data_total +) { + if (empty($data)) { + return graph_nodata_image($width, $height, 'pie'); + } + + $series = count($data); + if (($series != count($colors)) || ($series == 0)) { + return ''; + } + + $data = json_encode($data); + $colors = json_encode($colors); + + $graph_id = uniqid('graph_'); + + $out = "
    "; + $out .= include_javascript_d3(true); + $out .= ""; + + return $out; +} diff --git a/pandora_console/include/graphs/functions_flot.php b/pandora_console/include/graphs/functions_flot.php index 637d02b34b..4492da7834 100644 --- a/pandora_console/include/graphs/functions_flot.php +++ b/pandora_console/include/graphs/functions_flot.php @@ -82,9 +82,22 @@ function include_javascript_dependencies_flot_graph($return=false) } -// -// AREA GRAPHS //////// -// +/** + * Function create container for print charts. + * + * @param integer $agent_module_id Id module. + * @param array $array_data Data. + * @param array $legend Legend. + * @param array $series_type Series. + * @param array $color Color. + * @param array $date_array Date. + * @param array $data_module_graph Data module. + * @param array $params Params. + * @param string $water_mark Water. + * @param array $array_events_alerts Events array. + * + * @return string Return graphs. + */ function flot_area_graph( $agent_module_id, $array_data, @@ -99,8 +112,7 @@ function flot_area_graph( ) { global $config; - // include_javascript_dependencies_flot_graph(); - // Get a unique identifier to graph + // Get a unique identifier to graph. $graph_id = uniqid('graph_'); $background_style = ''; @@ -126,19 +138,26 @@ function flot_area_graph( break; } - $padding_vconsole = $params['dashboard'] ? 'padding: 1px 0px 10px 10px;' : ''; + $padding_vconsole = ($params['dashboard']) ? 'padding: 1px 0px 10px 10px;' : ''; - // Parent layer + // Parent layer. $return = "
    "; + + if (empty($params['title']) === false) { + $return .= '

    '.$params['title'].'

    '; + } + // Set some containers to legend, graph, timestamp tooltip, etc. if ($params['show_legend']) { - $return .= "

    "; + $return .= '

    '; } if (isset($params['graph_combined']) && $params['graph_combined'] && (!isset($params['from_interface']) || !$params['from_interface']) ) { - if (isset($params['threshold_data']) && is_array($params['threshold_data'])) { + if (isset($params['threshold_data']) + && is_array($params['threshold_data']) + ) { $yellow_threshold = $params['threshold_data']['yellow_threshold']; $red_threshold = $params['threshold_data']['red_threshold']; $yellow_up = $params['threshold_data']['yellow_up']; @@ -154,7 +173,7 @@ function flot_area_graph( } else if (!isset($params['combined']) || !$params['combined']) { $yellow_threshold = $data_module_graph['w_min']; $red_threshold = $data_module_graph['c_min']; - // Get other required module datas to draw warning and critical + // Get other required module datas to draw warning and critical. if ($agent_module_id == 0) { $yellow_up = 0; $red_up = 0; @@ -166,8 +185,12 @@ function flot_area_graph( $yellow_inverse = !($data_module_graph['w_inv'] == 0); $red_inverse = !($data_module_graph['c_inv'] == 0); } - } else if (isset($params['from_interface']) && $params['from_interface']) { - if (isset($params['threshold_data']) && is_array($params['threshold_data'])) { + } else if (isset($params['from_interface']) + && $params['from_interface'] + ) { + if (isset($params['threshold_data']) + && is_array($params['threshold_data']) + ) { $yellow_threshold = $params['threshold_data']['yellow_threshold']; $red_threshold = $params['threshold_data']['red_threshold']; $yellow_up = $params['threshold_data']['yellow_up']; @@ -200,7 +223,11 @@ function flot_area_graph( ); } - $return .= html_print_input_hidden('line_width_graph', $config['custom_graph_width'], true); + $return .= html_print_input_hidden( + 'line_width_graph', + $config['custom_graph_width'], + true + ); $return .= "
    "; + style='font-size: ".$params['font_size'].'pt; + display:none; position:absolute; overflow: auto; + max-height: '.($params['height'] + 50).'px; + width: '.$extra_width."px; + background:#fff; padding: 2px 2px 2px 2px; + border: solid #000 1px;'>
    "; - // Trick to get translated string from javascript + // Trick to get translated string from javascript. $return .= html_print_input_hidden('unknown_text', __('Unknown'), true); - $values = json_encode($array_data); + $values = json_encode($array_data); + $legend = json_encode($legend); $series_type = json_encode($series_type); $color = json_encode($color); @@ -261,7 +289,7 @@ function flot_area_graph( $params = json_encode($params); $array_events_alerts = json_encode($array_events_alerts); - // Javascript code + // Javascript code. if ($font_size == '') { $font_size = '\'\''; } @@ -272,7 +300,7 @@ function flot_area_graph( $return .= '});'; $return .= ''; - // Parent layer + // Parent layer. $return .= ''; return $return; diff --git a/pandora_console/include/graphs/functions_gd.php b/pandora_console/include/graphs/functions_gd.php index 262ed22bb3..10f566185d 100755 --- a/pandora_console/include/graphs/functions_gd.php +++ b/pandora_console/include/graphs/functions_gd.php @@ -344,9 +344,9 @@ function drawRating($rating, $width, $height, $font, $out_of_lim_str, $mode, $fo $image = imagecreate($width, $height); // colors - $back = imagecolorallocate($image, 255, 255, 255); + $back = imagecolorallocate($image, 241, 241, 241); - $bordercolor = imagecolorallocate($image, 174, 174, 174); + $bordercolor = imagecolorallocate($image, 241, 241, 241); $text = imagecolorallocate($image, 74, 74, 74); $red = imagecolorallocate($image, 255, 60, 75); $green = imagecolorallocate($image, 50, 205, 50); @@ -483,7 +483,7 @@ function gd_progress_bar($width, $height, $progress, $title, $font, $out_of_lim_ break; case 1: - drawRating($progress, $width, $height, $font, $out_of_lim_str, $mode, 6, $value_text, $color); + drawRating($progress, $width, $height, $font, $out_of_lim_str, $mode, 9, $value_text, $color); break; diff --git a/pandora_console/include/graphs/pandora.d3.js b/pandora_console/include/graphs/pandora.d3.js index 40dcd34ac1..427de827e3 100644 --- a/pandora_console/include/graphs/pandora.d3.js +++ b/pandora_console/include/graphs/pandora.d3.js @@ -20,7 +20,7 @@ // matrix = [[0, 0, 2], // a[a => a, a => b, a => c] // [5, 0, 1], // b[b => a, b => b, b => c] // [2, 3, 0]]; // c[c => a, c => b, c => c] -function chordDiagram(recipient, elements, matrix, unit, width) { +function chordDiagram(recipient, elements, matrix, width) { d3.chart = d3.chart || {}; d3.chart.chordWheel = function(options) { // Default values @@ -206,18 +206,14 @@ function chordDiagram(recipient, elements, matrix, unit, width) { " → " + elements[d.target.index] + ": " + - d.source.value.toFixed(2) + - " " + - unit + + valueToBytes(d.source.value) + "" + "
    " + elements[d.target.index] + " → " + elements[d.source.index] + ": " + - d.target.value.toFixed(2) + - " " + - unit + + valueToBytes(d.target.value) + "" ) ); @@ -227,18 +223,14 @@ function chordDiagram(recipient, elements, matrix, unit, width) { " → " + elements[d.target.index] + ": " + - d.source.value.toFixed(2) + - " " + - unit + + valueToBytes(d.source.value) + "" + "
    " + elements[d.target.index] + " → " + elements[d.source.index] + ": " + - d.target.value.toFixed(2) + - " " + - unit + + valueToBytes(d.target.value) + "" ); } @@ -1782,7 +1774,10 @@ function progress_bar_d3( color, unit, label, - label_color + label_color, + radiusx, + radiusy, + transition ) { var startPercent = 0; var endPercent = parseInt(percentile) / 100; @@ -1799,20 +1794,20 @@ function progress_bar_d3( .append("rect") .attr("fill", "#000000") .attr("fill-opacity", 0.5) - .attr("height", 20) + .attr("height", height) .attr("width", width) - .attr("rx", 10) - .attr("ry", 10) + .attr("rx", radiusx) + .attr("ry", radiusy) .attr("x", 0); var progress_front = circle .append("rect") .attr("fill", color) .attr("fill-opacity", 1) - .attr("height", 20) + .attr("height", height) .attr("width", 0) - .attr("rx", 10) - .attr("ry", 10) + .attr("rx", radiusx) + .attr("ry", radiusy) .attr("x", 0); var labelText = circle @@ -1834,7 +1829,7 @@ function progress_bar_d3( .style("font-weight", "bold") .style("font-size", 14) .attr("text-anchor", "middle") - .attr("dy", "-10"); + .attr("dy", (height - height / 2) / 4); function updateProgress(bar_progress) { var percent_value = Number(bar_progress * 100); @@ -1842,17 +1837,21 @@ function progress_bar_d3( progress_front.attr("width", width * bar_progress); } - var bar_progress = startPercent; - - (function loops() { + if (transition == 0) { + var bar_progress = endPercent; updateProgress(bar_progress); + } else { + var bar_progress = startPercent; + (function loops() { + updateProgress(bar_progress); - if (count > 0) { - count--; - bar_progress += step; - setTimeout(loops, 30); - } - })(); + if (count > 0) { + count--; + bar_progress += step; + setTimeout(loops, 30); + } + })(); + } } function progress_bubble_d3( @@ -1981,7 +1980,8 @@ function print_circular_progress_bar( color, unit, label, - label_color + label_color, + transition ) { var twoPi = Math.PI * 2; var radius = width / 2; @@ -2113,15 +2113,18 @@ function print_circular_progress_bar( var progress = startPercent; - (function loops() { - updateProgress(progress); + if (transition == 0) updateProgress(endPercent); + else { + (function loops() { + updateProgress(progress); - if (count > 0) { - count--; - progress += step; - setTimeout(loops, 30); - } - })(); + if (count > 0) { + count--; + progress += step; + setTimeout(loops, 30); + } + })(); + } } function print_interior_circular_progress_bar( @@ -2744,3 +2747,238 @@ function printClockDigital1( setTimeout(tick, 1000 - (now % 1000)); })(); } + +function valueToBytes(value) { + var shorts = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"]; + var pos = 0; + while (value >= 1024) { + // As long as the number can be divided by divider. + pos++; + // Position in array starting with 0. + value = value / 1024; + } + + // This will actually do the rounding and the decimals. + return value.toFixed(2) + shorts[pos] + "B"; +} + +function donutNarrowGraph(colores, width, height, total) { + // Default settings + var donutbody = d3.select("body"); + var data = {}; + // var showTitle = true; + + if (width == "") { + width = 180; + } + + if (height == "") { + height = 180; + } + + var radius = Math.min(width, height) / 2; + + var currentVal; + //var color = d3.scale.category20(); + + var colores_index = []; + var colores_value = []; + + $.each(colores, function(index, value) { + colores_index.push(index); + colores_value.push(value); + }); + + var color = d3.scale + .ordinal() + .domain(colores_index) + .range(colores_value); + + var pie = d3.layout + .pie() + .sort(null) + .value(function(d) { + return d.value; + }); + + var svg, g, arc; + + var object = {}; + + // Method for render/refresh graph + object.render = function() { + if (!svg) { + // Show normal status by default. This variable must be initialized here, before clearing data. + var normal_status = data.Normal; + + // Don't draw 0 or invalid values. console.log(data); + var data_map = $.map(data, function(value, index) { + if (value == 0 || isNaN(value)) { + return index; + } + }); + + $.each(data_map, function(i, val) { + delete data[val]; + }); + //New data: console.log(data); + + arc = d3.svg + .arc() + .outerRadius(radius) + .innerRadius(radius - radius / 2.5); + + svg = donutbody + .append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); + + g = svg + .selectAll(".arc") + .data(pie(d3.entries(data))) + .enter() + .append("g") + .attr("class", "arc"); + + g.append("path") + // Attach current value to g so that we can use it for animation + .each(function(d) { + this._current = d; + }) + .attr("d", arc) + .attr("stroke", "white") + .style("stroke-width", 2) + .style("fill", function(d) { + return color(d.data.key); + }); + // This is to show labels on the graph + /* g.append("text") + .attr("transform", function(d) { + return "translate(" + arc.centroid(d) + ")"; + }) + .attr("dy", ".35em") + .style("text-anchor", "middle"); + g.select("text").text(function(d) { + return d.data.key; + });*/ + + // Show normal status by default. + var percentage_normal; + svg + .append("text") + .datum(data) + .attr("x", 0) + .attr("y", 0 + radius / 10) + .attr("class", "text-tooltip") + .style("text-anchor", "middle") + .attr("font-weight", "bold") + .style("font-family", "Arial, Verdana") + //.attr("fill", "#82b92e") + .style("font-size", function(d) { + if (normal_status) { + percentage_normal = (normal_status * 100) / total; + if (Number.isInteger(percentage_normal)) { + percentage_normal = percentage_normal.toFixed(0); + return radius / 3 + "px"; + } else { + percentage_normal = percentage_normal.toFixed(1); + return radius / 3.5 + "px"; + } + } + }) + .text(function(d) { + if (normal_status) { + return percentage_normal + "%"; + } else { + return "0%"; + } + }); + + g.on("mouseover", function(obj) { + //console.log(obj); + var percentage; + svg + .select("text.text-tooltip") + // This is to paint the text of the corresponding color. + /* .attr("fill", function(d) { + return color(obj.data.key); + })*/ + .style("font-size", function(d) { + percentage = (d[obj.data.key] * 100) / total; + if (Number.isInteger(percentage)) { + percentage = percentage.toFixed(0); + return radius / 3 + "px"; + } else { + percentage = percentage.toFixed(1); + return radius / 3.5 + "px"; + } + }) + .text(percentage + "%"); + }); + + g.on("mouseout", function(obj) { + svg.select("text.text-tooltip").text(function(d) { + if (normal_status) { + return percentage_normal + "%"; + } else { + return "0%"; + } + }); + // .attr("fill", "#82b92e"); + }); + } else { + g.data(pie(d3.entries(data))) + .exit() + .remove(); + + g.select("path") + .transition() + .duration(200) + .attrTween("d", function(a) { + var i = d3.interpolate(this._current, a); + this._current = i(0); + return function(t) { + return arc(i(t)); + }; + }); + + g.select("text").attr("transform", function(d) { + return "translate(" + arc.centroid(d) + ")"; + }); + + svg.select("text.text-tooltip").datum(data); + } + return object; + }; + + // Getter and setter methods + object.data = function(value) { + if (!arguments.length) return data; + data = value; + return object; + }; + + object.donutbody = function(value) { + if (!arguments.length) return donutbody; + donutbody = value; + return object; + }; + + object.width = function(value) { + if (!arguments.length) return width; + width = value; + radius = Math.min(width, height) / 2; + return object; + }; + + object.height = function(value) { + if (!arguments.length) return height; + height = value; + radius = Math.min(width, height) / 2; + return object; + }; + + return object; +} diff --git a/pandora_console/include/help/clippy/modules_not_learning_mode.php b/pandora_console/include/help/clippy/modules_not_learning_mode.php index 85dc5c1182..ff26b065f6 100755 --- a/pandora_console/include/help/clippy/modules_not_learning_mode.php +++ b/pandora_console/include/help/clippy/modules_not_learning_mode.php @@ -1,20 +1,30 @@ true, 'position' => 'left', - 'intro' => '
    ".io_safe_output($extended_event['description'])."".date($config['date_format'], $extended_event['utimestamp']).'".io_safe_output($extended_event['description'])."".date($config['date_format'], $extended_event['utimestamp']).'".io_safe_output($extended_event['description'])."".date($config['date_format'], $extended_event['utimestamp']).'
    '.$text_title.' '; if (!$no_close_bool) { - // Use the no_meta parameter because this image is only in the base console + // Use the no_meta parameter because this image is only in + // the base console. $output .= ''.html_print_image('images/blade.png', true, false, false, false, true).''; } @@ -330,11 +352,12 @@ function ui_print_message($message, $class='', $attributes='', $return=false, $t /** * Prints an error message. * - * @param mixed The string error message or array ('title', 'message', 'icon', 'no_close') to be displayed - * @param string Any other attributes to be set for the tag. - * @param bool Whether to output the string or return it - * @param string What tag to use (you could specify something else than - * h3 like div or h2) + * @param mixed $message The string error message or array + * ('title', 'message', 'icon', 'no_close') to be displayed. + * @param string $attributes Any other attributes to be set for the tag. + * @param boolean $return Whether to output the string or return it. + * @param string $tag What tag to use (you could specify something else + * than h3 like div or h2). * * @return string HTML code if return parameter is true. */ @@ -347,11 +370,12 @@ function ui_print_error_message($message, $attributes='', $return=false, $tag='h /** * Prints an operation success message. * - * @param mixed The string message or array ('title', 'message', 'icon', 'no_close') to be displayed - * @param string Any other attributes to be set for the tag. - * @param bool Whether to output the string or return it - * @param string What tag to use (you could specify something else than - * h3 like div or h2) + * @param mixed $message The string message or array + * ('title', 'message', 'icon', 'no_close') to be displayed. + * @param string $attributes Any other attributes to be set for the tag. + * @param boolean $return Whether to output the string or return it. + * @param string $tag What tag to use (you could specify something else + * than h3 like div or h2). * * @return string HTML code if return parameter is true. */ @@ -364,11 +388,12 @@ function ui_print_success_message($message, $attributes='', $return=false, $tag= /** * Prints an operation info message. * - * @param mixed The string message or array ('title', 'message', 'icon', 'no_close') to be displayed - * @param string Any other attributes to be set for the tag. - * @param bool Whether to output the string or return it - * @param string What tag to use (you could specify something else than - * h3 like div or h2) + * @param mixed $message The string message or array + * ('title', 'message', 'icon', 'no_close') to be displayed. + * @param string $attributes Any other attributes to be set for the tag. + * @param boolean $return Whether to output the string or return it. + * @param string $tag What tag to use (you could specify something else + * than h3 like div or h2). * * @return string HTML code if return parameter is true. */ @@ -378,6 +403,18 @@ function ui_print_info_message($message, $attributes='', $return=false, $tag='h3 } +/** + * Prints an operation info message - empty data. + * + * @param mixed $message The string message or array + * ('title', 'message', 'icon', 'no_close') to be displayed. + * @param string $attributes Any other attributes to be set for the tag. + * @param boolean $return Whether to output the string or return it. + * @param string $tag What tag to use (you could specify something else + * than h3 like div or h2). + * + * @return string HTML code if return parameter is true. + */ function ui_print_empty_data($message, $attributes='', $return=false, $tag='h3') { return ui_print_message($message, 'info', $attributes, $return, $tag); @@ -387,14 +424,16 @@ function ui_print_empty_data($message, $attributes='', $return=false, $tag='h3') /** * Evaluates a result using empty() and then prints an error or success message * - * @param mixed The results to evaluate. 0, NULL, false, '' or - * array() is bad, the rest is good - * @param mixed The string or array ('title', 'message') to be displayed if the result was good - * @param mixed The string or array ('title', 'message') to be displayed if the result was bad - * @param string Any other attributes to be set for the h3 - * @param bool Whether to output the string or return it - * @param string What tag to use (you could specify something else than - * h3 like div or h2) + * @param mixed $result The results to evaluate. 0, NULL, false, '' or + * array() is bad, the rest is good. + * @param mixed $good The string or array ('title', 'message') to be + * displayed if the result was good. + * @param mixed $bad The string or array ('title', 'message') to be + * displayed if the result was bad. + * @param string $attributes Any other attributes to be set for the h3. + * @param boolean $return Whether to output the string or return it. + * @param string $tag What tag to use (you could specify something else + * than h3 like div or h2). * * @return string HTML code if return parameter is true. */ @@ -419,11 +458,12 @@ function ui_print_result_message($result, $good='', $bad='', $attributes='', $re /** * Prints an warning message. * - * @param mixed The string warning message or array ('title', 'message', 'icon', 'no_close') to be displayed - * @param string Any other attributes to be set for the tag. - * @param bool Whether to output the string or return it - * @param string What tag to use (you could specify something else than - * h3 like div or h2) + * @param mixed $message The string message or array + * ('title', 'message', 'icon', 'no_close') to be displayed. + * @param string $attributes Any other attributes to be set for the tag. + * @param boolean $return Whether to output the string or return it. + * @param string $tag What tag to use (you could specify something else + * than h3 like div or h2). * * @return string HTML code if return parameter is true. */ @@ -438,13 +478,13 @@ function ui_print_warning_message($message, $attributes='', $return=false, $tag= * with as title the correctly formatted full timestamp and a time comparation * in the tag * - * @param int Any type of timestamp really, but we prefer unixtime - * @param bool Whether to output the string or return it - * @param array An array with different options for this function - * Key html_attr: which html attributes to add (defaults to none) - * Key tag: Which html tag to use (defaults to span) - * Key prominent: Overrides user preference and display "comparation" or "timestamp" - * key units: The type of units. + * @param integer $unixtime Any type of timestamp really, but we prefer unixtime. + * @param boolean $return Whether to output the string or return it. + * @param array $option An array with different options for this function + * Key html_attr: which html attributes to add (defaults to none) + * Key tag: Which html tag to use (defaults to span) + * Key prominent: Overrides user preference and display "comparation" or "timestamp" + * key units: The type of units. * * @return string HTML code if return parameter is true. */ @@ -452,7 +492,8 @@ function ui_print_timestamp($unixtime, $return=false, $option=[]) { global $config; - // TODO: Add/use a javascript timer for the seconds so it automatically updates as time passes by + // TODO: Add/use a javascript timer for the seconds so it automatically + // updates as time passes by. if (isset($option['html_attr'])) { $attributes = $option['html_attr']; } else { @@ -481,7 +522,7 @@ function ui_print_timestamp($unixtime, $return=false, $option=[]) $unixtime = time_w_fixed_tz($unixtime); } - // prominent_time is either timestamp or comparation + // Prominent_time is either timestamp or comparation. if ($unixtime <= 0) { $title = __('Unknown').'/'.__('Never'); $data = __('Unknown'); @@ -507,13 +548,13 @@ function ui_print_timestamp($unixtime, $return=false, $option=[]) switch ($tag) { default: // Usually tags have title attributes, so by default we add, - // then fall through to add attributes and data + // then fall through to add attributes and data. $output .= ' title="'.$title.'" '.$style.'>'.$data.''; break; case 'h1': case 'h2': case 'h3': - // Above tags don't have title attributes + // Above tags don't have title attributes. $output .= ' '.$attributes.' '.$style.'>'.$data.''; break; } @@ -529,8 +570,8 @@ function ui_print_timestamp($unixtime, $return=false, $option=[]) /** * Prints a username with real name, link to the user_edit page etc. * - * @param string The username to render - * @param bool Whether to return or print + * @param string $username The username to render. + * @param boolean $return Whether to return or print. * * @return string HTML code if return parameter is true. */ @@ -546,6 +587,13 @@ function ui_print_username($username, $return=false) } +/** + * Show a notification. + * + * @param boolean $return Return or direct echo. + * + * @return string HTML. + */ function ui_print_tags_warning($return=false) { $msg = '
    '; @@ -563,11 +611,14 @@ function ui_print_tags_warning($return=false) /** * Print group icon within a link * - * @param int Group id - * @param bool Whether to return or print - * @param string What path to use (relative to images/). Defaults to groups_small - * @param string Style for group image - * @param bool Whether the group have link or not + * @param integer $id_group Group id. + * @param boolean $return Whether to return or print. + * @param string $path What path to use (relative to images/). + * Defaults to groups_small. + * @param string $style Style for group image. + * @param boolean $link Whether the group have link or not. + * @param boolean $force_show_image Force show image. + * @param boolean $show_as_image Show as image. * * @return string HTML code if return parameter is true. */ @@ -583,7 +634,7 @@ function ui_print_group_icon($id_group, $return=false, $path='groups_small', $st $output = ''; - // Don't show link in metaconsole + // Don't show link in metaconsole. if (defined('METACONSOLE')) { $link = false; } @@ -630,9 +681,12 @@ function ui_print_group_icon($id_group, $return=false, $path='groups_small', $st /** * Print group icon within a link. Other version. * - * @param int Group id - * @param bool Whether to return or print - * @param string What path to use (relative to images/). Defaults to groups_small + * @param integer $id_group Group id. + * @param boolean $return Whether to return or print. + * @param string $path What path to use (relative to images/). + * Defaults to groups_small. + * @param string $style Extra styles. + * @param boolean $link Add anchor. * * @return string HTML code if return parameter is true. */ @@ -674,10 +728,15 @@ function ui_print_group_icon_path($id_group, $return=false, $path='images/groups /** * Get the icon of an operating system. * - * @param int Operating system id - * @param bool Whether to also append the name of the OS after the icon - * @param bool Whether to return or echo the result - * @param bool Whether to apply skin or not + * @param integer $id_os Operating system id. + * @param boolean $name Whether to also append the name of OS after icon. + * @param boolean $return Whether to return or echo the result. + * @param boolean $apply_skin Whether to apply skin or not. + * @param boolean $networkmap Networkmap. + * @param boolean $only_src Only_src. + * @param boolean $relative Relative. + * @param boolean $options Options. + * @param boolean $big_icons Big_icons. * * @return string HTML with icon of the OS */ @@ -744,6 +803,18 @@ function ui_print_os_icon( } +/** + * Print type agent icon. + * + * @param boolean $id_os Id_os. + * @param boolean $remote_contact Remote_contact. + * @param boolean $contact Contact. + * @param boolean $return Return. + * @param integer $remote Remote. + * @param string $version Version. + * + * @return string HTML. + */ function ui_print_type_agent_icon( $id_os=false, $remote_contact=false, @@ -753,15 +824,15 @@ function ui_print_type_agent_icon( $version='' ) { if ($id_os == 19) { - // Satellite + // Satellite. $options['title'] = __('Satellite'); $output = html_print_image('images/op_satellite.png', true, $options, false, false, false, true); } else if ($remote_contact == $contact && $remote == 0 && $version == '') { - // Network + // Network. $options['title'] = __('Network'); $output = html_print_image('images/network.png', true, $options, false, false, false, true); } else { - // Software + // Software. $options['title'] = __('Software'); $output = html_print_image('images/data.png', true, $options, false, false, false, true); } @@ -773,20 +844,31 @@ function ui_print_type_agent_icon( /** * Prints an agent name with the correct link * - * @param int Agent id - * @param bool Whether to return the string or echo it too - * @param int Now uses styles to accomplish this - * @param string Style of name in css. - * @param string server url to concatenate at the begin of the link - * @param string extra parameters to concatenate in the link - * @param string name of the agent to avoid the query in some cases - * @param bool if the agent will provided with link or not - * @param bool use the agent alias or the name + * @param integer $id_agent Agent id. + * @param boolean $return Whether to return the string or echo it too. + * @param integer $cutoff Now uses styles to accomplish this. + * @param string $style Style of name in css. + * @param boolean $cutname Cut names. + * @param string $server_url Server url to concatenate at the begin of the link. + * @param string $extra_params Extra parameters to concatenate in the link. + * @param string $known_agent_name Name of the agent to avoid the query in some cases. + * @param boolean $link If the agent will provided with link or not. + * @param boolean $alias Use the agent alias or the name. * * @return string HTML with agent name and link */ -function ui_print_agent_name($id_agent, $return=false, $cutoff='agent_medium', $style='', $cutname=false, $server_url='', $extra_params='', $known_agent_name=false, $link=true, $alias=true) -{ +function ui_print_agent_name( + $id_agent, + $return=false, + $cutoff='agent_medium', + $style='', + $cutname=false, + $server_url='', + $extra_params='', + $known_agent_name=false, + $link=true, + $alias=true +) { if ($known_agent_name === false) { if ($alias) { $agent_name = (string) agents_get_alias($id_agent); @@ -808,14 +890,14 @@ function ui_print_agent_name($id_agent, $return=false, $cutoff='agent_medium', $ } if ($link) { - $url = $server_url.'index.php?sec=estado&'.'sec2=operation/agentes/ver_agente&'.'id_agente='.$id_agent.$extra_params; + $url = $server_url.'index.php?sec=estado&sec2=operation/agentes/ver_agente&id_agente='.$id_agent.$extra_params; - $output = ''.$agent_name.''; + $output = ''.$agent_name.''; } else { $output = ''.$agent_name.''; } - // TODO: Add a pretty javascript (using jQuery) popup-box with agent details + // TODO: Add a pretty javascript (using jQuery) popup-box with agent details. if ($return) { return $output; } @@ -827,16 +909,19 @@ function ui_print_agent_name($id_agent, $return=false, $cutoff='agent_medium', $ /** * Formats a row from the alert table and returns an array usable in the table function * - * @param array A valid (non empty) row from the alert table - * @param bool Whether or not this is a combined alert - * @param bool Whether to print the agent information with the module information - * @param string Tab where the function was called from (used for urls) - * @param mixed Style for agent name or default (false) + * @param array $alert A valid (non empty) row from the alert table. + * @param boolean $agent Whether or not this is a combined alert. + * @param string $url Tab where the function was called from (used for urls). + * @param mixed $agent_style Style for agent name or default (false). * * @return array A formatted array with proper html for use in $table->data (6 columns) */ -function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) -{ +function ui_format_alert_row( + $alert, + $agent=true, + $url='', + $agent_style=false +) { global $config; if (!isset($alert['server_data'])) { @@ -856,7 +941,7 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) include_once $config['homedir'].'/include/functions_alerts.php'; $isFunctionPolicies = enterprise_include_once('include/functions_policies.php'); $id_group = (int) get_parameter('ag_group', 0); - // 0 is the All group (selects all groups) + // 0 is the All group (selects all groups). if ($isFunctionPolicies !== ENTERPRISE_NOT_HOOK) { if ($agent) { $index = [ @@ -956,13 +1041,13 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) $server = db_get_row('tmetaconsole_setup', 'id', $alert['server_data']['id']); if (metaconsole_connect($server) == NOERR) { - // Get agent data from node + // Get agent data from node. $agente = db_get_row('tagente', 'id_agente', $alert['id_agent']); metaconsole_restore_db(); } } else { - // Get agent id + // Get agent id. $id_agent = modules_get_agentmodule_agent($alert['id_agent_module']); $agente = db_get_row('tagente', 'id_agente', $id_agent); } @@ -972,6 +1057,25 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) $data = []; + // Validate checkbox. + if (!defined('METACONSOLE')) { + if (check_acl($config['id_user'], $id_group, 'LW') + || check_acl($config['id_user'], $id_group, 'LM') + ) { + $data[$index['validate']] = ''; + + $data[$index['validate']] .= html_print_checkbox( + 'validate[]', + $alert['id'], + false, + true, + false, + '', + true + ); + } + } + if ($isFunctionPolicies !== ENTERPRISE_NOT_HOOK) { if (is_metaconsole()) { $node = metaconsole_get_connection_by_id($alert['server_data']['id']); @@ -979,7 +1083,6 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) // Restore the default connection. metaconsole_restore_db(); $errors++; - // break; It does not work in the php7 version remplace for: return false; } } @@ -1001,14 +1104,14 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) } } - // Standby + // Standby. $data[$index['standby']] = ''; if (isset($alert['standby']) && $alert['standby'] == 1) { $data[$index['standby']] = html_print_image('images/bell_pause.png', true, ['title' => __('Standby on')]); } if (!defined('METACONSOLE')) { - // Force alert execution + // Force alert execution. if (check_acl($config['id_user'], $id_group, 'AW') || check_acl($config['id_user'], $id_group, 'LM')) { if ($alert['force_execution'] == 0) { $data[$index['force_execution']] = ''.html_print_image('images/target.png', true, ['border' => '0', 'title' => __('Force')]).''; @@ -1078,7 +1181,12 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) $actionText .= '
    '; } else { if ($actionDefault != '') { - $actionText = db_get_sql("SELECT name FROM talert_actions WHERE id = $actionDefault").' ('.__('Default').')'; + $actionText = db_get_sql( + sprintf( + 'SELECT name FROM talert_actions WHERE id = %d', + $actionDefault + ) + ).' ('.__('Default').')'; } } @@ -1102,22 +1210,6 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) $data[$index['status']] = ui_print_status_image($status, $title, true); - if (!defined('METACONSOLE')) { - if (check_acl($config['id_user'], $id_group, 'LW') || check_acl($config['id_user'], $id_group, 'LM')) { - $data[$index['validate']] = ''; - - $data[$index['validate']] .= html_print_checkbox( - 'validate[]', - $alert['id'], - false, - true, - false, - '', - true - ); - } - } - return $data; } @@ -1125,12 +1217,12 @@ function ui_format_alert_row($alert, $agent=true, $url='', $agent_style=false) /** * Prints a substracted string, length specified by cutoff, the full string will be in a rollover. * - * @param string The string to be cut - * @param int At how much characters to cut - * @param bool Whether to return or print it out - * @param int Size font (fixed) in px, applyed as CSS style (optional) + * @param string $string The string to be cut.. + * @param integer $cutoff At how much characters to cut. + * @param boolean $return Whether to return or print it out. + * @param integer $fontsize Size font (fixed) in px, applyed as CSS style (optional). * - * @return An HTML string + * @return string HTML string. */ function ui_print_string_substr($string, $cutoff=16, $return=false, $fontsize=0) { @@ -1151,7 +1243,8 @@ function ui_print_string_substr($string, $cutoff=16, $return=false, $fontsize=0) $font_size_mod = "style='font-size: ".$fontsize."pt'"; } - $string = ''.mb_substr($string2, 0, $cutoff, 'UTF-8').$string3.''; + $string = ''; + $string .= mb_substr($string2, 0, $cutoff, 'UTF-8').$string3.''; if ($return === false) { echo $string; @@ -1165,9 +1258,9 @@ function ui_print_string_substr($string, $cutoff=16, $return=false, $fontsize=0) * Gets a helper text explaining the requirement needs for an alert template * to get it fired. * - * @param int Alert template id. - * @param bool Wheter to return or print it out. - * @param bool Wheter to put the values in the string or not. + * @param integer $id_alert_template Alert template id. + * @param boolean $return Wheter to return or print it out. + * @param boolean $print_values Wheter to put the values in the string or not. * * @return An HTML string if return was true. */ @@ -1181,21 +1274,21 @@ function ui_print_alert_template_example($id_alert_template, $return=false, $pri switch ($template['type']) { case 'equal': - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the value is '); break; case 'not_equal': - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the value is not '); break; case 'regex': if ($template['matches_value']) { - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the value matches '); } else { - // end if + // End if. $output .= __('The alert would fire when the value doesn\'t match '); } @@ -1204,42 +1297,46 @@ function ui_print_alert_template_example($id_alert_template, $return=false, $pri case 'max_min': if ($template['matches_value']) { - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the value is between and '); } else { - // end if + // End if. $output .= __('The alert would fire when the value is not between and '); } break; case 'max': - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the value is over '); - break; case 'min': - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the value is under '); - break; case 'warning': - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the module is in warning status'); - break; case 'critical': - // Do not translate the HTML attributes + // Do not translate the HTML attributes. $output .= __('The alert would fire when the module is in critical status'); break; + + default: + // Do nothing. + $output .= __('Unknown option.'); + break; } if ($print_values) { /* - Replace span elements with real values. This is done in such way to avoid - duplicating strings and make it easily modificable via Javascript. */ + * Replace span elements with real values. This is done in such way to avoid + * duplicating strings and make it easily modificable via Javascript. + */ + $output = str_replace('', $template['value'], $output); $output = str_replace('', $template['max_value'], $output); $output = str_replace('', $template['min_value'], $output); @@ -1257,19 +1354,26 @@ function ui_print_alert_template_example($id_alert_template, $return=false, $pri /** * Prints a help tip icon. * - * @param string Id of the help article - * @param bool Whether to return or output the result - * @param string Home url if its necessary - * @param string Image path - * @param bool Route is relative or not + * @param string $help_id Id of the help article. + * @param boolean $return Whether to return or output the result. + * @param string $home_url Home url if its necessary. + * @param string $image Image path. + * @param boolean $is_relative Route is relative or not. + * @param string $id Target id. * * @return string The help tip */ -function ui_print_help_icon($help_id, $return=false, $home_url='', $image='images/help.png', $is_relative=false) -{ +function ui_print_help_icon( + $help_id, + $return=false, + $home_url='', + $image='images/help_green.png', + $is_relative=false, + $id='' +) { global $config; - // Do not display the help icon if help is disabled + // Do not display the help icon if help is disabled. if ($config['disable_help']) { return ''; } @@ -1282,13 +1386,16 @@ function ui_print_help_icon($help_id, $return=false, $home_url='', $image='image $home_url = '../../'.$home_url; } + $url = get_help_info($help_id); + $output = html_print_image( $image, true, [ 'class' => 'img_help', 'title' => __('Help'), - 'onclick' => "open_help ('".$help_id."','".$home_url."','".$config['id_user']."')", + 'onclick' => "open_help ('".$url."')", + 'id' => $id, ], false, $is_relative && is_metaconsole() @@ -1308,12 +1415,13 @@ function ui_print_help_icon($help_id, $return=false, $home_url='', $image='image * file name should be like "name.css". The "name" would be the value * needed to pass to this function. * - * @param string Script name to add without the "jquery." prefix and the ".js" + * @param string $name Script name to add without the "jquery." prefix and the ".js" * suffix. Example: * * ui_require_css_file ('pandora'); * // Would include include/styles/pandora.js - * + * . + * @param string $path Path where script is placed. * * @return boolean True if the file was added. False if the file doesn't exist. */ @@ -1350,13 +1458,14 @@ function ui_require_css_file($name, $path='include/styles/') * file name should be like "name.js". The "name" would be the value * needed to pass to this function. * - * @param string Script name to add without the "jquery." prefix and the ".js" - * suffix. Example: - * - * ui_require_javascript_file ('pandora'); - * // Would include include/javascript/pandora.js - * - * @param bool Just echo the script tag of the file. + * @param string $name Script name to add without the "jquery." prefix and the ".js" + * suffix. Example: + * + * ui_require_javascript_file ('pandora'); + * // Would include include/javascript/pandora.js + * . + * @param string $path Path where script is placed. + * @param boolean $echo_tag Just echo the script tag of the file. * * @return boolean True if the file was added. False if the file doesn't exist. */ @@ -1368,7 +1477,7 @@ function ui_require_javascript_file($name, $path='include/javascript/', $echo_ta if ($echo_tag) { echo ''; - return; + return null; } if (! isset($config['js'])) { @@ -1379,7 +1488,7 @@ function ui_require_javascript_file($name, $path='include/javascript/', $echo_ta return true; } - // We checks two paths because it may fails on enterprise + // We checks two paths because it may fails on enterprise. if (! file_exists($filename) && ! file_exists($config['homedir'].'/'.$filename)) { return false; } @@ -1401,12 +1510,14 @@ function ui_require_javascript_file($name, $path='include/javascript/', $echo_ta * file name should be like "name.js". The "name" would be the value * needed to pass to this function. * - * @param string Script name to add without the "jquery." prefix and the ".js" - * suffix. Example: - * - * ui_require_javascript_file ('pandora'); - * // Would include include/javascript/pandora.js - * + * @param string $name Script name to add without the "jquery." + * prefix and the ".js" + * suffix. Example: + * + * ui_require_javascript_file ('pandora'); + * // Would include include/javascript/pandora.js + * . + * @param boolean $disabled_metaconsole Disabled metaconsole. * * @return boolean True if the file was added. False if the file doesn't exist. */ @@ -1429,7 +1540,7 @@ function ui_require_javascript_file_enterprise($name, $disabled_metaconsole=fals return true; } - // We checks two paths because it may fails on enterprise + // We checks two paths because it may fails on enterprise. if (!file_exists($filename) && !file_exists($config['homedir'].'/'.$filename) ) { @@ -1450,13 +1561,14 @@ function ui_require_javascript_file_enterprise($name, $disabled_metaconsole=fals * needed to pass to this function. Notice that this function does not manage * jQuery denpendencies. * - * @param string Script name to add without the "jquery." prefix and the ".js" - * suffix. Example: - * - * ui_require_jquery_file ('form'); - * // Would include include/javascript/jquery.form.js - * - * @param bool Just echo the script tag of the file. + * @param string $name Script name to add without the "jquery." prefix and the ".js" + * suffix. Example: + * + * ui_require_jquery_file ('form'); + * // Would include include/javascript/jquery.form.js + * . + * @param string $path Path where script is placed. + * @param boolean $echo_tag Just echo the script tag of the file. * * @return boolean True if the file was added. False if the file doesn't exist. */ @@ -1468,7 +1580,7 @@ function ui_require_jquery_file($name, $path='include/javascript/', $echo_tag=fa if ($echo_tag) { echo ''; - return; + return null; } if (! isset($config['jquery'])) { @@ -1479,8 +1591,10 @@ function ui_require_jquery_file($name, $path='include/javascript/', $echo_tag=fa return true; } - // We checks two paths because it may fails on enterprise - if (! file_exists($filename) && ! file_exists($config['homedir'].'/'.$filename)) { + // We checks two paths because it may fails on enterprise. + if (! file_exists($filename) + && ! file_exists($config['homedir'].'/'.$filename) + ) { return false; } @@ -1499,8 +1613,8 @@ function ui_require_jquery_file($name, $path='include/javascript/', $echo_tag=fa * To add css just put them in include/styles and then add them to the * $config['css'] array * - * @param string Callback will fill this with the current buffer. - * @param bitfield Callback will fill this with a bitfield (see ob_start) + * @param string $string Callback will fill this with the current buffer. + * @param mixed $bitfield Callback will fill this with a bitfield (see ob_start). * * @return string String to return to the browser */ @@ -1510,7 +1624,7 @@ function ui_process_page_head($string, $bitfield) global $vc_public_view; if (isset($config['ignore_callback']) && $config['ignore_callback'] == true) { - return; + return ''; } $output = ''; @@ -1520,7 +1634,7 @@ function ui_process_page_head($string, $bitfield) $config_refr = $config['refr']; } - // If user is logged or displayed view is the public view of visual console + // If user is logged or displayed view is the public view of visual console. if ($config_refr > 0 && (isset($config['id_user']) || $vc_public_view == 1) ) { @@ -1533,8 +1647,13 @@ function ui_process_page_head($string, $bitfield) || $_GET['sec2'] == 'enterprise/dashboard/main_dashboard' ) { $query = ui_get_url_refresh(false, false); - // $output .= ''; + + /* + * $output .= ''; + */ + + // End. } } @@ -1554,7 +1673,8 @@ function ui_process_page_head($string, $bitfield) '; if ($config['language'] != 'en') { - // Load translated strings - load them last so they overload all the objects + // Load translated strings - load them last so they overload all + // the objects. ui_require_javascript_file('time_'.$config['language']); ui_require_javascript_file('date'.$config['language']); ui_require_javascript_file('countdown_'.$config['language']); @@ -1562,27 +1682,32 @@ function ui_process_page_head($string, $bitfield) $output .= "\n\t"; - // - // Load CSS - // + /* + * Load CSS + */ + if (empty($config['css'])) { $config['css'] = []; } $login_ok = true; if (! isset($config['id_user']) && isset($_GET['login'])) { - if (isset($_POST['nick']) and isset($_POST['pass'])) { + if (isset($_POST['nick']) && isset($_POST['pass'])) { $nick = get_parameter_post('nick'); - // This is the variable with the login + // This is the variable with the login. $pass = get_parameter_post('pass'); - // This is the variable with the password + // This is the variable with the password. $nick = db_escape_string_sql($nick); $pass = db_escape_string_sql($pass); - // process_user_login is a virtual function which should be defined in each auth file. - // It accepts username and password. The rest should be internal to the auth file. - // The auth file can set $config["auth_error"] to an informative error output or reference their internal error messages to it - // process_user_login should return false in case of errors or invalid login, the nickname if correct + // Process_user_login is a virtual function which should be defined + // in each auth file. + // It accepts username and password. The rest should be internal to + // the auth file. + // The auth file can set $config["auth_error"] to an informative + // error output or reference their internal error messages to it + // process_user_login should return false in case of errors or + // invalid login, the nickname if correct. $nick_in_db = process_user_login($nick, $pass); if ($nick_in_db === false) { @@ -1592,7 +1717,7 @@ function ui_process_page_head($string, $bitfield) } // First, if user has assigned a skin then try to use css files of - // skin subdirectory + // skin subdirectory. $isFunctionSkins = enterprise_include_once('include/functions_skins.php'); if (!$login_ok) { if ($isFunctionSkins !== ENTERPRISE_NOT_HOOK) { @@ -1601,8 +1726,8 @@ function ui_process_page_head($string, $bitfield) } $exists_css = false; - if ($login_ok and $isFunctionSkins !== ENTERPRISE_NOT_HOOK) { - // Checks if user's skin is available + if ($login_ok && $isFunctionSkins !== ENTERPRISE_NOT_HOOK) { + // Checks if user's skin is available. $exists_skin = enterprise_hook('skins_is_path_set'); if ($exists_skin) { $skin_path = enterprise_hook('skins_get_skin_path'); @@ -1611,23 +1736,22 @@ function ui_process_page_head($string, $bitfield) } } - // Add the jquery UI styles CSS + // Add the jquery UI styles CSS. $config['css']['jquery-UI'] = 'include/styles/js/jquery-ui.min.css'; - // Add the dialog styles CSS + // Add the dialog styles CSS. $config['css']['dialog'] = 'include/styles/dialog.css'; - // Add the dialog styles CSS + // Add the dialog styles CSS. $config['css']['dialog'] = 'include/styles/js/introjs.css'; - // If skin's css files exists then add them + // If skin's css files exists then add them. if ($exists_css) { foreach ($skin_styles as $filename => $name) { $style = substr($filename, 0, (strlen($filename) - 4)); $config['css'][$style] = $skin_path.'include/styles/'.$filename; } - } - // Otherwise assign default and user's css - else { - // User style should go last so it can rewrite common styles + } else { + // Otherwise assign default and user's css. + // User style should go last so it can rewrite common styles. $config['css'] = array_merge( [ 'common' => 'include/styles/common.css', @@ -1638,7 +1762,7 @@ function ui_process_page_head($string, $bitfield) ); } - // We can't load empty and we loaded (conditionally) ie + // We can't load empty and we loaded (conditionally) ie. $loaded = [ '', 'ie', @@ -1655,33 +1779,35 @@ function ui_process_page_head($string, $bitfield) $output .= ''."\n\t"; } - // - // End load CSS - // - // - // Load JS - // + /* + * End load CSS + */ + + /* + * Load JS + */ + if (empty($config['js'])) { $config['js'] = []; - // If it's empty, false or not init set array to empty just in case + // If it's empty, false or not init set array to empty just in case. } - // Pandora specific JavaScript should go first + // Pandora specific JavaScript should go first. $config['js'] = array_merge(['pandora' => 'include/javascript/pandora.js'], $config['js']); - // Load base64 javascript library + // Load base64 javascript library. $config['js']['base64'] = 'include/javascript/encode_decode_base64.js'; - // Load webchat javascript library + // Load webchat javascript library. $config['js']['webchat'] = 'include/javascript/webchat.js'; - // Load qrcode library + // Load qrcode library. $config['js']['qrcode'] = 'include/javascript/qrcode.js'; - // Load intro.js library (for bubbles and clippy) + // Load intro.js library (for bubbles and clippy). $config['js']['intro'] = 'include/javascript/intro.js'; $config['js']['clippy'] = 'include/javascript/clippy.js'; - // Load Underscore.js library + // Load Underscore.js library. $config['js']['underscore'] = 'include/javascript/underscore-min.js'; - // Load other javascript - // We can't load empty + // Load other javascript. + // We can't load empty. $loaded = ['']; foreach ($config['js'] as $name => $filename) { if (in_array($name, $loaded)) { @@ -1694,18 +1820,20 @@ function ui_process_page_head($string, $bitfield) $output .= ''."\n\t"; } - // - // End load JS - // - // - // Load jQuery - // + /* + * End load JS + */ + + /* + * Load jQuery + */ + if (empty($config['jquery'])) { $config['jquery'] = []; - // If it's empty, false or not init set array to empty just in case + // If it's empty, false or not init set array to empty just in case. } - // Pandora specific jquery should go first + // Pandora specific jquery should go first. $black_list_pages_old_jquery = ['operation/gis_maps/index']; if (in_array(get_parameter('sec2'), $black_list_pages_old_jquery)) { $config['jquery'] = array_merge( @@ -1728,15 +1856,15 @@ function ui_process_page_head($string, $bitfield) ); } - // Include the datapicker language if exists + // Include the datapicker language if exists. if (file_exists('include/languages/datepicker/jquery.ui.datepicker-'.$config['language'].'.js')) { $config['jquery']['datepicker_language'] = 'include/languages/datepicker/jquery.ui.datepicker-'.$config['language'].'.js'; } - // Include countdown library + // Include countdown library. $config['jquery']['countdown'] = 'include/javascript/jquery.countdown.js'; - // Then add each script as necessary + // Then add each script as necessary. $loaded = ['']; foreach ($config['jquery'] as $name => $filename) { if (in_array($name, $loaded)) { @@ -1749,9 +1877,10 @@ function ui_process_page_head($string, $bitfield) $output .= ''."\n\t"; } - // - // End load JQuery - // + /* + * End load JQuery + */ + include_once __DIR__.'/graphs/functions_flot.php'; $output .= include_javascript_dependencies_flot_graph(true); @@ -1768,8 +1897,8 @@ function ui_process_page_head($string, $bitfield) /** * Callback function to add stuff to the body * - * @param string Callback will fill this with the current buffer. - * @param bitfield Callback will fill this with a bitfield (see ob_start) + * @param string $string Callback will fill this with the current buffer. + * @param mixed $bitfield Callback will fill this with a bitfield (see ob_start). * * @return string String to return to the browser */ @@ -1780,11 +1909,11 @@ function ui_process_page_body($string, $bitfield) if (isset($config['ignore_callback']) && $config['ignore_callback'] == true ) { - return; + return null; } - // Show custom background - $output = ''; + // Show custom background. + $output = ''; $output .= $string; @@ -1801,12 +1930,15 @@ function ui_process_page_body($string, $bitfield) * @param string $url URL of the pagination links. It must include all form * values as GET form. * @param integer $offset Current offset for the pagination. Default value would be - * taken from $_REQUEST['offset'] + * taken from $_REQUEST['offset']. * @param integer $pagination Current pagination size. If a user requests a larger - * pagination than config["block_size"] - * @param boolean $return Whether to return or print this + * pagination than config["block_size"]. + * @param boolean $return Whether to return or print this. * @param string $offset_name The name of parameter for the offset. * @param boolean $print_total_items Show the text with the total items. By default true. + * @param mixed $other_class Other_class. + * @param mixed $script Script. + * @param mixed $parameter_script Parameter_script. * @param string $set_id Set id of div. * * @return string The pagination div or nothing if no pagination needs to be done @@ -1847,10 +1979,12 @@ function ui_pagination( } if (!empty($set_id)) { - $set_id = " id = '$set_id'"; + $set_id = " id = '".$set_id."'"; } - // Pagination links for users include delete, create and other params, now not use these params, and not retry the previous action when go to pagination link. + // Pagination links for users include delete, create and other params, + // now not use these params, and not retry the previous action when go to + // pagination link. $remove = [ 'user_del', 'disable_user', @@ -1860,7 +1994,10 @@ function ui_pagination( $finalUrl = []; foreach ($url as $key => $value) { - if (strpos($value, $remove[0]) === false && strpos($value, $remove[1]) === false && strpos($value, $remove[2]) === false) { + if (strpos($value, $remove[0]) === false + && strpos($value, $remove[1]) === false + && strpos($value, $remove[2]) === false + ) { array_push($finalUrl, $value); } } @@ -1874,14 +2011,15 @@ function ui_pagination( Pagination needs $url to build the base URL to render links, its a base url, like " http://pandora/index.php?sec=godmode&sec2=godmode/admin_access_logs " */ + $block_limit = PAGINATION_BLOCKS_LIMIT; - // Visualize only $block_limit blocks + // Visualize only $block_limit blocks. if ($count <= $pagination) { if ($print_total_items) { - $output = "
    '.__('Label color').''.__('Value color').' '.html_print_input_text_extended( 'percentile_label_color', '#ffffff', @@ -1128,9 +1128,58 @@ function visual_map_editor_print_item_palette($visualConsole_id, $background) true ).''; + $form_items_advance['cache_expiration_row']['html'] .= __('Cache expiration'); + $form_items_advance['cache_expiration_row']['html'] .= ''; + $form_items_advance['cache_expiration_row']['html'] .= html_print_extended_select_for_time( + 'cache_expiration', + $config['vc_default_cache_expiration'], + '', + __('No cache'), + 0, + false, + true, + false, + true, + '', + false, + $intervals + ); + $form_items_advance['cache_expiration_row']['html'] .= '
    '.''.''.''.'
    '.__('Please note that you have your agent setup to do not add new modules coming from the data XML.').'
    '.__('That means if you have a local plugin or add manually new modules to the configuration file, you won\'t have it in your agent, unless you first create manually in the interface (with the exact name and type as coming in the XML file).').'
    '.__('You should use the "normal" mode (non learn) only when you don\'t intend to add more modules to the agent.').ui_print_help_icon('module_definition', true, '', 'images/help.png').'
    ', + 'intro' => '
    '.__('Please note that you have your agent setup to do not add new modules coming from the data XML.').'
    '.__('That means if you have a local plugin or add manually new modules to the configuration file, you won\'t have it in your agent, unless you first create manually in the interface (with the exact name and type as coming in the XML file).').'
    '.__('You should use the "normal" mode (non learn) only when you don\'t intend to add more modules to the agent.').'
    ', ]; $return_tours['tours']['modules_not_learning_mode']['conf'] = []; $return_tours['tours']['modules_not_learning_mode']['conf']['autostart'] = false; diff --git a/pandora_console/include/javascript/functions_pandora_networkmap.js b/pandora_console/include/javascript/functions_pandora_networkmap.js index 4c9f5b61b7..c2335903d7 100644 --- a/pandora_console/include/javascript/functions_pandora_networkmap.js +++ b/pandora_console/include/javascript/functions_pandora_networkmap.js @@ -1,5 +1,49 @@ +/* global jQuery */ +/* global $ */ +/* global context_minimap */ +/* global minimap_w */ +/* global minimap_h */ +/* global minimap_relation */ +/* global graph */ +/* global translation */ +/* global scale */ +/* global width_svg */ +/* global height_svg */ +/* global networkmap_dimensions */ +/* global node_radius */ +/* global holding_area_dimensions */ +/* global networkmap_id */ +/* global enterprise_installed */ +/* global force */ +/* global layer_graph_nodes */ +/* global layer_graph_links */ +/* global ellipsize */ +/* global d3 */ +/* global dialog_node_edit_title */ +/* global click_menu_position_svg */ +/* global show_labels:true */ +/* global show_minimap:true */ +/* global layer_graph */ +/* global zoom_obj */ +/* global disabled_drag_zoom */ +/* global scale:true */ +/* global link */ +/* global siblingCount:true */ +/* global xRotation:true */ +/* global largeArc:true */ +/* global node */ + +/* exported delete_link */ +/* exported update_fictional_node */ +/* exported update_node_name */ +/* exported change_shape */ +/* exported add_agent_node_from_the_filter_group */ +/* exported hide_labels */ +/* exported toggle_minimap */ +/* exported over_node */ + function draw_minimap() { - //Clean the canvas + // Clean the canvas. context_minimap.clearRect(0, 0, minimap_w, minimap_h); context_minimap.beginPath(); @@ -20,6 +64,9 @@ function draw_minimap() { jQuery.each(graph.nodes, function(key, value) { if (typeof value == "undefined") return; + var center_orig_x; + var center_orig_y; + context_minimap.beginPath(); //Paint the item if (graph.nodes.length > 100) { @@ -36,7 +83,7 @@ function draw_minimap() { context_minimap.arc(center_orig_x, center_orig_y, 2, 0, Math.PI * 2, false); //Check if the pandora point - if (value.id_agent == -1) { + if (value.type == 2) { context_minimap.fillStyle = "#364D1F"; } else { context_minimap.fillStyle = "#000"; @@ -96,11 +143,11 @@ function inner_minimap_box(param_x, param_y) { return false; } -function set_center(id, event) { - pos_x = width_svg / 2 - translation[0] / scale; - pos_y = height_svg / 2 - translation[1] / scale; - +function set_center(id) { + var pos_x = width_svg / 2 - translation[0] / scale; + var pos_y = height_svg / 2 - translation[1] / scale; var params = []; + params.push("set_center=1"); params.push("id=" + id); params.push("x=" + pos_x); @@ -111,11 +158,7 @@ function set_center(id, event) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), - success: function(data) { - if (data["correct"]) { - } - } + url: "ajax.php" }); } @@ -164,7 +207,7 @@ function delete_link( data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { var found = -1; @@ -222,7 +265,7 @@ function update_fictional_node(id_db_node) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { $("#dialog_node_edit").dialog("close"); @@ -262,7 +305,7 @@ function update_node_name(id_db_node) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { $("#dialog_node_edit").dialog("close"); @@ -308,13 +351,13 @@ function change_shape(id_db_node) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { $("#shape_icon_in_progress").css("display", "none"); if (data["correct"]) { $("#shape_icon_correct").css("display", ""); - count = graph.nodes.length; + var count = graph.nodes.length; jQuery.each(graph.nodes, function(i, element) { if (element.id_db == id_db_node) { @@ -585,7 +628,7 @@ function update_link(row_index, id_link) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { $(".edit_icon_progress_" + row_index).css("display", "none"); @@ -607,12 +650,10 @@ function update_link(row_index, id_link) { "']" ).prop("selected", true); - var id = ""; var index = -1; $.each(graph.links, function(j, link) { if (link["id_db"] == id_link) { index = j; - id = String(id_link); } }); @@ -706,7 +747,7 @@ function move_to_networkmap(node) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { window.location = @@ -726,6 +767,7 @@ function edit_node(data_node, dblClick) { //Only select one node var selection = d3.selectAll(".node_selected"); + var id; if (selection[0].length == 1) { edit_node = selection[0].pop(); @@ -746,12 +788,12 @@ function edit_node(data_node, dblClick) { .select(edit_node) .attr("id") .replace("id_node_", ""); - id_networkmap_lenght = networkmap_id.toString().length; - id_node_length = id.length - id_networkmap_lenght; + var id_networkmap_lenght = networkmap_id.toString().length; + var id_node_length = id.length - id_networkmap_lenght; id = id.substring(0, id_node_length); - node_selected = graph.nodes[id]; + var node_selected = graph.nodes[id]; - selected_links = get_relations(node_selected); + var selected_links = get_relations(node_selected); $( "select[name='shape'] option[value='" + node_selected.shape + "']" @@ -779,7 +821,7 @@ function edit_node(data_node, dblClick) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { $("#node_details-0-1").html( '"; } } else { - for (address in data["adressess"]) { + for (var address in data["adressess"]) { addresses += address + "
    "; } } @@ -826,7 +868,7 @@ function edit_node(data_node, dblClick) { ); // It doesn't eval the possible XSS so it's ok $("#dialog_node_edit").dialog("open"); - if (node_selected.id_agent == undefined || node_selected.id_agent == -2) { + if (node_selected.id_agent == undefined || node_selected.type == 3) { //Fictional node $("#node_options-fictional_node_name").css("display", ""); $("input[name='edit_name_fictional_node']").val(node_selected.text); // It doesn't eval the possible XSS so it's ok @@ -868,7 +910,7 @@ function get_interface_data_to_table(node_selected, selected_links) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data.length == 0) { $("#interface_information") @@ -880,10 +922,11 @@ function get_interface_data_to_table(node_selected, selected_links) { ); } else { jQuery.each(data, function(j, interface) { + var interf_graph; if (interface["graph"] == "") { - var interf_graph = "--"; + interf_graph = "--"; } else { - var interf_graph = interface["graph"]; + interf_graph = interface["graph"]; } $("#interface_information") .find("tbody") @@ -955,7 +998,7 @@ function load_interfaces(selected_links) { type: "POST", async: true, cache: false, - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { $( @@ -1075,7 +1118,7 @@ function add_node() { } function add_agent_node_from_the_filter_group() { - agents = $("select[name='agents_filter_group']").val(); + var agents = $("select[name='agents_filter_group']").val(); add_agent_node(agents); } @@ -1122,7 +1165,7 @@ function add_agent_node(agents) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { $("#agent_name").val(""); @@ -1210,24 +1253,17 @@ function add_agent_node(agents) { var temp_node = {}; temp_node["id"] = graph.nodes.length; - temp_node["id_db"] = data["id_node"]; - temp_node["id_agent"] = data["id_agent"]; + temp_node["id_db"] = graph.nodes.length; + temp_node["id_agent"] = id_agent; temp_node["id_module"] = ""; - temp_node["px"] = data["x"]; - temp_node["py"] = data["y"]; - temp_node["x"] = data["x"]; - temp_node["y"] = data["y"]; + temp_node["px"] = x; + temp_node["py"] = y; + temp_node["x"] = x; + temp_node["y"] = y; temp_node["z"] = 0; temp_node["fixed"] = true; temp_node["type"] = 0; - temp_node["color"] = data["status"]; - temp_node["shape"] = data["shape"]; - temp_node["text"] = data["text"]; - temp_node["image_url"] = data["image_url"]; - temp_node["image_width"] = data["width"]; - temp_node["image_height"] = data["height"]; - temp_node["map_id"] = data["map_id"]; - temp_node["state"] = data["state"]; + temp_node["map_id"] = networkmap_id; graph.nodes.push(temp_node); } @@ -1248,7 +1284,7 @@ function hide_labels_function() { $("#hide_labels_" + networkmap_id + " > a").attr("title", "Show Labels"); $("#hide_labels_" + networkmap_id + " > a > img").attr( "src", - "images/icono_pintar.png" + window.location.origin + "/pandora_console/images/icono_pintar.png" ); d3.selectAll(".node_text").style("display", "none"); @@ -1261,7 +1297,7 @@ function show_labels_function() { $("#hide_labels_" + networkmap_id + " > a").attr("title", "Hide Labels"); $("#hide_labels_" + networkmap_id + " > a > img").attr( "src", - "images/icono_borrar.png" + window.location.origin + "/pandora_console/images//icono_borrar.png" ); d3.selectAll(".node_text").style("display", ""); @@ -1320,9 +1356,10 @@ function delete_nodes() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { + var found = -1; do { found = -1; jQuery.each(graph.links, function(i, element) { @@ -1600,7 +1637,7 @@ function set_positions_graph() { } function over_node(d) { - over = d3.select("#id_node_" + d.id + networkmap_id).classed("node_over"); + var over = d3.select("#id_node_" + d.id + networkmap_id).classed("node_over"); in_a_node = !in_a_node; @@ -1644,7 +1681,8 @@ function clear_selection() { function update_networkmap() { if (enterprise_installed) { node.each(function(d) { - if (d.id_agent != -1) { + // Do not update Pandora FMS node. + if (d.type != 2) { var params = []; params.push("update_node_color=1"); params.push("id=" + d.id_db); @@ -1656,7 +1694,7 @@ function update_networkmap() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { d3.select( "#id_node_" + d.id + networkmap_id + " .node_shape" @@ -1668,7 +1706,7 @@ function update_networkmap() { link.each(function(d) { if (d.id_module_start != 0 || d.id_module_end != 0) { - if (d.id_module_start != 0) { + if (d.id_module_start && d.id_module_start > 0) { var params = []; params.push("module_get_status=1"); params.push("page=operation/agentes/pandora_networkmap.view"); @@ -1677,7 +1715,7 @@ function update_networkmap() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { d3.selectAll(".id_module_start_" + d.id_module_start).attr( "marker-start", @@ -1691,7 +1729,7 @@ function update_networkmap() { }); } - if (d.id_module_end != 0) { + if (d.id_module_end && d.id_module_end > 0) { var params = []; params.push("module_get_status=1"); params.push("page=operation/agentes/pandora_networkmap.view"); @@ -1700,7 +1738,7 @@ function update_networkmap() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { d3.selectAll(".id_module_end_" + d.id_module_end).attr( "marker-end", @@ -1859,7 +1897,7 @@ function show_menu(item, data) { icon: "interface_link_children", disabled: function() { if (enterprise_installed) { - if (data.type == 3) { + if (data.type == 3 || data.type == 2) { return true; } else { return false; @@ -1929,7 +1967,7 @@ function show_menu(item, data) { icon: "interface_link_parent", disabled: function() { if (enterprise_installed) { - if (data.type == 3) { + if (data.type == 3 || data.type == 2) { return true; } else { return false; @@ -2015,7 +2053,8 @@ function show_menu(item, data) { }; } - if (data.id_agent != -1) { + // Avoid deletion if Pandora FMS node. + if (data.type != 2) { items_list["delete"] = { name: delete_menu, icon: "delete", @@ -2141,21 +2180,25 @@ function show_menu(item, data) { }; } - $.contextMenu("destroy"); - $.contextMenu({ - disabled: false, - selector: "#networkconsole_" + networkmap_id, - // define the elements of the menu - items: items_list - }); + if (typeof $.contextMenu == "function") { + $.contextMenu("destroy"); + $.contextMenu({ + disabled: false, + selector: "#networkconsole_" + networkmap_id, + // define the elements of the menu + items: items_list + }); + } break; } //Force to show in the mouse position - $("#networkconsole_" + networkmap_id).contextMenu({ - x: mouse[0], - y: mouse[1] - }); + if (typeof $("#networkconsole_" + networkmap_id).contextMenu == "function") { + $("#networkconsole_" + networkmap_id).contextMenu({ + x: mouse[0], + y: mouse[1] + }); + } } function add_interface_link(data_parent) { @@ -2205,7 +2248,7 @@ function add_interface_link(data_parent) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { var parent_name = data_parent.text; var child_name = child_data.text; @@ -2250,32 +2293,32 @@ function add_interface_link_js() { cancel_set_parent_interface(); $("#dialog_interface_link").dialog("close"); - source_value = $("#interface_source_select").val(); - source_text = $("#interface_source_select") + var source_value = $("#interface_source_select").val(); + var source_text = $("#interface_source_select") .find("option:selected") .text(); - target_value = $("#interface_target_select").val(); - target_text = $("#interface_target_select") + var target_value = $("#interface_target_select").val(); + var target_text = $("#interface_target_select") .find("option:selected") .text(); - var params = []; - params.push("add_interface_relation=1"); - params.push("id=" + networkmap_id); - params.push("source_value=" + source_value); - params.push("target_value=" + target_value); - params.push("source_text=" + source_text); - params.push("target_text=" + target_text); - params.push("page=enterprise/operation/agentes/pandora_networkmap.view"); - jQuery.ajax({ - data: params.join("&"), + data: { + page: "enterprise/operation/agentes/pandora_networkmap.view", + add_interface_relation: 1, + id: networkmap_id, + source_value: source_value, + target_value: target_value, + source_text: source_text, + target_text: target_text + }, dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { var temp_link = {}; + temp_link["id"] = graph.links.length; temp_link["status_start"] = "0"; temp_link["status_end"] = "0"; @@ -2284,8 +2327,9 @@ function add_interface_link_js() { if (data["type_source"] == 1) { temp_link["arrow_start"] = "module"; temp_link["id_module_start"] = source_value; - temp_link["status_start"] = data["status"]; - temp_link["link_color"] = data["status"] == "1" ? "#FC4444" : "#999"; + temp_link["status_start"] = data["status_start"]; + temp_link["link_color"] = + data["status_start"] == "1" ? "#FC4444" : "#999"; } else { temp_link["arrow_start"] = ""; temp_link["id_agent_start"] = source_value; @@ -2294,8 +2338,9 @@ function add_interface_link_js() { if (data["type_target"] == 1) { temp_link["arrow_end"] = "module"; temp_link["id_module_end"] = target_value; - temp_link["status_end"] = data["status"]; - temp_link["link_color"] = data["status"] == "1" ? "#FC4444" : "#999"; + temp_link["status_end"] = data["status_end"]; + temp_link["link_color"] = + data["status_end"] == "1" ? "#FC4444" : "#999"; } else { temp_link["arrow_end"] = ""; temp_link["id_agent_end"] = target_value; @@ -2325,8 +2370,8 @@ function add_interface_link_js() { } function refresh_holding_area() { - holding_pos_x = d3.select("#holding_area_" + networkmap_id).attr("x"); - holding_pos_y = d3.select("#holding_area_" + networkmap_id).attr("y"); + var holding_pos_x = d3.select("#holding_area_" + networkmap_id).attr("x"); + var holding_pos_y = d3.select("#holding_area_" + networkmap_id).attr("y"); var pos_x = parseInt(holding_pos_x) + parseInt(node_radius); var pos_y = parseInt(holding_pos_y) + parseInt(node_radius); @@ -2342,7 +2387,7 @@ function refresh_holding_area() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { window.holding_area = data["holding_area"]; @@ -2510,28 +2555,29 @@ function proceed_to_restart_map() { text: ok_button, click: function() { $(this).dialog("close"); - var new_elements = []; - new_elements[0] = $("#text-name").val(); - new_elements[1] = $("#id_group").val(); - new_elements[2] = $("#text-node_radius").val(); - new_elements[3] = $("#textarea_description").val(); - new_elements[4] = $("input[name=source]:checked").val(); - if (new_elements[4] == "group") { - new_elements[5] = $("#checkbox-dont_show_subgroups").is(":checked"); - } else if (new_elements[4] == "recon_task") { - new_elements[5] = $("#recon_task_id").val(); - } else { - new_elements[5] = $("#text-ip_mask").val(); - } - new_elements[6] = $("#method").val(); - new_elements[7] = $("#text-pos_x").val(); - new_elements[8] = $("#text-pos_y").val(); - new_elements[9] = $("#text-scale_z").val(); - new_elements[10] = $("#text-node_sep").val(); - new_elements[11] = $("#text-mindist").val(); - new_elements[12] = $("#text-rank_sep").val(); - new_elements[13] = $("#text-kval").val(); - reset_map_from_form(new_elements); + var data = { + params: { + name: $("#text-name").val(), + id_group: $("#id_group").val(), + node_radius: $("#text-node_radius").val(), + description: $("#textarea_description").val(), + source: $("input[name=source]:checked").val(), + dont_show_subgroups: $("#checkbox-dont_show_subgroups").is( + ":checked" + ), + recon_task_id: $("#recon_task_id").val(), + ip_mask: $("#text-ip_mask").val(), + generation_method: $("#method").val(), + pos_x: $("#text-pos_x").val(), + pos_y: $("#text-pos_y").val(), + scale_z: $("#text-scale_z").val(), + node_sep: $("#text-node_sep").val(), + mindist: $("#text-mindist").val(), + rank_sep: $("#text-rank_sep").val(), + kval: $("#text-kval").val() + } + }; + reset_map_from_form(data); } }, { @@ -2551,31 +2597,29 @@ function proceed_to_restart_map() { data: params.join("&"), dataType: "html", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { - $("#restart_map_form").html(data); - $("#restart_map_form").dialog("open"); + $("#restart_map_form") + .html(data) + .dialog("open"); } }); } function reset_map_from_form(new_elements) { - var params = []; - params.push("reset_map=1"); - params.push("map_id=" + networkmap_id); - params.push("elems[]=" + new_elements); - params.push("page=enterprise/operation/agentes/pandora_networkmap.view"); + var data = new_elements; + data.map_id = networkmap_id; + data.reset_map = 1; + data.page = "enterprise/operation/agentes/pandora_networkmap.view"; jQuery.ajax({ - data: params.join("&"), + data: data, dataType: "json", type: "POST", - url: (action = "ajax.php"), - success: function(data) { - if (!data["error"]) { - window.location = - "index.php?sec=network&sec2=operation/agentes/pandora_networkmap&tab=view&id_networkmap=" + - networkmap_id; - } + url: "ajax.php", + success: function(d) { + window.location = + "index.php?sec=network&sec2=operation/agentes/pandora_networkmap&tab=view&id_networkmap=" + + networkmap_id; } }); } @@ -2636,7 +2680,7 @@ function set_parent(parent_data) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { //Add the relationship and paint @@ -2727,23 +2771,25 @@ function init_drag_and_drop() { var selection = d3.selectAll(".node_selected"); if (enterprise_installed) { - holding_pos_x = d3.select("#holding_area_" + networkmap_id).attr("x"); - holding_pos_y = d3.select("#holding_area_" + networkmap_id).attr("y"); + var holding_pos_x = d3 + .select("#holding_area_" + networkmap_id) + .attr("x"); + var holding_pos_y = d3 + .select("#holding_area_" + networkmap_id) + .attr("y"); delete d.raw_text; selection.each(function(d) { - var params = []; - params.push("update_node=1"); - params.push("node=" + JSON.stringify(d)); - params.push("x=" + holding_pos_x); - params.push("y=" + holding_pos_y); - params.push( - "page=enterprise/operation/agentes/pandora_networkmap.view" - ); jQuery.ajax({ - data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", + data: { + node: JSON.stringify(d), + x: holding_pos_x, + y: holding_pos_y, + update_node: 1, + page: "enterprise/operation/agentes/pandora_networkmap.view" + }, success: function(data) { if (d.state == "holding_area") { //It is out the holding area @@ -2777,7 +2823,7 @@ function init_drag_and_drop() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { $("#open_version_dialog").dialog(); @@ -2840,7 +2886,7 @@ function add_fictional_node() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { $("#dialog_node_add").dialog("close"); @@ -2909,6 +2955,13 @@ function init_graph(parameter_object) { window.height_svg = $(window).height() - $("#menu_tab_frame_view").height() - 20; // 20 of margin } + if (!window.height_svg) { + window.height_svg = $("#networkconsole_" + networkmap_id).height(); + } + + if (typeof parameter_object.font_size != "undefined") { + window.font_size = parameter_object.font_size; + } window.refresh_period = 5 * 1000; //milliseconds if (typeof parameter_object.refresh_period != "undefined") { @@ -3922,6 +3975,10 @@ function draw_elements_graph() { var font_size = node_radius / 1.5; + if (typeof window.font_size != "undefined") { + font_size = window.font_size; + } + node_temp .append("text") .attr("class", "node_text") @@ -3991,7 +4048,7 @@ function choose_group_for_show_agents() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "ajax.php"), + url: "ajax.php", success: function(data) { if (data["correct"]) { $("#agents_filter_group").html(""); @@ -4013,162 +4070,6 @@ function choose_group_for_show_agents() { '" ); $("#spinner_group").css("display", "none"); - function show_networkmap_node(id_agent_param, refresh_state) { - id_agent = id_agent_param; - - canvas = $("#node_info"); - context_popup = canvas[0].getContext("2d"); - - dirty_popup = true; - self.setInterval("check_popup_modification()", 1000 / 30); - - $("#node_info").mousemove(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - module_inner = inner_module(x, y); - - if (module_inner != null) { - document.body.style.cursor = "pointer"; - } else { - document.body.style.cursor = "default"; - } - }); - - $("#node_info").mousedown(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - if (module_inner != null) { - show_tooltip(module_inner, x, y); - } - - event.stopPropagation(); - return false; - }); - - $("#node_info").mouseup(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - drag = false; - drag_x = 0; - drag_y = 0; - dirty_popup = true; - - document.body.style.cursor = "default"; - - module_inner = null; - - event.stopPropagation(); - return false; - }); - - $("#node_info").mouseout(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - drag = false; - drag_x = 0; - drag_y = 0; - dirty_popup = true; - - document.body.style.cursor = "default"; - - module_inner = null; - - event.stopPropagation(); - return false; - }); - - $(window).resize(function() { - function show_networkmap_node(id_agent_param, refresh_state) { - id_agent = id_agent_param; - - canvas = $("#node_info"); - context_popup = canvas[0].getContext("2d"); - - dirty_popup = true; - self.setInterval("check_popup_modification()", 1000 / 30); - - $("#node_info").mousemove(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - module_inner = inner_module(x, y); - - if (module_inner != null) { - document.body.style.cursor = "pointer"; - } else { - document.body.style.cursor = "default"; - } - }); - - $("#node_info").mousedown(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - if (module_inner != null) { - show_tooltip(module_inner, x, y); - } - - event.stopPropagation(); - return false; - }); - - $("#node_info").mouseup(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - drag = false; - drag_x = 0; - drag_y = 0; - dirty_popup = true; - - document.body.style.cursor = "default"; - - module_inner = null; - - event.stopPropagation(); - return false; - }); - - $("#node_info").mouseout(function(event) { - var x = event.pageX - $("#node_info").offset().left; - var y = event.pageY - $("#node_info").offset().top; - - drag = false; - drag_x = 0; - drag_y = 0; - dirty_popup = true; - - document.body.style.cursor = "default"; - - module_inner = null; - - event.stopPropagation(); - return false; - }); - - $(window).resize(function() { - pos_scroll = Math.floor( - $("#content_node_info").width() / 2 - ); - - $("#content_node_info").scrollLeft(pos_scroll); - - dirty_popup = true; - check_popup_modification(); - }); - } - pos_scroll = Math.floor($("#content_node_info").width() / 2); - - $("#content_node_info").scrollLeft(pos_scroll); - - dirty_popup = true; - check_popup_modification(); - }); - } } } }); @@ -4213,7 +4114,7 @@ function get_status_node() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "../../../ajax.php"), + url: "../../../ajax.php", success: function(data) { if (data["correct"]) { color_status_node = data["status_agent"]; @@ -4235,7 +4136,7 @@ function get_status_module() { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "../../../ajax.php"), + url: "../../../ajax.php", success: function(data) { if (data["correct"]) { modules[data["id"]].status_color = data["status_color"]; @@ -4333,7 +4234,7 @@ function show_tooltip_content(id) { data: params.join("&"), dataType: "json", type: "POST", - url: (action = "../../../ajax.php"), + url: "../../../ajax.php", success: function(data) { if (data["correct"]) { $("#tooltip").html(data["content"]); @@ -4353,7 +4254,7 @@ function show_tooltip(id, x, y) { jQuery.ajax({ data: params1.join("&"), type: "POST", - url: (action = "../../../ajax.php"), + url: "../../../ajax.php", success: function(data) { $("#tooltip").html(data); $("#tooltip").css("display", ""); diff --git a/pandora_console/include/javascript/jquery.pandora.js b/pandora_console/include/javascript/jquery.pandora.js index bd0da308c4..9e0b3447af 100644 --- a/pandora_console/include/javascript/jquery.pandora.js +++ b/pandora_console/include/javascript/jquery.pandora.js @@ -163,12 +163,21 @@ $(document).ready (function () { background: "black" }, open: function() { - setTimeout(function(){ - $("#spinner_ok").hide(); + var remaining = 30; + + // Timeout counter. + var count = function() { + if (remaining > 0) { + $("#license_error_remaining").text(remaining); + remaining -= 1; + } else { + $("#license_error_remaining").hide(); $("#ok_buttom").show(); - }, - 30000 - ); + clearInterval(count); + } + } + + setInterval(count, 1000); } }); diff --git a/pandora_console/include/javascript/pandora.js b/pandora_console/include/javascript/pandora.js index c30736cd8b..351531a5e9 100644 --- a/pandora_console/include/javascript/pandora.js +++ b/pandora_console/include/javascript/pandora.js @@ -33,9 +33,21 @@ function winopeng_var(url, wid, width, height) { status = wid; } -function open_help(help_id, home_url, id_user) { +function open_help(url) { + if (!navigator.onLine) { + alert( + "The help system could not be started. Please, check your network connection." + ); + return; + } + if (url == "") { + alert( + "The help system is currently under maintenance. Sorry for the inconvenience." + ); + return; + } open( - home_url + "general/pandora_help.php?id=" + help_id + "&id_user=" + id_user, + url, "pandorahelp", "width=650,height=500,status=0,toolbar=0,menubar=0,scrollbars=1,location=0" ); @@ -1214,7 +1226,7 @@ function paint_qrcode(text, where, width, height) { text: text, width: width, height: height, - colorDark: "#3B6941", + colorDark: "#343434", colorLight: "#ffffff", correctLevel: QRCode.CorrectLevel.M }); @@ -1748,7 +1760,9 @@ function round_with_decimals(value, multiplier) { if (typeof multiplier === "undefined") multiplier = 1; // Return non numeric types without modification - if (typeof value !== "number") return value; + if (typeof value !== "number" || Number.isNaN(value)) { + return value; + } if (value * multiplier == 0) return 0; if (Math.abs(value) * multiplier >= 1) { diff --git a/pandora_console/include/javascript/pandora_alerts.js b/pandora_console/include/javascript/pandora_alerts.js index 41f8d20ad8..1c72d13cf3 100644 --- a/pandora_console/include/javascript/pandora_alerts.js +++ b/pandora_console/include/javascript/pandora_alerts.js @@ -19,8 +19,14 @@ function parse_alert_command(command, classs) { var regex = new RegExp(field, "gi"); - command = command.replace(regex, $(this).val()); + if ($(this).css("-webkit-text-security") == "disc") { + var hidden_character = "*"; + var hidden_string = hidden_character.repeat($(this).val().length); + command = command.replace(regex, hidden_string); + } else { + command = command.replace(regex, $(this).val()); + } nfield++; }); diff --git a/pandora_console/include/javascript/pandora_events.js b/pandora_console/include/javascript/pandora_events.js index 42d67fffd6..1f1ff42c3f 100644 --- a/pandora_console/include/javascript/pandora_events.js +++ b/pandora_console/include/javascript/pandora_events.js @@ -365,9 +365,9 @@ function get_event_name(event_id, meta, history) { function add_row_param(id_table, param) { $("#" + id_table).append( - '' + + '' + param + - ' 0) manager.tasks.pop().abort(); + }; + + return manager; + }; + + var taskManager = new xhrManager(); + + // Stop old ajax tasks. + taskManager.stopTasks(); + + // Show the spinners. + $("#textarea_explanation").hide(); + $("#spinner_layout").show(); + + var xhr = jQuery.ajax({ + data: { + page: "enterprise/include/ajax/hostDevices.ajax", + get_explanation: 1, + id: id, + id_rt: id_rt + }, + url: url, + type: "POST", + dataType: "text", + complete: function(xhr, textStatus) { + $("#spinner_layout").hide(); + }, + success: function(data, textStatus, xhr) { + $("#textarea_explanation").val(data); + $("#textarea_explanation").show(); + }, + error: function(xhr, textStatus, errorThrown) { + console.log(errorThrown); + } + }); + + taskManager.addTask(xhr); + + // Delete all the macro fields. + $(".macro_field").remove(); + $("#spinner_recon_script").show(); + + var xhr = jQuery.ajax({ + data: { + page: "enterprise/include/ajax/hostDevices.ajax", + get_recon_script_macros: 1, + id: id, + id_rt: id_rt + }, + url: url, + type: "POST", + dataType: "json", + complete: function(xhr, textStatus) { + $("#spinner_recon_script").hide(); + forced_title_callback(); + }, + success: function(data, textStatus, xhr) { + if (data.array !== null) { + $("#hidden-macros").val(data.base64); + + jQuery.each(data.array, function(i, macro) { + if (macro.desc != "") { + add_macro_field(macro, "table_recon-macro"); + } + }); + } + }, + error: function(xhr, textStatus, errorThrown) { + console.log(errorThrown); + } + }); + + taskManager.addTask(xhr); +} diff --git a/pandora_console/include/javascript/pandora_taskList.js b/pandora_console/include/javascript/pandora_taskList.js new file mode 100644 index 0000000000..75d95df026 --- /dev/null +++ b/pandora_console/include/javascript/pandora_taskList.js @@ -0,0 +1,109 @@ +/* + global $ + global jQuery +*/ + +/** + * Function for create modal with progress task + * and recalculate 3 second. + * @param {int} id + * @param {string} name + */ +function progress_task_list(id, title) { + var timeoutRef = null; + var xhr = null; + var $elem = $("#progress_task_" + id); + + $elem + .hide() + .empty() + .dialog({ + title: title, + autoOpen: false, + modal: false, + resizable: false, + draggable: false, + closeOnEscape: true, + width: 800, + height: 600, + close: function() { + if (xhr != null) xhr.abort(); + if (timeoutRef != null) clearTimeout(timeoutRef); + } + }); + + // Function var. + var handleFetchTaskList = function(err, data) { + if (err) { + // TODO: Show info about the problem. + } + + $elem.html(data.html); + if (!$elem.dialog("isOpen")) $elem.dialog("open"); + + if (data.status != -1) { + timeoutRef = setTimeout(function() { + xhr = fetchTaskList(id, handleFetchTaskList); + }, 3000); + } + }; + + xhr = fetchTaskList(id, handleFetchTaskList); +} + +/** + * Function that performs ajax request to return + * the progress of the task. + * @param {int} id Id task. + * @param {function} callback Function callback. + */ +function fetchTaskList(id, callback) { + return jQuery.ajax({ + data: { + page: "include/ajax/task_list.ajax", + progress_task_discovery: 1, + id: id + }, + type: "POST", + url: "ajax.php", + dataType: "json", + success: function(data) { + callback(null, data); + }, + error: function() { + callback(new Error("cannot fetch the list")); + } + }); +} + +function show_map(id, name) { + $("#map_task") + .empty() + .hide() + .append("

    Loading map

    ") + .dialog({ + title: "Task: " + name, + resizable: true, + draggable: true, + modal: false, + width: 1280, + height: 700 + }) + .show(); + + jQuery.ajax({ + data: { + page: "include/ajax/task_list.ajax", + showmap: 1, + id: id + }, + type: "POST", + url: "ajax.php", + dataType: "html", + success: function(data) { + $("#map_task") + .empty() + .append(data); + } + }); +} diff --git a/pandora_console/include/javascript/pandora_visual_console.js b/pandora_console/include/javascript/pandora_visual_console.js index 06209e092f..b78270f3dc 100755 --- a/pandora_console/include/javascript/pandora_visual_console.js +++ b/pandora_console/include/javascript/pandora_visual_console.js @@ -1,3 +1,265 @@ +// TODO: Add Artica ST header. +/* globals jQuery, VisualConsole, AsyncTaskManager */ + +/* + * ********************* + * * New VC functions. * + * ********************* + */ + +/** + * Generate a Visual Console client. + * @param {HTMLElement} container Node which will be used to contain the VC. + * @param {object} props VC container properties. + * @param {object[]} items List of item definitions. + * @param {string | null} baseUrl Base URL to perform API requests. + * @param {number | null} updateInterval Time in milliseconds between VC updates. + * @param {function | null} onUpdate Callback which will be execuded when the Visual Console. + * is updated. It will receive two arguments with the old and the new Visual Console's + * data structure. + * @return {VisualConsole | null} The Visual Console instance or a null value. + */ +// eslint-disable-next-line no-unused-vars +function createVisualConsole( + container, + props, + items, + baseUrl, + updateInterval, + onUpdate +) { + if (container == null || props == null || items == null) return null; + if (baseUrl == null) baseUrl = ""; + + var visualConsole = null; + var asyncTaskManager = new AsyncTaskManager(); + + function updateVisualConsole(visualConsoleId, updateInterval, tts) { + if (tts == null) tts = 0; // Time to start. + + asyncTaskManager.add( + "visual-console", + function(done) { + var abortable = loadVisualConsoleData( + baseUrl, + visualConsoleId, + function(error, data) { + if (error) { + console.log( + "[ERROR]", + "[VISUAL-CONSOLE-CLIENT]", + "[API]", + error.message + ); + done(); + return; + } + + // Replace Visual Console. + if (data != null && data.props != null && data.items != null) { + try { + var props = + typeof data.props === "string" + ? JSON.parse(data.props) + : data.props; + var items = + typeof data.items === "string" + ? JSON.parse(data.items) + : data.items; + + var prevProps = visualConsole.props; + // Update the data structure. + visualConsole.props = props; + // Update the items. + visualConsole.updateElements(items); + // Emit the VC update event. + if (onUpdate) onUpdate(prevProps, visualConsole.props); + } catch (ignored) {} // eslint-disable-line no-empty + + done(); + } + } + ); + + return { + cancel: function() { + abortable.abort(); + } + }; + }, + updateInterval + ); + + asyncTaskManager.add("visual-console-start", function(done) { + var ref = setTimeout(function() { + asyncTaskManager.init("visual-console"); + done(); + }, tts); + + return { + cancel: function() { + clearTimeout(ref); + } + }; + }); + + if (tts > 0) { + // Wait to start the fetch interval. + asyncTaskManager.init("visual-console-start"); + } else { + // Start the fetch interval immediately. + asyncTaskManager.init("visual-console"); + } + } + + // Initialize the Visual Console. + try { + visualConsole = new VisualConsole(container, props, items); + // VC Item clicked. + visualConsole.onClick(function(e) { + // Override the link to another VC if it isn't on remote console. + if ( + e.data && + e.data.linkedLayoutId != null && + e.data.linkedLayoutId > 0 && + e.data.link != null && + e.data.link.length > 0 && + (e.data.linkedLayoutAgentId == null || e.data.linkedLayoutAgentId === 0) + ) { + // Stop the current link behavior. + e.nativeEvent.preventDefault(); + // Fetch and update the old VC with the new. + updateVisualConsole(e.data.linkedLayoutId, updateInterval); + } + }); + + if (updateInterval != null && updateInterval > 0) { + // Start an interval to update the Visual Console. + updateVisualConsole(props.id, updateInterval, updateInterval); + } + } catch (error) { + console.log("[ERROR]", "[VISUAL-CONSOLE-CLIENT]", error.message); + } + + return { + visualConsole: visualConsole, + changeUpdateInterval: function(updateInterval) { + if (updateInterval != null && updateInterval > 0) { + updateVisualConsole( + visualConsole.props.id, + updateInterval, + updateInterval + ); + } else { + // Update interval disabled. Cancel possible pending tasks. + asyncTaskManager.cancel("visual-console"); + asyncTaskManager.cancel("visual-console-start"); + } + } + }; +} + +/** + * Fetch a Visual Console's structure and its items. + * @param {string} baseUrl Base URL to build the API path. + * @param {number} vcId Identifier of the Visual Console. + * @param {function} callback Function to be executed on request success or fail. + * On success, the function will receive an object with the next properties: + * - `props`: object with the Visual Console's data structure. + * - `items`: array of data structures of the Visual Console's items. + * @return {Object} Cancellable. Object which include and .abort([statusText]) function. + */ +// eslint-disable-next-line no-unused-vars +function loadVisualConsoleData(baseUrl, vcId, callback) { + // var apiPath = baseUrl + "/include/rest-api"; + var apiPath = baseUrl + "/ajax.php"; + var vcJqXHR = null; + var itemsJqXHR = null; + + // Initialize the final result. + var result = { + props: null, + items: null + }; + + // Cancel the ajax requests. + var abort = function(textStatus) { + if (textStatus == null) textStatus = "abort"; + + // -- XMLHttpRequest.readyState -- + // Value State Description + // 0 UNSENT Client has been created. open() not called yet. + // 4 DONE The operation is complete. + + if (vcJqXHR.readyState !== 0 && vcJqXHR.readyState !== 4) + vcJqXHR.abort(textStatus); + if (itemsJqXHR.readyState !== 0 && itemsJqXHR.readyState !== 4) + itemsJqXHR.abort(textStatus); + }; + + // Check if the required data is complete. + var checkResult = function() { + return result.props !== null && result.items !== null; + }; + + // Failed request handler. + var handleFail = function(jqXHR, textStatus, errorThrown) { + abort(); + // Manually aborted or not. + if (textStatus === "abort") { + callback(); + } else { + var error = new Error(errorThrown); + error.request = jqXHR; + callback(error); + } + }; + + // Curried function which handle success. + var handleSuccess = function(key) { + // Actual request handler. + return function(data) { + result[key] = data; + if (checkResult()) callback(null, result); + }; + }; + + // Visual Console container request. + vcJqXHR = jQuery + // .get(apiPath + "/visual-consoles/" + vcId, null, "json") + .get( + apiPath, + { + page: "include/rest-api/index", + getVisualConsole: 1, + visualConsoleId: vcId + }, + "json" + ) + .done(handleSuccess("props")) + .fail(handleFail); + // Visual Console items request. + itemsJqXHR = jQuery + // .get(apiPath + "/visual-consoles/" + vcId + "/items", null, "json") + .get( + apiPath, + { + page: "include/rest-api/index", + getVisualConsoleItems: 1, + visualConsoleId: vcId + }, + "json" + ) + .done(handleSuccess("items")) + .fail(handleFail); + + // Abortable. + return { + abort: abort + }; +} + +// TODO: Delete the functions below when you can. /************************************** These functions require jQuery library **************************************/ diff --git a/pandora_console/include/javascript/timezonepicker/README.md b/pandora_console/include/javascript/timezonepicker/README.md new file mode 100644 index 0000000000..fdaf77e099 --- /dev/null +++ b/pandora_console/include/javascript/timezonepicker/README.md @@ -0,0 +1,189 @@ +# timezonepicker + +A jQuery and ImageMap based timezone picker. + +This library only works with pre-built imagemaps generated from +http://timezonepicker.com. + +## Features + +- Simple implementation, lightweight footprint (160KB, 40KB gzipped). +- Includes 440+ clickable areas. +- HTML5 Geolocation to identify timezone. +- Islands include padding to increase ease of selection. +- Country mapping can be used to set timezone and country at the same time. +- Timezone highlighting on rollover (thanks to [jQuery maphilight](http://davidlynch.org/projects/maphilight/docs/)) + +## Usage + +Basic call using all defaults: + +```javascript +$("#img-with-usemap-attr").timezonePicker(); +``` + +A few simple options: + +```javascript +$("#img-with-usemap-attr").timezonePicker({ + pin: ".timezone-pin", + fillColor: "FFCCCC" +}); +``` + +## Options + +As pulled from the set of defaults. + +```javascript +$.fn.timezonePicker.defaults = { + // Selector for the pin that should be used. This selector only works in the + // immediate parent of the image map img tag. + pin: ".timezone-pin", + // Specify a URL for the pin image instead of using a DOM element. + pinUrl: null, + // Preselect a particular timezone. + timezone: null, + // Pass through options to the jQuery maphilight plugin. + maphilight: true, + // Selector for the select list, textfield, or hidden to update upon click. + target: null, + // Selector for the select list, textfield, or hidden to update upon click + // with the specified country. + countryTarget: null, + // If changing the country should use the first timezone within that country. + countryGuess: true, + // A list of country guess exceptions. These should only be needed if a + // country spans multiple timezones. + countryGuesses: { + AU: "Australia/Sydney", + BR: "America/Sao_Paulo", + CA: "America/Toronto", + CN: "Asia/Shanghai", + ES: "Europe/Madrid", + MX: "America/Mexico_City", + RU: "Europe/Moscow", + US: "America/New_York" + }, + // If this map should automatically adjust its size if scaled. Note that + // this can be very expensive computationally and will likely have a delay + // on resize. The maphilight library also is incompatible with this setting + // and will be disabled. + responsive: false, + // A function to be called upon timezone change + // timezoneName, countryName, and offset will be passed as arguments + changeHandler: null, + + // Default options passed along to the maphilight plugin. + fade: false, + stroke: true, + strokeColor: "FFFFFF", + strokeOpacity: 0.4, + fillColor: "FFFFFF", + fillOpacity: 0.4, + groupBy: "data-offset" +}; +``` + +## Additional methods + +After creating a timezone picker from an image tag, you can execute additional +commands on the image map with these methods: + +```javascript +// Query the user's browser for the current location and set timezone from that. +$("#img-with-usemap-attr").timezonePicker("detectTimezone"); + +// The detectTimezone method may also provide event callbacks. +$("#img-with-usemap-attr").timezonePicker("detectTimezone", { + success: successCallback, + error: errorCallback, + complete: completeCallback // Called on both success or failure. +}); + +// Set the active timezone to some value programatically. +$("#img-with-usemap-attr").timezonePicker("updateTimezone", "America/New_York"); + +// Resize the image map coordinates to match an adjusted size of the image. +// Note that this option does not work well and is very slow. Not recommended. +$("#img-with-usemap-attr").timezonePicker("resize"); +``` + +## Building new definition files + +The definition files are used to determine the polygons and rectangles used to +generate the resulting imagemap. Note this should rarely be necessary for normal +users as the timezone picker project will rebuild the shape files after updates +to the timezone database. + +1. Download latest shape file "tz_world" from + http://efele.net/maps/tz/world/. + + wget http://efele.net/maps/tz/world/tz_world.zip + unzip tz_world.zip + +2. Install PostGIS, which provides the shp2pgsql executable. + http://postgis.refractions.net/download/ + + For Mac OS X, I installed PostGres, GDAL Complete, and PostGIS binaries from + http://www.kyngchaos.com/software:postgres + + Then add psql and shp2pgsql to your $PATH variable in your shell profile. + export PATH=/usr/local/pgsql-9.1/bin:$PATH + +3. Convert the tz_world.shp file into SQL: + + ``` + cd world + shp2pgsql tz_world.shp timezones > tz_world.sql + ``` + +4. Create a temporary database and import the SQL file. + + ``` + psql -U postgres -c "CREATE DATABASE timezones" -d template1 + ``` + + And import the PostGIS functions into the database. + + ``` + psql -U postgres -d timezones -f /usr/local/pgsql-9.1/share/contrib/postgis-2.0/postgis.sql + + psql -U postgres -d timezones < tz_world.sql + ``` + +5. Export the data as text in a simplified format. + + ``` + psql -U postgres -d timezones -t -A -c " + + SELECT tzid, ST_AsText(ST_Simplify(ST_SnapToGrid(geom, 0.001), 0.3)) FROM timezones + + WHERE (ST_Area(geom) > 3 OR (gid IN ( + + SELECT MAX(gid) FROM timezones WHERE ST_Area(geom) <= 3 AND tzid NOT IN ( + + SELECT tzid FROM timezones WHERE ST_Area(geom) > 3 + + ) group by tzid ORDER BY MAX(ST_AREA(geom)) + + ))) AND tzid != 'uninhabited'; + + " > tz_world.txt + ``` + + And a special export for Islands that are hard to select otherwise. + + ``` + psql -U postgres -d timezones -t -A -c " + SELECT tzid, ST_Expand(ST_Extent(geom), GREATEST(3 - ST_Area(ST_Extent(geom)), 0)) FROM timezones + + WHERE ST_Area(geom) < 3 AND (tzid LIKE 'Pacific/%' OR tzid LIKE 'Indian/%' OR tzid LIKE 'Atlantic/%') GROUP BY tzid ORDER BY tzid; + " > tz_islands.txt + ``` + +## LICENSE + +Copyright 2011-2013 Nathan Haug + +Released under the MIT License. diff --git a/pandora_console/include/javascript/timezonepicker/images/blue-marble-1280.jpg b/pandora_console/include/javascript/timezonepicker/images/blue-marble-1280.jpg new file mode 100644 index 0000000000..9fa8633d88 Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/blue-marble-1280.jpg differ diff --git a/pandora_console/include/javascript/timezonepicker/images/custom.php b/pandora_console/include/javascript/timezonepicker/images/custom.php new file mode 100644 index 0000000000..bd15772119 --- /dev/null +++ b/pandora_console/include/javascript/timezonepicker/images/custom.php @@ -0,0 +1,66 @@ + 'png', + 'blue-marble' => 'jpg', + 'living' => 'jpg', + 'night-electric' => 'jpg', +]; +$width = isset($_GET['w']) ? min((int) $_GET['w'], 1280) : 600; +$height = round($width / 2); + +if (isset($_GET['base']) && isset($bases[$_GET['base']])) { + $base = $_GET['base']; + $extension = $bases[$_GET['base']]; +} else { + $base = reset(array_keys($bases)); + $extension = reset($bases); +} + +$source = $base.'-1280.'.$extension; +$open_extension = str_replace('jpg', 'jpeg', $extension); +$open_func = 'imagecreatefrom'.$open_extension; + +$im = $open_func($source); +if (!$im) { + return false; +} + +list($original_width, $original_height) = getimagesize($source); + +$res = imagecreatetruecolor($width, $height); +if ($extension == 'png') { + $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127); + imagealphablending($res, false); + imagefilledrectangle($res, 0, 0, $width, $height, $transparency); + imagealphablending($res, true); + imagesavealpha($res, true); +} else if ($extension == 'gif') { + // If we have a specific transparent color. + $transparency_index = imagecolortransparent($im); + if ($transparency_index >= 0) { + // Get the original image's transparent color's RGB values. + $transparent_color = imagecolorsforindex($im, $transparency_index); + // Allocate the same color in the new image resource. + $transparency_index = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); + // Completely fill the background of the new image with allocated color. + imagefill($res, 0, 0, $transparency_index); + // Set the background color for new image to transparent. + imagecolortransparent($res, $transparency_index); + // Find number of colors in the images palette. + $number_colors = imagecolorstotal($im); + // Convert from true color to palette to fix transparency issues. + imagetruecolortopalette($res, true, $number_colors); + } +} + +imagecopyresampled($res, $im, 0, 0, 0, 0, $width, $height, $original_width, $original_height); + +header('Content-Type: image/'.$extension); +header('Cache-Control: public, max-age: 3600'); + +$close_function = 'image'.$open_extension; +$close_function($res); + +imagedestroy($res); +imagedestroy($im); diff --git a/pandora_console/include/javascript/timezonepicker/images/gray-1280.png b/pandora_console/include/javascript/timezonepicker/images/gray-1280.png new file mode 100644 index 0000000000..538abc41e8 Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/gray-1280.png differ diff --git a/pandora_console/include/javascript/timezonepicker/images/gray-400.png b/pandora_console/include/javascript/timezonepicker/images/gray-400.png new file mode 100644 index 0000000000..631dd24628 Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/gray-400.png differ diff --git a/pandora_console/include/javascript/timezonepicker/images/gray-600.png b/pandora_console/include/javascript/timezonepicker/images/gray-600.png new file mode 100644 index 0000000000..fa00ca74f9 Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/gray-600.png differ diff --git a/pandora_console/include/javascript/timezonepicker/images/gray-800.png b/pandora_console/include/javascript/timezonepicker/images/gray-800.png new file mode 100644 index 0000000000..a59d22fbe8 Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/gray-800.png differ diff --git a/pandora_console/include/javascript/timezonepicker/images/living-1280.jpg b/pandora_console/include/javascript/timezonepicker/images/living-1280.jpg new file mode 100644 index 0000000000..3f216faaf9 Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/living-1280.jpg differ diff --git a/pandora_console/include/javascript/timezonepicker/images/map-outlines.psd b/pandora_console/include/javascript/timezonepicker/images/map-outlines.psd new file mode 100644 index 0000000000..490622a2fa Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/map-outlines.psd differ diff --git a/pandora_console/include/javascript/timezonepicker/images/night-electric-1280.jpg b/pandora_console/include/javascript/timezonepicker/images/night-electric-1280.jpg new file mode 100644 index 0000000000..b763a659ab Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/night-electric-1280.jpg differ diff --git a/pandora_console/include/javascript/timezonepicker/images/pin.png b/pandora_console/include/javascript/timezonepicker/images/pin.png new file mode 100644 index 0000000000..ad9b970de3 Binary files /dev/null and b/pandora_console/include/javascript/timezonepicker/images/pin.png differ diff --git a/pandora_console/include/javascript/timezonepicker/includes/parser.inc b/pandora_console/include/javascript/timezonepicker/includes/parser.inc new file mode 100644 index 0000000000..698c940752 --- /dev/null +++ b/pandora_console/include/javascript/timezonepicker/includes/parser.inc @@ -0,0 +1,167 @@ + 1280 ? 600 : (int) $_GET['w']; +$map_height = round($map_width / 2); +$timezones = timezone_picker_parse_files($map_width, $map_height, 'tz_world.txt', 'tz_islands.txt'); + +header('Content-Type: application/json'); +header('Cache-Control: public, max-age: 3600'); + +print json_encode($timezones, (JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT)); diff --git a/pandora_console/include/javascript/timezonepicker/lib/jquery.maphilight.min.js b/pandora_console/include/javascript/timezonepicker/lib/jquery.maphilight.min.js new file mode 100644 index 0000000000..0f93346263 --- /dev/null +++ b/pandora_console/include/javascript/timezonepicker/lib/jquery.maphilight.min.js @@ -0,0 +1,564 @@ +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define(["jquery"], factory); + } else { + factory(root.jQuery); + } +})(this, function($) { + var has_VML, + has_canvas, + create_canvas_for, + add_shape_to, + clear_canvas, + shape_from_area, + canvas_style, + hex_to_decimal, + css3color, + is_image_loaded, + options_from_area; + + has_canvas = !!document.createElement("canvas").getContext; + + // VML: more complex + has_VML = (function() { + var a = document.createElement("div"); + a.innerHTML = ''; + var b = a.firstChild; + b.style.behavior = "url(#default#VML)"; + return b ? typeof b.adj == "object" : true; + })(); + + if (!(has_canvas || has_VML)) { + $.fn.maphilight = function() { + return this; + }; + return; + } + + if (has_canvas) { + hex_to_decimal = function(hex) { + return Math.max(0, Math.min(parseInt(hex, 16), 255)); + }; + css3color = function(color, opacity) { + return ( + "rgba(" + + hex_to_decimal(color.substr(0, 2)) + + "," + + hex_to_decimal(color.substr(2, 2)) + + "," + + hex_to_decimal(color.substr(4, 2)) + + "," + + opacity + + ")" + ); + }; + create_canvas_for = function(img) { + var c = $( + '' + ).get(0); + c.getContext("2d").clearRect(0, 0, $(img).width(), $(img).height()); + return c; + }; + var draw_shape = function(context, shape, coords, x_shift, y_shift) { + x_shift = x_shift || 0; + y_shift = y_shift || 0; + + context.beginPath(); + if (shape == "rect") { + // x, y, width, height + context.rect( + coords[0] + x_shift, + coords[1] + y_shift, + coords[2] - coords[0], + coords[3] - coords[1] + ); + } else if (shape == "poly") { + context.moveTo(coords[0] + x_shift, coords[1] + y_shift); + for (i = 2; i < coords.length; i += 2) { + context.lineTo(coords[i] + x_shift, coords[i + 1] + y_shift); + } + } else if (shape == "circ") { + // x, y, radius, startAngle, endAngle, anticlockwise + context.arc( + coords[0] + x_shift, + coords[1] + y_shift, + coords[2], + 0, + Math.PI * 2, + false + ); + } + context.closePath(); + }; + add_shape_to = function(canvas, shape, coords, options, name) { + var i, + context = canvas.getContext("2d"); + + // Because I don't want to worry about setting things back to a base state + + // Shadow has to happen first, since it's on the bottom, and it does some clip / + // fill operations which would interfere with what comes next. + if (options.shadow) { + context.save(); + if (options.shadowPosition == "inside") { + // Cause the following stroke to only apply to the inside of the path + draw_shape(context, shape, coords); + context.clip(); + } + + // Redraw the shape shifted off the canvas massively so we can cast a shadow + // onto the canvas without having to worry about the stroke or fill (which + // cannot have 0 opacity or width, since they're what cast the shadow). + var x_shift = canvas.width * 100; + var y_shift = canvas.height * 100; + draw_shape(context, shape, coords, x_shift, y_shift); + + context.shadowOffsetX = options.shadowX - x_shift; + context.shadowOffsetY = options.shadowY - y_shift; + context.shadowBlur = options.shadowRadius; + context.shadowColor = css3color( + options.shadowColor, + options.shadowOpacity + ); + + // Now, work out where to cast the shadow from! It looks better if it's cast + // from a fill when it's an outside shadow or a stroke when it's an interior + // shadow. Allow the user to override this if they need to. + var shadowFrom = options.shadowFrom; + if (!shadowFrom) { + if (options.shadowPosition == "outside") { + shadowFrom = "fill"; + } else { + shadowFrom = "stroke"; + } + } + if (shadowFrom == "stroke") { + context.strokeStyle = "rgba(0,0,0,1)"; + context.stroke(); + } else if (shadowFrom == "fill") { + context.fillStyle = "rgba(0,0,0,1)"; + context.fill(); + } + context.restore(); + + // and now we clean up + if (options.shadowPosition == "outside") { + context.save(); + // Clear out the center + draw_shape(context, shape, coords); + context.globalCompositeOperation = "destination-out"; + context.fillStyle = "rgba(0,0,0,1);"; + context.fill(); + context.restore(); + } + } + + context.save(); + + draw_shape(context, shape, coords); + + // fill has to come after shadow, otherwise the shadow will be drawn over the fill, + // which mostly looks weird when the shadow has a high opacity + if (options.fill) { + context.fillStyle = css3color(options.fillColor, options.fillOpacity); + context.fill(); + } + // Likewise, stroke has to come at the very end, or it'll wind up under bits of the + // shadow or the shadow-background if it's present. + if (options.stroke) { + context.strokeStyle = css3color( + options.strokeColor, + options.strokeOpacity + ); + context.lineWidth = options.strokeWidth; + context.stroke(); + } + + context.restore(); + + if (options.fade) { + $(canvas) + .css("opacity", 0) + .animate({ opacity: 1 }, 100); + } + }; + clear_canvas = function(canvas) { + canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height); + }; + } else { + // ie executes this code + create_canvas_for = function(img) { + return $( + '' + ).get(0); + }; + add_shape_to = function(canvas, shape, coords, options, name) { + var fill, stroke, opacity, e; + for (var i in coords) { + coords[i] = parseInt(coords[i], 10); + } + fill = + ''; + stroke = options.stroke + ? 'strokeweight="' + + options.strokeWidth + + '" stroked="t" strokecolor="#' + + options.strokeColor + + '"' + : 'stroked="f"'; + opacity = ''; + if (shape == "rect") { + e = $( + '' + ); + } else if (shape == "poly") { + e = $( + '' + ); + } else if (shape == "circ") { + e = $( + '' + ); + } + e.get(0).innerHTML = fill + opacity; + $(canvas).append(e); + }; + clear_canvas = function(canvas) { + // jquery1.8 + ie7 + var $html = $("
    " + canvas.innerHTML + "
    "); + $html.children("[name=highlighted]").remove(); + canvas.innerHTML = $html.html(); + }; + } + + shape_from_area = function(area) { + var i, + coords = area.getAttribute("coords").split(","); + for (i = 0; i < coords.length; i++) { + coords[i] = parseFloat(coords[i]); + } + return [ + area + .getAttribute("shape") + .toLowerCase() + .substr(0, 4), + coords + ]; + }; + + options_from_area = function(area, options) { + var $area = $(area); + return $.extend( + {}, + options, + $.metadata ? $area.metadata() : false, + $area.data("maphilight") + ); + }; + + is_image_loaded = function(img) { + if (!img.complete) { + return false; + } // IE + if (typeof img.naturalWidth != "undefined" && img.naturalWidth === 0) { + return false; + } // Others + return true; + }; + + canvas_style = { + position: "absolute", + left: 0, + top: 0, + padding: 0, + border: 0 + }; + + var ie_hax_done = false; + $.fn.maphilight = function(opts) { + opts = $.extend({}, $.fn.maphilight.defaults, opts); + + if (!has_canvas && !ie_hax_done) { + $(window).ready(function() { + document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); + var style = document.createStyleSheet(); + var shapes = [ + "shape", + "rect", + "oval", + "circ", + "fill", + "stroke", + "imagedata", + "group", + "textbox" + ]; + $.each(shapes, function() { + style.addRule( + "v\\:" + this, + "behavior: url(#default#VML); antialias:true" + ); + }); + }); + ie_hax_done = true; + } + + return this.each(function() { + var img, + wrap, + options, + map, + canvas, + canvas_always, + highlighted_shape, + usemap; + img = $(this); + + if (!is_image_loaded(this)) { + // If the image isn't fully loaded, this won't work right. Try again later. + return window.setTimeout(function() { + img.maphilight(opts); + }, 200); + } + + options = $.extend( + {}, + opts, + $.metadata ? img.metadata() : false, + img.data("maphilight") + ); + + // jQuery bug with Opera, results in full-url#usemap being returned from jQuery's attr. + // So use raw getAttribute instead. + usemap = img.get(0).getAttribute("usemap"); + + if (!usemap) { + return; + } + + map = $('map[name="' + usemap.substr(1) + '"]'); + + if (!(img.is('img,input[type="image"]') && usemap && map.length > 0)) { + return; + } + + if (img.hasClass("maphilighted")) { + // We're redrawing an old map, probably to pick up changes to the options. + // Just clear out all the old stuff. + var wrapper = img.parent(); + img.insertBefore(wrapper); + wrapper.remove(); + $(map).unbind(".maphilight"); + } + + wrap = $("
    ").css({ + display: "block", + backgroundImage: 'url("' + this.src + '")', + backgroundSize: "contain", + position: "relative", + padding: 0, + width: this.width, + height: this.height + }); + if (options.wrapClass) { + if (options.wrapClass === true) { + wrap.addClass($(this).attr("class")); + } else { + wrap.addClass(options.wrapClass); + } + } + // Firefox has a bug that prevents tabbing into the image map if + // we set opacity of the image to 0, but very nearly 0 works! + img + .before(wrap) + .css("opacity", 0.0000000001) + .css(canvas_style) + .remove(); + if (has_VML) { + img.css("filter", "Alpha(opacity=0)"); + } + wrap.append(img); + + canvas = create_canvas_for(this); + $(canvas).css(canvas_style); + canvas.height = this.height; + canvas.width = this.width; + + $(map) + .bind("alwaysOn.maphilight", function() { + // Check for areas with alwaysOn set. These are added to a *second* canvas, + // which will get around flickering during fading. + if (canvas_always) { + clear_canvas(canvas_always); + } + if (!has_canvas) { + $(canvas).empty(); + } + $(map) + .find("area[coords]") + .each(function() { + var shape, area_options; + area_options = options_from_area(this, options); + if (area_options.alwaysOn) { + if (!canvas_always && has_canvas) { + canvas_always = create_canvas_for(img[0]); + $(canvas_always).css(canvas_style); + canvas_always.width = img[0].width; + canvas_always.height = img[0].height; + img.before(canvas_always); + } + area_options.fade = area_options.alwaysOnFade; // alwaysOn shouldn't fade in initially + shape = shape_from_area(this); + if (has_canvas) { + add_shape_to( + canvas_always, + shape[0], + shape[1], + area_options, + "" + ); + } else { + add_shape_to(canvas, shape[0], shape[1], area_options, ""); + } + } + }); + }) + .trigger("alwaysOn.maphilight") + .bind("mouseover.maphilight focusin.maphilight", function(e) { + var shape, + area_options, + area = e.target; + area_options = options_from_area(area, options); + if (!area_options.neverOn && !area_options.alwaysOn) { + shape = shape_from_area(area); + add_shape_to( + canvas, + shape[0], + shape[1], + area_options, + "highlighted" + ); + if (area_options.groupBy) { + var areas; + // two ways groupBy might work; attribute and selector + if (/^[a-zA-Z][\-a-zA-Z]+$/.test(area_options.groupBy)) { + areas = map.find( + "area[" + + area_options.groupBy + + '="' + + $(area).attr(area_options.groupBy) + + '"]' + ); + } else { + areas = map.find(area_options.groupBy); + } + var first = area; + areas.each(function() { + if (this != first) { + var subarea_options = options_from_area(this, options); + if (!subarea_options.neverOn && !subarea_options.alwaysOn) { + var shape = shape_from_area(this); + add_shape_to( + canvas, + shape[0], + shape[1], + subarea_options, + "highlighted" + ); + } + } + }); + } + // workaround for IE7, IE8 not rendering the final rectangle in a group + if (!has_canvas) { + $(canvas).append(""); + } + } + }) + .bind("mouseout.maphilight focusout.maphilight", function(e) { + clear_canvas(canvas); + }); + + img.before(canvas); // if we put this after, the mouseover events wouldn't fire. + + img.addClass("maphilighted"); + }); + }; + $.fn.maphilight.defaults = { + fill: true, + fillColor: "000000", + fillOpacity: 0.2, + stroke: true, + strokeColor: "ff0000", + strokeOpacity: 1, + strokeWidth: 1, + fade: true, + alwaysOn: false, + neverOn: false, + groupBy: false, + wrapClass: true, + // plenty of shadow: + shadow: false, + shadowX: 0, + shadowY: 0, + shadowRadius: 6, + shadowColor: "000000", + shadowOpacity: 0.8, + shadowPosition: "outside", + shadowFrom: false + }; +}); diff --git a/pandora_console/include/javascript/timezonepicker/lib/jquery.timezone-picker.js b/pandora_console/include/javascript/timezonepicker/lib/jquery.timezone-picker.js new file mode 100644 index 0000000000..f2134f47a0 --- /dev/null +++ b/pandora_console/include/javascript/timezonepicker/lib/jquery.timezone-picker.js @@ -0,0 +1,382 @@ +(function($) { + // We only support a single instance per call, so these variables are available + // for all subsequent calls. + var methods = {}; + var opts = {}; + var selectedTimezone = null; + var imgElement = null; + var mapElement = null; + var $pin = null; + + // Gets called upon timezone change. + // The expected method signature is changeHandler(timezoneName, countryName, offset). + var changeHandler = null; + + methods.init = function(initOpts) { + var $origCall = this; + + // Set the instance options. + opts = $.extend({}, $.fn.timezonePicker.defaults, initOpts); + selectedTimezone = opts.timezone; + + changeHandler = opts.changeHandler; + + return $origCall.each(function(index, item) { + imgElement = item; + mapElement = document.getElementsByName( + imgElement.useMap.replace(/^#/, "") + )[0]; + + // Wrap the img tag in a relatively positioned DIV for the pin. + $(imgElement) + .wrap('
    ') + .parent() + .css({ + position: "relative", + width: $(imgElement).width() + "px" + }); + + // Add the pin. + if (opts.pinUrl) { + $pin = $('') + .appendTo(imgElement.parentNode) + .css("display", "none"); + } else if (opts.pin) { + $pin = $(imgElement) + .parent() + .parent() + .find(opts.pin) + .appendTo(imgElement.parentNode) + .css("display", "none"); + } + + // Main event handler when a timezone is clicked. + $(mapElement) + .find("area") + .click(function() { + var areaElement = this; + // Enable the pin adjustment. + if ($pin) { + $pin.css("display", "block"); + var pinCoords = $(areaElement) + .attr("data-pin") + .split(","); + var pinWidth = parseInt($pin.width() / 2); + var pinHeight = $pin.height(); + + $pin.css({ + position: "absolute", + left: pinCoords[0] - pinWidth + "px", + top: pinCoords[1] - pinHeight + "px" + }); + } + + var timezoneName = $(areaElement).attr("data-timezone"); + var countryName = $(areaElement).attr("data-country"); + var offset = $(areaElement).attr("data-offset"); + + // Call the change handler + if (typeof changeHandler === "function") { + changeHandler(timezoneName, countryName, offset); + } + + // Update the target select list. + if (opts.target) { + if (timezoneName) $(opts.target).val(timezoneName); + } + if (opts.countryTarget) { + if (countryName) $(opts.countryTarget).val(countryName); + } + + return false; + }); + + // Adjust the timezone if the target changes. + if (opts.target) { + $(opts.target).bind("change", function() { + $origCall.timezonePicker("updateTimezone", $(this).val()); + }); + } + + // Adjust the timezone if the countryTarget changes. + if (opts.countryTarget && opts.countryGuess) { + $(opts.countryTarget).bind("change", function() { + var countryCode = $(this).val(); + if (opts.countryGuesses[countryCode]) { + $(mapElement) + .find( + 'area[data-timezone="' + opts.countryGuesses[countryCode] + '"]' + ) + .click(); + } else { + $(mapElement) + .find("area[data-country=" + countryCode + "]:first") + .click(); + } + }); + } + + // This is very expensive, so only run if enabled. + if (opts.responsive) { + var resizeTimeout = null; + $(window).resize(function() { + if (resizeTimeout) { + clearTimeout(resizeTimeout); + } + resizeTimeout = setTimeout(function() { + $origCall.timezonePicker("resize"); + }, 200); + }); + } + + // Give the page a slight time to load before selecting the default + // timezone on the map. + setTimeout(function() { + if ( + opts.responsive && + parseInt(imgElement.width) !== + parseInt(imgElement.getAttribute("width")) + ) { + $origCall.timezonePicker("resize"); + } else if (opts.maphilight && $.fn.maphilight) { + $(imgElement).maphilight(opts); + } + if (opts.target) { + $(opts.target).triggerHandler("change"); + } + }, 500); + }); + }; + + /** + * Update the currently selected timezone and update the pin location. + */ + methods.updateTimezone = function(newTimezone) { + selectedTimezone = newTimezone; + $pin.css("display", "none"); + $(mapElement) + .find("area") + .each(function(m, areaElement) { + if (areaElement.getAttribute("data-timezone") === selectedTimezone) { + $(areaElement).triggerHandler("click"); + return false; + } + }); + + return this; + }; + + /** + * Use browser geolocation to update the currently selected timezone and update the pin location. + */ + methods.detectLocation = function(detectOpts) { + var detectDefaults = { + success: undefined, + error: undefined, + complete: undefined + }; + detectOpts = $.extend(detectDefaults, detectOpts); + + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(showPosition, handleErrors); + } + + function showPosition(position) { + var $imgElement = $(imgElement); + var imageXY = convertXY( + position.coords.latitude, + position.coords.longitude, + $imgElement.width(), + $imgElement.height() + ); + + $(mapElement) + .find("area") + .each(function(m, areaElement) { + var coords = areaElement.getAttribute("coords").split(","); + var shape = areaElement.getAttribute("shape"); + var poly = []; + for (var n = 0; n < coords.length / 2; n++) { + poly[n] = [coords[n * 2], coords[n * 2 + 1]]; + } + + if ( + (shape === "poly" && isPointInPoly(poly, imageXY[0], imageXY[1])) || + (shape === "rect" && isPointInRect(coords, imageXY[0], imageXY[1])) + ) { + $(areaElement).triggerHandler("click", detectOpts["success"]); + return false; + } + }); + if (detectOpts["complete"]) { + detectOpts["complete"](position); + } + } + + function handleErrors(error) { + if (detectOpts["error"]) { + detectOpts["error"](error); + } + if (detectOpts["complete"]) { + detectOpts["complete"](error); + } + } + + // Converts lat and long into X,Y coodinates on a Equirectangular map. + function convertXY(latitude, longitude, map_width, map_height) { + var x = Math.round((longitude + 180) * (map_width / 360)); + var y = Math.round((latitude * -1 + 90) * (map_height / 180)); + return [x, y]; + } + + // Do a dual-check here to ensure accuracy. Ray-tracing algorithm gives us the + // basic idea of if we're in a polygon, but may be inaccurate if the ray goes + // through a single point exactly at its vertex. We double check positives + // against a bounding box, ensuring the item is actually in that area. + function isPointInPoly(poly, x, y) { + var inside = false; + var bbox = [1000000, 1000000, -1000000, -1000000]; + for (var i = 0, j = poly.length - 1; i < poly.length; j = i++) { + var xi = poly[i][0], + yi = poly[i][1]; + var xj = poly[j][0], + yj = poly[j][1]; + bbox[0] = Math.min(bbox[0], xi); + bbox[1] = Math.min(bbox[1], yi); + bbox[2] = Math.max(bbox[2], xi); + bbox[3] = Math.max(bbox[3], yi); + + var intersect = + yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; + if (intersect) inside = !inside; + } + + return inside && isPointInRect(bbox, x, y); + } + + // Simple check if a point is in between two X/Y coordinates. Input may be + // any two points, with a box made between them. + function isPointInRect(rect, x, y) { + // Adjust so we're always going top-left to lower-right. + rect = [ + Math.min(rect[0], rect[2]), + Math.min(rect[1], rect[3]), + Math.max(rect[0], rect[2]), + Math.max(rect[1], rect[3]) + ]; + return x >= rect[0] && x <= rect[2] && y >= rect[1] && y <= rect[2]; + } + + return this; + }; + + /** + * Experimental method to rewrite the imagemap based on new image dimensions. + * + * This does not resize the image itself, it recalculates the imagemap to match + * the current dimensions of the image. + */ + methods.resize = function() { + $(mapElement) + .find("area") + .each(function(m, areaElement) { + // Save the original coordinates for further resizing. + if (!areaElement.originalCoords) { + areaElement.originalCoords = { + timezone: areaElement.getAttribute("data-timezone"), + country: areaElement.getAttribute("data-country"), + coords: areaElement.getAttribute("coords"), + pin: areaElement.getAttribute("data-pin") + }; + } + var rescale = imgElement.width / imgElement.getAttribute("width"); + + // Adjust the image size. + $(imgElement) + .parent() + .css({ + width: $(imgElement).width() + "px" + }); + + // Adjust the coords attribute. + var originalCoords = areaElement.originalCoords.coords.split(","); + var newCoords = new Array(); + for (var j = 0; j < originalCoords.length; j++) { + newCoords[j] = Math.round(parseInt(originalCoords[j]) * rescale); + } + areaElement.setAttribute("coords", newCoords.join(",")); + + // Adjust the pin coordinates. + var pinCoords = areaElement.originalCoords.pin.split(","); + pinCoords[0] = Math.round(parseInt(pinCoords[0]) * rescale); + pinCoords[1] = Math.round(parseInt(pinCoords[1]) * rescale); + areaElement.setAttribute("data-pin", pinCoords.join(",")); + + // Fire the change handler on the target. + if (opts.target) { + $(opts.target).triggerHandler("change"); + } + }); + + return this; + }; + + $.fn.timezonePicker = function(method) { + // Method calling logic. + if (methods[method]) { + return methods[method].apply( + this, + Array.prototype.slice.call(arguments, 1) + ); + } else if (typeof method === "object" || !method) { + return methods.init.apply(this, arguments); + } else { + $.error("Method " + method + " does not exist on jQuery.timezonePicker"); + } + }; + + $.fn.timezonePicker.defaults = { + // Selector for the pin that should be used. This selector only works in the + // immediate parent of the image map img tag. + pin: ".timezone-pin", + // Specify a URL for the pin image instead of using a DOM element. + pinUrl: null, + // Preselect a particular timezone. + timezone: null, + // Pass through options to the jQuery maphilight plugin. + maphilight: true, + // Selector for the select list, textfield, or hidden to update upon click. + target: null, + // Selector for the select list, textfield, or hidden to update upon click + // with the specified country. + countryTarget: null, + // If changing the country should use the first timezone within that country. + countryGuess: true, + // A list of country guess exceptions. These should only be needed if a + // country spans multiple timezones. + countryGuesses: { + AU: "Australia/Sydney", + BR: "America/Sao_Paulo", + CA: "America/Toronto", + CN: "Asia/Shanghai", + ES: "Europe/Madrid", + MX: "America/Mexico_City", + RU: "Europe/Moscow", + US: "America/New_York" + }, + // If this map should automatically adjust its size if scaled. Note that + // this can be very expensive computationally and will likely have a delay + // on resize. The maphilight library also is incompatible with this setting + // and will be disabled. + responsive: false, + + // Default options passed along to the maphilight plugin. + fade: false, + stroke: true, + strokeColor: "FFFFFF", + strokeOpacity: 0.4, + fillColor: "FFFFFF", + fillOpacity: 0.4, + groupBy: "data-offset" + }; +})(jQuery); diff --git a/pandora_console/include/javascript/timezonepicker/lib/jquery.timezone-picker.min.js b/pandora_console/include/javascript/timezonepicker/lib/jquery.timezone-picker.min.js new file mode 100644 index 0000000000..fd4e7efc5c --- /dev/null +++ b/pandora_console/include/javascript/timezonepicker/lib/jquery.timezone-picker.min.js @@ -0,0 +1 @@ +(function($){var methods={};var opts={};var selectedTimzone=null;var imgElement=null;var mapElement=null;var $pin=null;var changeHandler=null;methods.init=function(initOpts){var $origCall=this;opts=$.extend({},$.fn.timezonePicker.defaults,initOpts);selectedTimzone=opts.timezone;changeHandler=opts.changeHandler;return $origCall.each(function(index,item){imgElement=item;mapElement=document.getElementsByName(imgElement.useMap.replace(/^#/,""))[0];$(imgElement).wrap('
    ').parent().css({position:"relative",width:$(imgElement).width()+"px"});if(opts.pinUrl){$pin=$('').appendTo(imgElement.parentNode).css("display","none")}else if(opts.pin){$pin=$(imgElement).parent().parent().find(opts.pin).appendTo(imgElement.parentNode).css("display","none")}$(mapElement).find("area").click(function(){var areaElement=this;if($pin){$pin.css("display","block");var pinCoords=$(areaElement).attr("data-pin").split(",");var pinWidth=parseInt($pin.width()/2);var pinHeight=$pin.height();$pin.css({position:"absolute",left:pinCoords[0]-pinWidth+"px",top:pinCoords[1]-pinHeight+"px"})}var timezoneName=$(areaElement).attr("data-timezone");var countryName=$(areaElement).attr("data-country");var offset=$(areaElement).attr("data-offset");if(typeof changeHandler==="function"){changeHandler(timezoneName,countryName,offset)}if(opts.target){if(timezoneName)$(opts.target).val(timezoneName)}if(opts.countryTarget){if(countryName)$(opts.countryTarget).val(countryName)}return false});if(opts.target){$(opts.target).bind("change",function(){$origCall.timezonePicker("updateTimezone",$(this).val())})}if(opts.countryTarget&&opts.countryGuess){$(opts.countryTarget).bind("change",function(){var countryCode=$(this).val();if(opts.countryGuesses[countryCode]){$(mapElement).find('area[data-timezone="'+opts.countryGuesses[countryCode]+'"]').click()}else{$(mapElement).find("area[data-country="+countryCode+"]:first").click()}})}if(opts.responsive){var resizeTimeout=null;$(window).resize(function(){if(resizeTimeout){clearTimeout(resizeTimeout)}resizeTimeout=setTimeout(function(){$origCall.timezonePicker("resize")},200)})}setTimeout(function(){if(opts.responsive&&parseInt(imgElement.width)!==parseInt(imgElement.getAttribute("width"))){$origCall.timezonePicker("resize")}else if(opts.maphilight&&$.fn.maphilight){$(imgElement).maphilight(opts)}if(opts.target){$(opts.target).triggerHandler("change")}},500)})};methods.updateTimezone=function(newTimezone){selectedTimzone=newTimezone;$pin.css("display","none");$(mapElement).find("area").each(function(m,areaElement){if(areaElement.getAttribute("data-timezone")===selectedTimzone){$(areaElement).triggerHandler("click");return false}});return this};methods.detectLocation=function(detectOpts){var detectDefaults={success:undefined,error:undefined,complete:undefined};detectOpts=$.extend(detectDefaults,detectOpts);if(navigator.geolocation){navigator.geolocation.getCurrentPosition(showPosition,handleErrors)}function showPosition(position){var $imgElement=$(imgElement);var imageXY=convertXY(position.coords.latitude,position.coords.longitude,$imgElement.width(),$imgElement.height());$(mapElement).find("area").each(function(m,areaElement){var coords=areaElement.getAttribute("coords").split(",");var shape=areaElement.getAttribute("shape");var poly=[];for(var n=0;ny!=yj>y&&x<(xj-xi)*(y-yi)/(yj-yi)+xi;if(intersect)inside=!inside}return inside&&isPointInRect(bbox,x,y)}function isPointInRect(rect,x,y){rect=[Math.min(rect[0],rect[2]),Math.min(rect[1],rect[3]),Math.max(rect[0],rect[2]),Math.max(rect[1],rect[3])];return x>=rect[0]&&x<=rect[2]&&y>=rect[1]&&y<=rect[2]}return this};methods.resize=function(){$(mapElement).find("area").each(function(m,areaElement){if(!areaElement.originalCoords){areaElement.originalCoords={timezone:areaElement.getAttribute("data-timezone"),country:areaElement.getAttribute("data-country"),coords:areaElement.getAttribute("coords"),pin:areaElement.getAttribute("data-pin")}}var rescale=imgElement.width/imgElement.getAttribute("width");$(imgElement).parent().css({width:$(imgElement).width()+"px"});var originalCoords=areaElement.originalCoords.coords.split(",");var newCoords=new Array;for(var j=0;j ' ); - if (typeof element.serviceDetail != "undefined") { + if ( + typeof element.serviceDetail != "undefined" && + element.name != null + ) { $serviceDetailImage .click(function(e) { e.preventDefault(); @@ -634,10 +637,11 @@ var TreeController = { .css("cursor", "pointer"); $content.append($serviceDetailImage); + $content.append(" " + element.name); + } else { + $content.remove($node); } - $content.append(" " + element.name); - break; case "modules": if ( @@ -747,9 +751,11 @@ var TreeController = { } } else { try { - winopeng( + winopeng_var( element.moduleGraph.url, - element.moduleGraph.handle + element.moduleGraph.handle, + 1000, + 650 ); } catch (error) { // console.log(error); diff --git a/pandora_console/include/javascript/update_manager.js b/pandora_console/include/javascript/update_manager.js index a091b9c504..bab4ef2aae 100644 --- a/pandora_console/include/javascript/update_manager.js +++ b/pandora_console/include/javascript/update_manager.js @@ -1,3 +1,7 @@ +/* + globals $, jQuery +*/ + var correct_install_progress = true; function form_upload(homeurl) { @@ -322,20 +326,20 @@ function install_package(package, homeurl) { }); var dialog_accept_package_mr_fail_text = - "
    "; + "
    "; dialog_accept_package_mr_fail_text = dialog_accept_package_mr_fail_text + - "
    "; + "

    INFO

    "; dialog_accept_package_mr_fail_text = dialog_accept_package_mr_fail_text + - "

    INFO

    "; - dialog_accept_package_mr_fail_text = - dialog_accept_package_mr_fail_text + - "

    " + + "

    " + mr_not_accepted_code_yes + "

    "; + dialog_accept_package_mr_fail_text = + dialog_accept_package_mr_fail_text + + "
    "; dialog_accept_package_mr_fail_text = dialog_accept_package_mr_fail_text + "
    "; @@ -393,20 +397,21 @@ function install_package(package, homeurl) { ] }); - var dialog_success_pkg_text = "
    "; + var dialog_success_pkg_text = + "
    "; dialog_success_pkg_text = dialog_success_pkg_text + - "
    "; + "

    SUCCESS

    "; dialog_success_pkg_text = dialog_success_pkg_text + - "

    SUCCESS

    "; - dialog_success_pkg_text = - dialog_success_pkg_text + - "

    " + + "

    " + package_success + "

    "; + dialog_success_pkg_text = + dialog_success_pkg_text + + "
    "; dialog_success_pkg_text = dialog_success_pkg_text + "
    "; @@ -452,20 +457,21 @@ function install_package(package, homeurl) { ] }); - var dialog_error_pkg_text = "
    "; + var dialog_error_pkg_text = + "
    "; dialog_error_pkg_text = dialog_error_pkg_text + - "
    "; + "

    ERROR

    "; dialog_error_pkg_text = dialog_error_pkg_text + - "

    ERROR

    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    " + + "

    " + package_error + "

    "; + dialog_error_pkg_text = + dialog_error_pkg_text + + "
    "; dialog_error_pkg_text = dialog_error_pkg_text + "
    "; @@ -529,20 +535,21 @@ function install_package(package, homeurl) { ] }); - var dialog_cancel_pkg_text = "
    "; + var dialog_cancel_pkg_text = + "
    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + - "
    "; + "

    INFO

    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + - "

    INFO

    "; - dialog_cancel_pkg_text = - dialog_cancel_pkg_text + - "

    " + + "

    " + package_cancel + "

    "; + dialog_cancel_pkg_text = + dialog_cancel_pkg_text + + "
    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + "
    "; @@ -567,20 +574,21 @@ function install_package(package, homeurl) { ] }); - var dialog_bad_message_text = "
    "; + var dialog_bad_message_text = + "
    "; dialog_bad_message_text = dialog_bad_message_text + - "
    "; + "

    ERROR

    "; dialog_bad_message_text = dialog_bad_message_text + - "

    ERROR

    "; - dialog_bad_message_text = - dialog_bad_message_text + - "

    " + + "

    " + bad_mr_file + "

    "; + dialog_bad_message_text = + dialog_bad_message_text + + "
    "; dialog_bad_message_text = dialog_bad_message_text + "
    "; @@ -612,20 +620,21 @@ function install_package(package, homeurl) { ] }); - var dialog_success_mr_text = "
    "; + var dialog_success_mr_text = + "
    "; dialog_success_mr_text = dialog_success_mr_text + - "
    "; + "

    SUCCESS

    "; dialog_success_mr_text = dialog_success_mr_text + - "

    SUCCESS

    "; - dialog_success_mr_text = - dialog_success_mr_text + - "

    " + + "

    " + mr_success + "

    "; + dialog_success_mr_text = + dialog_success_mr_text + + "
    "; dialog_success_mr_text = dialog_success_mr_text + "
    "; @@ -680,20 +689,21 @@ function install_package(package, homeurl) { ] }); - var dialog_success_pkg_text = "
    "; + var dialog_success_pkg_text = + "
    "; dialog_success_pkg_text = dialog_success_pkg_text + - "
    "; + "

    SUCCESS

    "; dialog_success_pkg_text = dialog_success_pkg_text + - "

    SUCCESS

    "; - dialog_success_pkg_text = - dialog_success_pkg_text + - "

    " + + "

    " + package_success + "

    "; + dialog_success_pkg_text = + dialog_success_pkg_text + + "
    "; dialog_success_pkg_text = dialog_success_pkg_text + "
    "; @@ -739,20 +749,21 @@ function install_package(package, homeurl) { ] }); - var dialog_error_pkg_text = "
    "; + var dialog_error_pkg_text = + "
    "; dialog_error_pkg_text = dialog_error_pkg_text + - "
    "; + "

    ERROR

    "; dialog_error_pkg_text = dialog_error_pkg_text + - "

    ERROR

    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    " + + "

    " + package_error + "

    "; + dialog_error_pkg_text = + dialog_error_pkg_text + + "
    "; dialog_error_pkg_text = dialog_error_pkg_text + "
    "; @@ -810,20 +821,21 @@ function install_package(package, homeurl) { ] }); - var dialog_error_mr_text = "
    "; + var dialog_error_mr_text = + "
    "; dialog_error_mr_text = dialog_error_mr_text + - "
    "; + "

    ERROR

    "; dialog_error_mr_text = dialog_error_mr_text + - "

    ERROR

    "; - dialog_error_mr_text = - dialog_error_mr_text + - "

    " + + "

    " + mr_error + "

    "; + dialog_error_mr_text = + dialog_error_mr_text + + "
    "; dialog_error_mr_text = dialog_error_mr_text + "
    "; @@ -870,20 +882,21 @@ function install_package(package, homeurl) { ] }); - var dialog_cancel_mr_text = "
    "; + var dialog_cancel_mr_text = + "
    "; dialog_cancel_mr_text = dialog_cancel_mr_text + - "
    "; + "

    INFO

    "; dialog_cancel_mr_text = dialog_cancel_mr_text + - "

    INFO

    "; - dialog_cancel_mr_text = - dialog_cancel_mr_text + - "

    " + + "

    " + mr_cancel + "

    "; + dialog_cancel_mr_text = + dialog_cancel_mr_text + + "
    "; dialog_cancel_mr_text = dialog_cancel_mr_text + "
    "; @@ -906,34 +919,33 @@ function install_package(package, homeurl) { ] }); - $("button:contains(Apply MR)").attr("id", "apply_rr_button"); + $("button:contains(Apply MR)") + .attr("id", "apply_rr_button") + .addClass("success_button"); $("button:contains(Cancel)").attr("id", "cancel_rr_button"); - var dialog_have_mr_text = "
    "; + var dialog_have_mr_text = "
    "; dialog_have_mr_text = dialog_have_mr_text + - "
    "; - dialog_have_mr_text = - dialog_have_mr_text + - "

    " + + "

    " + mr_available_header + - "

    "; + "

    "; + dialog_have_mr_text = + dialog_have_mr_text + "

    " + text1_mr_file + "

    "; dialog_have_mr_text = dialog_have_mr_text + - "

    " + - text1_mr_file + - "

    "; - dialog_have_mr_text = - dialog_have_mr_text + - "

    " + + "

    " + text2_mr_file + - "" + + '' + text3_mr_file + "" + text4_mr_file + "

    "; + dialog_have_mr_text = + dialog_have_mr_text + + "
    "; dialog_have_mr_text = dialog_have_mr_text + "
    "; $("#mr_dialog2").html(dialog_have_mr_text); @@ -988,20 +1000,21 @@ function install_package(package, homeurl) { ] }); - var dialog_success_pkg_text = "
    "; + var dialog_success_pkg_text = + "
    "; dialog_success_pkg_text = dialog_success_pkg_text + - "
    "; + "

    SUCCESS

    "; dialog_success_pkg_text = dialog_success_pkg_text + - "

    SUCCESS

    "; - dialog_success_pkg_text = - dialog_success_pkg_text + - "

    " + + "

    " + package_success + "

    "; + dialog_success_pkg_text = + dialog_success_pkg_text + + "
    "; dialog_success_pkg_text = dialog_success_pkg_text + "
    "; @@ -1043,20 +1056,20 @@ function install_package(package, homeurl) { ] }); - var dialog_error_pkg_text = "
    "; + var dialog_error_pkg_text = "
    "; dialog_error_pkg_text = dialog_error_pkg_text + - "
    "; + "

    ERROR

    "; dialog_error_pkg_text = dialog_error_pkg_text + - "

    ERROR

    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    " + + "

    " + package_error + "

    "; + dialog_error_pkg_text = + dialog_error_pkg_text + + "
    "; dialog_error_pkg_text = dialog_error_pkg_text + "
    "; $("#error_pkg").html(dialog_error_pkg_text); @@ -1120,20 +1133,16 @@ function install_package(package, homeurl) { ] }); - var dialog_cancel_pkg_text = "
    "; + var dialog_cancel_pkg_text = "
    "; + dialog_cancel_pkg_text = + dialog_cancel_pkg_text + "

    INFO

    "; + dialog_cancel_pkg_text = + dialog_cancel_pkg_text + "

    " + package_cancel + "

    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + - "
    "; - dialog_cancel_pkg_text = - dialog_cancel_pkg_text + - "

    INFO

    "; - dialog_cancel_pkg_text = - dialog_cancel_pkg_text + - "

    " + - package_cancel + - "

    "; + "images/icon_info_mr.png'>
    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + "
    "; $("#cancel_pkg").html(dialog_cancel_pkg_text); @@ -1210,22 +1219,18 @@ function install_package(package, homeurl) { ] }); - var dialog_text = "
    "; + var dialog_text = "
    "; dialog_text = dialog_text + - "
    "; - dialog_text = - dialog_text + - "

    " + + "

    " + text1_package_file + - "

    "; + "

    "; + dialog_text = dialog_text + "

    " + text2_package_file + "

    "; dialog_text = dialog_text + - "

    " + - text2_package_file + - "

    "; + "
    "; dialog_text = dialog_text + "
    "; $("#pkg_apply_dialog").html(dialog_text); @@ -1246,11 +1251,11 @@ function check_install_package(package, homeurl) { data: parameters, dataType: "json", success: function(data) { - // Print the updated files and take the scroll to the bottom - $("#log_zone").html(data.info); + // Print the updated files and take the scroll to the bottom. + $("#log_zone").append(data.info); $("#log_zone").scrollTop($("#log_zone").prop("scrollHeight")); - // Change the progress bar + // Change the progress bar. if ( $("#form-offline_update ul") .find("li") @@ -1271,14 +1276,19 @@ function check_install_package(package, homeurl) { .trigger("change"); } - // The class loading is present until the update ends + // The class loading is present until the update ends. var isInstalling = $("#form-offline_update ul") .find("li") .hasClass("loading"); if (data.progress < 100 && isInstalling) { - // Recursive call to check the update status + // Recursive call to check the update status. check_install_package(package, homeurl); } + + if (!isInstalling) { + //Check if exist remove files. + delete_desired_files(homeurl); + } } }); } @@ -1513,20 +1523,20 @@ function install_free_package_prev_step(package, version, homeurl) { }); var dialog_accept_package_mr_fail_text = - "
    "; + "
    "; dialog_accept_package_mr_fail_text = dialog_accept_package_mr_fail_text + - "
    "; + "

    INFO

    "; dialog_accept_package_mr_fail_text = dialog_accept_package_mr_fail_text + - "

    INFO

    "; - dialog_accept_package_mr_fail_text = - dialog_accept_package_mr_fail_text + - "

    " + + "

    " + mr_not_accepted_code_yes + "

    "; + dialog_accept_package_mr_fail_text = + dialog_accept_package_mr_fail_text + + "
    "; dialog_accept_package_mr_fail_text = dialog_accept_package_mr_fail_text + "
    "; @@ -1607,20 +1617,21 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - var dialog_cancel_pkg_text = "
    "; + var dialog_cancel_pkg_text = + "
    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + - "
    "; + "

    INFO

    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + - "

    INFO

    "; - dialog_cancel_pkg_text = - dialog_cancel_pkg_text + - "

    " + + "

    " + package_cancel + "

    "; + dialog_cancel_pkg_text = + dialog_cancel_pkg_text + + "
    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + "
    "; @@ -1637,20 +1648,21 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - var dialog_bad_message_text = "
    "; + var dialog_bad_message_text = + "
    "; dialog_bad_message_text = dialog_bad_message_text + - "
    "; + "

    ERROR

    "; dialog_bad_message_text = dialog_bad_message_text + - "

    ERROR

    "; - dialog_bad_message_text = - dialog_bad_message_text + - "

    " + + "

    " + bad_mr_file + "

    "; + dialog_bad_message_text = + dialog_bad_message_text + + "
    "; dialog_bad_message_text = dialog_bad_message_text + "
    "; @@ -1682,20 +1694,21 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - var dialog_success_mr_text = "
    "; + var dialog_success_mr_text = + "
    "; dialog_success_mr_text = dialog_success_mr_text + - "
    "; + "

    SUCCESS

    "; dialog_success_mr_text = dialog_success_mr_text + - "

    SUCCESS

    "; - dialog_success_mr_text = - dialog_success_mr_text + - "

    " + + "

    " + mr_success + "

    "; + dialog_success_mr_text = + dialog_success_mr_text + + "
    "; dialog_success_mr_text = dialog_success_mr_text + "
    "; @@ -1765,20 +1778,21 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - var dialog_error_mr_text = "
    "; + var dialog_error_mr_text = + "
    "; dialog_error_mr_text = dialog_error_mr_text + - "
    "; + "

    ERROR

    "; dialog_error_mr_text = dialog_error_mr_text + - "

    ERROR

    "; - dialog_error_mr_text = - dialog_error_mr_text + - "

    " + + "

    " + mr_error + "

    "; + dialog_error_mr_text = + dialog_error_mr_text + + "
    "; dialog_error_mr_text = dialog_error_mr_text + "
    "; @@ -1818,20 +1832,21 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - var dialog_cancel_mr_text = "
    "; + var dialog_cancel_mr_text = + "
    "; dialog_cancel_mr_text = dialog_cancel_mr_text + - "
    "; + "

    INFO

    "; dialog_cancel_mr_text = dialog_cancel_mr_text + - "

    INFO

    "; - dialog_cancel_mr_text = - dialog_cancel_mr_text + - "

    " + + "

    " + mr_cancel + "

    "; + dialog_cancel_mr_text = + dialog_cancel_mr_text + + "
    "; dialog_cancel_mr_text = dialog_cancel_mr_text + "
    "; @@ -1846,34 +1861,33 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - $("button:contains(Apply MR)").attr("id", "apply_rr_button"); + $("button:contains(Apply MR)") + .attr("id", "apply_rr_button") + .addClass("success_button"); $("button:contains(Cancel)").attr("id", "cancel_rr_button"); - var dialog_have_mr_text = "
    "; + var dialog_have_mr_text = "
    "; dialog_have_mr_text = dialog_have_mr_text + - "
    "; - dialog_have_mr_text = - dialog_have_mr_text + - "

    " + + "

    " + mr_available_header + - "

    "; + "

    "; + dialog_have_mr_text = + dialog_have_mr_text + "

    " + text1_mr_file + "

    "; dialog_have_mr_text = dialog_have_mr_text + - "

    " + - text1_mr_file + - "

    "; - dialog_have_mr_text = - dialog_have_mr_text + - "

    " + + "

    " + text2_mr_file + - "" + + '' + text3_mr_file + "" + text4_mr_file + "

    "; + dialog_have_mr_text = + dialog_have_mr_text + + "
    "; dialog_have_mr_text = dialog_have_mr_text + "
    "; $("#mr_dialog2").html(dialog_have_mr_text); @@ -1941,20 +1955,16 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - var dialog_cancel_pkg_text = "
    "; + var dialog_cancel_pkg_text = "
    "; + dialog_cancel_pkg_text = + dialog_cancel_pkg_text + "

    INFO

    "; + dialog_cancel_pkg_text = + dialog_cancel_pkg_text + "

    " + package_cancel + "

    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + - "
    "; - dialog_cancel_pkg_text = - dialog_cancel_pkg_text + - "

    INFO

    "; - dialog_cancel_pkg_text = - dialog_cancel_pkg_text + - "

    " + - package_cancel + - "

    "; + "images/icon_info_mr.png'>
    "; dialog_cancel_pkg_text = dialog_cancel_pkg_text + "
    "; $("#cancel_pkg").html(dialog_cancel_pkg_text); @@ -1968,22 +1978,18 @@ function install_free_package_prev_step(package, version, homeurl) { ] }); - var dialog_text = "
    "; + var dialog_text = "
    "; dialog_text = dialog_text + - "
    "; - dialog_text = - dialog_text + - "

    " + + "

    " + text1_package_file + - "

    "; + "

    "; + dialog_text = dialog_text + "

    " + text2_package_file + "

    "; dialog_text = dialog_text + - "

    " + - text2_package_file + - "

    "; + "
    "; dialog_text = dialog_text + "
    "; $("#pkg_apply_dialog").html(dialog_text); @@ -2030,20 +2036,16 @@ function install_free_package(package, version, homeurl) { ] }); - var dialog_error_pkg_text = "
    "; + var dialog_error_pkg_text = "
    "; + dialog_error_pkg_text = + dialog_error_pkg_text + "

    ERROR

    "; + dialog_error_pkg_text = + dialog_error_pkg_text + "

    " + data["message"] + "

    "; dialog_error_pkg_text = dialog_error_pkg_text + - "
    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    ERROR

    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    " + - data["message"] + - "

    "; + "images/icon_error_mr.png'>
    "; dialog_error_pkg_text = dialog_error_pkg_text + "
    "; $("#error_pkg").html(dialog_error_pkg_text); @@ -2083,20 +2085,17 @@ function install_free_package(package, version, homeurl) { ] }); - var dialog_success_pkg_text = "
    "; + var dialog_success_pkg_text = "
    "; dialog_success_pkg_text = dialog_success_pkg_text + - "

    SUCCESS

    "; + dialog_success_pkg_text = + dialog_success_pkg_text + "

    " + data["message"] + "

    "; + dialog_success_pkg_text = + dialog_success_pkg_text + + "
    "; - dialog_success_pkg_text = - dialog_success_pkg_text + - "

    SUCCESS

    "; - dialog_success_pkg_text = - dialog_success_pkg_text + - "

    " + - data["message"] + - "

    "; + "images/icon_success_mr.png'>
    "; dialog_success_pkg_text = dialog_success_pkg_text + "
    "; $("#success_pkg").html(dialog_success_pkg_text); @@ -2133,20 +2132,16 @@ function install_free_package(package, version, homeurl) { ] }); - var dialog_error_pkg_text = "
    "; + var dialog_error_pkg_text = "
    "; + dialog_error_pkg_text = + dialog_error_pkg_text + "

    ERROR

    "; + dialog_error_pkg_text = + dialog_error_pkg_text + "

    " + data["message"] + "

    "; dialog_error_pkg_text = dialog_error_pkg_text + - "
    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    ERROR

    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    " + - data["message"] + - "

    "; + "images/icon_error_mr.png'>
    "; dialog_error_pkg_text = dialog_error_pkg_text + "
    "; $("#error_pkg").html(dialog_error_pkg_text); @@ -2184,20 +2179,16 @@ function install_free_package(package, version, homeurl) { ] }); - var dialog_error_pkg_text = "
    "; + var dialog_error_pkg_text = "
    "; + dialog_error_pkg_text = + dialog_error_pkg_text + "

    ERROR

    "; + dialog_error_pkg_text = + dialog_error_pkg_text + "

    " + data["message"] + "

    "; dialog_error_pkg_text = dialog_error_pkg_text + - "
    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    ERROR

    "; - dialog_error_pkg_text = - dialog_error_pkg_text + - "

    " + - data["message"] + - "

    "; + "images/icon_error_mr.png'>
    "; dialog_error_pkg_text = dialog_error_pkg_text + "
    "; $("#error_pkg").html(dialog_error_pkg_text); @@ -2237,13 +2228,7 @@ function apply_minor_release(n_mr, pkg, ent, off, homeurl) { $("#mr_dialog2").html(data); error["error"] = true; } else { - $("#mr_dialog2").append( - "

    - " + - applying_mr + - " #" + - mr + - "

    " - ); + $("#mr_dialog2").append("

    - " + applying_mr + " #" + mr + "

    "); } } }); @@ -2290,3 +2275,107 @@ function remove_rr_file_to_extras(homeurl) { success: function(data) {} }); } + +/** + * Function delete files desired and add extras/delete_files.txt. + * + * @param string homeurl Url. + */ +function delete_desired_files(homeurl) { + var home_url = typeof homeurl !== "undefined" ? homeurl + "/" : ""; + + var parameters = { + page: "include/ajax/update_manager.ajax", + delete_desired_files: 1 + }; + + jQuery.ajax({ + data: parameters, + type: "POST", + url: home_url + "ajax.php", + dataType: "json", + success: function(data) { + var translation = data.translation; + // Print the deleted files. + // Print title. + $("#log_zone").append( + "

    " + + translation.title + + ":

    " + ); + $.each(data.status_list, function(key, value) { + var log_zone_line_class = "log_zone_line "; + var msg = ""; + switch (value.status) { + case -1: + //Not exits file. + msg = translation.not_file; + break; + case 0: + //File or directory deleted successfully. + if (value.type === "f") { + log_zone_line_class += ""; + } else { + log_zone_line_class += "bolder"; + } + + msg = value.path; + break; + case 1: + //Problem delete file or directory. + if (value.type === "f") { + log_zone_line_class += "log_zone_line_error"; + } else { + log_zone_line_class += "log_zone_line_error bolder"; + } + + msg = value.path + " ( " + translation.not_deleted + " ) "; + break; + case 2: + //Not found file or directory. + if (value.type === "f") { + log_zone_line_class += "log_zone_line_error"; + } else { + log_zone_line_class += "log_zone_line_error bolder"; + } + + msg = value.path + " ( " + translation.not_found + " ) "; + break; + case 3: + //Don`t read file deleet_files.txt. + log_zone_line_class += "log_zone_line_error bolder"; + msg = translation.not_read; + break; + case 4: + //"deleted" folder could not be created. + log_zone_line_class += "log_zone_line_error bolder"; + msg = value.path + " ( " + translation.folder_deleted_f + " ) "; + break; + case 5: + //"deleted" folder was created. + log_zone_line_class += "bolder"; + msg = translation.folder_deleted_t; + break; + case 6: + //The "delete files" could not be the "delete" folder. + log_zone_line_class += "log_zone_line_error bolder"; + msg = value.path + " ( " + translation.move_file_f + " ) "; + break; + case 7: + //The "delete files" is moved to the "delete" folder. + log_zone_line_class += "bolder"; + msg = translation.move_file_d; + break; + default: + // It can not come without state. + break; + } + + //Print line. + $("#log_zone").append( + "" + msg + "
    " + ); + }); + } + }); +} diff --git a/pandora_console/include/languages/en_GB.mo b/pandora_console/include/languages/en_GB.mo index f585765553..4b915e98e8 100644 Binary files a/pandora_console/include/languages/en_GB.mo and b/pandora_console/include/languages/en_GB.mo differ diff --git a/pandora_console/include/languages/en_GB.po b/pandora_console/include/languages/en_GB.po index 3eab48be5d..c6e833bc32 100644 --- a/pandora_console/include/languages/en_GB.po +++ b/pandora_console/include/languages/en_GB.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: pandora-fms\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-03-14 10:58+0100\n" -"PO-Revision-Date: 2018-12-07 12:05+0000\n" -"Last-Translator: Vanessa \n" +"PO-Revision-Date: 2019-03-25 16:10+0000\n" +"Last-Translator: Zarzuelo \n" "Language-Team: English (United Kingdom) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2018-12-07 12:16+0000\n" -"X-Generator: Launchpad (build 18831)\n" +"X-Launchpad-Export-Date: 2019-03-25 16:24+0000\n" +"X-Generator: Launchpad (build 18910)\n" "Language: en_GB\n" #: ../../extensions/agents_alerts.php:55 @@ -23480,7 +23480,7 @@ msgstr "Only avg, min and max are displayed by default" #: ../../enterprise/include/functions_reporting_pdf.php:1746 #: ../../enterprise/include/functions_reporting_pdf.php:2149 msgid "Fail" -msgstr "Error" +msgstr "Fail" #: ../../include/functions_config.php:602 msgid "Display lateral menus with left click" @@ -36481,8 +36481,8 @@ msgstr "" #: ../../enterprise/load_enterprise.php:584 #, php-format msgid "" -"License out of limits

    " -"This node has a metaconsole license and it allows %d agents and you have %d " +"Out of license limits

    " +"This node has a Metaconsole license that allows %d agents, and you have %d " "agents cached." msgstr "" "License expired

    This " @@ -36492,20 +36492,21 @@ msgstr "" #: ../../enterprise/load_enterprise.php:592 #, php-format msgid "" -"License out of limits

    " -"This license allows %d agents and you have %d agents configured." +"Out of license limits

    " +"This node has a Metaconsole license that allows %d agents, and you have %d " +"agents configured." msgstr "" "License expired

    This " -"license allows %d agents and you have %d agents configured." +"license allows %d agents, and you have %d agents configured." #: ../../enterprise/load_enterprise.php:597 #, php-format msgid "" -"License out of limits

    " +"Out of license limits

    " "This license allows %d modules and you have %d modules configured." msgstr "" -"License expired

    This " -"license allows %d modules and you have %d modules configured." +"Out of license limits

    This " +"license allows %d modules, and you have %d modules configured." #: ../../enterprise/load_enterprise.php:604 msgid "" diff --git a/pandora_console/include/rest-api/index.php b/pandora_console/include/rest-api/index.php new file mode 100644 index 0000000000..10f87488c7 --- /dev/null +++ b/pandora_console/include/rest-api/index.php @@ -0,0 +1,49 @@ + $visualConsoleId]); + $visualConsoleData = $visualConsole->toArray(); + $groupId = $visualConsoleData['groupId']; + + // ACL. + $aclRead = check_acl($config['id_user'], $groupId, 'VR'); + $aclWrite = check_acl($config['id_user'], $groupId, 'VW'); + $aclManage = check_acl($config['id_user'], $groupId, 'VM'); + + if (!$aclRead && !$aclWrite && !$aclManage) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without group access' + ); + exit; + } + + echo $visualConsole; +} else if ($getVisualConsoleItems === true) { + $vcItems = VisualConsole::getItemsFromDB($visualConsoleId, $aclUserGroups); + echo '['.implode($vcItems, ',').']'; +} + +exit; diff --git a/pandora_console/include/rest-api/models/CachedModel.php b/pandora_console/include/rest-api/models/CachedModel.php new file mode 100644 index 0000000000..e4cf65b15a --- /dev/null +++ b/pandora_console/include/rest-api/models/CachedModel.php @@ -0,0 +1,116 @@ + 0) { + // Obtain the item's data from cache. + $cachedData = static::fetchCachedData($filter); + if ($cachedData === null) { + $userId = (static::$indexCacheByUser === true) ? $config['id_user'] : null; + + // Delete expired data cache. + static::clearCachedData( + [ + 'vc_item_id' => $filter['id'], + 'vc_id' => $filter['id_layout'], + 'user_id' => $userId, + ] + ); + // Obtain the item's data from the database. + $data = static::fetchDataFromDB($filter); + // Save the item's data in cache. + static::saveCachedData( + [ + 'vc_item_id' => $filter['id'], + 'vc_id' => $filter['id_layout'], + 'user_id' => $userId, + 'expiration' => $filter['cache_expiration'], + ], + $data + ); + } else { + $data = $cachedData; + } + } else { + $data = static::fetchDataFromDB($filter); + } + + return static::fromArray($data); + } + + +} diff --git a/pandora_console/include/rest-api/models/Model.php b/pandora_console/include/rest-api/models/Model.php new file mode 100644 index 0000000000..78ef90468e --- /dev/null +++ b/pandora_console/include/rest-api/models/Model.php @@ -0,0 +1,231 @@ +validateData($unknownData); + $this->data = $this->decode($unknownData); + // Sort alphabetically. + ksort($this->data, (SORT_NATURAL | SORT_FLAG_CASE)); + } + + + /** + * Instance the class with the unknown input data. + * + * @param array $data Unknown data structure. + * + * @return self Instance of the model. + */ + public static function fromArray(array $data) + { + // The reserved word static refers to the invoked class at runtime. + return new static($data); + } + + + /** + * Obtain a data structure from the database using a filter. + * + * @param array $filter Filter to retrieve the modeled element. + * + * @return array The modeled element data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @abstract + */ + abstract protected static function fetchDataFromDB(array $filter); + + + /** + * Obtain a model's instance from the database using a filter. + * + * @param array $filter Filter to retrieve the modeled element. + * + * @return self A modeled element's instance. + */ + public static function fromDB(array $filter): self + { + // The reserved word static refers to the invoked class at runtime. + return static::fromArray(static::fetchDataFromDB($filter)); + } + + + /** + * JSON representation of the model. + * + * @return string + */ + public function toArray(): array + { + return $this->data; + } + + + /** + * JSON representation of the model. + * + * @return string + */ + public function toJson(): string + { + return \json_encode($this->data); + } + + + /** + * Text representation of the model. + * + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } + + + /* + * ------------- + * - UTILITIES - + * ------------- + */ + + + /** + * From a unknown value, it will try to extract a valid boolean value. + * + * @param mixed $value Unknown input. + * + * @return boolean Valid boolean value. + */ + protected static function parseBool($value): bool + { + if (\is_bool($value) === true) { + return $value; + } else if (\is_numeric($value) === true) { + return $value > 0; + } else if (\is_string($value) === true) { + return $value === '1' || $value === 'true'; + } else { + return false; + } + } + + + /** + * Return a not empty string or a default value from a unknown value. + * + * @param mixed $val Input value. + * @param mixed $def Default value. + * + * @return mixed A valid string (not empty) extracted from the input + * or the default value. + */ + protected static function notEmptyStringOr($val, $def) + { + return (\is_string($val) === true && strlen($val) > 0) ? $val : $def; + } + + + /** + * Return a valid integer or a default value from a unknown value. + * + * @param mixed $val Input value. + * @param mixed $def Default value. + * + * @return mixed A valid int extracted from the input or the default value. + */ + protected static function parseIntOr($val, $def) + { + return (is_numeric($val) === true) ? (int) $val : $def; + } + + + /** + * Return a valid float or a default value from a unknown value. + * + * @param mixed $val Input value. + * @param mixed $def Default value. + * + * @return mixed A valid float extracted from the input or the + * default value. + */ + protected static function parseFloatOr($val, $def) + { + return (is_numeric($val) === true) ? (float) $val : $def; + } + + + /** + * Get a value from a dictionary from a possible pool of keys. + * + * @param array $dict Input array. + * @param array $keys Possible keys. + * + * @return mixed The first value found with the pool of keys or null. + */ + protected static function issetInArray(array $dict, array $keys) + { + foreach ($keys as $key => $value) { + if (isset($dict[$value]) === true) { + return $dict[$value]; + } + } + + return null; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Container.php b/pandora_console/include/rest-api/models/VisualConsole/Container.php new file mode 100644 index 0000000000..ad1f34a2ff --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Container.php @@ -0,0 +1,377 @@ + (int) $data['id'], + 'name' => $data['name'], + 'groupId' => static::extractGroupId($data), + 'backgroundImage' => static::extractBackgroundImage($data), + 'backgroundColor' => static::extractBackgroundColor($data), + 'isFavorite' => static::extractFavorite($data), + 'width' => (int) $data['width'], + 'height' => (int) $data['height'], + 'backgroundURL' => static::extractBackgroundUrl($data), + 'relationLineWidth' => (int) $data['relationLineWidth'], + ]; + } + + + /** + * Extract a group Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a group. + * + * @throws \InvalidArgumentException When a valid group Id can't be found. + */ + private static function extractGroupId(array $data): int + { + $groupId = static::parseIntOr( + static::issetInArray($data, ['id_group', 'groupId']), + null + ); + + if ($groupId === null || $groupId < 0) { + throw new \InvalidArgumentException( + 'the group Id property is required and should be integer' + ); + } + + return $groupId; + } + + + /** + * Extract a image name value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the image name (not empty) or null. + */ + private static function extractBackgroundImage(array $data) + { + $backgroundImage = static::notEmptyStringOr( + static::issetInArray($data, ['background', 'backgroundURL']), + null + ); + + return ($backgroundImage === 'None.png') ? null : $backgroundImage; + } + + + /** + * Extract a image url value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the image url (not empty) or null. + */ + private static function extractBackgroundUrl(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['backgroundURL']), + null + ); + } + + + /** + * Extract a background color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the color (not empty) or null. + */ + private static function extractBackgroundColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray( + $data, + [ + 'backgroundColor', + 'background_color', + ] + ), + null + ); + } + + + /** + * Extract the "is favorite" switch value. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item is favorite or not. + */ + private static function extractFavorite(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['is_favourite', 'isFavorite']) + ); + } + + + /** + * Obtain a container data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console. + * + * @return self A Visual Console Container instance. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override Model::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter) + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $row = \db_get_row_filter('tlayout', $filter); + + if ($row === false) { + throw new \Exception('error fetching the data from the DB'); + } + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_io.php'; + include_once $config['homedir'].'/include/functions_ui.php'; + + // Clean HTML entities. + $row = \io_safe_output($row); + + $row['relationLineWidth'] = (int) $config['vc_line_thickness']; + + $backgroundUrl = static::extractBackgroundUrl($row); + $backgroundImage = static::extractBackgroundImage($row); + + if ($backgroundUrl === null && $backgroundImage !== null) { + $row['backgroundURL'] = ui_get_full_url( + 'images/console/background/'.$backgroundImage, + false, + false, + false + ); + } + + return \io_safe_output($row); + } + + + /** + * Obtain a item's class. + * + * @param integer $type Type of the item of the Visual Console. + * + * @return mixed A reference to the item's class. + */ + public static function getItemClass(int $type) + { + switch ($type) { + case STATIC_GRAPH: + return Items\StaticGraph::class; + + case MODULE_GRAPH: + return Items\ModuleGraph::class; + + case SIMPLE_VALUE: + case SIMPLE_VALUE_MAX: + case SIMPLE_VALUE_MIN: + case SIMPLE_VALUE_AVG: + return Items\SimpleValue::class; + + case PERCENTILE_BAR: + case PERCENTILE_BUBBLE: + case CIRCULAR_PROGRESS_BAR: + case CIRCULAR_INTERIOR_PROGRESS_BAR: + return Items\Percentile::class; + + case LABEL: + return Items\Label::class; + + case ICON: + return Items\Icon::class; + + // Enterprise item. It may not exist. + case SERVICE: + return \class_exists('\Enterprise\Models\VisualConsole\Items\Service') ? \Enterprise\Models\VisualConsole\Items\Service::class : Item::class; + + case GROUP_ITEM: + return Items\Group::class; + + case BOX_ITEM: + return Items\Box::class; + + case LINE_ITEM: + return Items\Line::class; + + case AUTO_SLA_GRAPH: + return Items\EventsHistory::class; + + case DONUT_GRAPH: + return Items\DonutGraph::class; + + case BARS_GRAPH: + return Items\BarsGraph::class; + + case CLOCK: + return Items\Clock::class; + + case COLOR_CLOUD: + return Items\ColorCloud::class; + + default: + return Item::class; + } + } + + + /** + * Obtain a list of items which belong to the Visual Console. + * + * @param integer $layoutId Identifier of the Visual Console. + * @param array $groupsFilter Groups can access user. + * + * @return array A list of items. + * @throws \Exception When the data cannot be retrieved from the DB. + */ + public static function getItemsFromDB( + int $layoutId, + array $groupsFilter=[] + ): array { + // Default filter. + $filter = ['id_layout' => $layoutId]; + $fields = [ + 'id', + 'type', + 'cache_expiration', + 'id_layout', + ]; + + // Override the filter if the groups filter is not empty. + if (count($groupsFilter) > 0) { + // Filter group for elements groups. + $filter = []; + $filter[] = \db_format_array_where_clause_sql( + [ + 'id_layout' => $layoutId, + 'element_group' => $groupsFilter, + ] + ); + + // Filter groups for type groups. + // Only true condition if type is GROUP_ITEM. + $filter[] = '('.\db_format_array_where_clause_sql( + [ + 'type' => GROUP_ITEM, + 'id_group' => $groupsFilter, + ] + ).')'; + } + + $rows = \db_get_all_rows_filter( + 'tlayout_data', + $filter, + $fields, + 'OR' + ); + + if ($rows === false) { + $rows = []; + } + + $items = []; + + foreach ($rows as $data) { + $itemId = (int) $data['id']; + $class = static::getItemClass((int) $data['type']); + + try { + array_push($items, $class::fromDB($data)); + } catch (\Throwable $e) { + // TODO: Log this? + } + } + + return $items; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Item.php b/pandora_console/include/rest-api/models/VisualConsole/Item.php new file mode 100644 index 0000000000..617b48e517 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Item.php @@ -0,0 +1,1190 @@ + (int) $data['id'], + 'type' => (int) $data['type'], + 'label' => static::extractLabel($data), + 'labelPosition' => static::extractLabelPosition($data), + 'isLinkEnabled' => static::extractIsLinkEnabled($data), + 'isOnTop' => static::extractIsOnTop($data), + 'parentId' => static::extractParentId($data), + 'aclGroupId' => static::extractAclGroupId($data), + 'width' => (int) $data['width'], + 'height' => (int) $data['height'], + 'x' => static::extractX($data), + 'y' => static::extractY($data), + ]; + + if (static::$useLinkedModule === true) { + $decodedData = array_merge( + $decodedData, + static::extractLinkedModule($data) + ); + } else if (static::$useLinkedAgent === true) { + $decodedData = array_merge( + $decodedData, + static::extractLinkedAgent($data) + ); + } + + if (static::$useLinkedVisualConsole === true) { + $decodedData = array_merge( + $decodedData, + static::extractLinkedVisualConsole($data) + ); + } + + if (static::$useHtmlOutput === true) { + $decodedData['encodedHtml'] = static::extractEncodedHtml($data); + } + + // Conditionally add the item link. + if ($decodedData['isLinkEnabled'] === true) { + $decodedData['link'] = static::notEmptyStringOr( + static::issetInArray($data, ['link']), + null + ); + } + + return $decodedData; + } + + + /** + * Extract x y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid x axis position of the item. + */ + private static function extractX(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['x', 'pos_x']), + 0 + ); + } + + + /** + * Extract a y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid y axis position of the item. + */ + private static function extractY(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['y', 'pos_y']), + 0 + ); + } + + + /** + * Extract a group Id (for ACL) value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a group. + */ + private static function extractAclGroupId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['id_group', 'aclGroupId']), + null + ); + } + + + /** + * Extract a parent Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of the item's parent. + */ + private static function extractParentId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['parentId', 'parent_item']), + null + ); + } + + + /** + * Extract the "is on top" switch value. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item is on top or not. + */ + private static function extractIsOnTop(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['isOnTop', 'show_on_top']) + ); + } + + + /** + * Extract the "is link enabled" switch value. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item has the link enabled or not. + */ + private static function extractIsLinkEnabled(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['isLinkEnabled', 'enable_link']) + ); + } + + + /** + * Extract a label value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the label (not empty) or null. + */ + private static function extractLabel(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['label']), + null + ); + } + + + /** + * Extract a label position value. + * + * @param array $data Unknown input data structure. + * + * @return mixed One string of up|right|left|down. down by default. + */ + private static function extractLabelPosition(array $data): string + { + $labelPosition = static::notEmptyStringOr( + static::issetInArray($data, ['labelPosition', 'label_position']), + null + ); + + switch ($labelPosition) { + case 'up': + case 'right': + case 'left': + return $labelPosition; + + default: + return 'down'; + } + } + + + /** + * Extract an agent Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of an agent. + */ + private static function extractAgentId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['agentId', 'id_agent', 'id_agente']), + null + ); + } + + + /** + * Extract an module Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a module. + */ + private static function extractModuleId(array $data) + { + return static::parseIntOr( + static::issetInArray( + $data, + [ + 'moduleId', + 'id_agente_modulo', + 'id_modulo', + ] + ), + null + ); + } + + + /** + * Extract an metaconsole node Id value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid identifier of a metaconsole node. + */ + private static function extractMetaconsoleId(array $data) + { + return static::parseIntOr( + static::issetInArray($data, ['metaconsoleId', 'id_metaconsole']), + null + ); + } + + + /** + * Extract the values of a linked agent. + * + * @param array $data Unknown input data structure. + * + * @return array Data structure of the linked agent info. + * + * @example [ + * 'metaconsoleId' => 1, + * 'agentId' => 2, + * 'agentName' => 'Foo', + * ] + * @example [ + * 'agentId' => 20, + * 'agentName' => 'Bar', + * ] + * @example [ + * 'agentId' => null, + * 'agentName' => null, + * ] + */ + protected static function extractLinkedAgent(array $data): array + { + $agentData = []; + + // We should add the metaconsole Id if we can. + $metaconsoleId = static::extractMetaconsoleId($data); + if ($metaconsoleId !== null && $metaconsoleId <= 0) { + $agentData['metaconsoleId'] = null; + } else { + $agentData['metaconsoleId'] = $metaconsoleId; + } + + // The agent Id should be a valid int or a null value. + $agentData['agentId'] = static::extractAgentId($data); + + // The agent name should be a valid string or a null value. + $agentData['agentName'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentName', 'agent_name']), + null + ); + + // The agent alias should be a valid string or a null value. + $agentData['agentAlias'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentAlias', 'agent_alias']), + null + ); + + // The agent description should be a valid string or a null value. + $agentData['agentDescription'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentDescription', 'agent_description']), + null + ); + + // The agent address should be a valid string or a null value. + $agentData['agentAddress'] = static::notEmptyStringOr( + static::issetInArray($data, ['agentAddress', 'agent_address']), + null + ); + + return $agentData; + } + + + /** + * Extract the values of a linked module. + * + * @param array $data Unknown input data structure. + * + * @return array Data structure of the linked module info. + * + * @example [ + * 'metaconsoleId' => 1, + * 'agentId' => 2, + * 'agentName' => 'Foo', + * 'moduleId' => 1, + * 'moduleName' => 'cpu', + * ] + * @example [ + * 'agentId' => 4, + * 'agentName' => 'Bar', + * 'moduleId' => null, + * 'moduleName' => null, + * ] + * @example [ + * 'agentId' => null, + * 'agentName' => null, + * 'moduleId' => null, + * 'moduleName' => null, + * ] + */ + protected static function extractLinkedModule(array $data): array + { + // Initialize the data with the agent data and then expand it. + $moduleData = static::extractLinkedAgent($data); + + // The module Id should be a valid int or a null value. + $moduleData['moduleId'] = static::extractModuleId($data); + + // The module name should be a valid string or a null value. + $moduleData['moduleName'] = static::notEmptyStringOr( + static::issetInArray($data, ['moduleName', 'module_name']), + null + ); + + // The module description should be a valid string or a null value. + $moduleData['moduleDescription'] = static::notEmptyStringOr( + static::issetInArray($data, ['moduleDescription', 'module_description']), + null + ); + + return $moduleData; + } + + + /** + * Extract the values of a linked visual console. + * + * @param array $data Unknown input data structure. + * + * @return array Data structure of the linked visual console info. + * + * @example [ + * 'metaconsoleId' => 2, + * 'linkedLayoutId' => 12, + * 'linkedLayoutAgentId' => 48, + * 'linkedLayoutStatusType' => 'default', + * ] + * @example [ + * 'linkedLayoutId' => 11, + * 'linkedLayoutAgentId' => null, + * 'linkedLayoutStatusType' => 'weight', + * 'linkedLayoutStatusTypeWeight' => 80, + * ] + * @example [ + * 'metaconsoleId' => 2, + * 'linkedLayoutId' => 10, + * 'linkedLayoutAgentId' => 48, + * 'linkedLayoutStatusType' => 'service', + * 'linkedLayoutStatusTypeWarningThreshold' => 50, + * 'linkedLayoutStatusTypeCriticalThreshold' => 80, + * ] + */ + private static function extractLinkedVisualConsole(array $data): array + { + $vcData = []; + + // We should add the metaconsole Id if we can. If not, + // it doesn't have to be into the structure. + $metaconsoleId = static::extractMetaconsoleId($data); + if ($metaconsoleId !== null) { + $vcData['metaconsoleId'] = $metaconsoleId; + } + + // The linked vc Id should be a valid int or a null value. + $vcData['linkedLayoutId'] = static::parseIntOr( + static::issetInArray($data, ['linkedLayoutId', 'id_layout_linked']), + null + ); + + // The linked vc agent Id should be a valid int or a null value. + $vcData['linkedLayoutAgentId'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutAgentId', + 'linked_layout_node_id', + ] + ), + null + ); + + // The linked vc status type should be a enum value. + $linkedLayoutStatusType = static::notEmptyStringOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusType', + 'linked_layout_status_type', + ] + ), + null + ); + + // Extract data for the calculation of the linked visual console status. + switch ($linkedLayoutStatusType) { + case 'default': + default: + $vcData['linkedLayoutStatusType'] = 'default'; + break; + + case 'weight': + $vcData['linkedLayoutStatusType'] = 'weight'; + $vcData['linkedLayoutStatusTypeWeight'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusTypeWeight', + 'id_layout_linked_weight', + ] + ), + 0 + ); + break; + + case 'service': + $vcData['linkedLayoutStatusType'] = 'service'; + $vcData['linkedLayoutStatusTypeWarningThreshold'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusTypeWarningThreshold', + 'linked_layout_status_as_service_warning', + ] + ), + 0 + ); + $vcData['linkedLayoutStatusTypeCriticalThreshold'] = static::parseIntOr( + static::issetInArray( + $data, + [ + 'linkedLayoutStatusTypeCriticalThreshold', + 'linked_layout_status_as_service_critical', + ] + ), + 0 + ); + break; + } + + return $vcData; + } + + + /** + * Extract a encoded HTML representation of the item. + * + * @param array $data Unknown input data structure. + * + * @return string The HTML representation in base64 encoding. + */ + private static function extractEncodedHtml(array $data): string + { + if (isset($data['encodedHtml']) === true) { + return $data['encodedHtml']; + } else if (isset($data['html']) === true) { + return base64_encode($data['html']); + } + + return ''; + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override Model::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_io.php'; + + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $row = \db_get_row_filter('tlayout_data', $filter); + + if ($row === false) { + throw new \Exception('error fetching the data from the DB'); + } + + // Clean up to two levels of HTML entities. + $row = \io_safe_output(\io_safe_output($row)); + + /* + * Retrieve extra data. + */ + + // The linked module includes the agent data. + if (static::$useLinkedModule === true) { + $row = array_merge($row, static::fetchModuleDataFromDB($row)); + } else if (static::$useLinkedAgent === true) { + $row = array_merge($row, static::fetchAgentDataFromDB($row)); + } + + // Build the item link if needed. + if (static::extractIsLinkEnabled($row) === true) { + $row['link'] = static::buildLink($row); + } + + return $row; + } + + + /** + * Fetch a cache item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override CachedModel::fetchCachedData. + */ + protected static function fetchCachedData(array $filter) + { + global $config; + + $filter = [ + 'vc_id' => (int) $filter['id_layout'], + 'vc_item_id' => (int) $filter['id'], + '(UNIX_TIMESTAMP(`created_at`) + `expiration`) > UNIX_TIMESTAMP()' + ]; + + if (static::$indexCacheByUser === true) { + $filter['user_id'] = $config['id_user']; + } + + $data = \db_get_value_filter( + 'data', + 'tvisual_console_elements_cache', + $filter + ); + + if ($data === false) { + return null; + } + + return json_decode(base64_decode($data), true); + } + + + /** + * Stores the data structure obtained. + * + * @param array $filter Filter to save the modeled element. + * @param array $data Modeled element to save. + * + * @return boolean The modeled element data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override CachedModel::saveCachedData. + */ + protected static function saveCachedData(array $filter, array $data): bool + { + return \db_process_sql_insert( + 'tvisual_console_elements_cache', + [ + 'vc_id' => $filter['vc_id'], + 'vc_item_id' => $filter['vc_item_id'], + 'user_id' => $filter['user_id'], + 'data' => base64_encode(json_encode($data)), + 'expiration' => $filter['expiration'], + ] + ) > 0; + } + + + /** + * Deletes previous data that are not useful. + * + * @param array $filter Filter to retrieve the modeled element. + * + * @return array The modeled element data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override CachedModel::clearCachedData. + */ + protected static function clearCachedData(array $filter): int + { + return \db_process_sql_delete( + 'tvisual_console_elements_cache', + $filter + ); + } + + + /** + * Fetch a data structure of an agent from the database using the + * vs item's data. + * + * @param array $itemData Visual Console Item's data structure. + * + * @return array The agent data structure stored into the DB. + * + * @throws \InvalidArgumentException When the input agent Id is invalid. + * @throws \Exception When the data cannot be retrieved from the DB. + */ + protected static function fetchAgentDataFromDB(array $itemData): array + { + $agentData = []; + + // We should add the metaconsole Id if we can. + $metaconsoleId = static::extractMetaconsoleId($itemData); + + // Can't fetch an agent with an invalid Id. + $agentId = static::extractAgentId($itemData); + if ($agentId === null) { + throw new \InvalidArgumentException('invalid agent Id'); + } + + // Staticgraph don't need to have an agent. + if ($agentId === 0) { + return $agentData; + } + + if (\is_metaconsole() && $metaconsoleId === null) { + throw new \InvalidArgumentException('missing metaconsole node Id'); + } + + $agent = false; + + if (\is_metaconsole()) { + $sql = sprintf( + 'SELECT nombre, alias, direccion, comentarios + FROM tmetaconsole_agent + WHERE id_tagente = %s and id_tmetaconsole_setup = %s', + $agentId, + $metaconsoleId + ); + } else { + $sql = sprintf( + 'SELECT nombre, alias, direccion, comentarios + FROM tagente + WHERE id_agente = %s', + $agentId + ); + } + + $agent = \db_get_row_sql($sql); + + if ($agent === false) { + throw new \Exception('error fetching the data from the DB'); + } + + // The agent name should be a valid string or a null value. + $agentData['agentName'] = $agent['nombre']; + $agentData['agentAlias'] = $agent['alias']; + $agentData['agentDescription'] = $agent['comentarios']; + $agentData['agentAddress'] = $agent['direccion']; + + return $agentData; + } + + + /** + * Fetch a data structure of an module from the database using the + * vs item's data. + * + * @param array $itemData Visual Console Item's data structure. + * + * @return array The module data structure stored into the DB. + * @throws \InvalidArgumentException When the input module Id is invalid. + * @throws \Exception When the data cannot be retrieved from the DB. + */ + protected static function fetchModuleDataFromDB(array $itemData): array + { + // Load side libraries. + if (\is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Initialize with the agent data. + $moduleData = static::fetchAgentDataFromDB($itemData); + + // Can't fetch an module with a invalid Id. + $moduleId = static::extractModuleId($itemData); + if ($moduleId === null) { + throw new \InvalidArgumentException('invalid module Id'); + } + + // Staticgraph don't need to have a module. + if ($moduleId === 0) { + return $moduleData; + } + + // We should add the metaconsole Id if we can. + $metaconsoleId = static::extractMetaconsoleId($itemData); + + if (\is_metaconsole() && $metaconsoleId === null) { + throw new \InvalidArgumentException('missing metaconsole node Id'); + } + + $moduleName = false; + + // Connect to node. + if (\is_metaconsole() + && \metaconsole_connect(null, $metaconsoleId) !== NOERR + ) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + + $sql = sprintf( + 'SELECT nombre, descripcion + FROM tagente_modulo + WHERE id_agente_modulo = %s', + $moduleId + ); + + $moduleName = \db_get_row_sql($sql); + + // Restore connection. + if (\is_metaconsole()) { + \metaconsole_restore_db(); + } + + if ($moduleName === false) { + throw new \Exception('error fetching the data from the DB'); + } + + $moduleData['moduleName'] = $moduleName['nombre']; + $moduleData['moduleDescription'] = $moduleName['descripcion']; + + return $moduleData; + } + + + /** + * Generate a link to something related with the item. + * + * @param array $data Visual Console Item's data structure. + * + * @return mixed The link or a null value. + * @throws \Exception Not really. It's controlled. + */ + protected static function buildLink(array $data) + { + global $config; + + // Load side libraries. + include_once $config['homedir'].'/include/functions_ui.php'; + if (\is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + \enterprise_include_once('meta/include/functions_ui_meta.php'); + } + + $linkedVisualConsole = static::extractLinkedVisualConsole($data); + $linkedModule = static::extractLinkedModule($data); + $linkedAgent = static::extractLinkedAgent($data); + + $baseUrl = \ui_get_full_url('index.php'); + + // TODO: There's a feature to get the link from the label. + if (static::$useLinkedVisualConsole === true + && $linkedVisualConsole['linkedLayoutId'] !== null + && $linkedVisualConsole['linkedLayoutId'] > 0 + ) { + // Linked Visual Console. + $vcId = $linkedVisualConsole['linkedLayoutId']; + // The layout can be from another node. + $linkedLayoutAgentId = $linkedVisualConsole['linkedLayoutAgentId']; + + if (empty($linkedLayoutAgentId) === true && \is_metaconsole()) { + /* + * A Visual Console from this console. + * We are in a metaconsole. + */ + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'screen', + 'sec2' => 'screens/screens', + 'action' => 'visualmap', + 'id_visualmap' => $vcId, + 'pure' => (int) $config['pure'], + ] + ); + } else if (empty($linkedLayoutAgentId) === true + && !\is_metaconsole() + ) { + /* + * A Visual Console from this console. + * We are in a regular console. + */ + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'network', + 'sec2' => 'operation/visual_console/view', + 'id' => $vcId, + 'pure' => (int) $config['pure'], + ] + ); + } else if (\is_metaconsole() && \can_user_access_node()) { + /* + * A Visual Console from a meta node. + * We are in a metaconsole. + */ + + try { + $node = \metaconsole_get_connection_by_id( + $linkedLayoutAgentId + ); + return \ui_meta_get_node_url( + $node, + 'network', + // TODO: Link to a public view. + 'operation/visual_console/view', + [], + // No autologin from the public view. + !$config['public_view'] + ); + } catch (\Throwable $ignored) { + return null; + } + } + } else { + if (static::$useLinkedModule === true + && $linkedModule['moduleId'] !== null + && $linkedModule['moduleId'] > 0 + ) { + // Module Id. + $moduleId = $linkedModule['moduleId']; + // The module can be from another node. + $metaconsoleId = $linkedModule['metaconsoleId']; + + if (empty($metaconsoleId) === true) { + /* + * A module from this console. + */ + + // Check if the module is from a service. + $serviceId = (int) \db_get_value_filter( + 'custom_integer_1', + 'tagente_modulo', + [ + 'id_agente_modulo' => $moduleId, + 'prediction_module' => 1, + ] + ); + + if (empty($serviceId) === false) { + // A service. + $queryParams = [ + 'sec' => 'services', + 'sec2' => 'enterprise/operation/services/services', + 'id_service' => $serviceId, + ]; + } else { + // A regular module. + $queryParams = [ + 'sec' => 'view', + 'sec2' => 'operation/agentes/status_monitor', + 'id_module' => $moduleId, + ]; + } + + return $baseUrl.'?'.http_build_query($queryParams); + } else if (\is_metaconsole() && \can_user_access_node()) { + /* + * A module from a meta node. + * We are in a metaconsole. + */ + + try { + $node = \metaconsole_get_connection_by_id( + $metaconsoleId + ); + + // Connect to node. + if (\metaconsole_connect($node) !== NOERR) { + // Will be catched below. + throw new \Exception( + 'error connecting to the node' + ); + } + + // Check if the module is a service. + $serviceId = (int) \db_get_value_filter( + 'custom_integer_1', + 'tagente_modulo', + [ + 'id_agente_modulo' => $moduleId, + 'prediction_module' => 1, + ] + ); + + // Restore connection. + \metaconsole_restore_db(); + + if (empty($serviceId) === false) { + // A service. + return \ui_meta_get_node_url( + $node, + 'services', + 'enterprise/operation/services/services', + ['id_service' => $serviceId], + // No autologin from the public view. + !$config['public_view'] + ); + } else { + // A regular module. + return \ui_meta_get_node_url( + $node, + 'view', + 'operation/agentes/status_monitor', + ['id_module' => $moduleId], + // No autologin from the public view. + !$config['public_view'] + ); + } + } catch (\Throwable $ignored) { + return null; + } + } + } else if ((static::$useLinkedAgent === true + || static::$useLinkedModule === true) + && $linkedAgent['agentId'] !== null + && $linkedAgent['agentId'] > 0 + ) { + // Linked agent. + // Agent Id. + $agentId = $linkedAgent['agentId']; + // The agent can be from another node. + $metaconsoleId = $linkedAgent['metaconsoleId']; + + if (empty($metaconsoleId) === true) { + /* + * An agent from this console. + * We are in a regular console. + */ + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'estado', + 'sec2' => 'operation/agentes/ver_agente', + 'id_agente' => $agentId, + ] + ); + } else if (\is_metaconsole() && \can_user_access_node()) { + /* + * An agent from a meta node. + * We are in a metaconsole. + */ + + try { + $node = \metaconsole_get_connection_by_id( + $metaconsoleId + ); + return \ui_meta_get_node_url( + $node, + 'estado', + 'operation/agentes/ver_agente', + ['id_agente' => $moduleId], + // No autologin from the public view. + !$config['public_view'] + ); + } catch (\Throwable $ignored) { + return null; + } + } + } + } + + return null; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/BarsGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/BarsGraph.php new file mode 100644 index 0000000000..4482b4d53d --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/BarsGraph.php @@ -0,0 +1,346 @@ +extractGridColor($data); + $return['backgroundColor'] = $this->extractBackgroundColor($data); + $return['typeGraph'] = $this->extractTypeGraph($data); + return $return; + } + + + /** + * Extract a grid color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the grid color (not empty) or null. + */ + private function extractGridColor(array $data): string + { + return static::notEmptyStringOr( + static::issetInArray($data, ['gridColor', 'border_color']), + '#000000' + ); + } + + + /** + * Extract a background color value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'white', 'black' or 'transparent'. + * 'white' by default. + */ + private function extractBackgroundColor(array $data): string + { + $backgroundColor = static::notEmptyStringOr( + static::issetInArray($data, ['backgroundColor', 'image']), + null + ); + + switch ($backgroundColor) { + case 'black': + case 'transparent': + return $backgroundColor; + + default: + return 'white'; + } + } + + + /** + * Extract a type graph value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'vertical' or 'horizontal'. 'vertical' by default. + */ + private function extractTypeGraph(array $data): string + { + $typeGraph = static::notEmptyStringOr( + static::issetInArray($data, ['typeGraph', 'type_graph']), + null + ); + + switch ($typeGraph) { + case 'horizontal': + return 'horizontal'; + + default: + return 'vertical'; + } + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When an agent Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load config. + global $config; + + // Load side libraries. + include_once $config['homedir'].'/include/functions_ui.php'; + include_once $config['homedir'].'/include/functions_visual_map.php'; + include_once $config['homedir'].'/include/graphs/fgraph.php'; + + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Extract needed properties. + $gridColor = static::extractGridColor($data); + $backGroundColor = static::extractBackgroundColor($data); + $typeGraph = static::extractTypeGraph($data); + + // Get the linked agent and module Ids. + $linkedModule = static::extractLinkedModule($data); + $agentId = $linkedModule['agentId']; + $moduleId = $linkedModule['moduleId']; + $metaconsoleId = $linkedModule['metaconsoleId']; + + if ($agentId === null) { + throw new \InvalidArgumentException('missing agent Id'); + } + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + // Add colors that will use the graphics. + $color = []; + + $color[0] = [ + 'border' => '#000000', + 'color' => $config['graph_color1'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[1] = [ + 'border' => '#000000', + 'color' => $config['graph_color2'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[2] = [ + 'border' => '#000000', + 'color' => $config['graph_color3'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[3] = [ + 'border' => '#000000', + 'color' => $config['graph_color4'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[4] = [ + 'border' => '#000000', + 'color' => $config['graph_color5'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[5] = [ + 'border' => '#000000', + 'color' => $config['graph_color6'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[6] = [ + 'border' => '#000000', + 'color' => $config['graph_color7'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[7] = [ + 'border' => '#000000', + 'color' => $config['graph_color8'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[8] = [ + 'border' => '#000000', + 'color' => $config['graph_color9'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[9] = [ + 'border' => '#000000', + 'color' => $config['graph_color10'], + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[11] = [ + 'border' => '#000000', + 'color' => COL_GRAPH9, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[12] = [ + 'border' => '#000000', + 'color' => COL_GRAPH10, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[13] = [ + 'border' => '#000000', + 'color' => COL_GRAPH11, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[14] = [ + 'border' => '#000000', + 'color' => COL_GRAPH12, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + $color[15] = [ + 'border' => '#000000', + 'color' => COL_GRAPH13, + 'alpha' => CHART_DEFAULT_ALPHA, + ]; + + // Maybe connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + $moduleData = \get_bars_module_data($moduleId); + + $waterMark = [ + 'file' => $config['homedir'].'/images/logo_vertical_water.png', + 'url' => \ui_get_full_url( + 'images/logo_vertical_water.png', + false, + false, + false + ), + ]; + + if ((int) $data['width'] === 0 || (int) $data['height'] === 0) { + $width = 400; + $height = 400; + } else { + $width = (int) $data['width']; + $height = (int) $data['height']; + } + + if ($typeGraph === 'horizontal') { + $graph = \hbar_graph( + $moduleData, + $width, + $height, + $color, + [], + [], + \ui_get_full_url( + 'images/image_problem_area.png', + false, + false, + false + ), + '', + '', + $waterMark, + $config['fontpath'], + 6, + '', + 0, + $config['homeurl'], + $backGroundColor, + $gridColor + ); + } else { + $graph = \vbar_graph( + $moduleData, + $width, + $height, + $color, + [], + [], + \ui_get_full_url( + 'images/image_problem_area.png', + false, + false, + false + ), + '', + '', + $waterMark, + $config['fontpath'], + 6, + '', + 0, + $config['homeurl'], + $backGroundColor, + true, + false, + $gridColor + ); + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + $data['html'] = $graph; + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Box.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Box.php new file mode 100644 index 0000000000..144518ebb1 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Box.php @@ -0,0 +1,85 @@ +extractBorderWidth($data); + $boxData['borderColor'] = $this->extractBorderColor($data); + $boxData['fillColor'] = $this->extractFillColor($data); + return $boxData; + } + + + /** + * Extract a border width value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid border width. 0 by default. + */ + private function extractBorderWidth(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['borderWidth', 'border_width']), + 0 + ); + } + + + /** + * Extract a border color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the border color (not empty) or null. + */ + private function extractBorderColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['borderColor', 'border_color']), + null + ); + } + + + /** + * Extract a fill color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the fill color (not empty) or null. + */ + private function extractFillColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['fillColor', 'fill_color']), + null + ); + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Clock.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Clock.php new file mode 100644 index 0000000000..ba72e0c30c --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Clock.php @@ -0,0 +1,154 @@ +getOffset( + $dateTimeUtc + ); + } catch (\Throwable $e) { + throw new \InvalidArgumentException($e->getMessage()); + } + + // $clockData['showClockTimezone'] = static::parseBool( + // static::issetInArray($data, ['showClockTimezone']) + // ); + // TODO: Remove the true by default when added into the editor. + $clockData['showClockTimezone'] = true; + $clockData['color'] = static::extractColor($data); + return $clockData; + } + + + /** + * Extract a clock type value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'digital' or 'analogic'. 'analogic' by default. + */ + private static function extractClockType(array $data): string + { + $clockType = static::notEmptyStringOr( + static::issetInArray($data, ['clockType', 'clock_animation']), + null + ); + + switch ($clockType) { + case 'digital': + case 'digital_1': + return 'digital'; + + default: + return 'analogic'; + } + } + + + /** + * Extract a clock format value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'time' or 'datetime'. 'datetime' by default. + */ + private static function extractClockFormat(array $data): string + { + $clockFormat = static::notEmptyStringOr( + static::issetInArray($data, ['clockFormat', 'time_format']), + null + ); + + switch ($clockFormat) { + case 'time': + return 'time'; + + default: + return 'datetime'; + } + } + + + /** + * Extract a clock timezone value. + * + * @param array $data Unknown input data structure. + * + * @return string + * @throws \InvalidArgumentException When a valid clock timezone cannot be + * extracted. + */ + private static function extractClockTimezone(array $data): string + { + $clockTimezone = static::notEmptyStringOr( + static::issetInArray($data, ['clockTimezone', 'timezone']), + null + ); + + if ($clockTimezone === null) { + throw new \InvalidArgumentException( + 'the clockTimezone property is required and should be string' + ); + } + + return $clockTimezone; + } + + + /** + * Extract the color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed returns a color or null. + */ + private static function extractColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['color', 'fill_color']), + null + ); + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/ColorCloud.php b/pandora_console/include/rest-api/models/VisualConsole/Items/ColorCloud.php new file mode 100644 index 0000000000..807d8fb3e9 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/ColorCloud.php @@ -0,0 +1,281 @@ +decode. + */ + protected function decode(array $data): array + { + $decodedData = parent::decode($data); + $decodedData['type'] = COLOR_CLOUD; + $decodedData['label'] = null; + $decodedData['defaultColor'] = static::extractDefaultColor($data); + $decodedData['colorRanges'] = static::extractColorRanges($data); + $decodedData['color'] = static::notEmptyStringOr( + static::issetInArray($data, ['color']), + null + ); + + return $decodedData; + } + + + /** + * Extract the default color value. + * + * @param array $data Unknown input data structure. + * + * @return string Default color. + * @throws \InvalidArgumentException If the default color cannot be + * extracted. + */ + private static function extractDefaultColor(array $data): string + { + if (isset($data['defaultColor'])) { + $defaultColor = static::notEmptyStringOr( + $data['defaultColor'], + null + ); + + if ($defaultColor === null) { + throw new \InvalidArgumentException( + 'the default color property is required and should be a not empty string' + ); + } + + return $defaultColor; + } else { + $dynamicData = static::extractDynamicData($data); + return $dynamicData['defaultColor']; + } + } + + + /** + * Extract a list of color ranges. + * + * @param array $data Unknown input data structure. + * + * @return array Color ranges list. + * @throws \InvalidArgumentException If any of the color ranges is invalid. + */ + private static function extractColorRanges(array $data): array + { + if (isset($data['colorRanges']) && \is_array($data['colorRanges'])) { + // Validate the color ranges. + foreach ($data['colorRanges'] as $colorRange) { + if (\is_numeric($colorRange['fromValue']) === false + || \is_numeric($colorRange['toValue']) === false + || static::notEmptyStringOr($colorRange['color'], null) === null + ) { + throw new \InvalidArgumentException('invalid color range'); + } + } + + return $data['colorRanges']; + } else if (isset($data['label']) === true) { + $dynamicData = static::extractDynamicData($data); + return $dynamicData['colorRanges']; + } else { + return []; + } + } + + + /** + * Extract a dynamic data structure from the 'label' field. + * + * @param array $data Unknown input data structure. + * + * @return array Dynamic data structure. + * @throws \InvalidArgumentException If the structure cannot be built. + * + * @example [ + * 'defaultColor' => '#FFF', + * 'colorRanges' => [ + * [ + * 'fromValue' => 50.0, + * 'toValue' => 150.5, + * 'color' => '#000', + * ], + * [ + * 'fromValue' => 200.0, + * 'toValue' => 300.5, + * 'color' => '#F0F0F0', + * ], + * ] + * ] + */ + private static function extractDynamicData(array $data): array + { + $dynamicDataEncoded = static::notEmptyStringOr($data['label'], null); + + if ($dynamicDataEncoded === null) { + throw new \InvalidArgumentException('dynamic data not found'); + } + + $result = []; + + try { + $dynamicData = \json_decode($dynamicDataEncoded, true); + + $result['defaultColor'] = $dynamicData['default_color']; + $result['colorRanges'] = []; + + if (\is_array($dynamicData['color_ranges']) === true) { + foreach ($dynamicData['color_ranges'] as $colorRange) { + if (\is_numeric($colorRange['from_value']) === true + && \is_numeric($colorRange['to_value']) === true + && static::notEmptyStringOr( + $colorRange['color'], + null + ) !== null + ) { + $result['colorRanges'][] = [ + 'color' => $colorRange['color'], + 'fromValue' => (float) $colorRange['from_value'], + 'toValue' => (float) $colorRange['to_value'], + ]; + } + } + } + } catch (\Throwable $e) { + throw new \InvalidArgumentException('invalid dynamic data'); + } + + return $result; + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When an agent Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_modules.php'; + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Get the linked module Id. + $linkedModule = static::extractLinkedModule($data); + $moduleId = $linkedModule['moduleId']; + $metaconsoleId = $linkedModule['metaconsoleId']; + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + $dynamicData = static::extractDynamicData($data); + // Set the initial color. + $data['color'] = $dynamicData['defaultColor']; + + // Search for a matching color range. + if (empty($dynamicData['colorRanges']) === false) { + // Connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + // Fetch module value. + $value = false; + if ($metaconsoleId === null + || ($metaconsoleId !== null && $nodeConnected) + ) { + $value = \modules_get_last_value($moduleId); + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + // Value found. + if ($value !== false) { + /* + * TODO: It would be ok to give support to string values in the + * future? + * + * It can be done by matching the range value with the value + * if it is a string. I think the function to retrieve the value + * only supports numeric values. + */ + + $value = (float) $value; + foreach ($dynamicData['colorRanges'] as $colorRange) { + if ($colorRange['fromValue'] <= $value + && $colorRange['toValue'] >= $value + ) { + // Range matched. Use the range color. + $data['color'] = $colorRange['color']; + break; + } + } + } + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php new file mode 100644 index 0000000000..1adc948da2 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/DonutGraph.php @@ -0,0 +1,193 @@ +'; + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/EventsHistory.php b/pandora_console/include/rest-api/models/VisualConsole/Items/EventsHistory.php new file mode 100644 index 0000000000..cb3313d35f --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/EventsHistory.php @@ -0,0 +1,159 @@ + 'eventos', + 'sec2' => 'operation/events/events', + 'id_agent' => $agentId, + 'module_search_hidden' => $moduleId, + 'event_view_hr' => (static::extractMaxTime($data) / 3600), + 'status' => -1, + ] + ); + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Group.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Group.php new file mode 100644 index 0000000000..e627003244 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Group.php @@ -0,0 +1,462 @@ + $groupId, + 'status' => AGENT_STATUS_CRITICAL, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numCritical = $agentsCritical[0]['total']; + $agentsWarning = \agents_get_agents( + [ + 'id_grupo' => $groupId, + 'status' => AGENT_STATUS_WARNING, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numWarning = $agentsWarning[0]['total']; + $agentsUnknown = \agents_get_agents( + [ + 'id_grupo' => $groupId, + 'status' => AGENT_STATUS_UNKNOWN, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numUnknown = $agentsUnknown[0]['total']; + $agentsOk = \agents_get_agents( + [ + 'id_grupo' => $groupId, + 'status' => AGENT_STATUS_OK, + ], + ['COUNT(*) AS total'], + 'AR', + false, + false, + true, + $isMetaconsole + ); + $numNormal = $agentsOk[0]['total']; + + $numTotal = ($numCritical + $numWarning + $numUnknown + $numNormal); + $agentStats = [ + 'critical' => ($numCritical / $numTotal * 100), + 'warning' => ($numWarning / $numTotal * 100), + 'normal' => ($numNormal / $numTotal * 100), + 'unknown' => ($numUnknown / $numTotal * 100), + ]; + + $groupName = \groups_get_name($groupId, true); + $data['html'] = static::printStatsTable( + $groupName, + $agentStats, + (int) $data['width'], + (int) $data['height'] + ); + } else { + if (\is_metaconsole()) { + $groupFilter = $groupId; + if ($groupId === 0) { + $groupFilter = implode( + ',', + array_keys(\users_get_groups()) + ); + } + + $sql = sprintf( + 'SELECT + SUM(fired_count) AS fired, + SUM(critical_count) AS critical, + SUM(warning_count) AS warning, + SUM(unknown_count) AS unknown + FROM tmetaconsole_agent + LEFT JOIN tmetaconsole_agent_secondary_group tasg + ON id_agente = tasg.id_agent + WHERE id_grupo IN (%s) + OR tasg.id_group IN (%s)', + $groupFilter, + $groupFilter + ); + + $countStatus = \db_get_row_sql($sql); + + if ($countStatus['fired'] > 0) { + $status = AGENT_STATUS_ALERT_FIRED; + } else if ($countStatus['critical'] > 0) { + $status = AGENT_STATUS_CRITICAL; + } else if ($countStatus['warning'] > 0) { + $status = AGENT_STATUS_WARNING; + } else if ($countStatus['unknown'] > 0) { + $status = AGENT_STATUS_UNKNOWN; + } else { + $status = AGENT_STATUS_NORMAL; + } + } else { + // Get the status img src. + $status = \groups_get_status($groupId); + } + + $imagePath = \visual_map_get_image_status_element($data, $status); + $data['statusImageSrc'] = \ui_get_full_url( + $imagePath, + false, + false, + false + ); + + // If the width or the height are equal to 0 we will extract them + // from the real image size. + $width = (int) $data['width']; + $height = (int) $data['height']; + if ($width === 0 || $height === 0) { + // TODO: This will be the default behaviour after we finish the + // builder. Don't delete this code. + // $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + // $data['width'] = $sizeImage[0]; + // $data['height'] = $sizeImage[1]; + $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + $imageHeight = $sizeImage[1]; + + if ($width === 0) { + $data['width'] = 70; + } + + if ($height === 0) { + $data['height'] = ($imageHeight > 70) ? 70 : $imageHeight; + } + } + + $data['html'] = ''; + } + + return $data; + } + + + /** + * HTML representation for the agent stats of a group. + * + * @param string $groupName Group name. + * @param array $agentStats Data structure with the agent statistics. + * @param integer $width Width. + * @param integer $height Height. + * + * @return string HTML representation. + */ + private static function printStatsTable( + string $groupName, + array $agentStats, + int $width=520, + int $height=80 + ): string { + $width = ($width > 0) ? $width : 520; + $height = ($height > 0) ? $height : 80; + + $tableStyle = \join( + [ + 'width:'.$width.'px;', + 'height:'.$height.'px;', + 'text-align:center;', + ] + ); + $headStyle = \join( + [ + 'text-align:center;', + 'background-color:#9d9ea0;', + 'color:black;', + 'font-weight:bold;', + ] + ); + $valueStyle = \join( + [ + 'margin-left: 2%;', + 'color: #FFF;', + 'font-size: 12px;', + 'display: inline;', + 'background-color: #FC4444;', + 'position: relative;', + 'height: 80%;', + 'width: 9.4%;', + 'height: 80%;', + 'border-radius: 2px;', + 'text-align: center;', + 'padding: 5px;', + ] + ); + $nameStyle = \join( + [ + 'background-color: white;', + 'color: black;', + 'font-size: 12px;', + 'display: inline;', + 'display: inline;', + 'position:relative;', + 'width: 9.4%;', + 'height: 80%;', + 'border-radius: 2px;', + 'text-align: center;', + 'padding: 5px;', + ] + ); + + $html = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= '
    '.$groupName.'
    '; + + // Critical. + $html .= '
    '; + $html .= \number_format($agentStats['critical'], 2).'%'; + $html .= '
    '; + $html .= '
    '.__('Critical').'
    '; + // Warning. + $html .= '
    '; + $html .= \number_format($agentStats['warning'], 2).'%'; + $html .= '
    '; + $html .= '
    '.__('Warning').'
    '; + // Normal. + $html .= '
    '; + $html .= \number_format($agentStats['normal'], 2).'%'; + $html .= '
    '; + $html .= '
    '.__('Normal').'
    '; + // Unknown. + $html .= '
    '; + $html .= \number_format($agentStats['unknown'], 2).'%'; + $html .= '
    '; + $html .= '
    '.__('Unknown').'
    '; + + $html .= '
    '; + + return $html; + } + + + /** + * Generate a link to something related with the item. + * + * @param array $data Visual Console Item's data structure. + * + * @return mixed The link or a null value. + * + * @override Item::buildLink. + */ + protected static function buildLink(array $data) + { + // This will return the link to a linked VC if this item has one. + $link = parent::buildLink($data); + if ($link !== null) { + return $link; + } + + global $config; + + $groupId = static::extractGroupId($data); + $baseUrl = $config['homeurl'].'index.php'; + + if (\is_metaconsole()) { + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'monitoring', + 'sec2' => 'operation/tree', + 'group_id' => $groupId, + ] + ); + } + + return $baseUrl.'?'.http_build_query( + [ + 'sec' => 'estado', + 'sec2' => 'operation/agentes/estado_agente', + 'group_id' => $groupId, + ] + ); + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Icon.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Icon.php new file mode 100644 index 0000000000..29605ffb4f --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Icon.php @@ -0,0 +1,144 @@ + 70) ? 70 : $imageHeight; + } + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Label.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Label.php new file mode 100644 index 0000000000..41c9c6c6d1 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Label.php @@ -0,0 +1,64 @@ +validateData. + */ + protected function validateData(array $data): void + { + parent::validateData($data); + if (static::notEmptyStringOr(static::issetInArray($data, ['label']), null) === null) { + throw new \InvalidArgumentException( + 'the label property is required and should be a not empty string' + ); + } + } + + + /** + * Returns a valid representation of the model. + * + * @param array $data Input data. + * + * @return array Data structure representing the model. + * + * @overrides Item->decode. + */ + protected function decode(array $data): array + { + $return = parent::decode($data); + $return['type'] = LABEL; + return $return; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Line.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Line.php new file mode 100644 index 0000000000..fd6f64b4cb --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Line.php @@ -0,0 +1,209 @@ +validateData. + */ + protected function validateData(array $data): void + { + if (isset($data['id']) === false + || \is_numeric($data['id']) === false + ) { + throw new \InvalidArgumentException( + 'the Id property is required and should be integer' + ); + } + + if (isset($data['type']) === false + || \is_numeric($data['type']) === false + ) { + throw new \InvalidArgumentException( + 'the Id property is required and should be integer' + ); + } + } + + + /** + * Returns a valid representation of the model. + * + * @param array $data Input data. + * + * @return array Data structure representing the model. + * + * @overrides Model->decode. + */ + protected function decode(array $data): array + { + return [ + 'id' => (int) $data['id'], + 'type' => LINE_ITEM, + 'startX' => static::extractStartX($data), + 'startY' => static::extractStartY($data), + 'endX' => static::extractEndX($data), + 'endY' => static::extractEndY($data), + 'isOnTop' => static::extractIsOnTop($data), + 'borderWidth' => static::extractBorderWidth($data), + 'borderColor' => static::extractBorderColor($data), + ]; + } + + + /** + * Extract a x axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid x axis of the start position of the line. + */ + private static function extractStartX(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['startX', 'pos_x']), + 0 + ); + } + + + /** + * Extract a y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid y axis of the start position of the line. + */ + private static function extractStartY(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['startY', 'pos_y']), + 0 + ); + } + + + /** + * Extract a x axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid x axis of the end position of the line. + */ + private static function extractEndX(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['endX', 'width']), + 0 + ); + } + + + /** + * Extract a y axis value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid y axis of the end position of the line. + */ + private static function extractEndY(array $data): int + { + return static::parseIntOr( + static::issetInArray($data, ['endY', 'height']), + 0 + ); + } + + + /** + * Extract a conditional value which tells if the item has visual priority. + * + * @param array $data Unknown input data structure. + * + * @return boolean If the item is on top or not. + */ + private static function extractIsOnTop(array $data): bool + { + return static::parseBool( + static::issetInArray($data, ['isOnTop', 'show_on_top']) + ); + } + + + /** + * Extract a border width value. + * + * @param array $data Unknown input data structure. + * + * @return integer Valid border width. 0 by default and minimum value. + */ + private static function extractBorderWidth(array $data): int + { + $borderWidth = static::parseIntOr( + static::issetInArray($data, ['borderWidth', 'border_width']), + 0 + ); + + return ($borderWidth >= 0) ? $borderWidth : 0; + } + + + /** + * Extract a border color value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the border color (not empty) or null. + */ + private static function extractBorderColor(array $data) + { + return static::notEmptyStringOr( + static::issetInArray($data, ['borderColor', 'border_color']), + null + ); + } + + + /** + * Obtain a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console line data structure stored into the DB. + * @throws \Exception When the data cannot be retrieved from the DB. + * + * @override Model::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $row = \db_get_row_filter('tlayout_data', $filter); + + if ($row === false) { + throw new \Exception('error fetching the data from the DB'); + } + + return $row; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php new file mode 100644 index 0000000000..d89a01efff --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php @@ -0,0 +1,274 @@ + $period, + 'width' => $data['width'], + 'height' => ($data['height'] - 30), + 'title' => '', + 'unit_name' => null, + 'show_alerts' => false, + 'only_image' => $imageOnly, + 'vconsole' => true, + 'backgroundColor' => $backgroundType, + ]; + + $paramsCombined = [ + 'id_graph' => $customGraphId, + 'stacked' => $customGraph['stacked'], + 'summatory' => $customGraph['summatory_series'], + 'average' => $customGraph['average_series'], + 'modules_series' => $customGraph['modules_series'], + ]; + + $data['html'] = \graphic_combined_module( + false, + $params, + $paramsCombined + ); + } else { + // Module graph. + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + $params = [ + 'agent_module_id' => $moduleId, + 'period' => $period, + 'show_events' => false, + 'width' => $data['width'], + 'height' => ($data['height'] - 30), + 'title' => \modules_get_agentmodule_name($moduleId), + 'unit' => \modules_get_unit($moduleId), + 'only_image' => $imageOnly, + 'menu' => false, + 'backgroundColor' => $backgroundType, + 'type_graph' => $graphType, + 'vconsole' => true, + ]; + + $data['html'] = \grafico_modulo_sparse($params); + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/Percentile.php b/pandora_console/include/rest-api/models/VisualConsole/Items/Percentile.php new file mode 100644 index 0000000000..d64c51bc4d --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/Percentile.php @@ -0,0 +1,245 @@ +validateData. + */ + protected function validateData(array $data): void + { + parent::validateData($data); + if (isset($data['value']) === false) { + throw new \InvalidArgumentException( + 'the value property is required and should be string' + ); + } + } + + + /** + * Returns a valid representation of the model. + * + * @param array $data Input data. + * + * @return array Data structure representing the model. + * + * @overrides Item->decode. + */ + protected function decode(array $data): array + { + $return = parent::decode($data); + $return['type'] = SIMPLE_VALUE; + $return['processValue'] = static::extractProcessValue($data); + $return['valueType'] = static::extractValueType($data); + $return['value'] = $data['value']; + + if ($return['processValue'] !== 'none') { + $return['period'] = static::extractPeriod($data); + } + + // Clear the size, as this element always have a dynamic size. + $return['width'] = 0; + $return['height'] = 0; + + return $return; + } + + + /** + * Extract a process value. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'none', 'avg', 'max' or 'min'. 'none' by default. + */ + private static function extractProcessValue(array $data): string + { + if (isset($data['processValue'])) { + switch ($data['processValue']) { + case 'none': + case 'avg': + case 'max': + case 'min': + return $data['processValue']; + + default: + return 'none'; + } + } else { + switch ($data['type']) { + case SIMPLE_VALUE_MAX: + return 'max'; + + case SIMPLE_VALUE_MIN: + return 'min'; + + case SIMPLE_VALUE_AVG: + return 'avg'; + + default: + return 'none'; + } + } + } + + + /** + * Extract the value of period. + * + * @param array $data Unknown input data structure. + * + * @return integer The period in seconds. 0 is the minimum value. + */ + private static function extractPeriod(array $data): int + { + $period = static::parseIntOr( + static::issetInArray($data, ['period']), + 0 + ); + return ($period >= 0) ? $period : 0; + } + + + /** + * Extract a value type. + * + * @param array $data Unknown input data structure. + * + * @return string One of 'string' or 'image'. 'string' by default. + */ + private static function extractValueType(array $data): string + { + switch ($data['valueType']) { + case 'string': + case 'image': + return $data['valueType']; + + default: + return 'string'; + } + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When a module Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_visual_map.php'; + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Get the linked module Id. + $linkedModule = static::extractLinkedModule($data); + $moduleId = static::parseIntOr($linkedModule['moduleId'], null); + $metaconsoleId = static::parseIntOr( + $linkedModule['metaconsoleId'], + null + ); + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + // Maybe connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + // Get the formatted value. + $value = \visual_map_get_simple_value( + $data['type'], + $moduleId, + static::extractPeriod($data) + ); + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + + // Some modules are image based. Extract the base64 image if needed. + $matches = []; + if (\preg_match('/src=\"(data:image.*)"/', $value, $matches) === 1) { + $data['valueType'] = 'image'; + $data['value'] = $matches[1]; + } else { + $data['valueType'] = 'string'; + $data['value'] = $value; + } + + return $data; + } + + +} diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/StaticGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/StaticGraph.php new file mode 100644 index 0000000000..9af4287d26 --- /dev/null +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/StaticGraph.php @@ -0,0 +1,256 @@ +decode. + */ + protected function decode(array $data): array + { + $return = parent::decode($data); + $return['type'] = STATIC_GRAPH; + $return['imageSrc'] = static::extractImageSrc($data); + $return['showLastValueTooltip'] = static::extractShowLastValueTooltip( + $data + ); + $return['statusImageSrc'] = static::notEmptyStringOr( + static::issetInArray($data, ['statusImageSrc']), + null + ); + $return['lastValue'] = static::notEmptyStringOr( + static::issetInArray($data, ['lastValue']), + null + ); + + return $return; + } + + + /** + * Extract a image src value. + * + * @param array $data Unknown input data structure. + * + * @return mixed String representing the image url (not empty) or null. + * + * @throws \InvalidArgumentException When a valid image src can't be found. + */ + private static function extractImageSrc(array $data): string + { + $imageSrc = static::notEmptyStringOr( + static::issetInArray($data, ['imageSrc', 'image']), + null + ); + + if ($imageSrc === null) { + throw new \InvalidArgumentException( + 'the image src property is required and should be a non empty string' + ); + } + + return $imageSrc; + } + + + /** + * Extract the value of showLastValueTooltip and + * return 'default', 'enabled' or 'disabled'. + * + * @param array $data Unknown input data structure. + * + * @return string + */ + private static function extractShowLastValueTooltip(array $data): string + { + $showLastValueTooltip = static::notEmptyStringOr( + static::issetInArray($data, ['showLastValueTooltip']), + null + ); + + if ($showLastValueTooltip === null) { + $showLastValueTooltip = static::parseIntOr( + static::issetInArray($data, ['show_last_value']), + null + ); + switch ($showLastValueTooltip) { + case 1: + return 'enabled'; + + case 2: + return 'disabled'; + + default: + return 'default'; + } + } else { + switch ($showLastValueTooltip) { + case 'enabled': + return 'enabled'; + + case 'disabled': + return 'disabled'; + + default: + return 'default'; + } + } + } + + + /** + * Fetch a vc item data structure from the database using a filter. + * + * @param array $filter Filter of the Visual Console Item. + * + * @return array The Visual Console Item data structure stored into the DB. + * @throws \InvalidArgumentException When an agent Id cannot be found. + * + * @override Item::fetchDataFromDB. + */ + protected static function fetchDataFromDB(array $filter): array + { + // Due to this DB call, this function cannot be unit tested without + // a proper mock. + $data = parent::fetchDataFromDB($filter); + + /* + * Retrieve extra data. + */ + + // Load side libraries. + global $config; + include_once $config['homedir'].'/include/functions_ui.php'; + include_once $config['homedir'].'/include/functions_io.php'; + include_once $config['homedir'].'/include/functions_visual_map.php'; + include_once $config['homedir'].'/include/functions_modules.php'; + if (is_metaconsole()) { + \enterprise_include_once('include/functions_metaconsole.php'); + } + + // Get the linked module Id. + $linkedModule = static::extractLinkedModule($data); + $moduleId = $linkedModule['moduleId']; + $metaconsoleId = $linkedModule['metaconsoleId']; + + if ($moduleId === null) { + throw new \InvalidArgumentException('missing module Id'); + } + + // Get the img src. + // There's no need to connect to the metaconsole before searching for + // the image status cause the function itself does that for us. + $imagePath = \visual_map_get_image_status_element($data); + $data['statusImageSrc'] = \ui_get_full_url( + $imagePath, + false, + false, + false + ); + + // If the width or the height are equal to 0 we will extract them + // from the real image size. + $width = (int) $data['width']; + $height = (int) $data['height']; + if ($width === 0 || $height === 0) { + // TODO: This will be the default behaviour after we finish the + // builder. Don't delete this code. + // $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + // $data['width'] = $sizeImage[0]; + // $data['height'] = $sizeImage[1]; + // Default value. Will be replaced by a dynamic image size + // calculation after the phase 3. + $sizeImage = getimagesize($config['homedir'].'/'.$imagePath); + $imageHeight = $sizeImage[1]; + + if ($width === 0) { + $data['width'] = 70; + } + + if ($height === 0) { + $data['height'] = ($imageHeight > 70) ? 70 : $imageHeight; + } + } + + // Get last value. + $showLastValueTooltip = static::extractShowLastValueTooltip($data); + if ($showLastValueTooltip !== 'disabled' && $moduleId > 0) { + // Maybe connect to node. + $nodeConnected = false; + if (\is_metaconsole() === true && $metaconsoleId !== null) { + $nodeConnected = \metaconsole_connect( + null, + $metaconsoleId + ) === NOERR; + + if ($nodeConnected === false) { + throw new \InvalidArgumentException( + 'error connecting to the node' + ); + } + } + + $imgTitle = ''; + + $unit = \trim(\io_safe_output(\modules_get_unit($moduleId))); + $value = \modules_get_last_value($moduleId); + + $isBooleanModule = \modules_is_boolean($moduleId); + if (!$isBooleanModule + || ($isBooleanModule && $showLastValueTooltip !== 'default') + ) { + if (\is_numeric($value)) { + $imgTitle .= __('Last value: ').\remove_right_zeros(\number_format((float) $value, (int) $config['graph_precision'])); + } else { + $imgTitle .= __('Last value: ').$value; + } + + if (empty($unit) === false && empty($imgTitle) === false) { + $imgTitle .= ' '.$unit; + } + + $data['lastValue'] = $imgTitle; + } + + // Restore connection. + if ($nodeConnected === true) { + \metaconsole_restore_db(); + } + } + + return $data; + } + + +} diff --git a/pandora_console/include/styles/agent_manager.css b/pandora_console/include/styles/agent_manager.css new file mode 100644 index 0000000000..fc648aefba --- /dev/null +++ b/pandora_console/include/styles/agent_manager.css @@ -0,0 +1,158 @@ +/* + * --------------------------------------------------------------------- + * - Create/Update Agent - agent_manager.php + * --------------------------------------------------------------------- + */ +.agent_options { + width: 100%; + margin-right: 0px; + display: flex; + flex-flow: row wrap; + flex-direction: row; + justify-content: space-between; + box-sizing: border-box; +} + +.agent_options_update { + width: 85%; + margin-right: 20px; +} + +.agent_options_column_left, +.agent_options_column_right { + width: 50%; + box-sizing: border-box; +} + +.agent_options_column_left { + padding-right: 50px; +} + +.agent_qr { + width: 15%; + text-align: center; + box-sizing: border-box; + padding: 20px 0px; + display: flex; + justify-content: center; + flex-direction: column; + min-width: 150px; +} + +a#qr_code_agent_view { + margin-top: 5px; +} + +.first_row .agent_options_column_right select, +.first_row .agent_options_column_right input, +.first_row .agent_options_column_left select#grupo { + width: 95%; + box-sizing: border-box; +} + +.first_row .agent_options_column_left .p-switch { + margin-right: 5px; + margin-left: 10px; +} + +.agent_options_column_left input#text-direccion, +.agent_options_column_left select#address_list, +.agent_options_column_left input#text-agente, +.agent_options_column_left input#text-alias { + width: 100%; + box-sizing: border-box; +} + +.agent_options_agent_name > * { + margin-left: 5px; +} + +.custom_fields_table { + border-spacing: 0px; +} + +.custom_fields_table tr.datos { + background-color: #f7f7f7; + display: none; +} + +.custom_fields_table tr.datos2 { + cursor: pointer; +} + +.custom_fields_table tr td { + border: 1px solid #e0e0e0; +} + +.custom_fields_table tr.datos2 td div.field_title { + padding: 10px; +} + +.custom_fields_table tr.datos2 td { + border-radius: 4px; + padding: 0px; +} + +.custom_fields_table tr.datos td { + padding: 15px 10px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + +.custom_fields_table .custom_field_row_opened td { + border-bottom-left-radius: 0px !important; + border-bottom-right-radius: 0px !important; +} + +.secondary_groups_select { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 15px; +} + +.secondary_groups_select .secondary_groups_select_arrows input { + display: grid; + margin: 0 auto; +} + +.secondary_groups_select .secondary_groups_list_left { + text-align: right; + width: 50%; +} + +.secondary_groups_select .secondary_groups_list_right { + text-align: left; + width: 50%; +} + +.secondary_groups_select .secondary_groups_select_arrows { + padding: 0 50px; +} + +.secondary_groups_select_arrows a { + display: block; +} + +.agent_options_adv .agent_options_column_right .label_select select, +.agent_options_adv .agent_options_column_right .label_select input[type="text"], +.agent_options_adv #text-custom_id, +.agent_options_adv #cps { + width: 100%; + box-sizing: border-box; +} + +.agent_options_adv .label_select_simple.label_simple_one_item .p-switch { + margin-right: 5px; +} + +.agent_description { + min-height: 4.8em !important; +} +.agent_custom_id { + padding-bottom: 0.7em; + padding-top: 0.5em; + display: inline-block; + border-radius: 5px !important; + border: 1px solid #ccc; +} diff --git a/pandora_console/include/styles/agent_view.css b/pandora_console/include/styles/agent_view.css new file mode 100644 index 0000000000..fca8aaa4fc --- /dev/null +++ b/pandora_console/include/styles/agent_view.css @@ -0,0 +1,19 @@ +text.text-tooltip { + font-family: "lato-bolder", "Open Sans", sans-serif !important; +} + +div#bullets_modules span { + font-family: "lato-bolder", "Open Sans", sans-serif !important; +} + +table#agent_interface_info .noresizevc.graph { + width: 500px !important; +} + +div.agent_details_agent_alias { + flex: 1; +} + +div.agent_details_agent_alias * { + font-family: "lato-bolder", "Open Sans", sans-serif !important; +} diff --git a/pandora_console/include/styles/cluster_view.css b/pandora_console/include/styles/cluster_view.css index 74e30f1a8c..4d8f02ccb9 100644 --- a/pandora_console/include/styles/cluster_view.css +++ b/pandora_console/include/styles/cluster_view.css @@ -58,7 +58,7 @@ overflow-y: scroll; } -#cluster_map { +#simple_map { border: 1px solid lightgray; width: 900px; height: 500px; diff --git a/pandora_console/include/styles/discovery.css b/pandora_console/include/styles/discovery.css index 36711a8474..0209a43a8e 100644 --- a/pandora_console/include/styles/discovery.css +++ b/pandora_console/include/styles/discovery.css @@ -27,6 +27,11 @@ li.discovery > a label { cursor: pointer; } +div.data_container > label { + font-family: "lato-bolder", "Open Sans", sans-serif !important; + font-weight: lighter; +} + div.data_container { width: 100%; height: 100%; @@ -49,7 +54,6 @@ div.data_container:hover { h1.wizard { padding: 0; margin: 0; - margin-top: -1.25em; } h1.wizard a { margin-left: -20px; @@ -65,7 +69,6 @@ h1.wizard a:hover { div.arrow_box { display: inline-block; position: relative; - background: #ccc; color: #888; padding: 1.3em; margin-left: 20px; @@ -73,16 +76,6 @@ div.arrow_box { padding-left: 3em; } -.arrow_box:before { - top: 50%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; - z-index: 1; -} .arrow_box.selected { background: #424242; color: #ccc; @@ -111,3 +104,156 @@ div.arrow_box:before { .arrow_box:hover { color: #000; } + +/* + * Breadcrum + */ + +#menu_tab_frame_view_bc { + display: flex; + justify-content: space-between; + border-bottom: 2px solid #82b92e; + max-height: 70px; + min-height: 55px; + width: 100%; + padding-right: 0px; + margin-left: 0px !important; + margin-bottom: 20px; + height: 55px; + box-sizing: border-box; + background-color: #fafafa; + border-top-right-radius: 7px; + border-top-left-radius: 7px; + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.1); +} + +#menu_tab_frame_view_bc .breadcrumbs_container { + align-self: flex-start; +} + +.breadcrumbs_container { + padding-left: 10px; + padding-top: 4px; + text-indent: 0.25em; +} + +.breadcrumb_link { + color: #848484; + font-size: 10pt !important; + font-family: "lato-bolder", "Open Sans", sans-serif !important; + text-decoration: none !important; +} + +span.breadcrumb_link { + color: #d0d0d0; + font-size: 12pt !important; +} + +.breadcrumb_link.selected { + color: #95b750; +} + +.breadcrumb_link.selected:hover { + color: #95b750; +} +.breadcrumb_link:hover { + color: #95b750; +} + +/* + * Discovery forms structure + */ + +form.discovery * { + font-size: 10pt; +} + +.edit_discovery_info { + display: flex; + align-items: flex-start; + padding-top: 25px; +} + +.edit_discovery_input { + align-items: center; + margin-bottom: 25px; +} + +/* + * Discovery text inputs + */ + +.discovery_label_hint { + display: flex; +} + +label { + color: #343434 !important; + font-weight: bold; +} + +.discovery_full_width_input { + width: 100%; +} + +li > input[type="text"], +li > input[type="password"], +.discovery_text_input > input[type="password"], +.discovery_text_input > input[type="text"], +#interval_manual > input[type="text"] { + background-color: transparent !important; + border: none; + border-radius: 0 !important; + border-bottom: 1px solid #ccc; + font-family: "lato-bolder", "Open Sans", sans-serif !important; + font-weight: lighter; + padding: 0px 0px 2px 0px; + box-sizing: border-box; + margin-bottom: 4px; +} + +#interval_manual > input[type="text"] { + width: 50px; + margin-left: 10px; + margin-right: 10px; +} + +.discovery_list_input { + width: 100%; + border: 1px solid #cbcbcb; + overflow-y: auto; +} + +.discovery_list_input option { + text-align: left; +} + +.discovery_list_input option:checked { + background: #1aab8e -webkit-linear-gradient(bottom, #7db742 0%, #7db742 100%); + color: #fff; +} + +.discovery_textarea_input { + background-color: #fbfbfb !important; + padding-left: 10px; + width: 100%; + height: 100px; + max-height: 100px; + max-width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + resize: none; +} + +a.tip { + margin-left: 8px; +} + +.inline_switch > label { + float: right; +} + +.discovery_interval_select_width { + width: 90%; +} diff --git a/pandora_console/include/styles/events.css b/pandora_console/include/styles/events.css new file mode 100644 index 0000000000..20b1f59d15 --- /dev/null +++ b/pandora_console/include/styles/events.css @@ -0,0 +1,20 @@ +div.criticity { + width: 150px; + height: 2em; + color: #fff; + text-align: center; + border-radius: 5px; + font-size: 0.8em; + padding: 3px; + margin: 0; + display: table-cell; + vertical-align: middle; +} + +div.mini-criticity { + width: 5px; + height: 4em; + padding: 0; + margin: 0; + display: inline-block; +} diff --git a/pandora_console/include/styles/firts_task.css b/pandora_console/include/styles/firts_task.css index 32be0eb832..1b26b2bfbe 100755 --- a/pandora_console/include/styles/firts_task.css +++ b/pandora_console/include/styles/firts_task.css @@ -72,6 +72,7 @@ div.new_task_cluster > div { } #description_task { font-size: 12px; + line-height: 1.8em; } #fuerte { diff --git a/pandora_console/include/styles/js/cluetip.css b/pandora_console/include/styles/js/cluetip.css index dea766762b..233dbf3cbb 100644 --- a/pandora_console/include/styles/js/cluetip.css +++ b/pandora_console/include/styles/js/cluetip.css @@ -13,7 +13,7 @@ width: 43px; height: 11px; position: absolute; - background-image: url(../../images/wait.gif); + background-image: url(../../../images/wait.gif); } .cluetip-arrows { display: none; diff --git a/pandora_console/include/styles/js/jquery-ui_custom.css b/pandora_console/include/styles/js/jquery-ui_custom.css index ca2cdb51e7..f5bd560d69 100644 --- a/pandora_console/include/styles/js/jquery-ui_custom.css +++ b/pandora_console/include/styles/js/jquery-ui_custom.css @@ -70,13 +70,13 @@ float: right; } .ui-dialog .ui-dialog-buttonpane button { - margin: 0.5em 1em 0.5em 0 !important; - cursor: pointer !important; - background: white !important; - background-color: white !important; - border: 1px solid #82b92e !important; - height: 30px !important; - width: 90px !important; + margin: 0.5em 1em 0.5em 0; + cursor: pointer; + background: white; + background-color: white; + border: 1px solid #82b92e; + height: 30px; + width: 90px; } .ui-widget-header .ui-icon { background-image: url(../images/ui-icons_444444_256x240.png) !important; diff --git a/pandora_console/include/styles/js/jquery.contextMenu.css b/pandora_console/include/styles/js/jquery.contextMenu.css index 64d86cf7bc..7343aa5de1 100644 --- a/pandora_console/include/styles/js/jquery.contextMenu.css +++ b/pandora_console/include/styles/js/jquery.contextMenu.css @@ -107,61 +107,61 @@ background-position: 4px 2px; } .context-menu-item.icon-edit { - background-image: url(../../images/page_white_edit.png); + background-image: url(../../../images/page_white_edit.png); } .context-menu-item.icon-cut { - background-image: url(../../images/cut.png); + background-image: url(../../../images/cut.png); } .context-menu-item.icon-copy { - background-image: url(../../images/page_white_copy.png); + background-image: url(../../../images/page_white_copy.png); } .context-menu-item.icon-paste { - background-image: url(../../images/page_white_paste.png); + background-image: url(../../../images/page_white_paste.png); } .context-menu-item.icon-delete { - background-image: url(../../images/delete.png); + background-image: url(../../../images/delete.png); } .context-menu-item.icon-add { - background-image: url(../../images/page_white_add.png); + background-image: url(../../../images/page_white_add.png); } .context-menu-item.icon-quit { - background-image: url(../../images/door.png); + background-image: url(../../../images/door.png); } .context-menu-item.icon-refresh { - background-image: url(../../images/refresh.png); + background-image: url(../../../images/refresh.png); } .context-menu-item.icon-center { - background-image: url(../../images/set_center.png); + background-image: url(../../../images/set_center.png); } .context-menu-item.icon-details { - background-image: url(../../images/show_details.png); + background-image: url(../../../images/show_details.png); } .context-menu-item.icon-children { - background-image: url(../../images/children.png); + background-image: url(../../../images/children.png); } .context-menu-item.icon-cancel_set_parent { - background-image: url(../../images/link_delete.png); + background-image: url(../../../images/link_delete.png); } .context-menu-item.icon-set_parent { - background-image: url(../../images/father.png); + background-image: url(../../../images/father.png); } .context-menu-item.icon-add_node { - background-image: url(../../images/add.png); + background-image: url(../../../images/add.png); } .context-menu-item.icon-refresh_holding_area { - background-image: url(../../images/refresh_holding_area.png); + background-image: url(../../../images/refresh_holding_area.png); } .context-menu-item.icon-restart_map { - background-image: url(../../images/reset.png); + background-image: url(../../../images/reset.png); } .context-menu-item.icon-interface_link_children { - background-image: url(../../images/icono_link_hijo.png); + background-image: url(../../../images/icono_link_hijo.png); } .context-menu-item.icon-interface_link_parent { - background-image: url(../../images/icono_link_padre.png); + background-image: url(../../../images/icono_link_padre.png); } .context-menu-item.icon-interface_link_cancel { - background-image: url(../../images/link_abortar.png); + background-image: url(../../../images/link_abortar.png); } /* vertically align inside labels */ diff --git a/pandora_console/include/styles/menu.css b/pandora_console/include/styles/menu.css index e8811ddf83..7846a3f9a6 100644 --- a/pandora_console/include/styles/menu.css +++ b/pandora_console/include/styles/menu.css @@ -31,8 +31,7 @@ .operation .menu_icon ul.submenu > li, .godmode .menu_icon ul.submenu > li { background-color: #282828; - padding-left: 10px; - padding-right: 10px; + padding-left: 1.5em; } .menu ul { @@ -58,7 +57,7 @@ li:hover ul { .submenu_text { margin-left: 0px; width: 100%; - color: #9ca4a6; + color: #b9b9b9; font-family: "Open Sans", sans-serif; font-size: 9.4pt; } @@ -71,21 +70,20 @@ li:hover ul { margin: 0px 0px 0px 0px; position: absolute; z-index: 999; - width: 214px; + width: 240px; } .submenu2 { position: absolute; z-index: 999; - left: 214px; - width: 214px; + left: 240px; + width: 240px; } .sub_subMenu { font-weight: normal !important; background-color: #202020; - padding-left: 10px; - padding-right: 10px; + padding-left: 1.5em; } .sub_subMenu.selected { @@ -262,11 +260,6 @@ ul li { } /* End */ -ul li a:hover { - color: #e2144a; -} /* Hover Styles */ -/*li ul li a { padding: 2px 5px; } Sub Menu Styles */ - /* * --------------------------------------------------------------------- * - MAIN LEFT MENU and SUBMENU - @@ -291,7 +284,7 @@ ul li a:hover { } .operation { - background-color: #343434 !important; + background-color: #3d3d3d !important; padding-top: 20px !important; } @@ -389,7 +382,7 @@ ul li a:hover { .button_collapse { height: 38px; - background-color: #6c6c6d; + background-color: #505050; width: 60px; /* This is overwritten by the classic menu (215px) */ text-align: center; color: #fff; @@ -468,11 +461,11 @@ ul li a:hover { } .header_table_classic { - padding-left: 250px !important; /* 215 + 35 */ + padding-left: 235px !important; /* 215 + 35 */ } .header_table_collapsed { - padding-left: 95px !important; /* 60 + 35 */ + padding-left: 80px !important; /* 60 + 35 */ } .title_menu_classic { diff --git a/pandora_console/include/styles/networkmap.css b/pandora_console/include/styles/networkmap.css new file mode 100644 index 0000000000..f933fa07c3 --- /dev/null +++ b/pandora_console/include/styles/networkmap.css @@ -0,0 +1,35 @@ +.node { + stroke: #fff; + stroke-width: 1px; +} + +.node_over { + stroke: #999; +} + +.node_selected { + stroke: #343434; + stroke-width: 5; +} + +.node_children { + stroke: #00f; +} + +.link { + stroke-opacity: 0.6; +} + +.link_over { + stroke: #000; + stroke-opacity: 0.6; +} + +.holding_area { + stroke: #0f0; + stroke-dasharray: 12, 3; +} + +.holding_area_link { + stroke-dasharray: 12, 3; +} diff --git a/pandora_console/include/styles/pandora.css b/pandora_console/include/styles/pandora.css index 4dca1f865c..126f5c02a7 100644 --- a/pandora_console/include/styles/pandora.css +++ b/pandora_console/include/styles/pandora.css @@ -266,6 +266,36 @@ th { letter-spacing: 0.3pt; } +/* Remove background when autocomplete */ +input:-webkit-autofill, +input:-webkit-autofill:hover, +input:-webkit-autofill:focus textarea:-webkit-autofill, +textarea:-webkit-autofill:hover textarea:-webkit-autofill:focus, +select:-webkit-autofill, +select:-webkit-autofill:hover, +select:-webkit-autofill:focus { + -webkit-box-shadow: 0 0 0px 1000px #ffffff inset !important; +} + +/* All select type multiple */ +select[multiple] option:checked { + background: #82b92e linear-gradient(0deg, #82b92e 0%, #82b92e 100%); + color: #fff !important; +} + +select option:checked { + background-color: #82b92e; + color: #fff !important; +} + +select > option:hover { + background-color: #cbcbcb; +} + +select:-internal-list-box { + border: none; +} + /* --- Font ttf --- */ @font-face { font-family: "DejaVuSerif-BoldFont"; @@ -273,7 +303,7 @@ th { } .DejaVuSerif-Bold { - font-family: DejaVuSerif-BoldFont; + font-family: DejaVuSerif-BoldFont, sans-serif; } @font-face { @@ -282,7 +312,7 @@ th { } .DejaVuSerif-BoldItalic { - font-family: DejaVuSerif-BoldItalicFont; + font-family: DejaVuSerif-BoldItalicFont, sans-serif; } @font-face { @@ -291,7 +321,7 @@ th { } .DejaVuSerif-Italic { - font-family: DejaVuSerif-ItalicFont; + font-family: DejaVuSerif-ItalicFont, sans-serif; } @font-face { @@ -300,7 +330,7 @@ th { } .DejaVuSerif { - font-family: DejaVuSerifFont; + font-family: DejaVuSerifFont, sans-serif; } @font-face { @@ -309,7 +339,7 @@ th { } .DejaVuSerifCondensed-Bold { - font-family: DejaVuSerifCondensed-BoldFont; + font-family: DejaVuSerifCondensed-BoldFont, sans-serif; } @font-face { @@ -318,7 +348,7 @@ th { } .DejaVuSerifCondensed-BoldItalic { - font-family: DejaVuSerifCondensed-BoldItalicFont; + font-family: DejaVuSerifCondensed-BoldItalicFont, sans-serif; } @font-face { @@ -327,7 +357,7 @@ th { } .DejaVuSerifCondensed-Italic { - font-family: DejaVuSerifCondensed-ItalicFont; + font-family: DejaVuSerifCondensed-ItalicFont, sans-serif; } @font-face { @@ -336,7 +366,7 @@ th { } .DejaVuSerifCondensed { - font-family: DejaVuSerifCondensedFont; + font-family: DejaVuSerifCondensedFont, sans-serif; } @font-face { @@ -345,7 +375,7 @@ th { } .FreeSans { - font-family: FreeSansFont; + font-family: FreeSansFont, sans-serif; } @font-face { @@ -354,7 +384,7 @@ th { } .FreeSansBold { - font-family: FreeSansBoldFont; + font-family: FreeSansBoldFont, sans-serif; } @font-face { @@ -363,7 +393,7 @@ th { } .smallfont { - font-family: smallfontFont; + font-family: smallfontFont, sans-serif; } @font-face { @@ -391,7 +421,7 @@ th { src: url("../../fonts/leaguegothic.woff") format("woff"); } .unicode { - font-family: unicodeFont; + font-family: unicodeFont, sans-serif; } /* @@ -399,15 +429,61 @@ th { * - GLOBAL STYLES - * --------------------------------------------------------------------- */ +.w10p { + max-width: 10%; +} + +.w20p { + max-width: 20%; +} + +.w30p { + max-width: 30%; +} + +.w40p { + max-width: 40%; +} + +.w50p { + max-width: 50%; +} + +.w100p { + max-width: 100%; +} + +.no-padding { + padding: 0; +} +.no-padding-imp { + padding: 0 !important; +} +.no-margin { + margin: 0; +} +.box-shadow { + box-shadow: 0px 0px 15px -4px #dadada; +} +.align-top td { + vertical-align: top; +} +.no-td-borders td { + border: none !important; +} +.no-td-padding td { + padding: 0 !important; +} + div#page { - background: #fff; + background: #fbfbfb; background-image: none; clear: both; width: auto; - margin-top: 20px !important; + padding-top: 5px !important; padding-left: 60px; /* This is overwritten by the classic menu (215px)*/ - margin-right: 35px; - margin-left: 35px; + padding-right: 35px; + margin-left: 20px; } body.pure { @@ -491,14 +567,18 @@ input.sub { font-weight: normal; -moz-border-radius: 2px; -webkit-border-radius: 2px; + height: auto !important; border-radius: 2px; - font-size: 8pt; - background-color: #333 !important; - background-repeat: no-repeat !important; - background-position: 92% 3px !important; - color: white !important; - padding: 3px 3px 5px 12px; - border-color: #333; + font-size: 1.2em; + background-color: #fff; + background-repeat: no-repeat; + background-position: 92% 10px; + color: #000; + padding-bottom: 10px; + padding-top: 10px; + padding-left: 15px; + border-color: #888; + font-family: "lato", "Open Sans", sans-serif !important; } input.sub[disabled] { @@ -647,11 +727,10 @@ a.footer span { } div#foot { - padding-top: 8px; - padding-bottom: 5px; + padding-top: 10px; + padding-bottom: 10px; text-align: center; background: #343434; - height: 30px; clear: both; width: auto; } @@ -659,7 +738,7 @@ div#foot { div#foot a, div#foot span { font-family: "Open Sans", sans-serif; - font-size: 8.5pt; + font-size: 8pt; color: #9ca4a6; } @@ -999,22 +1078,6 @@ div#cont { background-color: #80BA27 !important; }*/ -/*tr.datos, tr.datost, tr.datosb , tr.datos_id, -tr.datosf9 { - #background-color: #eaeaea; -} - -tr.datos2, tr.datos2t, -tr.datos2b, tr.datos2_id , tr.datos2f9 { - #background-color: #f2f2f2; -} - -tr.datos:hover, tr.datost:hover, tr.datosb:hover, tr.datos_id:hover, -tr.datosf9:hover, tr.datos2:hover, tr.datos2t:hover, -tr.datos2b:hover, tr.datos2_id:hover, tr.datos2f9:hover { - #background-color: #efefef; -}*/ - /* * --------------------------------------------------------------------- * - REPORTS - @@ -1031,17 +1094,26 @@ tr.datos2b:hover, tr.datos2_id:hover, tr.datos2f9:hover { } td.datos3, -td.datos3 * { - background-color: #666; - color: white !important; +td.datos4 { + background-color: #fff; + color: #000 !important; + border-bottom: 2px solid #82b92e !important; + border-left: none !important; + border-right: none !important; + height: 30px; + font-size: 8.6pt; + font-weight: normal; } -td.datos4, -td.datos4 * { +td.datos4 { /*Add !important because in php the function html_print_table write style in cell and this is style head.*/ text-align: center !important; - background-color: #666; - color: white !important; +} + +td.datos3 *, +td.datos4 * { + font-size: 8.6pt; + font-weight: normal; } /*td.datos_id { @@ -1254,20 +1326,62 @@ div.title_line { * --------------------------------------------------------------------- */ #menu_tab_frame, -#menu_tab_frame_view { - display: block !important; +#menu_tab_frame_view, +#menu_tab_frame_view_bc { + display: flex; + align-items: flex-end; + justify-content: space-between; border-bottom: 2px solid #82b92e; - max-height: 31px; - min-height: 31px; + min-height: 50px; width: 100%; padding-right: 0px; margin-left: 0px !important; margin-bottom: 20px; - height: 31px; + height: 50px; + box-sizing: border-box; + background-color: #fafafa; + border-top-right-radius: 7px; + border-top-left-radius: 7px; + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.1); } +/* Breadcrum */ +#menu_tab_frame_view_bc { + min-height: 55px; + align-items: unset; +} + +#menu_tab_frame_view_bc .breadcrumbs_container { + align-self: flex-start; +} + +.menu_tab_left_bc { + flex-direction: column; + display: flex; + justify-content: space-between; + margin-right: 20px; + overflow: hidden; +} + +.breadcrumbs_container { + padding-left: 10px; + padding-top: 4px; + text-indent: 0.25em; + color: #848484; + font-size: 10pt !important; + font-family: "lato-bolder", "Open Sans", sans-serif !important; +} + +.breadcrumb_active { + color: #82b92e; + font-size: 10pt !important; + font-family: "lato-bolder", "Open Sans", sans-serif !important; +} +/* End - Breadcrum */ + #menu_tab { - margin: 0px 0px 0px 0px !important; + margin-right: 10px; + min-width: 510px; } #menu_tab .mn, @@ -1299,19 +1413,13 @@ div.title_line { text-decoration: none; padding: 0px; margin: 0px; + padding-top: 6px; } #menu_tab li.nomn:hover a, #menu_tab li:hover ul a:hover { color: #fff; } -#menu_tab li.nomn, -#menu_tab li.nomn_high { - min-width: 30px; - height: 28px; - margin-top: 0; -} - /* --- Tabs Submenu --- */ ul.subsubmenu { border-bottom-right-radius: 5px; @@ -1367,52 +1475,33 @@ div#agent_wizard_subtabs { */ /* TAB TITLE */ #menu_tab_left { - margin-left: 0px !important; + max-width: 60%; + margin-right: 20px; } #menu_tab_left .mn, #menu_tab_left ul, #menu_tab_left .mn ul { - background-color: #000; - color: #fff; - font-weight: bold; + color: #343434; padding: 0px 0px 0px 0px; list-style: none; margin: 0px 0px 0px 0px; } -#menu_tab_left .mn li { - float: left; - position: relative; - height: 26px; - max-height: 26px; -} + #menu_tab_left .mn li a { display: block; text-decoration: none; } #menu_tab_left li.view a { - color: #fff; - font-weight: bold; - font-weight: 100; - line-height: 18px; + color: #343434; display: none; } #menu_tab_left li.view { - background: #82b92e; - max-width: 60%; - min-width: 30%; - padding: 5px 5px 0px; - text-align: center; - -moz-border-top-right-radius: 3px; - -webkit-border-top-right-radius: 3px; - border-top-right-radius: 3px; - - -moz-border-top-left-radius: 3px; - -webkit-border-top-left-radius: 3px; - border-top-left-radius: 3px; margin-left: 0px !important; - overflow-y: hidden; + padding-left: 10px; + padding-bottom: 4px; + white-space: nowrap; } #menu_tab_left li.view img.bottom { @@ -1422,13 +1511,10 @@ div#agent_wizard_subtabs { #menu_tab_left li a, #menu_tab_left li span { - text-transform: uppercase; - color: #fff; - font-size: 100%; - line-height: 20px; - letter-spacing: 0px; - font-family: verdana, sans-serif; - font-weight: bold; + color: #343434; + font-family: "lato-bolder", "Open Sans", sans-serif !important; + font-size: 18pt; + line-height: 18pt; } /* @@ -1733,12 +1819,14 @@ div#main_pure { /* big_data is used in tactical and logon_ok */ .big_data { text-decoration: none; - font: bold 2em Arial, Sans-serif; + font-family: "lato", "Open Sans", sans-serif; + font-size: 2em; } .med_data { text-decoration: none; - font: bold 1.5em Arial, Sans-serif; + font-family: "lato", "Open Sans", sans-serif; + font-size: 1.5em; } .notify { @@ -2037,21 +2125,36 @@ div#pandora_logo_header { #header_table_inner { height: 60px; + min-width: 790px; /*width: 100%;*/ display: flex; align-items: center; justify-content: space-between; } +.header_title { + font-weight: 600; + font-size: 10.5pt; + font-family: "lato-bolder", "Open Sans", sans-serif !important; +} + +.header_subtitle { + font-size: 10pt; + font-family: "lato-bolder", "Open Sans", sans-serif !important; +} + #header_table_inner a, #header_table_inner span { font-family: "Open Sans", sans-serif; } +#header_table_inner a:hover { + text-decoration: none; +} + .header_left { display: flex; - justify-content: flex-start; - align-items: center; + flex-direction: column; } .header_center { @@ -2066,64 +2169,40 @@ div#pandora_logo_header { align-items: center; } -.header_left > div, -.header_center > div { - padding-right: 15px; -} - .header_right > div { padding-left: 15px; } - -#header_table_inner #header_user span { - color: #777; -} - .header_left img, .header_center img, .header_right img { vertical-align: middle; } +#header_chat { + padding-left: 0; + padding-right: 15px; +} + +#header_autorefresh { + padding-left: 0; +} + +#header_table_inner #header_user span { + color: #777; + align-self: center; +} + +#header_table_inner #header_user a { + display: flex; + justify-content: center; +} + #header_user img { padding-right: 5px; } -#header_alert, -#header_message { - padding-right: 0px; -} - -#header_alert p, -#header_message p { - border-radius: 50%; - padding: 0; - margin: 0; - width: 15px; - position: relative; - height: 15px; - top: -5px; - text-align: center; - display: inline-block; - vertical-align: super; -} - -#header_alert p { - background-color: #ec4e1b; - left: -12px; -} - -#header_message p { - background-color: #82b92e; - left: -9px; -} - -#header_alert span, -#header_message span { - color: #fff; - font-size: 7pt; - font-weight: 600; - font-family: "Open Sans", sans-serif; +div#header_autorefresh_counter { + padding-left: 0; } .autorefresh_disabled { @@ -2135,10 +2214,21 @@ a.autorefresh_txt, color: #1c1c1c; font-size: 8.5pt; } -/* -#combo_refr select { - margin-right: 8px; -}*/ + +@media screen and (max-width: 1200px) { + #header_searchbar input.search_input { + width: 135px; + } + .header_right > div { + padding-left: 10px; + } +} + +@media screen and (max-width: 1300px) { + .header_right > div { + padding-left: 10px; + } +} .disabled_module { color: #aaa; @@ -2185,10 +2275,6 @@ div#logo_text3 { margin-left: 4px; padding-top: 0px; }*/ -.pagination { - margin-top: 15px; - margin-bottom: 5px; -} .pagination * { margin-left: 0px !important; margin-right: 0px !important; @@ -2287,6 +2373,7 @@ td.cellBig { tr.group_view_data, .group_view_data { color: #3f3f3f; + font-family: "lato", "Open Sans", sans-serif; } tr.group_view_crit, @@ -2415,6 +2502,8 @@ select { -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; + font-family: "lato-bolder", "Open Sans", sans-serif; + font-size: 10pt; } /* plugins */ @@ -2853,6 +2942,12 @@ a.tip { a.tip > img { margin-left: 2px; margin-right: 2px; + margin-top: -3px; +} + +.head_tip { + display: inline; + margin-left: 10px; } /* @@ -2868,9 +2963,8 @@ input.search_input { background-color: #f2f6f7 !important; padding: 0px; margin: 0; - width: 200px; - height: 35px; - margin-bottom: 5px; + width: 250px; + height: 30px; margin-left: 2px; padding-left: 15px; padding-right: 40px; @@ -3211,7 +3305,8 @@ table#policy_modules td * { color: #fff; margin: 2px; padding: 10px 30px; - font-size: 15px; + font-size: 16px; + font-family: "lato-bolder", "Open Sans", sans-serif; font-weight: bold; border-radius: 2px; } @@ -3264,7 +3359,7 @@ div.div_groups_status { } #title_menu { - color: #9ca4a6; + color: #b9b9b9; float: right; width: 72%; letter-spacing: 0pt; @@ -3280,31 +3375,36 @@ div.div_groups_status { #menu_tab li.nomn, #menu_tab li.nomn_high { - background-color: #fff; - padding-right: 3px; - padding-left: 3px; + padding-right: 8px; + padding-left: 8px; font-weight: bold; text-decoration: none; font-size: 14px; - border-left: 2px solid #e2e2e2; margin-top: 0; min-width: 30px; - height: 28px; + min-height: 50px; + max-height: 53px; +} + +#menu_tab_frame_view_bc #menu_tab li.nomn, +#menu_tab_frame_view_bc #menu_tab li.nomn_high { + min-height: 53px; } #menu_tab li:hover { - background-color: #ececec; + box-shadow: inset 0px 4px #82b92e; } #menu_tab li.nomn_high, #menu_tab li.nomn_high span { color: #fff; - background-color: #ececec; + box-shadow: inset 0px 4px #82b92e; + background-color: #fff; } #menu_tab li.nomn img, #menu_tab li img { - margin-top: 3px; + margin-top: 10px; margin-left: 3px; } @@ -3322,8 +3422,7 @@ div.div_groups_status { } .databox.filters, -.databox.data, -.databox.profile_list { +.databox.data { margin-bottom: 20px; } @@ -3331,14 +3430,6 @@ div.div_groups_status { padding: 10px; padding-left: 20px; } -.databox.profile_list > tbody > tr > td { - padding: 4px 1px; - padding-left: 5px; - border-bottom: 1px solid #e2e2e2; -} -.databox.profile_list > tbody > tr > a.tip > img { - margin: 0px; -} .databox.filters > tbody > tr > td > img, .databox.filters > tbody > tr > td > div > a > img, @@ -3628,6 +3719,80 @@ div.simple_value > a > span.text p { cursor: pointer; } +.table_modal_alternate { + border-spacing: 0px; + text-align: left; +} + +/* Modal window - Show More */ +table.table_modal_alternate tr:nth-child(odd) td { + background-color: #ffffff; +} + +table.table_modal_alternate tr:nth-child(even) td { + background-color: #f9f9f9; + border-top: 1px solid #e0e0e0; + border-bottom: 1px solid #e0e0e0; +} + +table.table_modal_alternate tr td { + height: 33px; + max-height: 33px; + min-height: 33px; +} + +table.table_modal_alternate tr td:first-child { + width: 35%; + font-weight: bold; + padding-left: 20px; +} + +ul.events_tabs { + background: #ffffff !important; + border: 0px; + display: flex; + justify-content: space-between; + padding: 0px !important; +} + +ul.events_tabs:before, +ul.events_tabs:after { + content: none !important; +} + +ul.events_tabs > li { + margin: 0 !important; + width: 100%; + text-align: center; + float: none !important; + outline-width: 0; +} + +ul.events_tabs > li.ui-state-default { + background: #fff !important; + border: none !important; + border-bottom: 2px solid #cacaca !important; +} + +ul.events_tabs > li a { + text-align: center; + float: none !important; + padding: 8px !important; + display: block; +} + +ul.events_tabs > li span { + position: relative; + top: -6px; + left: 5px; + margin-right: 10px; +} + +ul.events_tabs > li.ui-tabs-active { + border-bottom: 2px solid #82b92e !important; + border-top: 2px solid #82b92e !important; +} + /* * --------------------------------------------------------------------- * - modal window and edit user - @@ -3920,7 +4085,19 @@ div.simple_value > a > span.text p { font-size: 18pt; } +span.log_zone_line { + font-size: 12px; +} + +span.log_zone_line_error { + color: #fc4444; +} + /* global */ +.bolder { + font-weight: bolder; +} + .readonly { background-color: #dedede !important; } @@ -3956,7 +4133,7 @@ div.simple_value > a > span.text p { background-color: #eee; } .checkselected { - background-color: #eee; + background-color: #eee !important; } .tag-wrapper { padding: 0 10px 0 0; @@ -4320,6 +4497,18 @@ form ul.form_flex li ul li { text-align: center; } +.div-v-centered { + display: flex; + align-items: center; +} + +.div-v-centered > form > input[type="image"] { + margin: 0; + padding: 0; + width: 14px; + padding-left: 5px; +} + .pandora_upper { text-transform: uppercase; } @@ -4390,6 +4579,13 @@ tr:first-child > td > a.up_arrow { margin-right: 5px; } +.button-as-link { + text-decoration: underline; + background: none !important; + border: none; + padding: 0 !important; +} + /* * --------------------------------------------------------------------- * - MESSAGE LIST POPUP - @@ -4410,14 +4606,19 @@ div#dialog_messages table th:last-child { */ .notification-ball { - border: #343434 solid 2px; border-radius: 50%; - width: 20px; - height: 20px; + width: 24px; + height: 24px; display: flex; justify-content: center; align-items: center; cursor: pointer; + color: #fff; + font-weight: bold; +} + +.notification-ball.notification-ball-new-messages:hover { + box-shadow: 0 0 3px #888; } .notification-ball-no-messages { @@ -4433,7 +4634,7 @@ div#dialog_messages table th:last-child { border: #a5a5a5 solid 1px; z-index: 900000; position: absolute; - width: 400px; + width: 550px; margin-top: -5px; border-radius: 5px; } @@ -4447,8 +4648,7 @@ div#dialog_messages table th:last-child { border-width: 12px; border-style: solid; bottom: 100%; - left: 78%; - left: calc(78% - 2px); + left: calc(58% - 7px); margin-left: -12px; border-bottom-color: white; } @@ -4477,14 +4677,14 @@ div#dialog_messages table th:last-child { } .notification-item:hover { border: #ccc solid 1px; + text-decoration: none; } .notification-item > * { padding-left: 15px; pointer-events: none; } - -.notification-item:hover { - text-decoration: none; +.notification-item > img { + width: 75px; } .notification-info { @@ -4737,3 +4937,1189 @@ input:checked + .p-slider:before { background-color: #fff; border: 2px solid #82b92e; } + +/* New white rounded boxes */ +.white_box { + background-color: #fff; + border: 1px solid #e1e1e1; + border-radius: 5px; + padding: 20px 50px; +} + +/* + * --------------------------------------------------------------------- + * - User edit + * --------------------------------------------------------------------- + */ + +#user_form * { + color: #4d4d4d; +} + +#user_form { + width: 100%; +} + +#user_form a.tip img { + margin-left: 8px; +} + +#edit_user_profiles { + margin-top: 40px; + margin-bottom: 30px; +} + +#edit_user_profiles table { + margin-bottom: 0 !important; +} + +.user_edit_first_row { + display: flex; +} + +.user_edit_second_row { + display: flex; + flex-flow: row wrap; + flex-direction: row; + justify-content: space-between; +} + +.user_edit_first_row, +.user_edit_second_row, +.user_edit_third_row { + margin-bottom: 20px; +} + +.edit_user_info { + width: 58%; + margin-right: 20px; + display: flex; + align-items: center; +} + +.edit_user_info_left { + width: 25%; + margin-right: 50px; + text-align: center; +} + +.edit_user_info_right { + width: 75%; +} + +.edit_user_info_right input { + background-color: transparent !important; + border: none; + border-radius: 0 !important; + border-bottom: 1px solid #343434; + padding: 10px 0px 2px 35px; + box-sizing: border-box; + background-repeat: no-repeat; + background-position: left bottom 2px; + width: 100%; + margin-bottom: 4px; +} + +.edit_user_info_right input:focus { + font-weight: bold; +} + +.edit_user_info_right #fullname { + background-image: url("../../images/user_name.png"); +} + +.edit_user_info_right #email { + background-image: url("../../images/user_email.png"); +} + +.edit_user_info_right #phone { + background-image: url("../../images/user_phone.png"); +} + +.edit_user_info_right #password_new, +.edit_user_info_right #password_conf { + background-image: url("../../images/user_password.png"); +} + +.edit_user_autorefresh { + width: 42%; +} + +.edit_user_options { + width: 50%; + padding-right: 50px; +} + +.edit_user_options #text-block_size { + background-color: transparent !important; + border: none; + border-radius: 0 !important; + border-bottom: 1px solid #343434; + padding: 0px 0px 0px 10px; +} + +.edit_user_options input#text-block_size:disabled { + border-bottom-color: #848484; + color: #848484; +} + +.edit_user_timezone { + width: 40%; +} + +.edit_user_timezone #zonepicker { + width: 100%; +} + +.edit_user_comments #textarea_comments { + background-color: #fbfbfb !important; + padding-left: 10px; +} + +.edit_user_labels { + color: #343434 !important; + font-weight: bold; + padding-right: 10px; + margin: 0px 0px 5px 0px; +} + +.label_select, +.label_select_simple { + margin-bottom: 15px; +} + +.label_select_simple .edit_user_labels { + margin-bottom: 0; + display: inline; +} + +.edit_user_options .label_select_simple { + display: flex; + align-items: center; +} + +.edit_user_options .label_select_simple .p-switch { + margin-right: 5px; + margin-left: 10px; +} + +.user_edit_first_row .edit_user_autorefresh > div:last-child, +.user_edit_first_row .edit_user_info_left > div:last-child, +.user_edit_first_row .edit_user_info_right > div:last-child, +.user_edit_second_row .edit_user_options > div:last-child { + margin-bottom: 0px !important; +} + +.user_avatar { + width: 100%; + margin-bottom: 20px; +} + +.autorefresh_select { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 15px; +} + +.autorefresh_select_text { + margin: 0px 0px 5px 0px; +} + +#right_autorefreshlist { + margin-bottom: 10px; +} + +#autorefresh_list_out, +#autorefresh_list { + text-align: left; +} + +.autorefresh_select .autorefresh_select_list_out, +.autorefresh_select .autorefresh_select_list { + width: 45%; +} + +.autorefresh_select .autorefresh_select_arrows { + width: 10%; + text-align: center; +} + +.autorefresh_select_arrows a { + display: block; +} + +.edit_user_autorefresh select, +.user_edit_second_row select { + width: 100%; +} + +.edit_user_button { + margin-top: 20px; + width: 100%; + text-align: right; +} + +#user-notifications-wrapper { + width: 100%; + box-sizing: border-box; + color: #4d4d4d; +} + +/* This is to use divs like tables */ +.table_div { + display: table; +} +.table_thead, +.table_tbody { + display: table-row; +} +.table_th { + font-weight: bold; +} +.table_th, +.table_td { + display: table-cell; + vertical-align: middle; + padding: 10px; +} +/* Tables with 3 columns */ +.table_three_columns .table_th, +.table_three_columns .table_td { + width: 33%; +} + +/* + * --------------------------------------------------------------------- + * - TABLES TO SHOW INFORMATION + * --------------------------------------------------------------------- + */ + +table.info_table { + background-color: #fff; + margin-bottom: 10px; + border-spacing: 0; + border-collapse: collapse; + overflow: hidden; + border-radius: 5px; +} + +table.info_table > tbody > tr:nth-child(even) { + background-color: #f5f5f5; +} + +table.info_table tr:first-child > th { + background-color: #fff; + color: #000; + text-align: left; + vertical-align: middle; +} + +table.info_table th { + font-size: 7.5pt; + letter-spacing: 0.3pt; + color: #000; + background-color: #fff; +} + +table.info_table tr th { + border-bottom: 1px solid #e2e2e2; +} + +/* Radius top */ +table.info_table > thead > tr:first-child > th:first-child { + border-top-left-radius: 4px; +} +table.info_table > thead > tr:first-child > th:last-child { + border-top-right-radius: 4px; +} + +/* Radius bottom */ +table.info_table > tbody > tr:last-child > td:first-child { + border-top-left-radius: 4px; +} +table.info_table > tbody > tr:last-child > td:last-child { + border-top-right-radius: 4px; +} + +table.info_table > thead > tr > th, +table.info_table > tbody > tr > th, +table.info_table > thead > tr > th a { + padding-left: 9px; + padding-right: 9px; + padding-top: 9px; + padding-bottom: 9px; + font-weight: normal; + color: #000; + font-size: 8.6pt; +} + +table.info_table > tbody > tr { + border-bottom: 1px solid #e2e2e2; +} + +table.info_table > tbody > tr > td { + -moz-border-radius: 0px; + -webkit-border-radius: 0px; + border-radius: 0px; + border: none; + padding-left: 9px; + padding-right: 9px; + padding-top: 7px; + padding-bottom: 7px; +} + +table.info_table > tbody > tr > td > img, +table.info_table > thead > tr > th > img, +table.info_table > tbody > tr > td > div > a > img, +table.info_table > tbody > tr > td > span > img, +table.info_table > tbody > tr > td > span > a > img, +table.info_table > tbody > tr > td > a > img, +table.info_table > tbody > tr > td > form > a > img { + vertical-align: middle; +} + +table.info_table > tbody > tr:hover { + background-color: #eee !important; +} + +.info_table.profile_list > thead > tr > th > a.tip { + padding: 0px; +} + +/* This class is for the icons of actions and operations in the tables. */ +.action_buttons a[href] img, +.action_buttons input[type="image"], +.action_button_img { + border-radius: 4px; + border: 1px solid #dcdcdc !important; + padding: 1px; + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + max-width: 21px; + background-color: transparent !important; /*BORRAR*/ +} + +/* This class is for only one icon to be a button type. */ +.action_button_img { + cursor: pointer; +} + +.action_buttons a, +.action_buttons input[type="image"] { + margin-right: 5px; +} + +.action_buttons a:last-child, +.action_buttons input[type="image"]:last-child { + margin-right: 0px; +} + +.action_buttons a:hover { + background-color: #fff; + display: inline-block; + border-radius: 4px; +} + +.action_buttons input[type="image"]:hover { + background-color: #fff !important; +} + +/* Tables to upload files */ +#table_filemanager tr:first-child th span { + font-weight: bold; +} + +.file_table_buttons { + text-align: right; + margin-bottom: 10px; +} + +.file_table_buttons a img { + border: 1px solid #e2e2e2; + padding: 5px; + border-radius: 4px; + margin-right: 10px; + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); +} + +#file_table_modal { + display: flex; + justify-content: space-between; + margin-bottom: 40px; +} + +#file_table_modal .create_folder, +#file_table_modal .create_text_file, +#file_table_modal .upload_file { + width: 33%; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + margin-right: 2px; + background-color: #e6e6e6; +} + +.file_table_buttons a:last-child img, +#file_table_modal .upload_file { + margin-right: 0px !important; +} + +#file_table_modal li a { + display: block; + padding: 5px; +} + +#file_table_modal li img, +#file_table_modal li span { + vertical-align: middle; +} + +#file_table_modal li img { + margin-right: 10px; +} + +#create_folder, +#create_text_file, +#upload_file { + margin-bottom: 30px; +} + +#create_folder input#text-dirname, +#create_text_file input#text-name_file { + width: 100%; + margin-right: 5px; + box-sizing: border-box; + margin-bottom: 10px; +} + +#upload_file input#file-file { + width: 70%; +} + +#create_folder input#submit-crt, +#create_text_file input#submit-create, +#upload_file input#submit-go { + float: right; +} + +#upload_file input#submit-go { + margin-top: 10px; +} + +.file_table_modal_active { + background-color: #fff !important; + border: 1px solid #e6e6e6; + border-bottom: none; +} + +/* Inventory table */ +.inventory_table_buttons { + text-align: right; + margin-bottom: 10px; +} + +.inventory_table_buttons a { + font-weight: bolder; + display: inline-block; + border: 1px solid #e2e2e2; + padding: 8px; + border-radius: 4px; + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); +} + +.inventory_table_buttons a img, +.inventory_table_buttons a span { + vertical-align: middle; +} + +.inventory_table_buttons a img { + padding-left: 10px; +} + +.inventory_tables thead th span:first-child { + float: left; + font-weight: bold; +} + +.inventory_tables thead th span { + font-size: 8.6pt; +} + +.inventory_tables tbody > tr:first-child { + font-weight: bold; +} + +/* Tag view */ +table.info_table.agent_info_table { + margin-bottom: 20px; +} +table.agent_info_table tr { + background-color: #fff !important; +} + +table.agent_info_table > tbody > tr > td { + border-bottom: none; +} + +table.agent_info_table > tbody > tr:last-child > td { + border-bottom: 1px solid #e2e2e2; +} + +table.agent_info_table thead > tr:first-child th { + background-color: #f5f5f5; +} + +table.agent_info_table thead > tr:first-child th span, +table.agent_info_table thead > tr:first-child th { + font-weight: bold; + font-size: 8.6pt; +} + +table.info_table.agent_info_table td { + padding-left: 20px; + padding-right: 20px; +} + +table.info_table.agent_info_table table#agent_table { + padding-top: 15px; +} + +table.info_table.agent_info_table table#module_table { + padding-top: 10px; + padding-bottom: 15px; +} + +table.info_table.agent_info_table table.info_table { + margin-bottom: 0; +} + +.agent_info_table_opened { + background-color: #82b92e !important; + color: #fff !important; + border-color: #82b92e !important; +} + +.agent_info_table_closed { + background-color: #fff !important; + color: #000 !important; + border-radius: 4px; +} + +/* Tag view */ +table.info_table.policy_table tr { + background-color: #fff !important; +} + +table.info_table.policy_sub_table thead > tr:first-child th { + background-color: #f5f5f5; +} + +table.info_table.policy_sub_table { + padding: 20px 15px; + margin-bottom: 0; +} + +/* Arrows to sort the tables. */ +.sort_arrow { + display: inline-grid; + vertical-align: middle; +} +.sort_arrow a { + padding: 0 0 0 5px !important; +} +.sort_arrow img { + width: 0.8em; +} + +/* + * --------------------------------------------------------------------- + * - PAGINATION + * --------------------------------------------------------------------- + */ +.pagination { + display: flex; + justify-content: space-between; + align-items: flex-end; + margin-bottom: 10px; + margin-top: 15px; +} + +.pagination .page_number { + border: 1px solid #cacaca; + border-right: 0px; + text-align: center; +} + +.pagination .page_number a { + padding: 5px; + min-width: 12px; + display: block; +} + +.pagination .page_number:hover, +.pagination .pagination-arrows:hover { + background-color: #e2e2e2; +} + +.pagination .total_number > *:first-child { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +.pagination .total_number > *:last-child { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-right: 1px solid #cacaca !important; +} + +.pagination .page_number_active { + font-weight: bold; + background-color: #82b92e; + color: #fff; + border-color: #82b92e; +} + +.pagination .page_number_active a { + color: #fff; +} + +.pagination .total_number { + display: flex; + justify-content: flex-end; +} + +.pagination a { + margin: 0 !important; +} + +.pagination .pagination-arrows { + border: 1px solid #cacaca; + border-right: 0px; +} + +.pagination-bottom { + margin-bottom: 15px; + margin-top: 0px; + align-items: flex-start; +} + +/* + * --------------------------------------------------------------------- + * - Layout for the new forms + * --------------------------------------------------------------------- + */ +.first_row { + margin-bottom: 20px; + display: flex; +} + +.label_simple_one_item { + display: flex; + align-items: flex-end; +} + +.label_simple_items { + display: flex; + align-items: center; + flex-wrap: wrap; +} + +.label_simple_items > * { + margin-right: 5px; +} + +.input_label { + color: #343434 !important; + font-weight: bold; + padding-right: 10px; + margin: 0px 0px 5px 0px; +} + +.input_label_simple { + margin-bottom: 0; + margin-top: 0; + display: inline; +} + +.label_select_parent { + display: flex; + justify-content: space-between; + align-items: center; +} + +.label_select_child_left { + width: 80%; + min-width: 100px; +} + +.label_select_child_right { + width: 20%; + min-width: 140px; + align-items: center; + display: flex; +} + +.label_select_child_icons { + text-align: right; + width: 5%; + min-width: 30px; +} + +/* Inputs type text shown as a black line */ +.agent_options input[type="text"] { + background-color: transparent !important; + border: none; + border-radius: 0 !important; + border-bottom: 1px solid #ccc; + font-family: "lato-bolder", "Open Sans", sans-serif !important; + font-size: 10pt; + padding: 2px 5px; + box-sizing: border-box; + background-repeat: no-repeat; + background-position: left bottom 2px; + margin-bottom: 4px; +} + +/* + * --------------------------------------------------------------------- + * - CLASSES FOR THE NEW TOGGLES - + * --------------------------------------------------------------------- + */ +.ui_toggle { + margin-bottom: 20px; +} + +.ui_toggle > a:first-child { + background-color: #fff; + border: 1px solid #e1e1e1; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + margin-bottom: -1px; + padding: 5px; + display: block; +} + +.white_box_opened { + border-top-left-radius: 0px; + border-top-right-radius: 0px; +} + +/* + * --------------------------------------------------------------------- + * - SWITCH RADIO BUTTONS - + * --------------------------------------------------------------------- + */ +.switch_radio_button { + display: flex; + overflow: hidden; +} + +.switch_radio_button input { + position: absolute !important; + clip: rect(0, 0, 0, 0); + height: 1px; + width: 1px; + border: 0; + overflow: hidden; +} + +.switch_radio_button label { + background-color: #fff; + color: rgba(0, 0, 0, 0.6); + line-height: 1; + text-align: center; + padding: 7px 14px; + margin-right: -1px; + border: 1px solid #cbcbcb; + border-radius: 4px; + transition: all 0.1s ease-in-out; +} + +.switch_radio_button label:hover { + cursor: pointer; +} + +.switch_radio_button input:checked + label { + background-color: #82b92e; + box-shadow: none; + color: #fff; +} + +.switch_radio_button label:last-child { + margin-right: 0px; +} + +/* + * --------------------------------------------------------------------- + * - MODULE GRAPHS + * --------------------------------------------------------------------- + */ +.module_graph_menu_dropdown { + padding-top: 20px; + padding-bottom: 20px; + position: absolute; + top: 10px; + width: 100%; + z-index: 1001; +} + +.module_graph_menu_content, +.module_graph_menu_header { + width: 95%; + border: 1px solid #e2e2e2; + margin: 0 auto; + box-sizing: border-box; + background-color: #fff; +} + +.module_graph_menu_header { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + padding: 6px 10px; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; +} + +.module_graph_menu_header span > img { + vertical-align: middle; + padding-left: 5px; +} + +.module_graph_menu_content { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + padding: 15px; + border-top: none; +} + +/* + * --------------------------------------------------------------------- + * - AGENT VIEW + * --------------------------------------------------------------------- + */ + +.bullet_modules { + width: 15px; + height: 15px; + border-radius: 50%; + margin-right: 5px; + margin-top: -2px; +} + +div#bullets_modules { + display: flex; + margin-left: 2em; +} + +div#bullets_modules div { + display: flex; + align-items: center; + margin: 0 5px; +} + +.orange_background { + background: #ffa631; +} +.red_background { + background: #fc4444; +} +.yellow_background { + background: #fad403; +} +.grey_background { + background: #b2b2b2; +} +.blue_background { + background: #3ba0ff; +} +.green_background { + background: #80ba27; +} + +/* First row in agent view */ +#agent_details_first_row { + display: flex; + margin-bottom: 20px; + width: 100%; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; +} + +.agent_details_col { + display: table-cell; + background-color: #fff; + border: 1px solid #e2e2e2; + border-radius: 5px; + flex: 0 1 auto; +} + +.agent_details_col_left { + width: 40%; + min-width: 300px; +} +.agent_details_col_right { + width: 59%; + min-width: 480px; +} + +.agent_access_rate_events { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; +} + +.white_table_graph#table_access_rate { + flex: 1 1 auto; + min-width: 450px; + margin-right: 1%; +} + +.white_table_graph#table_events { + flex: 1 1 auto; + min-width: 450px; +} + +@media screen and (max-width: 1150px) { + .agent_details_col { + flex: 1 1 auto; + } + .white_table_graph#table_access_rate { + margin-right: 0; + } +} + +.buttons_agent_view { + display: flex; + justify-content: flex-end; +} + +.buttons_agent_view a img { + border: 1px solid #dcdcdc; + border-radius: 4px; + padding: 1px; + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + max-width: 21px; + margin-left: 5px; +} + +/* Agent details in agent view */ +div#status_pie path { + stroke-width: 8px !important; +} +div#status_pie { + margin-bottom: 2em; +} + +.agent_details_header { + display: flex; + justify-content: flex-end; + align-items: center; + border-bottom: 1px solid #e2e2e2; + padding: 6px 20px; +} + +.agent_details_content { + display: flex; + align-items: center; + padding: 20px; + padding-bottom: 0; +} + +.agent_details_agent_name { + display: flex; + align-items: center; +} + +.agent_details_remote_cfg { + align-self: flex-start; +} + +.agent_details_graph { + text-align: center; + margin: 0 auto; +} + +.agent_details_info { + max-width: 45%; + overflow: hidden; + min-width: 220px; +} +.agent_details_info span { + text-overflow: ellipsis; +} + +.agent_details_info p { + display: flex; + align-items: center; +} + +.agent_details_info img { + padding-right: 5px; +} + +.agent_details_bullets #bullets_modules { + display: flex; + justify-content: flex-start; +} + +.agent_details_bullets #bullets_modules > div { + display: flex; + align-items: center; + padding-bottom: 20px; +} + +#agent_contact_main tr td img { + max-width: 100%; +} + +/* White tables to show graphs */ +.white_table_graph { + margin-bottom: 20px; +} + +.white_table_graph_header { + padding: 10px 20px; + background-color: #fff; + color: #000; + border: 1px solid #e2e2e2; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + font-weight: bold; +} + +.white_table_graph_header div#bullets_modules { + float: right; +} +.white_table_graph_header img, +.white_table_graph_header span { + vertical-align: middle; +} + +.white_table_graph_header span { + padding-left: 10px; +} + +.white-box-content { + width: 100%; + height: 100%; + background-color: #fff; + display: flex; + align-items: center; + flex-wrap: wrap; +} + +.white_table_graph_content { + border: 1px solid #e2e2e2; + border-top: none; + background-color: #fff; + padding: 20px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + align-items: center; + flex-wrap: wrap; +} + +.white_table_graph_content.no-padding-imp .info_box { + margin: 0 !important; +} + +.white_table_graph_content.min-height-100 { + min-height: 100px; +} + +.white_table_graph_content.min-height-50 { + min-height: 50px; +} + +.white_table_graph_content.min-height-200 { + min-height: 200px; +} + +.white_table_graph_content div.pagination { + width: 100%; + padding: 0 1em; +} +.white_table_graph_content div.action-buttons { + padding: 10px; +} + +/* White tables */ +.white_table, +.white_table tr:first-child > th { + background-color: #fff; + color: #000; +} + +.white_table { + border: 1px solid #e2e2e2; + border-radius: 4px; + margin-bottom: 20px; + padding-bottom: 10px; +} + +.white_table thead tr:first-child > th { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #e2e2e2; +} + +.white_table tbody tr:first-child > td { + padding-top: 25px; +} + +.white_table tr td:first-child, +.white_table tr th:first-child { + padding-left: 50px; +} + +.white_table tr td:last-child, +.white_table tr th:last-child { + padding-right: 50px; +} + +.white_table th, +.white_table td { + padding: 10px 20px; +} + +.white_table_droppable > thead > tr > th > img { + vertical-align: middle; +} + +.white_table_droppable tr th:first-child { + padding-left: 20px; +} + +.white_table_no_border { + border: none !important; +} + +/* + * --------------------------------------------------------------------- + * - SERVICES TABLE VIEW + * --------------------------------------------------------------------- + */ +#table_services { + display: grid; + grid-gap: 20px; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + grid-template-rows: 1fr; + margin-bottom: 30px; +} + +.table_services_item_link { + font-size: 16px; + display: grid; + min-height: 80px; + box-sizing: border-box; + padding: 10px 10px 10px 0; +} + +.table_services_item { + display: grid; + align-items: center; + grid-template-columns: 50px auto; +} diff --git a/pandora_console/include/styles/progress.css b/pandora_console/include/styles/progress.css new file mode 100644 index 0000000000..84c3224f27 --- /dev/null +++ b/pandora_console/include/styles/progress.css @@ -0,0 +1,20 @@ +span.progress_text { + position: absolute; + font-family: "lato-bolder", "Open Sans", sans-serif; + font-size: 2em; + margin-left: -0.5em; +} + +div.progress_main { + display: inline-block; + text-align: center; + height: 2.5em; + border: 1px solid #80ba27; +} + +div.progress { + width: 0%; + background: #80ba27; + height: 100%; + float: left; +} diff --git a/pandora_console/include/styles/register.css b/pandora_console/include/styles/register.css new file mode 100644 index 0000000000..d2589a3f01 --- /dev/null +++ b/pandora_console/include/styles/register.css @@ -0,0 +1,57 @@ +input[type="submit"].submit-cancel, +button.submit-cancel { + color: #fb4444 !important; + border: 1px solid #fb4444 !important; + background: #fff !important; + padding: 5px; + font-size: 1.3em; + margin: 0.5em 1em 0.5em 0 !important; + cursor: pointer !important; + text-align: center !important; + height: 30px !important; + width: 90px !important; +} + +input[type="submit"].submit-cancel:hover, +button.submit-cancel:hover { + color: #fff !important; + background: #fb4444 !important; +} + +input[type="submit"].submit-next, +button.submit-next, +input[type="button"].submit-next { + color: #82b92e !important; + border: 1px solid #82b92e !important; + background: #fff !important; + padding: 5px; + font-size: 1.3em; + margin: 0.5em 1em 0.5em 0 !important; + cursor: pointer !important; + text-align: center !important; + height: 30px !important; +} + +input[type="submit"].submit-next:hover, +button.submit-next:hover, +input[type="button"].submit-next:hover { + color: #fff !important; + background: #82b92e !important; +} + +div.submit_buttons_container { + position: absolute; + margin: 10px auto 0px; + bottom: 0px; + width: 90%; + position: relative; + clear: both; + height: 4em; +} + +.license_text { + width: 95%; + overflow-y: auto; + text-align: justify; + margin: 1em auto; +} diff --git a/pandora_console/include/styles/task_list.css b/pandora_console/include/styles/task_list.css new file mode 100644 index 0000000000..d3ffb5d546 --- /dev/null +++ b/pandora_console/include/styles/task_list.css @@ -0,0 +1,42 @@ +/** + * + * Name: Default theme + * + * Extension to manage a list of gateways and the node address where they should + * point to. + * + * @category css + * @package Pandora FMS + * @subpackage Community + * @version 1.0.0 + * @license See below + * + * ______ ___ _______ _______ ________ + * | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __| + * | __/| _ | | _ || _ | _| _ | | ___| |__ | + * |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______| + * + * ============================================================================ + * Copyright (c) 2005-2019 Artica Soluciones Tecnologicas + * Please see http://pandorafms.org for full contribution list + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation for version 2. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * ============================================================================ + */ + +ul.progress_task_discovery { + width: 80%; + margin: 0 auto; + display: flex; + flex-direction: column; +} + +ul.progress_task_discovery li span { + font-size: 9pt; + margin-left: 20px; +} diff --git a/pandora_console/include/styles/wizard.css b/pandora_console/include/styles/wizard.css index 869ea85f2c..029addf5cb 100644 --- a/pandora_console/include/styles/wizard.css +++ b/pandora_console/include/styles/wizard.css @@ -6,7 +6,8 @@ ul.wizard { } ul.wizard li { - padding: 10px; + padding-bottom: 10px; + padding-top: 10px; } ul.wizard li > label:not(.p-switch) { @@ -16,8 +17,10 @@ ul.wizard li > label:not(.p-switch) { } ul.wizard li > textarea { - width: 250px; + width: 600px; + height: 15em; display: inline-block; + font-family: monospace; } .hidden { diff --git a/pandora_console/include/visual-console-client/alarm-clock.ttf b/pandora_console/include/visual-console-client/alarm-clock.ttf new file mode 100644 index 0000000000..9e9b593459 Binary files /dev/null and b/pandora_console/include/visual-console-client/alarm-clock.ttf differ diff --git a/pandora_console/include/visual-console-client/vc.main.css b/pandora_console/include/visual-console-client/vc.main.css new file mode 100644 index 0000000000..20f5a1da7c --- /dev/null +++ b/pandora_console/include/visual-console-client/vc.main.css @@ -0,0 +1,95 @@ +#visual-console-container { + margin: 0px auto; + position: relative; + background-repeat: no-repeat; + background-size: 100% 100%; + background-position: center; +} + +.visual-console-item { + position: absolute; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: initial; + -webkit-box-direction: initial; + -ms-flex-direction: initial; + flex-direction: initial; + justify-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +@font-face { + font-family: Alarm Clock; + src: url(alarm-clock.ttf); +} + +/* Digital clock */ + +.visual-console-item .digital-clock { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + justify-items: center; + -ms-flex-line-pack: center; + align-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.visual-console-item .digital-clock > span { + font-family: "Alarm Clock", "Courier New", Courier, monospace; + font-size: 50px; + + /* To improve legibility */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px; +} + +.visual-console-item .digital-clock > span.date { + font-size: 25px; +} + +.visual-console-item .digital-clock > span.timezone { + font-size: 25px; +} + +/* Analog clock */ + +.visual-console-item .analogic-clock { + text-align: center; +} + +.visual-console-item .analogic-clock .hour-hand { + -webkit-animation: rotate-hour 43200s infinite linear; + animation: rotate-hour 43200s infinite linear; +} + +.visual-console-item .analogic-clock .minute-hand { + -webkit-animation: rotate-minute 3600s infinite linear; + animation: rotate-minute 3600s infinite linear; +} + +.visual-console-item .analogic-clock .second-hand { + -webkit-animation: rotate-second 60s infinite linear; + animation: rotate-second 60s infinite linear; +} + + +/*# sourceMappingURL=vc.main.css.map*/ \ No newline at end of file diff --git a/pandora_console/include/visual-console-client/vc.main.css.map b/pandora_console/include/visual-console-client/vc.main.css.map new file mode 100644 index 0000000000..2b723c93b4 --- /dev/null +++ b/pandora_console/include/visual-console-client/vc.main.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///main.css","webpack:///styles.css"],"names":[],"mappings":"AAAA;EACE,gBAAgB;EAChB,kBAAkB;EAClB,4BAA4B;EAC5B,0BAA0B;EAC1B,2BAA2B;AAC7B;;AAEA;EACE,kBAAkB;EAClB,oBAAa;EAAb,oBAAa;EAAb,aAAa;EACb,2BAAuB;EAAvB,8BAAuB;MAAvB,2BAAuB;UAAvB,uBAAuB;EACvB,qBAAqB;EACrB,yBAAmB;MAAnB,sBAAmB;UAAnB,mBAAmB;EACnB,yBAAiB;KAAjB,sBAAiB;MAAjB,qBAAiB;UAAjB,iBAAiB;AACnB;;ACfA;EACE,wBAAwB;EACxB,0BAA2B;AAC7B;;AAEA,kBAAkB;;AAElB;EACE,oBAAa;EAAb,oBAAa;EAAb,aAAa;EACb,4BAAsB;EAAtB,6BAAsB;MAAtB,0BAAsB;UAAtB,sBAAsB;EACtB,wBAAuB;MAAvB,qBAAuB;UAAvB,uBAAuB;EACvB,qBAAqB;EACrB,0BAAqB;MAArB,qBAAqB;EACrB,yBAAmB;MAAnB,sBAAmB;UAAnB,mBAAmB;AACrB;;AAEA;EACE,6DAA6D;EAC7D,eAAe;;EAEf,0BAA0B;EAC1B,mCAAmC;EACnC,kCAAkC;EAClC,kCAAkC;EAClC,wCAAwC;AAC1C;;AAEA;EACE,eAAe;AACjB;;AAEA;EACE,eAAe;AACjB;;AAEA,iBAAiB;;AAEjB;EACE,kBAAkB;AACpB;;AAEA;EACE,qDAA6C;UAA7C,6CAA6C;AAC/C;;AAEA;EACE,sDAA8C;UAA9C,8CAA8C;AAChD;;AAEA;EACE,oDAA4C;UAA5C,4CAA4C;AAC9C","file":"vc.main.css","sourcesContent":["#visual-console-container {\n margin: 0px auto;\n position: relative;\n background-repeat: no-repeat;\n background-size: 100% 100%;\n background-position: center;\n}\n\n.visual-console-item {\n position: absolute;\n display: flex;\n flex-direction: initial;\n justify-items: center;\n align-items: center;\n user-select: text;\n}\n","@font-face {\n font-family: Alarm Clock;\n src: url(./alarm-clock.ttf);\n}\n\n/* Digital clock */\n\n.visual-console-item .digital-clock {\n display: flex;\n flex-direction: column;\n justify-content: center;\n justify-items: center;\n align-content: center;\n align-items: center;\n}\n\n.visual-console-item .digital-clock > span {\n font-family: \"Alarm Clock\", \"Courier New\", Courier, monospace;\n font-size: 50px;\n\n /* To improve legibility */\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px;\n}\n\n.visual-console-item .digital-clock > span.date {\n font-size: 25px;\n}\n\n.visual-console-item .digital-clock > span.timezone {\n font-size: 25px;\n}\n\n/* Analog clock */\n\n.visual-console-item .analogic-clock {\n text-align: center;\n}\n\n.visual-console-item .analogic-clock .hour-hand {\n animation: rotate-hour 43200s infinite linear;\n}\n\n.visual-console-item .analogic-clock .minute-hand {\n animation: rotate-minute 3600s infinite linear;\n}\n\n.visual-console-item .analogic-clock .second-hand {\n animation: rotate-second 60s infinite linear;\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/pandora_console/include/visual-console-client/vc.main.min.js b/pandora_console/include/visual-console-client/vc.main.min.js new file mode 100644 index 0000000000..9f2f23a09a --- /dev/null +++ b/pandora_console/include/visual-console-client/vc.main.min.js @@ -0,0 +1,2 @@ +!function(t){var e={};function n(i){if(e[i])return e[i].exports;var r=e[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(i,r,function(e){return t[e]}.bind(null,r));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=9)}([function(t,e,n){"use strict";n.d(e,"i",function(){return r}),n.d(e,"h",function(){return s}),n.d(e,"n",function(){return o}),n.d(e,"f",function(){return a}),n.d(e,"g",function(){return c}),n.d(e,"j",function(){return u}),n.d(e,"m",function(){return h}),n.d(e,"e",function(){return p}),n.d(e,"d",function(){return _}),n.d(e,"k",function(){return f}),n.d(e,"a",function(){return d}),n.d(e,"b",function(){return y}),n.d(e,"c",function(){return m}),n.d(e,"l",function(){return b});var i=function(){return(i=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0&&!isNaN(parseInt(t))?parseInt(t):e}function s(t,e){return"number"==typeof t?t:"string"==typeof t&&t.length>0&&!isNaN(parseFloat(t))?parseFloat(t):e}function o(t){return null==t||0===t.length}function a(t,e){return"string"==typeof t&&t.length>0?t:e}function c(t){return"boolean"==typeof t?t:"number"==typeof t?t>0:"string"==typeof t&&("1"===t||"true"===t)}function l(t,e,n){void 0===n&&(n=" "),"number"==typeof t&&(t=""+t),"number"==typeof n&&(n=""+n);var i=e-t.length;if(0===i)return t;if(i<0)return t.substr(Math.abs(i));if(i===n.length)return""+n+t;if(i0){var n=document.createElement("table"),i=document.createElement("tr"),r=document.createElement("tr"),s=document.createElement("tr"),o=document.createElement("td");switch(o.innerHTML=e,i.append(o),n.append(r,i,s),n.style.textAlign="center",this.props.labelPosition){case"up":case"down":this.props.width>0&&(n.style.width=this.props.width+"px",n.style.height=null);break;case"left":case"right":this.props.height>0&&(n.style.width=null,n.style.height=this.props.height+"px")}t.append(n)}return t},t.prototype.getLabelWithMacrosReplaced=function(){var t=this.props;return Object(i.l)([{macro:"_date_",value:Object(i.b)(new Date)},{macro:"_time_",value:Object(i.c)(new Date)},{macro:"_agent_",value:null!=t.agentAlias?t.agentAlias:""},{macro:"_agentdescription_",value:null!=t.agentDescription?t.agentDescription:""},{macro:"_address_",value:null!=t.agentAddress?t.agentAddress:""},{macro:"_module_",value:null!=t.moduleName?t.moduleName:""},{macro:"_moduledescription_",value:null!=t.moduleDescription?t.moduleDescription:""}],this.props.label||"")},t.prototype.updateDomElement=function(t){t.innerHTML=this.createDomElement().innerHTML},Object.defineProperty(t.prototype,"props",{get:function(){return s({},this.itemProps)},set:function(t){var e=this.props;this.itemProps=t,this.shouldBeUpdated(e,t)&&this.render(e)},enumerable:!0,configurable:!0}),t.prototype.shouldBeUpdated=function(t,e){return t!==e},t.prototype.render=function(t){void 0===t&&(t=null),this.updateDomElement(this.childElementRef),t&&!this.positionChanged(t,this.props)||this.moveElement(this.props.x,this.props.y),t&&!this.sizeChanged(t,this.props)||this.resizeElement(this.props.width,this.props.height);var e=this.labelElementRef.innerHTML,n=this.createLabelDomElement().innerHTML;if(e!==n&&(this.labelElementRef.innerHTML=n),t&&t.labelPosition===this.props.labelPosition||this.changeLabelPosition(this.props.labelPosition),t&&(t.isLinkEnabled!==this.props.isLinkEnabled||this.props.isLinkEnabled&&t.link!==this.props.link)){var i=this.createContainerDomElement();i.innerHTML=this.elementRef.innerHTML;for(var r=this.elementRef.attributes,s=0;s0?e.item(0):null;if(n)switch(this.props.labelPosition){case"up":case"down":this.props.width>0&&(n.style.width=this.props.width+"px",n.style.height=null);break;case"left":case"right":this.props.height>0&&(n.style.width=null,n.style.height=this.props.height+"px")}},t.prototype.moveElement=function(t,e){this.elementRef.style.left=t+"px",this.elementRef.style.top=e+"px"},t.prototype.move=function(t,e){this.moveElement(t,e),this.itemProps=s({},this.props,{x:t,y:e})},t.prototype.sizeChanged=function(t,e){return t.width!==e.width||t.height!==e.height},t.prototype.resizeElement=function(t,e){this.childElementRef.style.width=t>0?t+"px":null,this.childElementRef.style.height=e>0?e+"px":null},t.prototype.resize=function(t,e){this.resizeElement(t,e),this.itemProps=s({},this.props,{width:t,height:e})},t.prototype.onClick=function(t){var e=this.clickEventManager.on(t);return this.disposables.push(e),e},t.prototype.onRemove=function(t){var e=this.removeEventManager.on(t);return this.disposables.push(e),e},t}();e.a=c},function(t,e,n){"use strict";var i=function(){return function(){var t=this;this.listeners=[],this.listenersOncer=[],this.on=function(e){return t.listeners.push(e),{dispose:function(){return t.off(e)}}},this.once=function(e){t.listenersOncer.push(e)},this.off=function(e){var n=t.listeners.indexOf(e);n>-1&&t.listeners.splice(n,1)},this.emit=function(e){t.listeners.forEach(function(t){return t(e)}),t.listenersOncer.forEach(function(t){return t(e)}),t.listenersOncer=[]},this.pipe=function(e){return t.on(function(t){return e.emit(t)})}}}();e.a=i},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.d(__webpack_exports__,"b",function(){return eventsHistoryPropsDecoder});var _lib__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(0),_Item__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(1),__extends=(extendStatics=function(t,e){return(extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(t,e)},function(t,e){function n(){this.constructor=t}extendStatics(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}),extendStatics,__assign=function(){return(__assign=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0){var m=document.createElementNS(t,"text");m.setAttribute("text-anchor","middle"),m.setAttribute("font-size","8"),m.setAttribute("transform","translate(30 50) rotate(90)"),m.setAttribute("fill",i),m.textContent=y,f.append(m)}var b=document.createElementNS(t,"g");b.setAttribute("class","marks");var v=document.createElementNS(t,"g");v.setAttribute("class","mark"),v.setAttribute("transform","translate(50 50)");var g=document.createElementNS(t,"line");g.setAttribute("x1","36"),g.setAttribute("y1","0"),g.setAttribute("x2","46"),g.setAttribute("y2","0"),g.setAttribute("stroke",i),g.setAttribute("stroke-width","5");var x=document.createElementNS(t,"line");x.setAttribute("x1","36"),x.setAttribute("y1","0"),x.setAttribute("x2","46"),x.setAttribute("y2","0"),x.setAttribute("stroke",e),x.setAttribute("stroke-width","1"),v.append(g,x),b.append(v);for(var E=1;E<60;E++){var O=document.createElementNS(t,"line");O.setAttribute("y1","0"),O.setAttribute("y2","0"),O.setAttribute("stroke",i),O.setAttribute("transform","translate(50 50) rotate("+6*E+")"),E%5==0?(O.setAttribute("x1","38"),O.setAttribute("x2","46"),O.setAttribute("stroke-width",E%15==0?"2":"1")):(O.setAttribute("x1","42"),O.setAttribute("x2","46"),O.setAttribute("stroke-width","0.5")),b.append(O)}var w=document.createElementNS(t,"g");w.setAttribute("class","hour-hand"),w.setAttribute("transform","translate(50 50)");var T=document.createElementNS(t,"line");T.setAttribute("class","hour-hand-a"),T.setAttribute("x1","0"),T.setAttribute("y1","0"),T.setAttribute("x2","30"),T.setAttribute("y2","0"),T.setAttribute("stroke",o),T.setAttribute("stroke-width","4"),T.setAttribute("stroke-linecap","round");var A=document.createElementNS(t,"line");A.setAttribute("class","hour-hand-b"),A.setAttribute("x1","0"),A.setAttribute("y1","0"),A.setAttribute("x2","29.9"),A.setAttribute("y2","0"),A.setAttribute("stroke",s),A.setAttribute("stroke-width","3.1"),A.setAttribute("stroke-linecap","round"),w.append(T,A);var k=document.createElementNS(t,"g");k.setAttribute("class","minute-hand"),k.setAttribute("transform","translate(50 50)");var P=document.createElementNS(t,"line");P.setAttribute("class","minute-hand-a"),P.setAttribute("x1","0"),P.setAttribute("y1","0"),P.setAttribute("x2","40"),P.setAttribute("y2","0"),P.setAttribute("stroke",o),P.setAttribute("stroke-width","2"),P.setAttribute("stroke-linecap","round");var M=document.createElementNS(t,"line");M.setAttribute("class","minute-hand-b"),M.setAttribute("x1","0"),M.setAttribute("y1","0"),M.setAttribute("x2","39.9"),M.setAttribute("y2","0"),M.setAttribute("stroke",s),M.setAttribute("stroke-width","1.5"),M.setAttribute("stroke-linecap","round");var j=document.createElementNS(t,"circle");j.setAttribute("r","3"),j.setAttribute("fill",s),k.append(P,M,j);var S=document.createElementNS(t,"g");S.setAttribute("class","second-hand"),S.setAttribute("transform","translate(50 50)");var I=document.createElementNS(t,"line");I.setAttribute("x1","0"),I.setAttribute("y1","0"),I.setAttribute("x2","46"),I.setAttribute("y2","0"),I.setAttribute("stroke",a),I.setAttribute("stroke-width","1"),I.setAttribute("stroke-linecap","round");var N=document.createElementNS(t,"circle");N.setAttribute("r","2"),N.setAttribute("fill",a),S.append(I,N);var D=document.createElementNS(t,"circle");D.setAttribute("cx","50"),D.setAttribute("cy","50"),D.setAttribute("r","0.3"),D.setAttribute("fill",s);var L=this.getOriginDate(),R=L.getSeconds(),C=L.getMinutes(),B=6*R,z=6*C+R/60*6,W=30*L.getHours()+C/60*30;if(w.setAttribute("transform","translate(50 50) rotate("+W+")"),k.setAttribute("transform","translate(50 50) rotate("+z+")"),S.setAttribute("transform","translate(50 50) rotate("+B+")"),_.append(f,b,w,k,S,D),_.setAttribute("transform","rotate(-90)"),p.innerHTML="\n \n ",p.append(_),"datetime"===this.props.clockFormat){var H=document.createElement("span");H.className="date",H.textContent=Object(r.b)(L,"default"),H.style.fontSize=h+"px",this.props.color&&(H.style.color=this.props.color),p.append(H)}return p},e.prototype.createDigitalClock=function(){var t=document.createElement("div");t.className="digital-clock";var e=this.getElementSize().width,n=6/this.props.clockTimezone.length,i=20*e/100,s=10*e/100,o=Math.min(20*n*e/100,e/100*10),a=this.getOriginDate();if("datetime"===this.props.clockFormat){var c=document.createElement("span");c.className="date",c.textContent=Object(r.b)(a,"default"),c.style.fontSize=s+"px",this.props.color&&(c.style.color=this.props.color),t.append(c)}var l=document.createElement("span");l.className="time",l.textContent=Object(r.c)(a),l.style.fontSize=i+"px",this.props.color&&(l.style.color=this.props.color),t.append(l);var u=this.getHumanTimezone();if(u.length>0){var h=document.createElement("span");h.className="timezone",h.textContent=u,h.style.fontSize=o+"px",this.props.color&&(h.style.color=this.props.color),t.append(h)}return t},e.prototype.getOriginDate=function(t){void 0===t&&(t=null);var e=t||new Date,n=1e3*this.props.clockTimezoneOffset,i=60*e.getTimezoneOffset()*1e3,r=e.getTime()+n+i;return new Date(r)},e.prototype.getHumanTimezone=function(t){void 0===t&&(t=this.props.clockTimezone);var e=t.split("/")[1];return(void 0===e?"":e).replace("_"," ")},e.prototype.getElementSize=function(t,e){switch(void 0===t&&(t=this.props.width),void 0===e&&(e=this.props.height),this.props.clockType){case"analogic":var n=100;return t>0&&e>0?n=Math.min(t,e):t>0?n=t:e>0&&(n=e),{width:n,height:n};case"digital":return t>0&&e>0?e=t/20?e=t/2:e>0?t=2*e:(t=100,e=50),{width:t,height:e};default:throw new Error("invalid clock type.")}},e.TICK_INTERVAL=1e3,e}(s.a),j=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),S=function(){return(S=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0){t.style.borderStyle="solid";var e=Math.min(this.props.width,this.props.height)/2,n=Math.min(this.props.borderWidth,e);t.style.borderWidth=n+"px",this.props.borderColor&&(t.style.borderColor=this.props.borderColor)}return t},e}(s.a),D=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),L=function(){return(L=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0&&(n=Object(r.l)([{macro:/\(?_VALUE_\)?/i,value:n}],i)),t.innerHTML=n}return t},e.prototype.createLabelDomElement=function(){var t=document.createElement("div");return t.className="visual-console-item-label",t},e}(s.a),X=n(3),Z=Math.PI,Q=2*Z,J=Q-1e-6;function $(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function tt(){return new $}$.prototype=tt.prototype={constructor:$,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,i){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+i)},bezierCurveTo:function(t,e,n,i,r,s){this._+="C"+ +t+","+ +e+","+ +n+","+ +i+","+(this._x1=+r)+","+(this._y1=+s)},arcTo:function(t,e,n,i,r){t=+t,e=+e,n=+n,i=+i,r=+r;var s=this._x1,o=this._y1,a=n-t,c=i-e,l=s-t,u=o-e,h=l*l+u*u;if(r<0)throw new Error("negative radius: "+r);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>1e-6)if(Math.abs(u*a-c*l)>1e-6&&r){var p=n-s,_=i-o,f=a*a+c*c,d=p*p+_*_,y=Math.sqrt(f),m=Math.sqrt(h),b=r*Math.tan((Z-Math.acos((f+h-d)/(2*y*m)))/2),v=b/m,g=b/y;Math.abs(v-1)>1e-6&&(this._+="L"+(t+v*l)+","+(e+v*u)),this._+="A"+r+","+r+",0,0,"+ +(u*p>l*_)+","+(this._x1=t+g*a)+","+(this._y1=e+g*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e);else;},arc:function(t,e,n,i,r,s){t=+t,e=+e;var o=(n=+n)*Math.cos(i),a=n*Math.sin(i),c=t+o,l=e+a,u=1^s,h=s?i-r:r-i;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+l:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-l)>1e-6)&&(this._+="L"+c+","+l),n&&(h<0&&(h=h%Q+Q),h>J?this._+="A"+n+","+n+",0,1,"+u+","+(t-o)+","+(e-a)+"A"+n+","+n+",0,1,"+u+","+(this._x1=c)+","+(this._y1=l):h>1e-6&&(this._+="A"+n+","+n+",0,"+ +(h>=Z)+","+u+","+(this._x1=t+n*Math.cos(r))+","+(this._y1=e+n*Math.sin(r))))},rect:function(t,e,n,i){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +i+"h"+-n+"Z"},toString:function(){return this._}};var et=tt,nt=function(t){return function(){return t}},it=Math.abs,rt=Math.atan2,st=Math.cos,ot=Math.max,at=Math.min,ct=Math.sin,lt=Math.sqrt,ut=1e-12,ht=Math.PI,pt=ht/2,_t=2*ht;function ft(t){return t>=1?pt:t<=-1?-pt:Math.asin(t)}function dt(t){return t.innerRadius}function yt(t){return t.outerRadius}function mt(t){return t.startAngle}function bt(t){return t.endAngle}function vt(t){return t&&t.padAngle}function gt(t,e,n,i,r,s,o){var a=t-n,c=e-i,l=(o?s:-s)/lt(a*a+c*c),u=l*c,h=-l*a,p=t+u,_=e+h,f=n+u,d=i+h,y=(p+f)/2,m=(_+d)/2,b=f-p,v=d-_,g=b*b+v*v,x=r-s,E=p*d-f*_,O=(v<0?-1:1)*lt(ot(0,x*x*g-E*E)),w=(E*v-b*O)/g,T=(-E*b-v*O)/g,A=(E*v+b*O)/g,k=(-E*b+v*O)/g,P=w-y,M=T-m,j=A-y,S=k-m;return P*P+M*M>j*j+S*S&&(w=A,T=k),{cx:w,cy:T,x01:-u,y01:-h,x11:w*(r/x-1),y11:T*(r/x-1)}}var xt=function(){var t=dt,e=yt,n=nt(0),i=null,r=mt,s=bt,o=vt,a=null;function c(){var c,l,u,h=+t.apply(this,arguments),p=+e.apply(this,arguments),_=r.apply(this,arguments)-pt,f=s.apply(this,arguments)-pt,d=it(f-_),y=f>_;if(a||(a=c=et()),put)if(d>_t-ut)a.moveTo(p*st(_),p*ct(_)),a.arc(0,0,p,_,f,!y),h>ut&&(a.moveTo(h*st(f),h*ct(f)),a.arc(0,0,h,f,_,y));else{var m,b,v=_,g=f,x=_,E=f,O=d,w=d,T=o.apply(this,arguments)/2,A=T>ut&&(i?+i.apply(this,arguments):lt(h*h+p*p)),k=at(it(p-h)/2,+n.apply(this,arguments)),P=k,M=k;if(A>ut){var j=ft(A/h*ct(T)),S=ft(A/p*ct(T));(O-=2*j)>ut?(x+=j*=y?1:-1,E-=j):(O=0,x=E=(_+f)/2),(w-=2*S)>ut?(v+=S*=y?1:-1,g-=S):(w=0,v=g=(_+f)/2)}var I=p*st(v),N=p*ct(v),D=h*st(E),L=h*ct(E);if(k>ut){var R,C=p*st(g),B=p*ct(g),z=h*st(x),W=h*ct(x);if(d1?0:u<-1?ht:Math.acos(u))/2),V=lt(R[0]*R[0]+R[1]*R[1]);P=at(k,(h-V)/(q-1)),M=at(k,(p-V)/(q+1))}}w>ut?M>ut?(m=gt(z,W,I,N,p,M,y),b=gt(C,B,D,L,p,M,y),a.moveTo(m.cx+m.x01,m.cy+m.y01),Mut&&O>ut?P>ut?(m=gt(D,L,C,B,h,-P,y),b=gt(I,N,z,W,h,-P,y),a.lineTo(m.cx+m.x01,m.cy+m.y01),P0)for(var i,r=t[0],s=e[0],o=t[n]-r,a=e[n]-s,c=-1;++c<=n;)i=c/n,this._basis.point(this._beta*t[c]+(1-this._beta)*(r+i*o),this._beta*e[c]+(1-this._beta)*(s+i*a));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};(function t(e){function n(t){return 1===e?new Mt(t):new It(t,e)}return n.beta=function(e){return t(+e)},n})(.85);function Nt(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function Dt(t,e){this._context=t,this._k=(1-e)/6}Dt.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Nt(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:Nt(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return new Dt(t,e)}return n.tension=function(e){return t(+e)},n})(0);function Lt(t,e){this._context=t,this._k=(1-e)/6}Lt.prototype={areaStart:kt,areaEnd:kt,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Nt(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return new Lt(t,e)}return n.tension=function(e){return t(+e)},n})(0);function Rt(t,e){this._context=t,this._k=(1-e)/6}Rt.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Nt(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return new Rt(t,e)}return n.tension=function(e){return t(+e)},n})(0);function Ct(t,e,n){var i=t._x1,r=t._y1,s=t._x2,o=t._y2;if(t._l01_a>ut){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);i=(i*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,r=(r*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>ut){var l=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,u=3*t._l23_a*(t._l23_a+t._l12_a);s=(s*l+t._x1*t._l23_2a-e*t._l12_2a)/u,o=(o*l+t._y1*t._l23_2a-n*t._l12_2a)/u}t._context.bezierCurveTo(i,r,s,o,t._x2,t._y2)}function Bt(t,e){this._context=t,this._alpha=e}Bt.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,i=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:Ct(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return e?new Bt(t,e):new Dt(t,0)}return n.alpha=function(e){return t(+e)},n})(.5);function zt(t,e){this._context=t,this._alpha=e}zt.prototype={areaStart:kt,areaEnd:kt,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,i=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Ct(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return e?new zt(t,e):new Lt(t,0)}return n.alpha=function(e){return t(+e)},n})(.5);function Wt(t,e){this._context=t,this._alpha=e}Wt.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,i=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+i*i,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Ct(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};(function t(e){function n(t){return e?new Wt(t,e):new Rt(t,0)}return n.alpha=function(e){return t(+e)},n})(.5);function Ht(t){this._context=t}Ht.prototype={areaStart:kt,areaEnd:kt,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}};function Ut(t){return t<0?-1:1}function Kt(t,e,n){var i=t._x1-t._x0,r=e-t._x1,s=(t._y1-t._y0)/(i||r<0&&-0),o=(n-t._y1)/(r||i<0&&-0),a=(s*r+o*i)/(i+r);return(Ut(s)+Ut(o))*Math.min(Math.abs(s),Math.abs(o),.5*Math.abs(a))||0}function Gt(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function qt(t,e,n){var i=t._x0,r=t._y0,s=t._x1,o=t._y1,a=(s-i)/3;t._context.bezierCurveTo(i+a,r+a*e,s-a,o-a*n,s,o)}function Vt(t){this._context=t}function Ft(t){this._context=new Yt(t)}function Yt(t){this._context=t}function Xt(t){this._context=t}function Zt(t){var e,n,i=t.length-1,r=new Array(i),s=new Array(i),o=new Array(i);for(r[0]=0,s[0]=2,o[0]=t[0]+2*t[1],e=1;e=0;--e)r[e]=(o[e]-r[e+1])/s[e];for(s[i-1]=(t[i]+r[i-1])/2,e=0;e=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var Jt=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),$t=function(){return($t=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0){var p=document.createElementNS(ie,"tspan");p.setAttribute("x","0"),p.setAttribute("dy","1em"),p.textContent=""+t,p.style.fontSize="8pt";var _=document.createElementNS(ie,"tspan");_.setAttribute("x","0"),_.setAttribute("dy","1em"),_.textContent=""+this.props.unit,_.style.fontSize="8pt",h.append(p,_),h.setAttribute("transform","translate(50 33)")}else h.textContent=""+t,h.style.fontSize="8pt",h.setAttribute("transform","translate(50 50)");else h.textContent=n+"%",h.setAttribute("transform","translate(50 50)");r.append(h)}return i.append(r),i},e.prototype.getProgress=function(){var t=this.props.minValue||0,e=this.props.maxValue||100,n=null==this.props.value?0:this.props.value;return n<=t?0:n>=e?100:Math.trunc((n-t)/(e-t)*100)},e}(s.a),se=n(2),oe=n(4),ae=n(5),ce=n(6),le=function(){var t=function(e,n){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(e,n)};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),ue=function(){return(ue=Object.assign||function(t){for(var e,n=1,i=arguments.length;ne.id?1:-1})).forEach(function(t){try{var e=fe(t);i.elementsById[e.props.id]=e,i.elementIds.push(e.props.id),e.onClick(i.handleElementClick),e.onRemove(i.handleElementRemove),i.containerRef.append(e.elementRef)}catch(t){console.log("Error creating a new element:",t.message)}}),this.buildRelations()}return Object.defineProperty(t.prototype,"elements",{get:function(){var t=this;return this.elementIds.map(function(e){return t.elementsById[e]}).filter(function(t){return null!=t})},enumerable:!0,configurable:!0}),t.prototype.updateElements=function(t){var e=this,n=t.map(function(t){return t.id||null}).filter(function(t){return null!=t});this.elementIds.filter(function(t){return n.indexOf(t)<0}).forEach(function(t){null!=e.elementsById[t]&&(e.elementsById[t].remove(),delete e.elementsById[t])}),this.elementIds=n,t.forEach(function(t){if(t.id)if(null==e.elementsById[t.id])try{var n=fe(t);e.elementsById[n.props.id]=n,n.onClick(e.handleElementClick),n.onRemove(e.handleElementRemove),e.containerRef.append(n.elementRef)}catch(t){console.log("Error creating a new element:",t.message)}else try{e.elementsById[t.id].props=function(t){var e=Object(r.i)(t.type,null);if(null==e)throw new TypeError("missing item type.");switch(e){case 0:return l(t);case 1:return Object(ce.b)(t);case 2:case 6:case 7:case 8:return F(t);case 3:case 9:case 15:case 16:return ne(t);case 4:return H(t);case 5:return _(t);case 10:return he(t);case 11:return E(t);case 12:return I(t);case 13:return R(t);case 14:return Object(X.b)(t);case 17:return Object(oe.b)(t);case 18:return Object(ae.a)(t);case 19:return P(t);case 20:return m(t);default:throw new TypeError("decoder not found")}}(t)}catch(t){console.log("Error updating an element:",t.message)}}),this.buildRelations()},Object.defineProperty(t.prototype,"props",{get:function(){return _e({},this._props)},set:function(t){var e=this.props;this._props=t,this.render(e)},enumerable:!0,configurable:!0}),t.prototype.render=function(t){void 0===t&&(t=null),t?(t.backgroundURL!==this.props.backgroundURL&&(this.containerRef.style.backgroundImage=null!==this.props.backgroundURL?"url("+this.props.backgroundURL+")":null),t.backgroundColor!==this.props.backgroundColor&&(this.containerRef.style.backgroundColor=this.props.backgroundColor),this.sizeChanged(t,this.props)&&this.resizeElement(this.props.width,this.props.height)):(this.containerRef.style.backgroundImage=null!==this.props.backgroundURL?"url("+this.props.backgroundURL+")":null,this.containerRef.style.backgroundColor=this.props.backgroundColor,this.resizeElement(this.props.width,this.props.height))},t.prototype.sizeChanged=function(t,e){return t.width!==e.width||t.height!==e.height},t.prototype.resizeElement=function(t,e){this.containerRef.style.width=t+"px",this.containerRef.style.height=e+"px"},t.prototype.resize=function(t,e){this.props=_e({},this.props,{width:t,height:e})},t.prototype.remove=function(){this.disposables.forEach(function(t){return t.dispose()}),this.elements.forEach(function(t){return t.remove()}),this.elementsById={},this.elementIds=[],this.clearRelations(),this.containerRef.innerHTML=""},t.prototype.buildRelations=function(){var t=this;this.clearRelations(),this.elements.forEach(function(e){if(null!==e.props.parentId){var n=t.elementsById[e.props.parentId],i=t.elementsById[e.props.id];n&&i&&t.addRelationLine(n,i)}})},t.prototype.clearRelations=function(t){if(null!=t)for(var e in this.relations){var n=e.split("|"),i=Number.parseInt(n[0]),r=Number.parseInt(n[1]);t!==i&&t!==r||(this.relations[e].remove(),delete this.relations[e])}else for(var e in this.relations)this.relations[e].remove(),delete this.relations[e]},t.prototype.getRelationLine=function(t,e){var n=t+"|"+e;return this.relations[n]||null},t.prototype.addRelationLine=function(t,e){var n=t.props.id+"|"+e.props.id;null!=this.relations[n]&&this.relations[n].remove();var i=t.props.x+t.elementRef.clientWidth/2,r=t.props.y+(t.elementRef.clientHeight-t.labelElementRef.clientHeight)/2,s=e.props.x+e.elementRef.clientWidth/2,o=e.props.y+(e.elementRef.clientHeight-e.labelElementRef.clientHeight)/2,a=new B(R({id:0,type:13,startX:i,startY:r,endX:s,endY:o,width:0,height:0,lineWidth:this.props.relationLineWidth,color:"#CCCCCC"}));return this.relations[n]=a,a.elementRef.style.zIndex="0",this.containerRef.append(a.elementRef),a},t.prototype.onClick=function(t){var e=this.clickEventManager.on(t);return this.disposables.push(e),e},t}(),ye=function(){function t(t){this.cancellable={cancel:function(){}},this._status="waiting",this.statusChangeEventManager=new se.a,this.disposables=[],this.taskInitiator=t}return Object.defineProperty(t.prototype,"status",{get:function(){return this._status},set:function(t){this._status=t,this.statusChangeEventManager.emit(t)},enumerable:!0,configurable:!0}),t.prototype.init=function(){var t=this;this.cancellable=this.taskInitiator(function(){t.status="finished"}),this.status="started"},t.prototype.cancel=function(){this.cancellable.cancel(),this.status="cancelled"},t.prototype.onStatusChange=function(t){var e=this.statusChangeEventManager.on(t);return this.disposables.push(e),e},t}();var me=function(){function t(){this.tasks={}}return t.prototype.add=function(t,e,n){void 0===n&&(n=0),this.tasks[t]&&"started"===this.tasks[t].status&&this.tasks[t].cancel();var i=n>0?function(t,e){return new ye(function(){var n=null;return t.onStatusChange(function(i){"finished"===i&&(n=window.setTimeout(function(){t.init()},e))}),t.init(),{cancel:function(){n&&clearTimeout(n),t.cancel()}}})}(new ye(e),n):new ye(e);return this.tasks[t]=i,this.tasks[t]},t.prototype.init=function(t){!this.tasks[t]||"waiting"!==this.tasks[t].status&&"cancelled"!==this.tasks[t].status&&"finished"!==this.tasks[t].status||this.tasks[t].init()},t.prototype.cancel=function(t){this.tasks[t]&&"started"===this.tasks[t].status&&this.tasks[t].cancel()},t}();window.VisualConsole=de,window.AsyncTaskManager=me}]); +//# sourceMappingURL=vc.main.min.js.map \ No newline at end of file diff --git a/pandora_console/include/visual-console-client/vc.main.min.js.map b/pandora_console/include/visual-console-client/vc.main.min.js.map new file mode 100644 index 0000000000..518b003c07 --- /dev/null +++ b/pandora_console/include/visual-console-client/vc.main.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/lib/index.ts","webpack:///./src/Item.ts","webpack:///./src/TypedEvent.ts","webpack:///./src/items/EventsHistory.ts","webpack:///./src/items/DonutGraph.ts","webpack:///./src/items/BarsGraph.ts","webpack:///./src/items/ModuleGraph.ts","webpack:///./src/items/StaticGraph.ts","webpack:///./src/items/Icon.ts","webpack:///./src/items/ColorCloud.ts","webpack:///./src/items/Group.ts","webpack:///./src/items/Clock/index.ts","webpack:///./src/items/Box.ts","webpack:///./src/items/Line.ts","webpack:///./src/items/Label.ts","webpack:///./src/items/SimpleValue.ts","webpack:///./node_modules/d3-path/src/path.js","webpack:///./node_modules/d3-shape/src/constant.js","webpack:///./node_modules/d3-shape/src/math.js","webpack:///./node_modules/d3-shape/src/arc.js","webpack:///./node_modules/d3-shape/src/curve/linear.js","webpack:///./node_modules/d3-shape/src/curve/radial.js","webpack:///./node_modules/d3-shape/src/array.js","webpack:///./node_modules/d3-shape/src/symbol/diamond.js","webpack:///./node_modules/d3-shape/src/symbol/circle.js","webpack:///./node_modules/d3-shape/src/symbol/star.js","webpack:///./node_modules/d3-shape/src/noop.js","webpack:///./node_modules/d3-shape/src/symbol/triangle.js","webpack:///./node_modules/d3-shape/src/symbol/wye.js","webpack:///./node_modules/d3-shape/src/curve/basis.js","webpack:///./node_modules/d3-shape/src/curve/basisClosed.js","webpack:///./node_modules/d3-shape/src/curve/basisOpen.js","webpack:///./node_modules/d3-shape/src/curve/bundle.js","webpack:///./node_modules/d3-shape/src/curve/cardinal.js","webpack:///./node_modules/d3-shape/src/curve/cardinalClosed.js","webpack:///./node_modules/d3-shape/src/curve/cardinalOpen.js","webpack:///./node_modules/d3-shape/src/curve/catmullRom.js","webpack:///./node_modules/d3-shape/src/curve/catmullRomClosed.js","webpack:///./node_modules/d3-shape/src/curve/catmullRomOpen.js","webpack:///./node_modules/d3-shape/src/curve/linearClosed.js","webpack:///./node_modules/d3-shape/src/curve/monotone.js","webpack:///./node_modules/d3-shape/src/curve/natural.js","webpack:///./node_modules/d3-shape/src/curve/step.js","webpack:///./node_modules/d3-shape/src/order/descending.js","webpack:///./src/items/Percentile.ts","webpack:///./src/items/Service.ts","webpack:///./src/VisualConsole.ts","webpack:///./src/lib/AsyncTaskManager.ts","webpack:///./src/index.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","parseIntOr","defaultValue","length","isNaN","parseInt","parseFloatOr","parseFloat","stringIsEmpty","notEmptyStringOr","parseBoolean","leftPad","pad","diffLength","substr","Math","abs","substring","repeatTimes","floor","restLength","newPad","positionPropsDecoder","data","x","y","sizePropsDecoder","width","height","TypeError","modulePropsDecoder","__assign","moduleName","moduleDescription","agentProps","agentId","agent","agentName","agentAlias","agentDescription","agentAddress","metaconsoleId","agentPropsDecoder","linkedVCPropsDecoder","id","linkedLayoutId","linkedLayoutAgentId","linkedLayoutStatusProps","linkedLayoutStatusType","weight","linkedLayoutStatusTypeWeight","warningThreshold","linkedLayoutStatusTypeWarningThreshold","criticalThreshold","linkedLayoutStatusTypeCriticalThreshold","linkedLayoutBaseProps","prefixedCssRules","ruleName","ruleValue","rule","decodeBase64","input","decodeURIComponent","escape","window","atob","humanDate","date","locale","Intl","DateTimeFormat","day","month","year","format","getDate","getMonth","getFullYear","humanTime","getHours","getMinutes","getSeconds","replaceMacros","macros","text","reduce","acc","_a","macro","replace","parseLabelPosition","labelPosition","itemBasePropsDecoder","type","label","_lib__WEBPACK_IMPORTED_MODULE_0__","isLinkEnabled","link","isOnTop","parentId","aclGroupId","VisualConsoleItem","props","this","clickEventManager","_TypedEvent__WEBPACK_IMPORTED_MODULE_1__","removeEventManager","disposables","itemProps","elementRef","createContainerDomElement","labelElementRef","createLabelDomElement","childElementRef","createDomElement","append","resizeElement","changeLabelPosition","box","_this","document","createElement","href","className","style","zIndex","left","top","onclick","e","emit","nativeEvent","element","getLabelWithMacrosReplaced","table","row","emptyRow1","emptyRow2","cell","innerHTML","textAlign","Date","updateDomElement","newProps","prevProps","shouldBeUpdated","render","positionChanged","moveElement","sizeChanged","oldLabelHtml","newLabelHtml","container","attrs","attributes","nodeName","setAttributeNode","parentNode","replaceChild","remove","forEach","disposable","dispose","ignored","prevPosition","newPosition","position","flexDirection","tables","getElementsByTagName","item","move","prevSize","newSize","resize","onClick","listener","on","push","onRemove","__webpack_exports__","TypedEvent","listeners","listenersOncer","off","once","callbackIndex","indexOf","splice","event","pipe","te","eventsHistoryPropsDecoder","html","encodedHtml","_Item__WEBPACK_IMPORTED_MODULE_1__","maxTime","EventsHistory","_super","__extends","scripts","src","setTimeout","eval","trim","aux","donutGraphPropsDecoder","DonutGraph","barsGraphPropsDecoder","BarsGraph","moduleGraphPropsDecoder","ModuleGraph","legendP","margin","overviewGraphs","getElementsByClassName","parseShowLastValueTooltip","showLastValueTooltip","staticGraphPropsDecoder","imageSrc","Item","statusImageSrc","lib","lastValue","StaticGraph","imgSrc","background","backgroundSize","backgroundPosition","setAttribute","iconPropsDecoder","Icon_assign","Icon","Icon_extends","colorCloudPropsDecoder","color","ColorCloud_assign","ColorCloud_svgNS","ColorCloud","ColorCloud_extends","createSvgElement","gradientId","svg","createElementNS","defs","radialGradient","stop0","stop100","circle","groupPropsDecoder","groupId","showStatistics","extractHtml","Group_assign","Group","Group_extends","parseClockType","clockType","parseClockFormat","clockFormat","clockPropsDecoder","clockTimezone","Clock_assign","clockTimezoneOffset","showClockTimezone","items_Clock","Clock","intervalRef","startTick","createClock","TICK_INTERVAL","Clock_extends","stopTick","clearInterval","handler","interval","setInterval","getElementSize","newWidth","newHeight","createAnalogicClock","createDigitalClock","Error","svgNS","colors","dateFontSize","baseTimeFontSize","div","clockFace","clockFaceBackground","city","getHumanTimezone","timezoneComplication","textContent","marksGroup","mainMarkGroup","mark1a","mark1b","mark","hourHand","hourHandA","hourHandB","minuteHand","minuteHandA","minuteHandB","minuteHandPin","secondHand","secondHandBar","secondHandPin","pin","getOriginDate","seconds","minutes","secAngle","minuteAngle","hourAngle","join","dateElem","fontSize","tzFontSizeMultiplier","timeFontSize","tzFontSize","min","timeElem","tzElem","initialDate","targetTZOffset","localTZOffset","getTimezoneOffset","utimestamp","getTime","timezone","_b","split","diameter","boxPropsDecoder","Box_assign","borderWidth","borderColor","fillColor","Box","Box_extends","boxSizing","backgroundColor","borderStyle","maxBorderWidth","linePropsDecoder","Line_assign","startPosition","startX","startY","endPosition","endX","endY","lineWidth","Line","extractBoxSizeAndPosition","Line_extends","toString","line","labelPropsDecoder","Label_assign","Label","Label_extends","parseValueType","valueType","parseProcessValue","processValue","simpleValuePropsDecoder","SimpleValue_assign","period","SimpleValue","SimpleValue_extends","img","pi","PI","tau","tauEpsilon","Path","_x0","_y0","_x1","_y1","_","path","constructor","moveTo","closePath","lineTo","quadraticCurveTo","x1","y1","bezierCurveTo","x2","y2","arcTo","x0","y0","x21","y21","x01","y01","l01_2","x20","y20","l21_2","l20_2","l21","sqrt","l01","tan","acos","t01","t21","arc","a0","a1","ccw","dx","cos","dy","sin","cw","da","rect","w","h","src_path","constant","atan2","max","math_epsilon","math_pi","halfPi","math_tau","asin","arcInnerRadius","innerRadius","arcOuterRadius","outerRadius","arcStartAngle","startAngle","arcEndAngle","endAngle","arcPadAngle","padAngle","cornerTangents","r1","rc","lo","ox","oy","x11","y11","x10","y10","x00","y00","d2","D","cx0","cy0","cx1","cy1","dx0","dy0","dx1","dy1","cx","cy","src_arc","cornerRadius","padRadius","context","buffer","r0","apply","arguments","t0","t1","a01","a11","a00","a10","da0","da1","ap","rp","rc0","rc1","p0","p1","oc","x3","y3","x32","y32","intersect","ax","ay","bx","by","kc","lc","centroid","a","Linear","_context","areaStart","_line","areaEnd","NaN","lineStart","_point","lineEnd","point","linear","curveRadial","Radial","curve","_curve","radial","Array","slice","kr","noop","that","Basis","BasisClosed","_x2","_x3","_x4","_y2","_y3","_y4","BasisOpen","Bundle","beta","_basis","_beta","_x","_y","j","custom","bundle","cardinal_point","_k","Cardinal","tension","cardinal","CardinalClosed","_x5","_y5","CardinalOpen","catmullRom_point","_l01_a","_l01_2a","_l12_a","_l12_2a","_l23_a","b","_l23_2a","CatmullRom","alpha","_alpha","x23","y23","pow","catmullRom","CatmullRomClosed","CatmullRomOpen","LinearClosed","sign","slope3","h0","h1","s0","s1","slope2","monotone_point","MonotoneX","MonotoneY","ReflectContext","Natural","controlPoints","_t0","px","py","i0","i1","Step","_t","extractPercentileType","extractValueType","percentilePropsDecoder","Percentile_assign","percentileType","minValue","maxValue","labelColor","unit","Percentile_svgNS","Percentile","Percentile_extends","formatValue","progress","getProgress","NumberFormat","backgroundRect","progressRect","backgroundCircle","progressCircle","arcProps","trunc","servicePropsDecoder","encodedTitle","serviceId","Service_assign","Service","Service_extends","itemInstanceFrom","items_StaticGraph","items_SimpleValue","items_Percentile","items_Label","items_Icon","items_Service","items_Group","items_Box","items_Line","items_ColorCloud","VisualConsole","items","elementsById","elementIds","relations","handleElementClick","handleElementRemove","filter","clearRelations","containerRef","_props","backgroundURL","isFavorite","relationLineWidth","VisualConsole_assign","visualConsolePropsDecoder","sort","itemInstance","error","console","log","message","buildRelations","map","updateElements","itemIds","decodeProps","backgroundImage","elements","parent_1","child","addRelationLine","itemId","ids","Number","childId","getRelationLine","identifier","parent","clientWidth","clientHeight","AsyncTaskManager_AsyncTask","AsyncTask","taskInitiator","cancellable","cancel","_status","statusChangeEventManager","status","init","onStatusChange","AsyncTaskManager","tasks","add","asyncTask","task","ref","clearTimeout","asyncPeriodic","src_VisualConsole","lib_AsyncTaskManager"],"mappings":"aACA,IAAAA,EAAA,GAGA,SAAAC,EAAAC,GAGA,GAAAF,EAAAE,GACA,OAAAF,EAAAE,GAAAC,QAGA,IAAAC,EAAAJ,EAAAE,GAAA,CACAG,EAAAH,EACAI,GAAA,EACAH,QAAA,IAUA,OANAI,EAAAL,GAAAM,KAAAJ,EAAAD,QAAAC,IAAAD,QAAAF,GAGAG,EAAAE,GAAA,EAGAF,EAAAD,QAKAF,EAAAQ,EAAAF,EAGAN,EAAAS,EAAAV,EAGAC,EAAAU,EAAA,SAAAR,EAAAS,EAAAC,GACAZ,EAAAa,EAAAX,EAAAS,IACAG,OAAAC,eAAAb,EAAAS,EAAA,CAA0CK,YAAA,EAAAC,IAAAL,KAK1CZ,EAAAkB,EAAA,SAAAhB,GACA,oBAAAiB,eAAAC,aACAN,OAAAC,eAAAb,EAAAiB,OAAAC,YAAA,CAAwDC,MAAA,WAExDP,OAAAC,eAAAb,EAAA,cAAiDmB,OAAA,KAQjDrB,EAAAsB,EAAA,SAAAD,EAAAE,GAEA,GADA,EAAAA,IAAAF,EAAArB,EAAAqB,IACA,EAAAE,EAAA,OAAAF,EACA,KAAAE,GAAA,iBAAAF,QAAAG,WAAA,OAAAH,EACA,IAAAI,EAAAX,OAAAY,OAAA,MAGA,GAFA1B,EAAAkB,EAAAO,GACAX,OAAAC,eAAAU,EAAA,WAAyCT,YAAA,EAAAK,UACzC,EAAAE,GAAA,iBAAAF,EAAA,QAAAM,KAAAN,EAAArB,EAAAU,EAAAe,EAAAE,EAAA,SAAAA,GAAgH,OAAAN,EAAAM,IAAqBC,KAAA,KAAAD,IACrI,OAAAF,GAIAzB,EAAA6B,EAAA,SAAA1B,GACA,IAAAS,EAAAT,KAAAqB,WACA,WAA2B,OAAArB,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAH,EAAAU,EAAAE,EAAA,IAAAA,GACAA,GAIAZ,EAAAa,EAAA,SAAAiB,EAAAC,GAAsD,OAAAjB,OAAAkB,UAAAC,eAAA1B,KAAAuB,EAAAC,IAGtD/B,EAAAkC,EAAA,GAIAlC,IAAAmC,EAAA,qrBClEO,SAASC,EAAcf,EAAgBgB,GAC5C,MAAqB,iBAAVhB,EAA2BA,EACjB,iBAAVA,GAAsBA,EAAMiB,OAAS,IAAMC,MAAMC,SAASnB,IAC5DmB,SAASnB,GACNgB,EASP,SAASI,EAAgBpB,EAAgBgB,GAC9C,MAAqB,iBAAVhB,EAA2BA,EAEnB,iBAAVA,GACPA,EAAMiB,OAAS,IACdC,MAAMG,WAAWrB,IAEXqB,WAAWrB,GACRgB,EAQP,SAASM,EAActB,GAC5B,OAAgB,MAATA,GAAkC,IAAjBA,EAAMiB,OASzB,SAASM,EACdvB,EACAgB,GAEA,MAAwB,iBAAVhB,GAAsBA,EAAMiB,OAAS,EAAIjB,EAAQgB,EAQ1D,SAASQ,EAAaxB,GAC3B,MAAqB,kBAAVA,EAA4BA,EACb,iBAAVA,EAA2BA,EAAQ,EACzB,iBAAVA,IAAqC,MAAVA,GAA2B,SAAVA,GAavD,SAASyB,EACdzB,EACAiB,EACAS,QAAA,IAAAA,MAAA,KAEqB,iBAAV1B,IAAoBA,EAAQ,GAAGA,GACvB,iBAAR0B,IAAkBA,EAAM,GAAGA,GAEtC,IAAMC,EAAaV,EAASjB,EAAMiB,OAClC,GAAmB,IAAfU,EAAkB,OAAO3B,EAC7B,GAAI2B,EAAa,EAAG,OAAO3B,EAAM4B,OAAOC,KAAKC,IAAIH,IAEjD,GAAIA,IAAeD,EAAIT,OAAQ,MAAO,GAAGS,EAAM1B,EAC/C,GAAI2B,EAAaD,EAAIT,OAAQ,MAAO,GAAGS,EAAIK,UAAU,EAAGJ,GAAc3B,EAMtE,IAJA,IAAMgC,EAAcH,KAAKI,MAAMN,EAAaD,EAAIT,QAC1CiB,EAAaP,EAAaD,EAAIT,OAASe,EAEzCG,EAAS,GACJpD,EAAI,EAAGA,EAAIiD,EAAajD,IAAKoD,GAAUT,EAEhD,OAAmB,IAAfQ,EAAyB,GAAGC,EAASnC,EAClC,GAAGmC,EAAST,EAAIK,UAAU,EAAGG,GAAclC,EAU7C,SAASoC,EAAqBC,GACnC,MAAO,CACLC,EAAGvB,EAAWsB,EAAKC,EAAG,GACtBC,EAAGxB,EAAWsB,EAAKE,EAAG,IAUnB,SAASC,EAAiBH,GAC/B,GACgB,MAAdA,EAAKI,OACLvB,MAAMC,SAASkB,EAAKI,SACL,MAAfJ,EAAKK,QACLxB,MAAMC,SAASkB,EAAKK,SAEpB,MAAM,IAAIC,UAAU,iBAGtB,MAAO,CACLF,MAAOtB,SAASkB,EAAKI,OACrBC,OAAQvB,SAASkB,EAAKK,SA+BnB,SAASE,EAAmBP,GACjC,OAAAQ,EAAA,CACEjE,SAAUmC,EAAWsB,EAAKzD,SAAU,MACpCkE,WAAYvB,EAAiBc,EAAKS,WAAY,MAC9CC,kBAAmBxB,EAAiBc,EAAKU,kBAAmB,OA1BzD,SAA2BV,GAChC,IAAMW,EAA6B,CACjCC,QAASlC,EAAWsB,EAAKa,MAAO,MAChCC,UAAW5B,EAAiBc,EAAKc,UAAW,MAC5CC,WAAY7B,EAAiBc,EAAKe,WAAY,MAC9CC,iBAAkB9B,EAAiBc,EAAKgB,iBAAkB,MAC1DC,aAAc/B,EAAiBc,EAAKiB,aAAc,OAGpD,OAA6B,MAAtBjB,EAAKkB,cACTV,EAAA,CACGU,cAAelB,EAAKkB,eACjBP,GAELA,EAaCQ,CAAkBnB,IAUlB,SAASoB,EACdpB,GAIE,IAAAkB,EAAAlB,EAAAkB,cACAG,EAAArB,EAAAsB,eACAV,EAAAZ,EAAAuB,oBAGEC,EAA0D,CAC5DC,uBAAwB,WAE1B,OAAQzB,EAAKyB,wBACX,IAAK,SACH,IAAMC,EAAShD,EAAWsB,EAAK2B,6BAA8B,MAC7D,GAAc,MAAVD,EACF,MAAM,IAAIpB,UAAU,0CAElBN,EAAK2B,+BACPH,EAA0B,CACxBC,uBAAwB,SACxBE,6BAA8BD,IAElC,MAEF,IAAK,UACH,IAAME,EAAmBlD,EACvBsB,EAAK6B,uCACL,MAEIC,EAAoBpD,EACxBsB,EAAK+B,wCACL,MAEF,GAAwB,MAApBH,GAAiD,MAArBE,EAC9B,MAAM,IAAIxB,UAAU,0CAGtBkB,EAA0B,CACxBC,uBAAwB,UACxBI,uCAAwCD,EACxCG,wCAAyCD,GAM/C,IAAME,EAAqBxB,EAAA,CACzBc,eAAgB5C,EAAW2C,EAAI,MAC/BE,oBAAqB7C,EAAWkC,EAAS,OACtCY,GAGL,OAAwB,MAAjBN,EACJV,EAAA,CACGU,cAAaA,GACVc,GAELA,EASC,SAASC,EACdC,EACAC,GAEA,IAAMC,EAAUF,EAAQ,KAAKC,EAAS,IACtC,MAAO,CACL,WAAWC,EACX,QAAQA,EACR,OAAOA,EACP,MAAMA,EACN,GAAGA,GASA,SAASC,EAAaC,GAC3B,OAAOC,mBAAmBC,OAAOC,OAAOC,KAAKJ,KAUxC,SAASK,EAAUC,EAAYC,GACpC,QADoC,IAAAA,MAAA,MAChCA,GAAUC,MAAQA,KAAKC,eAAgB,CAOzC,OAAOD,KAAKC,eAAeF,EALiB,CAC1CG,IAAK,UACLC,MAAO,UACPC,KAAM,YAEoCC,OAAOP,GASnD,OANYxD,EAAQwD,EAAKQ,UAAW,EAAG,GAM1B,IAJChE,EAAQwD,EAAKS,WAAa,EAAG,EAAG,GAIxB,IAHTjE,EAAQwD,EAAKU,cAAe,EAAG,GAazC,SAASC,EAAUX,GAKxB,OAJcxD,EAAQwD,EAAKY,WAAY,EAAG,GAI3B,IAHCpE,EAAQwD,EAAKa,aAAc,EAAG,GAGpB,IAFVrE,EAAQwD,EAAKc,aAAc,EAAG,GAczC,SAASC,EAAcC,EAAiBC,GAC7C,OAAOD,EAAOE,OACZ,SAACC,EAAKC,OAAEC,EAAAD,EAAAC,MAAOtG,EAAAqG,EAAArG,MAAY,OAAAoG,EAAIG,QAAQD,EAAOtG,IAC9CkG,mSCvQEM,EAAqB,SACzBC,GAEA,OAAQA,GACN,IAAK,KACL,IAAK,QACL,IAAK,OACL,IAAK,OACH,OAAOA,EACT,QACE,MAAO,SAaN,SAASC,EAAqBrE,GACnC,GAAe,MAAXA,EAAKqB,IAAcxC,MAAMC,SAASkB,EAAKqB,KACzC,MAAM,IAAIf,UAAU,eAEtB,GAAiB,MAAbN,EAAKsE,MAAgBzF,MAAMC,SAASkB,EAAKsE,OAC3C,MAAM,IAAIhE,UAAU,iBAGtB,OAAAE,EAAA,CACEa,GAAIvC,SAASkB,EAAKqB,IAClBiD,KAAMxF,SAASkB,EAAKsE,MACpBC,MAAOnH,OAAAoH,EAAA,EAAApH,CAAiB4C,EAAKuE,MAAO,MACpCH,cAAeD,EAAmBnE,EAAKoE,eACvCK,cAAerH,OAAAoH,EAAA,EAAApH,CAAa4C,EAAKyE,eACjCC,KAAMtH,OAAAoH,EAAA,EAAApH,CAAiB4C,EAAK0E,KAAM,MAClCC,QAASvH,OAAAoH,EAAA,EAAApH,CAAa4C,EAAK2E,SAC3BC,SAAUxH,OAAAoH,EAAA,EAAApH,CAAW4C,EAAK4E,SAAU,MACpCC,WAAYzH,OAAAoH,EAAA,EAAApH,CAAW4C,EAAK6E,WAAY,OACrCzH,OAAAoH,EAAA,EAAApH,CAAiB4C,GACjB5C,OAAAoH,EAAA,EAAApH,CAAqB4C,IAO5B,IAAA8E,EAAA,WAuBE,SAAAA,EAAmBC,GAdFC,KAAAC,kBAAoB,IAAIC,EAAA,EAExBF,KAAAG,mBAAqB,IAAID,EAAA,EAIzBF,KAAAI,YAA4B,GAS3CJ,KAAKK,UAAYN,EAQjBC,KAAKM,WAAaN,KAAKO,4BACvBP,KAAKQ,gBAAkBR,KAAKS,wBAO5BT,KAAKU,gBAAkBV,KAAKW,mBAG5BX,KAAKM,WAAWM,OAAOZ,KAAKU,gBAAiBV,KAAKQ,iBAGlDR,KAAKa,cAAcd,EAAM3E,MAAO2E,EAAM1E,QAEtC2E,KAAKc,oBAAoBf,EAAMX,eAiYnC,OA1XUU,EAAAxG,UAAAiH,0BAAR,eACMQ,EADNC,EAAAhB,KAkBE,OAhBIA,KAAKD,MAAMN,eACbsB,EAAME,SAASC,cAAc,KAEzBlB,KAAKD,MAAML,OAAMqB,EAAII,KAAOnB,KAAKD,MAAML,OAE3CqB,EAAME,SAASC,cAAc,OAI/BH,EAAIK,UAAY,sBAChBL,EAAIM,MAAMC,OAAStB,KAAKD,MAAMJ,QAAU,IAAM,IAC9CoB,EAAIM,MAAME,KAAUvB,KAAKD,MAAM9E,EAAC,KAChC8F,EAAIM,MAAMG,IAASxB,KAAKD,MAAM7E,EAAC,KAC/B6F,EAAIU,QAAU,SAAAC,GACZ,OAAAV,EAAKf,kBAAkB0B,KAAK,CAAE3G,KAAMgG,EAAKjB,MAAO6B,YAAaF,KAExDX,GAOCjB,EAAAxG,UAAAmH,sBAAV,WACE,IAAMoB,EAAUZ,SAASC,cAAc,OACvCW,EAAQT,UAAY,4BAEpB,IAAM7B,EAAQS,KAAK8B,6BACnB,GAAIvC,EAAM3F,OAAS,EAAG,CAEpB,IAAMmI,EAAQd,SAASC,cAAc,SAC/Bc,EAAMf,SAASC,cAAc,MAC7Be,EAAYhB,SAASC,cAAc,MACnCgB,EAAYjB,SAASC,cAAc,MACnCiB,EAAOlB,SAASC,cAAc,MAQpC,OANAiB,EAAKC,UAAY7C,EACjByC,EAAIpB,OAAOuB,GACXJ,EAAMnB,OAAOqB,EAAWD,EAAKE,GAC7BH,EAAMV,MAAMgB,UAAY,SAGhBrC,KAAKD,MAAMX,eACjB,IAAK,KACL,IAAK,OACCY,KAAKD,MAAM3E,MAAQ,IACrB2G,EAAMV,MAAMjG,MAAW4E,KAAKD,MAAM3E,MAAK,KACvC2G,EAAMV,MAAMhG,OAAS,MAEvB,MACF,IAAK,OACL,IAAK,QACC2E,KAAKD,MAAM1E,OAAS,IACtB0G,EAAMV,MAAMjG,MAAQ,KACpB2G,EAAMV,MAAMhG,OAAY2E,KAAKD,MAAM1E,OAAM,MAM/CwG,EAAQjB,OAAOmB,GAGjB,OAAOF,GAMC/B,EAAAxG,UAAAwI,2BAAV,WAEE,IAAM/B,EAAQC,KAAKD,MAEnB,OAAO3H,OAAAoH,EAAA,EAAApH,CACL,CACE,CACE6G,MAAO,SACPtG,MAAOP,OAAAoH,EAAA,EAAApH,CAAU,IAAIkK,OAEvB,CACErD,MAAO,SACPtG,MAAOP,OAAAoH,EAAA,EAAApH,CAAU,IAAIkK,OAEvB,CACErD,MAAO,UACPtG,MAA2B,MAApBoH,EAAMhE,WAAqBgE,EAAMhE,WAAa,IAEvD,CACEkD,MAAO,qBACPtG,MAAiC,MAA1BoH,EAAM/D,iBAA2B+D,EAAM/D,iBAAmB,IAEnE,CACEiD,MAAO,YACPtG,MAA6B,MAAtBoH,EAAM9D,aAAuB8D,EAAM9D,aAAe,IAE3D,CACEgD,MAAO,WACPtG,MAA2B,MAApBoH,EAAMtE,WAAqBsE,EAAMtE,WAAa,IAEvD,CACEwD,MAAO,sBACPtG,MAAkC,MAA3BoH,EAAMrE,kBAA4BqE,EAAMrE,kBAAoB,KAGvEsE,KAAKD,MAAMR,OAAS,KAQdO,EAAAxG,UAAAiJ,iBAAV,SAA2BV,GACzBA,EAAQO,UAAYpC,KAAKW,mBAAmByB,WAO9ChK,OAAAC,eAAWyH,EAAAxG,UAAA,QAAK,KAAhB,WACE,OAAAkC,EAAA,GAAYwE,KAAKK,gBASnB,SAAiBmC,GACf,IAAMC,EAAYzC,KAAKD,MAEvBC,KAAKK,UAAYmC,EAKbxC,KAAK0C,gBAAgBD,EAAWD,IAAWxC,KAAK2C,OAAOF,oCAenD3C,EAAAxG,UAAAoJ,gBAAV,SAA0BD,EAAkBD,GAC1C,OAAOC,IAAcD,GAOhB1C,EAAAxG,UAAAqJ,OAAP,SAAcF,QAAA,IAAAA,MAAA,MACZzC,KAAKuC,iBAAiBvC,KAAKU,iBAGtB+B,IAAazC,KAAK4C,gBAAgBH,EAAWzC,KAAKD,QACrDC,KAAK6C,YAAY7C,KAAKD,MAAM9E,EAAG+E,KAAKD,MAAM7E,GAGvCuH,IAAazC,KAAK8C,YAAYL,EAAWzC,KAAKD,QACjDC,KAAKa,cAAcb,KAAKD,MAAM3E,MAAO4E,KAAKD,MAAM1E,QAGlD,IAAM0H,EAAe/C,KAAKQ,gBAAgB4B,UACpCY,EAAehD,KAAKS,wBAAwB2B,UASlD,GARIW,IAAiBC,IACnBhD,KAAKQ,gBAAgB4B,UAAYY,GAG9BP,GAAaA,EAAUrD,gBAAkBY,KAAKD,MAAMX,eACvDY,KAAKc,oBAAoBd,KAAKD,MAAMX,eAIpCqD,IACCA,EAAUhD,gBAAkBO,KAAKD,MAAMN,eACrCO,KAAKD,MAAMN,eAAiBgD,EAAU/C,OAASM,KAAKD,MAAML,MAC7D,CACA,IAAMuD,EAAYjD,KAAKO,4BAEvB0C,EAAUb,UAAYpC,KAAKM,WAAW8B,UAGtC,IADA,IAAMc,EAAQlD,KAAKM,WAAW6C,WACrBzL,EAAI,EAAGA,EAAIwL,EAAMtJ,OAAQlC,IACN,OAAtBwL,EAAMxL,GAAG0L,UACXH,EAAUI,iBAAiBH,EAAMxL,IAIF,OAA/BsI,KAAKM,WAAWgD,YAClBtD,KAAKM,WAAWgD,WAAWC,aAAaN,EAAWjD,KAAKM,YAI1DN,KAAKM,WAAa2C,IAOfnD,EAAAxG,UAAAkK,OAAP,WAEExD,KAAKG,mBAAmBwB,KAAK,CAAE3G,KAAMgF,KAAKD,QAE1CC,KAAKI,YAAYqD,QAAQ,SAAAC,GACvB,IACEA,EAAWC,UACX,MAAOC,OAGX5D,KAAKM,WAAWkD,UAUR1D,EAAAxG,UAAAsJ,gBAAV,SACEiB,EACAC,GAEA,OAAOD,EAAa5I,IAAM6I,EAAY7I,GAAK4I,EAAa3I,IAAM4I,EAAY5I,GAOlE4E,EAAAxG,UAAAwH,oBAAV,SAA8BiD,GAC5B,OAAQA,GACN,IAAK,KACH/D,KAAKM,WAAWe,MAAM2C,cAAgB,iBACtC,MACF,IAAK,OACHhE,KAAKM,WAAWe,MAAM2C,cAAgB,cACtC,MACF,IAAK,QACHhE,KAAKM,WAAWe,MAAM2C,cAAgB,MACtC,MACF,IAAK,OACL,QACEhE,KAAKM,WAAWe,MAAM2C,cAAgB,SAK1C,IAAMC,EAASjE,KAAKQ,gBAAgB0D,qBAAqB,SACnDnC,EAAQkC,EAAOrK,OAAS,EAAIqK,EAAOE,KAAK,GAAK,KAEnD,GAAIpC,EACF,OAAQ/B,KAAKD,MAAMX,eACjB,IAAK,KACL,IAAK,OACCY,KAAKD,MAAM3E,MAAQ,IACrB2G,EAAMV,MAAMjG,MAAW4E,KAAKD,MAAM3E,MAAK,KACvC2G,EAAMV,MAAMhG,OAAS,MAEvB,MACF,IAAK,OACL,IAAK,QACC2E,KAAKD,MAAM1E,OAAS,IACtB0G,EAAMV,MAAMjG,MAAQ,KACpB2G,EAAMV,MAAMhG,OAAY2E,KAAKD,MAAM1E,OAAM,QAYzCyE,EAAAxG,UAAAuJ,YAAV,SAAsB5H,EAAWC,GAC/B8E,KAAKM,WAAWe,MAAME,KAAUtG,EAAC,KACjC+E,KAAKM,WAAWe,MAAMG,IAAStG,EAAC,MAQ3B4E,EAAAxG,UAAA8K,KAAP,SAAYnJ,EAAWC,GACrB8E,KAAK6C,YAAY5H,EAAGC,GACpB8E,KAAKK,UAAS7E,EAAA,GACTwE,KAAKD,MAAK,CACb9E,EAACA,EACDC,EAACA,KAWK4E,EAAAxG,UAAAwJ,YAAV,SAAsBuB,EAAgBC,GACpC,OACED,EAASjJ,QAAUkJ,EAAQlJ,OAASiJ,EAAShJ,SAAWiJ,EAAQjJ,QAS1DyE,EAAAxG,UAAAuH,cAAV,SAAwBzF,EAAeC,GAErC2E,KAAKU,gBAAgBW,MAAMjG,MAAQA,EAAQ,EAAOA,EAAK,KAAO,KAC9D4E,KAAKU,gBAAgBW,MAAMhG,OAASA,EAAS,EAAOA,EAAM,KAAO,MAQ5DyE,EAAAxG,UAAAiL,OAAP,SAAcnJ,EAAeC,GAC3B2E,KAAKa,cAAczF,EAAOC,GAC1B2E,KAAKK,UAAS7E,EAAA,GACTwE,KAAKD,MAAK,CACb3E,MAAKA,EACLC,OAAMA,KAQHyE,EAAAxG,UAAAkL,QAAP,SAAeC,GAMb,IAAMf,EAAa1D,KAAKC,kBAAkByE,GAAGD,GAG7C,OAFAzE,KAAKI,YAAYuE,KAAKjB,GAEfA,GAOF5D,EAAAxG,UAAAsL,SAAP,SAAgBH,GAMd,IAAMf,EAAa1D,KAAKG,mBAAmBuE,GAAGD,GAG9C,OAFAzE,KAAKI,YAAYuE,KAAKjB,GAEfA,GAEX5D,EAjbA,GAmbe+E,EAAA,kCC/hBf,IAAAC,EAAA,WA8BA,OA9BA,eAAA9D,EAAAhB,KACUA,KAAA+E,UAA2B,GAC3B/E,KAAAgF,eAAgC,GAEjChF,KAAA0E,GAAK,SAACD,GAEX,OADAzD,EAAK+D,UAAUJ,KAAKF,GACb,CACLd,QAAS,WAAM,OAAA3C,EAAKiE,IAAIR,MAIrBzE,KAAAkF,KAAO,SAACT,GACbzD,EAAKgE,eAAeL,KAAKF,IAGpBzE,KAAAiF,IAAM,SAACR,GACZ,IAAMU,EAAgBnE,EAAK+D,UAAUK,QAAQX,GACzCU,GAAiB,GAAGnE,EAAK+D,UAAUM,OAAOF,EAAe,IAGxDnF,KAAA2B,KAAO,SAAC2D,GAEbtE,EAAK+D,UAAUtB,QAAQ,SAAAgB,GAAY,OAAAA,EAASa,KAG5CtE,EAAKgE,eAAevB,QAAQ,SAAAgB,GAAY,OAAAA,EAASa,KACjDtE,EAAKgE,eAAiB,IAGjBhF,KAAAuF,KAAO,SAACC,GAAkC,OAAAxE,EAAK0D,GAAG,SAAAhD,GAAK,OAAA8D,EAAG7D,KAAKD,OA7BxE,82BCgBO,SAAS+D,0BACdzK,GAEA,GAAI5C,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,OAAStN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK2K,aACjD,MAAM,IAAIrK,UAAU,yBAGtB,OAAAE,SAAA,GACKpD,OAAAwN,mCAAA,EAAAxN,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJuG,QAASzN,OAAAoH,kCAAA,EAAApH,CAAW4C,EAAK6K,QAAS,MAClCH,KAAOtN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,MAEtBtN,OAAAoH,kCAAA,EAAApH,CAAa4C,EAAK2K,aADlB3K,EAAK0K,MAENtN,OAAAoH,kCAAA,EAAApH,CAAmB4C,IAI1B,IAAA8K,cAAA,SAAAC,QAAA,SAAAD,yEAkCA,OAlC2CE,UAAAF,cAAAC,QAC/BD,cAAAxM,UAAAqH,iBAAV,WACE,IAAMkB,QAAUZ,SAASC,cAAc,OACvCW,QAAQT,UAAY,iBACpBS,QAAQO,UAAYpC,KAAKD,MAAM2F,KAI/B,IADA,IAAMO,QAAUpE,QAAQqC,qBAAqB,2BACpCxM,GACuB,IAA1BuO,QAAQvO,GAAGwO,IAAItM,QACjBuM,WAAW,WACT,IACEC,KAAKH,QAAQvO,GAAG0K,UAAUiE,QAC1B,MAAOzC,MACR,IANElM,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,YAA3BA,GAUT,OAAOmK,SAGCiE,cAAAxM,UAAAiJ,iBAAV,SAA2BV,SACzBA,QAAQO,UAAYpC,KAAKD,MAAM2F,KAG/B,IAAMY,IAAMrF,SAASC,cAAc,OACnCoF,IAAIlE,UAAYpC,KAAKD,MAAM2F,KAE3B,IADA,IAAMO,QAAUK,IAAIpC,qBAAqB,UAChCxM,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,IACJ,IAA1BuO,QAAQvO,GAAGwO,IAAItM,QACjBwM,KAAKH,QAAQvO,GAAG0K,UAAUiE,SAIlCP,cAlCA,CAA2CF,mCAAA,y4BCdpC,SAASW,uBACdvL,GAEA,GAAI5C,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,OAAStN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK2K,aACjD,MAAM,IAAIrK,UAAU,yBAGtB,OAAAE,SAAA,GACKpD,OAAAwN,mCAAA,EAAAxN,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJoG,KAAOtN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,MAEtBtN,OAAAoH,kCAAA,EAAApH,CAAa4C,EAAK2K,aADlB3K,EAAK0K,MAENtN,OAAAoH,kCAAA,EAAApH,CAAmB4C,GACnB5C,OAAAoH,kCAAA,EAAApH,CAAqB4C,IAI5B,IAAAwL,WAAA,SAAAT,QAAA,SAAAS,sEA8BA,OA9BwCR,UAAAQ,WAAAT,QAC5BS,WAAAlN,UAAAqH,iBAAV,WACE,IAAMkB,QAAUZ,SAASC,cAAc,OACvCW,QAAQT,UAAY,cACpBS,QAAQO,UAAYpC,KAAKD,MAAM2F,KAI/B,IADA,IAAMO,QAAUpE,QAAQqC,qBAAqB,2BACpCxM,GACPyO,WAAW,WACqB,IAA1BF,QAAQvO,GAAGwO,IAAItM,QAAcwM,KAAKH,QAAQvO,GAAG0K,UAAUiE,SAC1D,IAHI3O,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,YAA3BA,GAMT,OAAOmK,SAGC2E,WAAAlN,UAAAiJ,iBAAV,SAA2BV,SACzBA,QAAQO,UAAYpC,KAAKD,MAAM2F,KAG/B,IAAMY,IAAMrF,SAASC,cAAc,OACnCoF,IAAIlE,UAAYpC,KAAKD,MAAM2F,KAE3B,IADA,IAAMO,QAAUK,IAAIpC,qBAAqB,UAChCxM,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,IACJ,IAA1BuO,QAAQvO,GAAGwO,IAAItM,QACjBwM,KAAKH,QAAQvO,GAAG0K,UAAUiE,SAIlCG,WA9BA,CAAwCZ,mCAAA,q4BC5BjC,SAASa,sBACdzL,GAEA,GAAI5C,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,OAAStN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK2K,aACjD,MAAM,IAAIrK,UAAU,yBAGtB,OAAAE,SAAA,GACKpD,OAAAwN,mCAAA,EAAAxN,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJoG,KAAOtN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,MAEtBtN,OAAAoH,kCAAA,EAAApH,CAAa4C,EAAK2K,aADlB3K,EAAK0K,MAENtN,OAAAoH,kCAAA,EAAApH,CAAmB4C,IAI1B,IAAA0L,UAAA,SAAAX,QAAA,SAAAW,qEA8BA,OA9BuCV,UAAAU,UAAAX,QAC3BW,UAAApN,UAAAqH,iBAAV,WACE,IAAMkB,QAAUZ,SAASC,cAAc,OACvCW,QAAQT,UAAY,aACpBS,QAAQO,UAAYpC,KAAKD,MAAM2F,KAI/B,IADA,IAAMO,QAAUpE,QAAQqC,qBAAqB,2BACpCxM,GACPyO,WAAW,WACqB,IAA1BF,QAAQvO,GAAGwO,IAAItM,QAAcwM,KAAKH,QAAQvO,GAAG0K,UAAUiE,SAC1D,IAHI3O,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,YAA3BA,GAMT,OAAOmK,SAGC6E,UAAApN,UAAAiJ,iBAAV,SAA2BV,SACzBA,QAAQO,UAAYpC,KAAKD,MAAM2F,KAG/B,IAAMY,IAAMrF,SAASC,cAAc,OACnCoF,IAAIlE,UAAYpC,KAAKD,MAAM2F,KAE3B,IADA,IAAMO,QAAUK,IAAIpC,qBAAqB,UAChCxM,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,IACJ,IAA1BuO,QAAQvO,GAAGwO,IAAItM,QACjBwM,KAAKH,QAAQvO,GAAG0K,UAAUiE,SAIlCK,UA9BA,CAAuCd,mCAAA,s4BCPhC,SAASe,wBACd3L,GAEA,GAAI5C,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,OAAStN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK2K,aACjD,MAAM,IAAIrK,UAAU,yBAGtB,OAAAE,SAAA,GACKpD,OAAAwN,mCAAA,EAAAxN,CAAqB4C,GAAK,CAC7BsE,KAAI,EACJoG,KAAOtN,OAAAoH,kCAAA,EAAApH,CAAc4C,EAAK0K,MAEtBtN,OAAAoH,kCAAA,EAAApH,CAAa4C,EAAK2K,aADlB3K,EAAK0K,MAENtN,OAAAoH,kCAAA,EAAApH,CAAmB4C,GACnB5C,OAAAoH,kCAAA,EAAApH,CAAqB4C,IAI5B,IAAA4L,YAAA,SAAAb,QAAA,SAAAa,uEAsEA,OAtEyCZ,UAAAY,YAAAb,QAS7Ba,YAAAtN,UAAAuH,cAAV,SAAwBzF,GACtB2K,OAAAzM,UAAMuH,cAAahJ,KAAAmI,KAAC5E,EAAO,IAGnBwL,YAAAtN,UAAAqH,iBAAV,WACE,IAAMkB,QAAUZ,SAASC,cAAc,OACvCW,QAAQT,UAAY,eACpBS,QAAQO,UAAYpC,KAAKD,MAAM2F,KAI/B,IADA,IAAMmB,QAAUhF,QAAQqC,qBAAqB,KACpCxM,EAAI,EAAGA,EAAImP,QAAQjN,OAAQlC,IAClCmP,QAAQnP,GAAG2J,MAAMyF,OAAS,MAK5B,IADA,IAAMC,eAAiBlF,QAAQmF,uBAAuB,kBAC7CtP,EAAI,EAAGA,EAAIqP,eAAenN,OAAQlC,IACzCqP,eAAerP,GAAG8L,SAKpB,IADA,IAAMyC,QAAUpE,QAAQqC,qBAAqB,2BACpCxM,GACuB,IAA1BuO,QAAQvO,GAAGwO,IAAItM,QACjBuM,WAAW,WACT,IACEC,KAAKH,QAAQvO,GAAG0K,UAAUiE,QAC1B,MAAOzC,MACR,IANElM,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,YAA3BA,GAUT,OAAOmK,SAGC+E,YAAAtN,UAAAiJ,iBAAV,SAA2BV,SACzBA,QAAQO,UAAYpC,KAAKD,MAAM2F,KAI/B,IADA,IAAMmB,QAAUhF,QAAQqC,qBAAqB,KACpCxM,EAAI,EAAGA,EAAImP,QAAQjN,OAAQlC,IAClCmP,QAAQnP,GAAG2J,MAAMyF,OAAS,MAK5B,IADA,IAAMC,eAAiBlF,QAAQmF,uBAAuB,kBAC7CtP,EAAI,EAAGA,EAAIqP,eAAenN,OAAQlC,IACzCqP,eAAerP,GAAG8L,SAIpB,IAAM8C,IAAMrF,SAASC,cAAc,OACnCoF,IAAIlE,UAAYpC,KAAKD,MAAM2F,KAE3B,IADA,IAAMO,QAAUK,IAAIpC,qBAAqB,UAChCxM,EAAI,EAAGA,EAAIuO,QAAQrM,OAAQlC,IACJ,IAA1BuO,QAAQvO,GAAGwO,IAAItM,QACjBwM,KAAKH,QAAQvO,GAAG0K,UAAUiE,SAIlCO,YAtEA,CAAyChB,mCAAA,0oBCrBnCqB,EAA4B,SAChCC,GAEA,OAAQA,GACN,IAAK,UACL,IAAK,UACL,IAAK,WACH,OAAOA,EACT,QACE,MAAO,YAaN,SAASC,EACdnM,GAEA,GAA6B,iBAAlBA,EAAKoM,UAAkD,IAAzBpM,EAAKoM,SAASxN,OACrD,MAAM,IAAI0B,UAAU,sBAGtB,OAAAE,EAAA,GACKpD,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,EACJ8H,SAAUpM,EAAKoM,SACfF,qBAAsBD,EAA0BjM,EAAKkM,sBACrDI,eAAgBlP,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKsM,eAAgB,MACtDE,UAAWpP,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKwM,UAAW,OACzCpP,OAAAmP,EAAA,EAAAnP,CAAmB4C,GACnB5C,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,eAAA+K,GAAA,SAAA0B,mDAqBA,OArByCzB,EAAAyB,EAAA1B,GAC7B0B,EAAAnO,UAAAqH,iBAAV,WACE,IAAM+G,EAAS1H,KAAKD,MAAMuH,gBAAkBtH,KAAKD,MAAMqH,SACjDvF,EAAUZ,SAASC,cAAc,OAgBvC,OAfAW,EAAQT,UAAY,eACpBS,EAAQR,MAAMsG,WAAa,OAAOD,EAAM,cACxC7F,EAAQR,MAAMuG,eAAiB,UAC/B/F,EAAQR,MAAMwG,mBAAqB,SAIR,OAAzB7H,KAAKD,MAAMyH,WACyB,aAApCxH,KAAKD,MAAMmH,uBAEXrF,EAAQT,UAAY,kCACpBS,EAAQiG,aAAa,iCAAkC,KACvDjG,EAAQiG,aAAa,aAAc9H,KAAKD,MAAMyH,YAGzC3F,GAEX4F,EArBA,CAAyCJ,EAAA,6hBChDlC,SAASU,EAAiB/M,GAC/B,GAA6B,iBAAlBA,EAAKoM,UAAkD,IAAzBpM,EAAKoM,SAASxN,OACrD,MAAM,IAAI0B,UAAU,sBAGtB,OAAO0M,EAAA,GACF5P,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,EACJ8H,SAAUpM,EAAKoM,UACZhP,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,eAAA+K,GAAA,SAAAkC,mDAUA,OAVkCC,EAAAD,EAAAlC,GACtBkC,EAAA3O,UAAAqH,iBAAV,WACE,IAAMkB,EAAUZ,SAASC,cAAc,OAMvC,OALAW,EAAQT,UAAY,OACpBS,EAAQR,MAAMsG,WAAa,OAAO3H,KAAKD,MAAMqH,SAAQ,cACrDvF,EAAQR,MAAMuG,eAAiB,UAC/B/F,EAAQR,MAAMwG,mBAAqB,SAE5BhG,GAEXoG,EAVA,CAAkCZ,EAAA,6hBCP3B,SAASc,EACdnN,GAGA,GAA0B,iBAAfA,EAAKoN,OAA4C,IAAtBpN,EAAKoN,MAAMxO,OAC/C,MAAM,IAAI0B,UAAU,kBAGtB,OAAO+M,EAAA,GACFjQ,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJ8I,MAAOpN,EAAKoN,OACThQ,OAAAmP,EAAA,EAAAnP,CAAmB4C,GACnB5C,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,IAAMsN,EAAQ,+BAEd,SAAAvC,GAAA,SAAAwC,mDAuDA,OAvDwCC,EAAAD,EAAAxC,GAC5BwC,EAAAjP,UAAAqH,iBAAV,WACE,IAAMsC,EAA4BhC,SAASC,cAAc,OAMzD,OALA+B,EAAU7B,UAAY,cAGtB6B,EAAUrC,OAAOZ,KAAKyI,oBAEfxF,GAGFsF,EAAAjP,UAAAmP,iBAAP,WACE,IAAMC,EAAa,QAAQ1I,KAAKD,MAAM1D,GAEhCsM,EAAM1H,SAAS2H,gBAAgBN,EAAO,OAE5CK,EAAIb,aAAa,UAAW,eAG5B,IAAMe,EAAO5H,SAAS2H,gBAAgBN,EAAO,QAEvCQ,EAAiB7H,SAAS2H,gBAAgBN,EAAO,kBACvDQ,EAAehB,aAAa,KAAMY,GAClCI,EAAehB,aAAa,KAAM,OAClCgB,EAAehB,aAAa,KAAM,OAClCgB,EAAehB,aAAa,IAAK,OACjCgB,EAAehB,aAAa,KAAM,OAClCgB,EAAehB,aAAa,KAAM,OAElC,IAAMiB,EAAQ9H,SAAS2H,gBAAgBN,EAAO,QAC9CS,EAAMjB,aAAa,SAAU,MAC7BiB,EAAMjB,aACJ,QACA,cAAc9H,KAAKD,MAAMqI,MAAK,qBAEhC,IAAMY,EAAU/H,SAAS2H,gBAAgBN,EAAO,QAChDU,EAAQlB,aAAa,SAAU,QAC/BkB,EAAQlB,aACN,QACA,cAAc9H,KAAKD,MAAMqI,MAAK,mBAGhC,IAAMa,EAAShI,SAAS2H,gBAAgBN,EAAO,UAW/C,OAVAW,EAAOnB,aAAa,OAAQ,QAAQY,EAAU,KAC9CO,EAAOnB,aAAa,KAAM,OAC1BmB,EAAOnB,aAAa,KAAM,OAC1BmB,EAAOnB,aAAa,IAAK,OAGzBgB,EAAelI,OAAOmI,EAAOC,GAC7BH,EAAKjI,OAAOkI,GACZH,EAAI/H,OAAOiI,EAAMI,GAEVN,GAEXJ,EAvDA,CAAwClB,EAAA,6hBCRjC,SAAS6B,EAAkBlO,GAChC,IAC4B,iBAAlBA,EAAKoM,UAAkD,IAAzBpM,EAAKoM,SAASxN,SAC/B,OAArBoB,EAAK2K,YAEL,MAAM,IAAIrK,UAAU,sBAEtB,GAAuC,OAAnClD,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKmO,QAAS,MAC3B,MAAM,IAAI7N,UAAU,qBAGtB,IAAM8N,EAAiBhR,OAAAmP,EAAA,EAAAnP,CAAa4C,EAAKoO,gBACnC1D,EAAO0D,EA3Bf,SAAqBpO,GACnB,OAAK5C,OAAAmP,EAAA,EAAAnP,CAAc4C,EAAK0K,MACnBtN,OAAAmP,EAAA,EAAAnP,CAAc4C,EAAK2K,aACjB,KADsCvN,OAAAmP,EAAA,EAAAnP,CAAa4C,EAAK2K,aADzB3K,EAAK0K,KA0Bb2D,CAAYrO,GAAQ,KAElD,OAAOsO,EAAA,GACFlR,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJ6J,QAASrP,SAASkB,EAAKmO,SACvB/B,SAAUhP,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKoM,SAAU,MAC1CE,eAAgBlP,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKsM,eAAgB,MACtD8B,eAAcA,EACd1D,KAAIA,GACDtN,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,eAAA+K,GAAA,SAAAwD,mDAiBA,OAjBmCC,EAAAD,EAAAxD,GACvBwD,EAAAjQ,UAAAqH,iBAAV,WACE,IAAMkB,EAAUZ,SAASC,cAAc,OAavC,OAZAW,EAAQT,UAAY,QAEfpB,KAAKD,MAAMqJ,gBAAgD,OAA9BpJ,KAAKD,MAAMuH,eAKlCtH,KAAKD,MAAMqJ,gBAAqC,MAAnBpJ,KAAKD,MAAM2F,OAEjD7D,EAAQO,UAAYpC,KAAKD,MAAM2F,OAL/B7D,EAAQR,MAAMsG,WAAa,OAAO3H,KAAKD,MAAMuH,eAAc,cAC3DzF,EAAQR,MAAMuG,eAAiB,UAC/B/F,EAAQR,MAAMwG,mBAAqB,UAM9BhG,GAEX0H,EAjBA,CAAmClC,EAAA,oiBCjC7BoC,EAAiB,SAACC,GACtB,OAAQA,GACN,IAAK,WACL,IAAK,UACH,OAAOA,EACT,QACE,MAAO,aAQPC,EAAmB,SAACC,GACxB,OAAQA,GACN,IAAK,WACL,IAAK,OACH,OAAOA,EACT,QACE,MAAO,aAaN,SAASC,EAAkB7O,GAChC,GACgC,iBAAvBA,EAAK8O,eACkB,IAA9B9O,EAAK8O,cAAclQ,OAEnB,MAAM,IAAI0B,UAAU,qBAGtB,OAAOyO,EAAA,GACF3R,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJoK,UAAWD,EAAezO,EAAK0O,WAC/BE,YAAaD,EAAiB3O,EAAK4O,aACnCE,cAAe9O,EAAK8O,cACpBE,oBAAqB5R,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKgP,oBAAqB,GAC1DC,kBAAmB7R,OAAAmP,EAAA,EAAAnP,CAAa4C,EAAKiP,mBACrC7B,MAAOhQ,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKoN,MAAO,OACjChQ,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,IAAqBkP,EAArB,SAAAnE,GAIE,SAAAoE,EAAmBpK,GAAnB,IAAAiB,EAEE+E,EAAAlO,KAAAmI,KAAMD,IAAMC,YAJNgB,EAAAoJ,YAA6B,KAoBnCpJ,EAAKqJ,UACH,WAEErJ,EAAKN,gBAAgB0B,UAAYpB,EAAKsJ,cAAclI,WAM7B,aAAzBpB,EAAKjB,MAAM2J,UAA2B,IAAQS,EAAMI,iBAif1D,OAhhBmCC,EAAAL,EAAApE,GAsCzBoE,EAAA7Q,UAAAmR,SAAR,WAC2B,OAArBzK,KAAKoK,cACP3M,OAAOiN,cAAc1K,KAAKoK,aAC1BpK,KAAKoK,YAAc,OAUfD,EAAA7Q,UAAA+Q,UAAR,SACEM,EACAC,QAAA,IAAAA,MAAmBT,EAAMI,eAEzBvK,KAAKyK,WACLzK,KAAKoK,YAAc3M,OAAOoN,YAAYF,EAASC,IAQvCT,EAAA7Q,UAAAqH,iBAAV,WACE,OAAOX,KAAKsK,eAOPH,EAAA7Q,UAAAkK,OAAP,WAEExD,KAAKyK,WAEL1E,EAAAzM,UAAMkK,OAAM3L,KAAAmI,OASJmK,EAAA7Q,UAAAuH,cAAV,SAAwBzF,EAAeC,GAC/B,IAAA2D,EAAAgB,KAAA8K,eAAA1P,EAAAC,GAAE0P,EAAA/L,EAAA5D,MAAiB4P,EAAAhM,EAAA3D,OAIzB0K,EAAAzM,UAAMuH,cAAahJ,KAAAmI,KAAC+K,EAAUC,GAED,YAAzBhL,KAAKD,MAAM2J,YAEb1J,KAAKU,gBAAgB0B,UAAYpC,KAAKsK,cAAclI,YAUhD+H,EAAA7Q,UAAAgR,YAAR,WACE,OAAQtK,KAAKD,MAAM2J,WACjB,IAAK,WACH,OAAO1J,KAAKiL,sBACd,IAAK,UACH,OAAOjL,KAAKkL,qBACd,QACE,MAAM,IAAIC,MAAM,yBAQdhB,EAAA7Q,UAAA2R,oBAAR,WACE,IAAMG,EAAQ,6BACRC,EACO,UADPA,EAEa,UAFbA,EAGE,UAHFA,EAIM,UAJNA,EAKO,UALPA,EAMQ,UAGRrM,EAAAgB,KAAA8K,iBAAE1P,EAAA4D,EAAA5D,MAAOC,EAAA2D,EAAA3D,OAKTiQ,EACHC,GAA4CnQ,EAAS,IAElDoQ,EAAMvK,SAASC,cAAc,OACnCsK,EAAIpK,UAAY,iBAChBoK,EAAInK,MAAMjG,MAAWA,EAAK,KAC1BoQ,EAAInK,MAAMhG,OAAYA,EAAM,KAG5B,IAAMsN,EAAM1H,SAAS2H,gBAAgBwC,EAAO,OAE5CzC,EAAIb,aAAa,UAAW,eAG5B,IAAM2D,EAAYxK,SAAS2H,gBAAgBwC,EAAO,KAClDK,EAAU3D,aAAa,QAAS,aAChC,IAAM4D,EAAsBzK,SAAS2H,gBAAgBwC,EAAO,UAC5DM,EAAoB5D,aAAa,KAAM,MACvC4D,EAAoB5D,aAAa,KAAM,MACvC4D,EAAoB5D,aAAa,IAAK,MACtC4D,EAAoB5D,aAAa,OAAQuD,GACzCK,EAAoB5D,aAAa,SAAUuD,GAC3CK,EAAoB5D,aAAa,eAAgB,KACjD4D,EAAoB5D,aAAa,iBAAkB,SAEnD2D,EAAU7K,OAAO8K,GAGjB,IAAMC,EAAO3L,KAAK4L,mBAClB,GAAID,EAAK/R,OAAS,EAAG,CACnB,IAAMiS,EAAuB5K,SAAS2H,gBAAgBwC,EAAO,QAC7DS,EAAqB/D,aAAa,cAAe,UACjD+D,EAAqB/D,aAAa,YAAa,KAC/C+D,EAAqB/D,aACnB,YACA,+BAEF+D,EAAqB/D,aAAa,OAAQuD,GAC1CQ,EAAqBC,YAAcH,EACnCF,EAAU7K,OAAOiL,GAInB,IAAME,EAAa9K,SAAS2H,gBAAgBwC,EAAO,KACnDW,EAAWjE,aAAa,QAAS,SAEjC,IAAMkE,EAAgB/K,SAAS2H,gBAAgBwC,EAAO,KACtDY,EAAclE,aAAa,QAAS,QACpCkE,EAAclE,aAAa,YAAa,oBACxC,IAAMmE,EAAShL,SAAS2H,gBAAgBwC,EAAO,QAC/Ca,EAAOnE,aAAa,KAAM,MAC1BmE,EAAOnE,aAAa,KAAM,KAC1BmE,EAAOnE,aAAa,KAAM,MAC1BmE,EAAOnE,aAAa,KAAM,KAC1BmE,EAAOnE,aAAa,SAAUuD,GAC9BY,EAAOnE,aAAa,eAAgB,KACpC,IAAMoE,EAASjL,SAAS2H,gBAAgBwC,EAAO,QAC/Cc,EAAOpE,aAAa,KAAM,MAC1BoE,EAAOpE,aAAa,KAAM,KAC1BoE,EAAOpE,aAAa,KAAM,MAC1BoE,EAAOpE,aAAa,KAAM,KAC1BoE,EAAOpE,aAAa,SAAUuD,GAC9Ba,EAAOpE,aAAa,eAAgB,KAEpCkE,EAAcpL,OAAOqL,EAAQC,GAE7BH,EAAWnL,OAAOoL,GAElB,IAAK,IAAItU,EAAI,EAAGA,EAAI,GAAIA,IAAK,CAC3B,IAAMyU,EAAOlL,SAAS2H,gBAAgBwC,EAAO,QAC7Ce,EAAKrE,aAAa,KAAM,KACxBqE,EAAKrE,aAAa,KAAM,KACxBqE,EAAKrE,aAAa,SAAUuD,GAC5Bc,EAAKrE,aAAa,YAAa,2BAA+B,EAAJpQ,EAAK,KAE3DA,EAAI,GAAM,GACZyU,EAAKrE,aAAa,KAAM,MACxBqE,EAAKrE,aAAa,KAAM,MACxBqE,EAAKrE,aAAa,eAAgBpQ,EAAI,IAAO,EAAI,IAAM,OAEvDyU,EAAKrE,aAAa,KAAM,MACxBqE,EAAKrE,aAAa,KAAM,MACxBqE,EAAKrE,aAAa,eAAgB,QAIpCiE,EAAWnL,OAAOuL,GAMpB,IAAMC,EAAWnL,SAAS2H,gBAAgBwC,EAAO,KACjDgB,EAAStE,aAAa,QAAS,aAC/BsE,EAAStE,aAAa,YAAa,oBAEnC,IAAMuE,EAAYpL,SAAS2H,gBAAgBwC,EAAO,QAClDiB,EAAUvE,aAAa,QAAS,eAChCuE,EAAUvE,aAAa,KAAM,KAC7BuE,EAAUvE,aAAa,KAAM,KAC7BuE,EAAUvE,aAAa,KAAM,MAC7BuE,EAAUvE,aAAa,KAAM,KAC7BuE,EAAUvE,aAAa,SAAUuD,GACjCgB,EAAUvE,aAAa,eAAgB,KACvCuE,EAAUvE,aAAa,iBAAkB,SAEzC,IAAMwE,EAAYrL,SAAS2H,gBAAgBwC,EAAO,QAClDkB,EAAUxE,aAAa,QAAS,eAChCwE,EAAUxE,aAAa,KAAM,KAC7BwE,EAAUxE,aAAa,KAAM,KAC7BwE,EAAUxE,aAAa,KAAM,QAC7BwE,EAAUxE,aAAa,KAAM,KAC7BwE,EAAUxE,aAAa,SAAUuD,GACjCiB,EAAUxE,aAAa,eAAgB,OACvCwE,EAAUxE,aAAa,iBAAkB,SAEzCsE,EAASxL,OAAOyL,EAAWC,GAG3B,IAAMC,EAAatL,SAAS2H,gBAAgBwC,EAAO,KACnDmB,EAAWzE,aAAa,QAAS,eACjCyE,EAAWzE,aAAa,YAAa,oBAErC,IAAM0E,EAAcvL,SAAS2H,gBAAgBwC,EAAO,QACpDoB,EAAY1E,aAAa,QAAS,iBAClC0E,EAAY1E,aAAa,KAAM,KAC/B0E,EAAY1E,aAAa,KAAM,KAC/B0E,EAAY1E,aAAa,KAAM,MAC/B0E,EAAY1E,aAAa,KAAM,KAC/B0E,EAAY1E,aAAa,SAAUuD,GACnCmB,EAAY1E,aAAa,eAAgB,KACzC0E,EAAY1E,aAAa,iBAAkB,SAE3C,IAAM2E,EAAcxL,SAAS2H,gBAAgBwC,EAAO,QACpDqB,EAAY3E,aAAa,QAAS,iBAClC2E,EAAY3E,aAAa,KAAM,KAC/B2E,EAAY3E,aAAa,KAAM,KAC/B2E,EAAY3E,aAAa,KAAM,QAC/B2E,EAAY3E,aAAa,KAAM,KAC/B2E,EAAY3E,aAAa,SAAUuD,GACnCoB,EAAY3E,aAAa,eAAgB,OACzC2E,EAAY3E,aAAa,iBAAkB,SAC3C,IAAM4E,EAAgBzL,SAAS2H,gBAAgBwC,EAAO,UACtDsB,EAAc5E,aAAa,IAAK,KAChC4E,EAAc5E,aAAa,OAAQuD,GAEnCkB,EAAW3L,OAAO4L,EAAaC,EAAaC,GAG5C,IAAMC,EAAa1L,SAAS2H,gBAAgBwC,EAAO,KACnDuB,EAAW7E,aAAa,QAAS,eACjC6E,EAAW7E,aAAa,YAAa,oBACrC,IAAM8E,EAAgB3L,SAAS2H,gBAAgBwC,EAAO,QACtDwB,EAAc9E,aAAa,KAAM,KACjC8E,EAAc9E,aAAa,KAAM,KACjC8E,EAAc9E,aAAa,KAAM,MACjC8E,EAAc9E,aAAa,KAAM,KACjC8E,EAAc9E,aAAa,SAAUuD,GACrCuB,EAAc9E,aAAa,eAAgB,KAC3C8E,EAAc9E,aAAa,iBAAkB,SAC7C,IAAM+E,EAAgB5L,SAAS2H,gBAAgBwC,EAAO,UACtDyB,EAAc/E,aAAa,IAAK,KAChC+E,EAAc/E,aAAa,OAAQuD,GAEnCsB,EAAW/L,OAAOgM,EAAeC,GAGjC,IAAMC,EAAM7L,SAAS2H,gBAAgBwC,EAAO,UAC5C0B,EAAIhF,aAAa,KAAM,MACvBgF,EAAIhF,aAAa,KAAM,MACvBgF,EAAIhF,aAAa,IAAK,OACtBgF,EAAIhF,aAAa,OAAQuD,GAGzB,IAAMzN,EAAOoC,KAAK+M,gBACZC,EAAUpP,EAAKc,aACfuO,EAAUrP,EAAKa,aAEfyO,EAAW,EAAaF,EACxBG,EAAc,EAAaF,EAAwBD,EAAU,GAAxB,EACrCI,EAAY,GAHJxP,EAAKY,WAGkCyO,EAAU,GAAxB,GA0EvC,GAxEAb,EAAStE,aAAa,YAAa,2BAA2BsF,EAAS,KACvEb,EAAWzE,aACT,YACA,2BAA2BqF,EAAW,KAExCR,EAAW7E,aACT,YACA,2BAA2BoF,EAAQ,KAIrCvE,EAAI/H,OAAO6K,EAAWM,EAAYK,EAAUG,EAAYI,EAAYG,GAEpEnE,EAAIb,aAAa,YAAa,eAS9B0D,EAAIpJ,UAAY,oFAINhK,OAAAmP,EAAA,EAAAnP,CACA,YACA,gCAAgCgV,EAAS,QACzCC,KAAK,MAAK,8CAGVjV,OAAAmP,EAAA,EAAAnP,CACA,YACA,iCAAgCgV,EAAY,KAAG,QAC/CC,KAAK,MAAK,+FAKVjV,OAAAmP,EAAA,EAAAnP,CACA,YACA,gCAAgC+U,EAAW,QAC3CE,KAAK,MAAK,8CAGVjV,OAAAmP,EAAA,EAAAnP,CACA,YACA,iCAAgC+U,EAAc,KAAG,QACjDE,KAAK,MAAK,+FAKVjV,OAAAmP,EAAA,EAAAnP,CACA,YACA,gCAAgC8U,EAAQ,QACxCG,KAAK,MAAK,8CAGVjV,OAAAmP,EAAA,EAAAnP,CACA,YACA,iCAAgC8U,EAAW,KAAG,QAC9CG,KAAK,MAAK,iDAMpB7B,EAAI5K,OAAO+H,GAGoB,aAA3B3I,KAAKD,MAAM6J,YAA4B,CACzC,IAAM0D,EAA4BrM,SAASC,cAAc,QACzDoM,EAASlM,UAAY,OACrBkM,EAASxB,YAAc1T,OAAAmP,EAAA,EAAAnP,CAAUwF,EAAM,WACvC0P,EAASjM,MAAMkM,SAAcjC,EAAY,KACrCtL,KAAKD,MAAMqI,QAAOkF,EAASjM,MAAM+G,MAAQpI,KAAKD,MAAMqI,OACxDoD,EAAI5K,OAAO0M,GAGb,OAAO9B,GAODrB,EAAA7Q,UAAA4R,mBAAR,WACE,IAAMrJ,EAA0BZ,SAASC,cAAc,OACvDW,EAAQT,UAAY,gBAEZ,IAAAhG,EAAA4E,KAAA8K,iBAAA1P,MAKFoS,EAAuB,EAAIxN,KAAKD,MAAM+J,cAAclQ,OACpD6T,EAHmB,GAGgBrS,EAAS,IAC5CkQ,EACHC,GAA4CnQ,EAAS,IAClDsS,EAAalT,KAAKmT,IANC,GAOHH,EAAuBpS,EAAS,IACnDA,EAAQ,IAAO,IAIZwC,EAAOoC,KAAK+M,gBAGlB,GAA+B,aAA3B/M,KAAKD,MAAM6J,YAA4B,CACzC,IAAM0D,EAA4BrM,SAASC,cAAc,QACzDoM,EAASlM,UAAY,OACrBkM,EAASxB,YAAc1T,OAAAmP,EAAA,EAAAnP,CAAUwF,EAAM,WACvC0P,EAASjM,MAAMkM,SAAcjC,EAAY,KACrCtL,KAAKD,MAAMqI,QAAOkF,EAASjM,MAAM+G,MAAQpI,KAAKD,MAAMqI,OACxDvG,EAAQjB,OAAO0M,GAIjB,IAAMM,EAA4B3M,SAASC,cAAc,QACzD0M,EAASxM,UAAY,OACrBwM,EAAS9B,YAAc1T,OAAAmP,EAAA,EAAAnP,CAAUwF,GACjCgQ,EAASvM,MAAMkM,SAAcE,EAAY,KACrCzN,KAAKD,MAAMqI,QAAOwF,EAASvM,MAAM+G,MAAQpI,KAAKD,MAAMqI,OACxDvG,EAAQjB,OAAOgN,GAGf,IAAMjC,EAAO3L,KAAK4L,mBAClB,GAAID,EAAK/R,OAAS,EAAG,CACnB,IAAMiU,EAA0B5M,SAASC,cAAc,QACvD2M,EAAOzM,UAAY,WACnByM,EAAO/B,YAAcH,EACrBkC,EAAOxM,MAAMkM,SAAcG,EAAU,KACjC1N,KAAKD,MAAMqI,QAAOyF,EAAOxM,MAAM+G,MAAQpI,KAAKD,MAAMqI,OACtDvG,EAAQjB,OAAOiN,GAGjB,OAAOhM,GAODsI,EAAA7Q,UAAAyT,cAAR,SAAsBe,QAAA,IAAAA,MAAA,MACpB,IAAM9V,EAAI8V,GAA4B,IAAIxL,KACpCyL,EAAkD,IAAjC/N,KAAKD,MAAMiK,oBAC5BgE,EAAwC,GAAxBhW,EAAEiW,oBAA2B,IAC7CC,EAAalW,EAAEmW,UAAYJ,EAAiBC,EAElD,OAAO,IAAI1L,KAAK4L,IAOX/D,EAAA7Q,UAAAsS,iBAAP,SAAwBwC,QAAA,IAAAA,MAAmBpO,KAAKD,MAAM+J,eAC9C,IAAGuE,EAAHD,EAAAE,MAAA,KAAG,GACT,YADS,IAAAD,EAAA,GAAAA,GACGnP,QAAQ,IAAK,MAOnBiL,EAAA7Q,UAAAwR,eAAR,SACE1P,EACAC,GAEA,YAHA,IAAAD,MAAgB4E,KAAKD,MAAM3E,YAC3B,IAAAC,MAAiB2E,KAAKD,MAAM1E,QAEpB2E,KAAKD,MAAM2J,WACjB,IAAK,WACH,IAAI6E,EAAW,IAUf,OARInT,EAAQ,GAAKC,EAAS,EACxBkT,EAAW/T,KAAKmT,IAAIvS,EAAOC,GAClBD,EAAQ,EACjBmT,EAAWnT,EACFC,EAAS,IAClBkT,EAAWlT,GAGN,CACLD,MAAOmT,EACPlT,OAAQkT,GAGZ,IAAK,UAcH,OAbInT,EAAQ,GAAKC,EAAS,EAExBA,EAASD,EAAQ,EAAIC,EAASD,EAAQ,EAAIC,EACjCD,EAAQ,EACjBC,EAASD,EAAQ,EACRC,EAAS,EAElBD,EAAiB,EAATC,GAERD,EAAQ,IACRC,EAAS,IAGJ,CACLD,MAAKA,EACLC,OAAMA,GAGV,QACE,MAAM,IAAI8P,MAAM,yBA5gBChB,EAAAI,cAAgB,IA+gBzCJ,EAhhBA,CAAmC9C,EAAA,6hBCzD5B,SAASmH,EAAgBxT,GAC9B,OAAOyT,EAAA,GACFrW,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJC,MAAO,KACPE,eAAe,EACfG,SAAU,KACVC,WAAY,KAEZ6O,YAAatW,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAK0T,YAAa,GAC1CC,YAAavW,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAK2T,YAAa,MAChDC,UAAWxW,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAK4T,UAAW,QAIhD,eAAA7I,GAAA,SAAA8I,mDA0BA,OA1BiCC,EAAAD,EAAA9I,GACrB8I,EAAAvV,UAAAqH,iBAAV,WACE,IAAMI,EAAsBE,SAASC,cAAc,OAUnD,GATAH,EAAIK,UAAY,MAEhBL,EAAIM,MAAM0N,UAAY,aAElB/O,KAAKD,MAAM6O,YACb7N,EAAIM,MAAM2N,gBAAkBhP,KAAKD,MAAM6O,WAIrC5O,KAAKD,MAAM2O,YAAc,EAAG,CAC9B3N,EAAIM,MAAM4N,YAAc,QAExB,IAAMC,EAAiB1U,KAAKmT,IAAI3N,KAAKD,MAAM3E,MAAO4E,KAAKD,MAAM1E,QAAU,EACjEqT,EAAclU,KAAKmT,IAAI3N,KAAKD,MAAM2O,YAAaQ,GACrDnO,EAAIM,MAAMqN,YAAiBA,EAAW,KAElC1O,KAAKD,MAAM4O,cACb5N,EAAIM,MAAMsN,YAAc3O,KAAKD,MAAM4O,aAIvC,OAAO5N,GAEX8N,EA1BA,CAAiCxH,EAAA,6hBCd1B,SAAS8H,EAAiBnU,GAC/B,IAAM+E,EAAKqP,EAAA,GACNhX,OAAAiP,EAAA,EAAAjP,CAAqBgX,EAAA,GAAKpU,EAAI,CAAEI,MAAO,EAAGC,OAAQ,KAAI,CACzDiE,KAAI,GACJC,MAAO,KACPE,eAAe,EACfG,SAAU,KACVC,WAAY,KAEZ5E,EAAG,EACHC,EAAG,EACHE,MAAO,EACPC,OAAQ,EAERgU,cAAe,CACbpU,EAAG7C,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKsU,OAAQ,GAC3BpU,EAAG9C,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKuU,OAAQ,IAE7BC,YAAa,CACXvU,EAAG7C,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKyU,KAAM,GACzBvU,EAAG9C,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAK0U,KAAM,IAE3BC,UAAWvX,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAK2U,WAAa3U,EAAK0T,YAAa,GAC1DtG,MAAOhQ,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAK2T,aAAe3T,EAAKoN,MAAO,QAW1D,OAAOgH,EAAA,GACFrP,EAGA6P,EAAKC,0BAA0B9P,IAItC,IAAA6P,EAAA,SAAA7J,GAIE,SAAA6J,EAAmB7P,UAOjBgG,EAAAlO,KAAAmI,KAAAoP,EAAA,GACKrP,EACA6P,EAAKC,0BAA0B9P,MAClCC,KA+DN,OA7EkC8P,EAAAF,EAAA7J,GAsBtB6J,EAAAtW,UAAAqH,iBAAV,WACE,IAAMkB,EAA0BZ,SAASC,cAAc,OACvDW,EAAQT,UAAY,OAEpB,IAAMgK,EAAQ,6BAERzC,EAAM1H,SAAS2H,gBAAgBwC,EAAO,OAE5CzC,EAAIb,aACF,SACC9H,KAAKD,MAAM3E,MAAQ4E,KAAKD,MAAM4P,WAAWI,YAE5CpH,EAAIb,aACF,UACC9H,KAAKD,MAAM1E,OAAS2E,KAAKD,MAAM4P,WAAWI,YAE7C,IAAMC,EAAO/O,SAAS2H,gBAAgBwC,EAAO,QAuB7C,OAtBA4E,EAAKlI,aACH,KACA,IAAG9H,KAAKD,MAAMsP,cAAcpU,EAAI+E,KAAKD,MAAM9E,EAAI+E,KAAKD,MAAM4P,UAAY,IAExEK,EAAKlI,aACH,KACA,IAAG9H,KAAKD,MAAMsP,cAAcnU,EAAI8E,KAAKD,MAAM7E,EAAI8E,KAAKD,MAAM4P,UAAY,IAExEK,EAAKlI,aACH,KACA,IAAG9H,KAAKD,MAAMyP,YAAYvU,EAAI+E,KAAKD,MAAM9E,EAAI+E,KAAKD,MAAM4P,UAAY,IAEtEK,EAAKlI,aACH,KACA,IAAG9H,KAAKD,MAAMyP,YAAYtU,EAAI8E,KAAKD,MAAM7E,EAAI8E,KAAKD,MAAM4P,UAAY,IAEtEK,EAAKlI,aAAa,SAAU9H,KAAKD,MAAMqI,OAAS,SAChD4H,EAAKlI,aAAa,eAAgB9H,KAAKD,MAAM4P,UAAUI,YAEvDpH,EAAI/H,OAAOoP,GACXnO,EAAQjB,OAAO+H,GAER9G,GAQK+N,EAAAC,0BAAd,SAAwC9P,GACtC,MAAO,CACL3E,MAAOZ,KAAKC,IAAIsF,EAAMsP,cAAcpU,EAAI8E,EAAMyP,YAAYvU,GAC1DI,OAAQb,KAAKC,IAAIsF,EAAMsP,cAAcnU,EAAI6E,EAAMyP,YAAYtU,GAC3DD,EAAGT,KAAKmT,IAAI5N,EAAMsP,cAAcpU,EAAG8E,EAAMyP,YAAYvU,GACrDC,EAAGV,KAAKmT,IAAI5N,EAAMsP,cAAcnU,EAAG6E,EAAMyP,YAAYtU,KAG3D0U,EA7EA,CAAkCvI,EAAA,iiBCnD3B,SAAS4I,EAAkBjV,GAChC,OAAOkV,EAAA,GACF9X,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,GACDlH,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,eAAA+K,GAAA,SAAAoK,mDAoBA,OApBmCC,EAAAD,EAAApK,GACvBoK,EAAA7W,UAAAqH,iBAAV,WACE,IAAMkB,EAAUZ,SAASC,cAAc,OAIvC,OAHAW,EAAQT,UAAY,QACpBS,EAAQO,UAAYpC,KAAK8B,6BAElBD,GAQFsO,EAAA7W,UAAAmH,sBAAP,WACE,IAAMoB,EAAUZ,SAASC,cAAc,OAGvC,OAFAW,EAAQT,UAAY,4BAEbS,GAEXsO,EApBA,CAAmC9I,EAAA,6hBCO7BgJ,EAAiB,SAACC,GACtB,OAAQA,GACN,IAAK,SACL,IAAK,QACH,OAAOA,EACT,QACE,MAAO,WAQPC,EAAoB,SACxBC,GAEA,OAAQA,GACN,IAAK,OACL,IAAK,MACL,IAAK,MACL,IAAK,MACH,OAAOA,EACT,QACE,MAAO,SAaN,SAASC,EACdzV,GAEA,GAA0B,iBAAfA,EAAKrC,OAA4C,IAAtBqC,EAAKrC,MAAMiB,OAC/C,MAAM,IAAI0B,UAAU,iBAGtB,IAAMkV,EAAeD,EAAkBvV,EAAKwV,cAE5C,OAAOE,EAAA,GACFtY,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,EACJgR,UAAWD,EAAerV,EAAKsV,WAC/B3X,MAAOqC,EAAKrC,OACS,SAAjB6X,EACA,CAAEA,aAAYA,GACd,CAAEA,aAAYA,EAAEG,OAAQvY,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAK2V,OAAQ,IACjDvY,OAAAmP,EAAA,EAAAnP,CAAmB4C,GACnB5C,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,eAAA+K,GAAA,SAAA6K,mDAkCA,OAlCyCC,EAAAD,EAAA7K,GAC7B6K,EAAAtX,UAAAqH,iBAAV,WACE,IAAMkB,EAAUZ,SAASC,cAAc,OAGvC,GAFAW,EAAQT,UAAY,eAES,UAAzBpB,KAAKD,MAAMuQ,UAAuB,CACpC,IAAMQ,EAAM7P,SAASC,cAAc,OACnC4P,EAAI5K,IAAMlG,KAAKD,MAAMpH,MACrBkJ,EAAQjB,OAAOkQ,OACV,CAEL,IAAIjS,EAAOmB,KAAKD,MAAMpH,MAClB4G,EAAQS,KAAK8B,6BACbvC,EAAM3F,OAAS,IACjBiF,EAAOzG,OAAAmP,EAAA,EAAAnP,CAAc,CAAC,CAAE6G,MAAO,iBAAkBtG,MAAOkG,IAASU,IAGnEsC,EAAQO,UAAYvD,EAGtB,OAAOgD,GAQC+O,EAAAtX,UAAAmH,sBAAV,WACE,IAAMoB,EAAUZ,SAASC,cAAc,OAGvC,OAFAW,EAAQT,UAAY,4BAEbS,GAEX+O,EAlCA,CAAyCvJ,EAAA,UC5FzC0J,EAAAvW,KAAAwW,GACAC,EAAA,EAAAF,EAEAG,EAAAD,EADA,KAGA,SAAAE,IACAnR,KAAAoR,IAAApR,KAAAqR,IACArR,KAAAsR,IAAAtR,KAAAuR,IAAA,KACAvR,KAAAwR,EAAA,GAGA,SAAAC,KACA,WAAAN,EAGAA,EAAA7X,UAAAmY,GAAAnY,UAAA,CACAoY,YAAAP,EACAQ,OAAA,SAAA1W,EAAAC,GACA8E,KAAAwR,GAAA,KAAAxR,KAAAoR,IAAApR,KAAAsR,KAAArW,GAAA,KAAA+E,KAAAqR,IAAArR,KAAAuR,KAAArW,IAEA0W,UAAA,WACA,OAAA5R,KAAAsR,MACAtR,KAAAsR,IAAAtR,KAAAoR,IAAApR,KAAAuR,IAAAvR,KAAAqR,IACArR,KAAAwR,GAAA,MAGAK,OAAA,SAAA5W,EAAAC,GACA8E,KAAAwR,GAAA,KAAAxR,KAAAsR,KAAArW,GAAA,KAAA+E,KAAAuR,KAAArW,IAEA4W,iBAAA,SAAAC,EAAAC,EAAA/W,EAAAC,GACA8E,KAAAwR,GAAA,MAAAO,EAAA,MAAAC,EAAA,KAAAhS,KAAAsR,KAAArW,GAAA,KAAA+E,KAAAuR,KAAArW,IAEA+W,cAAA,SAAAF,EAAAC,EAAAE,EAAAC,EAAAlX,EAAAC,GACA8E,KAAAwR,GAAA,MAAAO,EAAA,MAAAC,EAAA,MAAAE,EAAA,MAAAC,EAAA,KAAAnS,KAAAsR,KAAArW,GAAA,KAAA+E,KAAAuR,KAAArW,IAEAkX,MAAA,SAAAL,EAAAC,EAAAE,EAAAC,EAAA3Z,GACAuZ,KAAAC,KAAAE,KAAAC,KAAA3Z,KACA,IAAA6Z,EAAArS,KAAAsR,IACAgB,EAAAtS,KAAAuR,IACAgB,EAAAL,EAAAH,EACAS,EAAAL,EAAAH,EACAS,EAAAJ,EAAAN,EACAW,EAAAJ,EAAAN,EACAW,EAAAF,IAAAC,IAGA,GAAAla,EAAA,YAAA2S,MAAA,oBAAA3S,GAGA,UAAAwH,KAAAsR,IACAtR,KAAAwR,GAAA,KAAAxR,KAAAsR,IAAAS,GAAA,KAAA/R,KAAAuR,IAAAS,QAIA,GAAAW,EApDA,KAyDA,GAAAnY,KAAAC,IAAAiY,EAAAH,EAAAC,EAAAC,GAzDA,MAyDAja,EAKA,CACA,IAAAoa,EAAAV,EAAAG,EACAQ,EAAAV,EAAAG,EACAQ,EAAAP,IAAAC,IACAO,EAAAH,IAAAC,IACAG,EAAAxY,KAAAyY,KAAAH,GACAI,EAAA1Y,KAAAyY,KAAAN,GACAhb,EAAAa,EAAAgC,KAAA2Y,KAAApC,EAAAvW,KAAA4Y,MAAAN,EAAAH,EAAAI,IAAA,EAAAC,EAAAE,KAAA,GACAG,EAAA1b,EAAAub,EACAI,EAAA3b,EAAAqb,EAGAxY,KAAAC,IAAA4Y,EAAA,GA1EA,OA2EArT,KAAAwR,GAAA,KAAAO,EAAAsB,EAAAZ,GAAA,KAAAT,EAAAqB,EAAAX,IAGA1S,KAAAwR,GAAA,IAAAhZ,EAAA,IAAAA,EAAA,WAAAka,EAAAE,EAAAH,EAAAI,GAAA,KAAA7S,KAAAsR,IAAAS,EAAAuB,EAAAf,GAAA,KAAAvS,KAAAuR,IAAAS,EAAAsB,EAAAd,QApBAxS,KAAAwR,GAAA,KAAAxR,KAAAsR,IAAAS,GAAA,KAAA/R,KAAAuR,IAAAS,UAuBAuB,IAAA,SAAAtY,EAAAC,EAAA1C,EAAAgb,EAAAC,EAAAC,GACAzY,KAAAC,KACA,IAAAyY,GADAnb,MACAgC,KAAAoZ,IAAAJ,GACAK,EAAArb,EAAAgC,KAAAsZ,IAAAN,GACAnB,EAAApX,EAAA0Y,EACArB,EAAApX,EAAA2Y,EACAE,EAAA,EAAAL,EACAM,EAAAN,EAAAF,EAAAC,IAAAD,EAGA,GAAAhb,EAAA,YAAA2S,MAAA,oBAAA3S,GAGA,OAAAwH,KAAAsR,IACAtR,KAAAwR,GAAA,IAAAa,EAAA,IAAAC,GAIA9X,KAAAC,IAAAuF,KAAAsR,IAAAe,GAnGA,MAmGA7X,KAAAC,IAAAuF,KAAAuR,IAAAe,GAnGA,QAoGAtS,KAAAwR,GAAA,IAAAa,EAAA,IAAAC,GAIA9Z,IAGAwb,EAAA,IAAAA,IAAA/C,KAGA+C,EAAA9C,EACAlR,KAAAwR,GAAA,IAAAhZ,EAAA,IAAAA,EAAA,QAAAub,EAAA,KAAA9Y,EAAA0Y,GAAA,KAAAzY,EAAA2Y,GAAA,IAAArb,EAAA,IAAAA,EAAA,QAAAub,EAAA,KAAA/T,KAAAsR,IAAAe,GAAA,KAAArS,KAAAuR,IAAAe,GAIA0B,EAnHA,OAoHAhU,KAAAwR,GAAA,IAAAhZ,EAAA,IAAAA,EAAA,SAAAwb,GAAAjD,GAAA,IAAAgD,EAAA,KAAA/T,KAAAsR,IAAArW,EAAAzC,EAAAgC,KAAAoZ,IAAAH,IAAA,KAAAzT,KAAAuR,IAAArW,EAAA1C,EAAAgC,KAAAsZ,IAAAL,OAGAQ,KAAA,SAAAhZ,EAAAC,EAAAgZ,EAAAC,GACAnU,KAAAwR,GAAA,KAAAxR,KAAAoR,IAAApR,KAAAsR,KAAArW,GAAA,KAAA+E,KAAAqR,IAAArR,KAAAuR,KAAArW,GAAA,MAAAgZ,EAAA,MAAAC,EAAA,KAAAD,EAAA,KAEAnE,SAAA,WACA,OAAA/P,KAAAwR,IAIe,IAAA4C,GAAA,GCjIAC,GAAA,SAAApZ,GACf,kBACA,OAAAA,ICFOR,GAAAD,KAAAC,IACA6Z,GAAA9Z,KAAA8Z,MACAV,GAAApZ,KAAAoZ,IACAW,GAAA/Z,KAAA+Z,IACA5G,GAAAnT,KAAAmT,IACAmG,GAAAtZ,KAAAsZ,IACAb,GAAAzY,KAAAyY,KAEIuB,GAAO,MACPC,GAAEja,KAAAwW,GACN0D,GAAaD,GAAE,EACXE,GAAG,EAAOF,GAMd,SAAAG,GAAA3Z,GACP,OAAAA,GAAA,EAAAyZ,GAAAzZ,IAAA,GAAAyZ,GAAAla,KAAAoa,KAAA3Z,GCdA,SAAA4Z,GAAA7c,GACA,OAAAA,EAAA8c,YAGA,SAAAC,GAAA/c,GACA,OAAAA,EAAAgd,YAGA,SAAAC,GAAAjd,GACA,OAAAA,EAAAkd,WAGA,SAAAC,GAAAnd,GACA,OAAAA,EAAAod,SAGA,SAAAC,GAAArd,GACA,OAAAA,KAAAsd,SAcA,SAAAC,GAAAlD,EAAAC,EAAAP,EAAAC,EAAAwD,EAAAC,EAAA1B,GACA,IAAAtB,EAAAJ,EAAAN,EACAW,EAAAJ,EAAAN,EACA0D,GAAA3B,EAAA0B,MAA6BxC,GAAIR,IAAAC,KACjCiD,EAAAD,EAAAhD,EACAkD,GAAAF,EAAAjD,EACAoD,EAAAxD,EAAAsD,EACAG,EAAAxD,EAAAsD,EACAG,EAAAhE,EAAA4D,EACAK,EAAAhE,EAAA4D,EACAK,GAAAJ,EAAAE,GAAA,EACAG,GAAAJ,EAAAE,GAAA,EACArC,EAAAoC,EAAAF,EACAhC,EAAAmC,EAAAF,EACAK,EAAAxC,IAAAE,IACArb,EAAAgd,EAAAC,EACAW,EAAAP,EAAAG,EAAAD,EAAAD,EACA9d,GAAA6b,EAAA,QAA8BZ,GAAKsB,GAAG,EAAA/b,IAAA2d,EAAAC,MACtCC,GAAAD,EAAAvC,EAAAF,EAAA3b,GAAAme,EACAG,IAAAF,EAAAzC,EAAAE,EAAA7b,GAAAme,EACAI,GAAAH,EAAAvC,EAAAF,EAAA3b,GAAAme,EACAK,IAAAJ,EAAAzC,EAAAE,EAAA7b,GAAAme,EACAM,EAAAJ,EAAAJ,EACAS,EAAAJ,EAAAJ,EACAS,EAAAJ,EAAAN,EACAW,EAAAJ,EAAAN,EAMA,OAFAO,IAAAC,IAAAC,IAAAC,MAAAP,EAAAE,EAAAD,EAAAE,GAEA,CACAK,GAAAR,EACAS,GAAAR,EACA7D,KAAAkD,EACAjD,KAAAkD,EACAC,IAAAQ,GAAAb,EAAAhd,EAAA,GACAsd,IAAAQ,GAAAd,EAAAhd,EAAA,IAIe,IAAAue,GAAA,WACf,IAAAjC,EAAAD,GACAG,EAAAD,GACAiC,EAAqB3C,GAAQ,GAC7B4C,EAAA,KACA/B,EAAAD,GACAG,EAAAD,GACAG,EAAAD,GACA6B,EAAA,KAEA,SAAA3D,IACA,IAAA4D,EACA3e,ED3EOyC,EC4EPmc,GAAAtC,EAAAuC,MAAArX,KAAAsX,WACA9B,GAAAR,EAAAqC,MAAArX,KAAAsX,WACA9D,EAAA0B,EAAAmC,MAAArX,KAAAsX,WAAiD5C,GACjDjB,EAAA2B,EAAAiC,MAAArX,KAAAsX,WAA+C5C,GAC/CV,EAAavZ,GAAGgZ,EAAAD,GAChBO,EAAAN,EAAAD,EAQA,GANA0D,MAAAC,EAAqC/C,MAGrCoB,EAAA4B,IAAA5e,EAAAgd,IAAA4B,IAAA5e,GAGAgd,EAAehB,GAGf,GAAAR,EAAkBW,GAAMH,GACxB0C,EAAAvF,OAAA6D,EAA0B5B,GAAGJ,GAAAgC,EAAW1B,GAAGN,IAC3C0D,EAAA3D,IAAA,IAAAiC,EAAAhC,EAAAC,GAAAM,GACAqD,EAAe5C,KACf0C,EAAAvF,OAAAyF,EAA4BxD,GAAGH,GAAA2D,EAAWtD,GAAGL,IAC7CyD,EAAA3D,IAAA,IAAA6D,EAAA3D,EAAAD,EAAAO,QAKA,CACA,IAWAwD,EACAC,EAZAC,EAAAjE,EACAkE,EAAAjE,EACAkE,EAAAnE,EACAoE,EAAAnE,EACAoE,EAAA7D,EACA8D,EAAA9D,EACA+D,EAAAzC,EAAA+B,MAAArX,KAAAsX,WAAA,EACAU,EAAAD,EAAqBvD,KAAOyC,KAAAI,MAAArX,KAAAsX,WAAsDrE,GAAImE,IAAA5B,MACtFC,EAAe9H,GAAIlT,GAAG+a,EAAA4B,GAAA,GAAAJ,EAAAK,MAAArX,KAAAsX,YACtBW,EAAAxC,EACAyC,EAAAzC,EAKA,GAAAuC,EAAexD,GAAO,CACtB,IAAA2D,EAAiBvD,GAAIoD,EAAAZ,EAAWtD,GAAGiE,IACnCK,EAAiBxD,GAAIoD,EAAAxC,EAAW1B,GAAGiE,KACnCF,GAAA,EAAAM,GAA8B3D,IAAOmD,GAAAQ,GAAApE,EAAA,KAAA6D,GAAAO,IACrCN,EAAA,EAAAF,EAAAC,GAAApE,EAAAC,GAAA,IACAqE,GAAA,EAAAM,GAA8B5D,IAAOiD,GAAAW,GAAArE,EAAA,KAAA2D,GAAAU,IACrCN,EAAA,EAAAL,EAAAC,GAAAlE,EAAAC,GAAA,GAGA,IAAAhB,EAAA+C,EAAqB5B,GAAG6D,GACxB/E,EAAA8C,EAAqB1B,GAAG2D,GACxB1B,EAAAqB,EAAqBxD,GAAGgE,GACxB5B,EAAAoB,EAAqBtD,GAAG8D,GAGxB,GAAAnC,EAAejB,GAAO,CACtB,IAIA6D,EAJAxC,EAAAL,EAAuB5B,GAAG8D,GAC1B5B,EAAAN,EAAuB1B,GAAG4D,GAC1BzB,EAAAmB,EAAuBxD,GAAG+D,GAC1BzB,EAAAkB,EAAuBtD,GAAG6D,GAI1B,GAAA3D,EAAiBS,KAAE4D,EAlInB,SAAAhG,EAAAC,EAAAP,EAAAC,EAAAE,EAAAC,EAAAmG,EAAAC,GACA,IAAAxC,EAAAhE,EAAAM,EAAA2D,EAAAhE,EAAAM,EACAkG,EAAAF,EAAApG,EAAAuG,EAAAF,EAAApG,EACAvZ,EAAA6f,EAAA1C,EAAAyC,EAAAxC,EACA,KAAApd,IAAc4b,IAEd,OAAAnC,GADAzZ,GAAA4f,GAAAlG,EAAAH,GAAAsG,GAAApG,EAAAH,IAAAtZ,GACAmd,EAAAzD,EAAA1Z,EAAAod,GA4HmB0C,CAAAjG,EAAAC,EAAAuD,EAAAC,EAAAL,EAAAC,EAAAC,EAAAC,IAAA,CACnB,IAAA2C,EAAAlG,EAAA4F,EAAA,GACAO,EAAAlG,EAAA2F,EAAA,GACAQ,EAAAhD,EAAAwC,EAAA,GACAS,EAAAhD,EAAAuC,EAAA,GACAU,EAAA,EAAuBjF,KDlJhB7Y,GCkJwB0d,EAAAE,EAAAD,EAAAE,IAAwB7F,GAAI0F,IAAAC,KAAsB3F,GAAI4F,IAAAC,ODjJrF,IAAA7d,GAAA,EAA8BwZ,GAAEja,KAAA4Y,KAAAnY,ICiJqD,GACrF+d,EAAmB/F,GAAIoF,EAAA,GAAAA,EAAA,GAAAA,EAAA,GAAAA,EAAA,IACvBJ,EAAgBtK,GAAG8H,GAAA2B,EAAA4B,IAAAD,EAAA,IACnBb,EAAgBvK,GAAG8H,GAAAD,EAAAwD,IAAAD,EAAA,KAKnBjB,EAAkBtD,GAGlB0D,EAAqB1D,IACrB+C,EAAAhC,GAAAU,EAAAC,EAAAzD,EAAAC,EAAA8C,EAAA0C,EAAAnE,GACAyD,EAAAjC,GAAAM,EAAAC,EAAAC,EAAAC,EAAAR,EAAA0C,EAAAnE,GAEAmD,EAAAvF,OAAA4F,EAAAV,GAAAU,EAAA9E,IAAA8E,EAAAT,GAAAS,EAAA7E,KAGAwF,EAAAzC,EAAAyB,EAAA3D,IAAAgE,EAAAV,GAAAU,EAAAT,GAAAoB,EAAqD5D,GAAKiD,EAAA7E,IAAA6E,EAAA9E,KAAkB6B,GAAKkD,EAAA9E,IAAA8E,EAAA/E,MAAAsB,IAIjFmD,EAAA3D,IAAAgE,EAAAV,GAAAU,EAAAT,GAAAoB,EAAyC5D,GAAKiD,EAAA7E,IAAA6E,EAAA9E,KAAkB6B,GAAKiD,EAAAzB,IAAAyB,EAAA1B,MAAA9B,GACrEmD,EAAA3D,IAAA,IAAAiC,EAAgClB,GAAKiD,EAAAT,GAAAS,EAAAzB,IAAAyB,EAAAV,GAAAU,EAAA1B,KAAkCvB,GAAKkD,EAAAV,GAAAU,EAAA1B,IAAA0B,EAAAX,GAAAW,EAAA3B,MAAA9B,GAC5EmD,EAAA3D,IAAAiE,EAAAX,GAAAW,EAAAV,GAAAoB,EAAyC5D,GAAKkD,EAAA1B,IAAA0B,EAAA3B,KAAkBvB,GAAKkD,EAAA9E,IAAA8E,EAAA/E,MAAAsB,MAKrEmD,EAAAvF,OAAAc,EAAAC,GAAAwE,EAAA3D,IAAA,IAAAiC,EAAAiC,EAAAC,GAAA3D,IArByBmD,EAAAvF,OAAAc,EAAAC,GAyBzB0E,EAAiB5C,IAAOqD,EAAarD,GAGrCyD,EAAqBzD,IACrB+C,EAAAhC,GAAAQ,EAAAC,EAAAH,EAAAC,EAAAsB,GAAAa,EAAAlE,GACAyD,EAAAjC,GAAA9C,EAAAC,EAAAuD,EAAAC,EAAAkB,GAAAa,EAAAlE,GAEAmD,EAAArF,OAAA0F,EAAAV,GAAAU,EAAA9E,IAAA8E,EAAAT,GAAAS,EAAA7E,KAGAuF,EAAAxC,EAAAyB,EAAA3D,IAAAgE,EAAAV,GAAAU,EAAAT,GAAAmB,EAAqD3D,GAAKiD,EAAA7E,IAAA6E,EAAA9E,KAAkB6B,GAAKkD,EAAA9E,IAAA8E,EAAA/E,MAAAsB,IAIjFmD,EAAA3D,IAAAgE,EAAAV,GAAAU,EAAAT,GAAAmB,EAAyC3D,GAAKiD,EAAA7E,IAAA6E,EAAA9E,KAAkB6B,GAAKiD,EAAAzB,IAAAyB,EAAA1B,MAAA9B,GACrEmD,EAAA3D,IAAA,IAAA6D,EAAgC9C,GAAKiD,EAAAT,GAAAS,EAAAzB,IAAAyB,EAAAV,GAAAU,EAAA1B,KAAkCvB,GAAKkD,EAAAV,GAAAU,EAAA1B,IAAA0B,EAAAX,GAAAW,EAAA3B,KAAA9B,GAC5EmD,EAAA3D,IAAAiE,EAAAX,GAAAW,EAAAV,GAAAmB,EAAyC3D,GAAKkD,EAAA1B,IAAA0B,EAAA3B,KAAkBvB,GAAKkD,EAAA9E,IAAA8E,EAAA/E,MAAAsB,KAKrEmD,EAAA3D,IAAA,IAAA6D,EAAAQ,EAAAD,EAAA5D,GArB4CmD,EAAArF,OAAAkE,EAAAC,QA1FtBkB,EAAAvF,OAAA,KAoHtB,GAFAuF,EAAAtF,YAEAuF,EAAA,OAAAD,EAAA,KAAAC,EAAA,SAyCA,OAtCA5D,EAAA0F,SAAA,WACA,IAAAzgB,IAAAsc,EAAAuC,MAAArX,KAAAsX,aAAAtC,EAAAqC,MAAArX,KAAAsX,YAAA,EACA4B,IAAAhE,EAAAmC,MAAArX,KAAAsX,aAAAlC,EAAAiC,MAAArX,KAAAsX,YAAA,EAA0F7C,GAAE,EAC5F,OAAYb,GAAGsF,GAAA1gB,EAASsb,GAAGoF,GAAA1gB,IAG3B+a,EAAAuB,YAAA,SAAAtD,GACA,OAAA8F,UAAA1d,QAAAkb,EAAA,mBAAAtD,IAA2E6C,IAAQ7C,GAAA+B,GAAAuB,GAGnFvB,EAAAyB,YAAA,SAAAxD,GACA,OAAA8F,UAAA1d,QAAAob,EAAA,mBAAAxD,IAA2E6C,IAAQ7C,GAAA+B,GAAAyB,GAGnFzB,EAAAyD,aAAA,SAAAxF,GACA,OAAA8F,UAAA1d,QAAAod,EAAA,mBAAAxF,IAA4E6C,IAAQ7C,GAAA+B,GAAAyD,GAGpFzD,EAAA0D,UAAA,SAAAzF,GACA,OAAA8F,UAAA1d,QAAAqd,EAAA,MAAAzF,EAAA,wBAAAA,IAA4F6C,IAAQ7C,GAAA+B,GAAA0D,GAGpG1D,EAAA2B,WAAA,SAAA1D,GACA,OAAA8F,UAAA1d,QAAAsb,EAAA,mBAAA1D,IAA0E6C,IAAQ7C,GAAA+B,GAAA2B,GAGlF3B,EAAA6B,SAAA,SAAA5D,GACA,OAAA8F,UAAA1d,QAAAwb,EAAA,mBAAA5D,IAAwE6C,IAAQ7C,GAAA+B,GAAA6B,GAGhF7B,EAAA+B,SAAA,SAAA9D,GACA,OAAA8F,UAAA1d,QAAA0b,EAAA,mBAAA9D,IAAwE6C,IAAQ7C,GAAA+B,GAAA+B,GAGhF/B,EAAA2D,QAAA,SAAA1F,GACA,OAAA8F,UAAA1d,QAAAsd,EAAA,MAAA1F,EAAA,KAAAA,EAAA+B,GAAA2D,GAGA3D,GCnQA,SAAA4F,GAAAjC,GACAlX,KAAAoZ,SAAAlC,EAGAiC,GAAA7f,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAA0Z,OAAA,GAEAC,QAAA,YACA3Z,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA5W,EAAAC,GAAA8E,KAAAoZ,SAAAzH,OAAA1W,EAAAC,GAAsE,MACpG,OAAA8E,KAAA0Z,OAAA,EACA,QAAA1Z,KAAAoZ,SAAAvH,OAAA5W,EAAAC,MAKe,IAAA2e,GAAA,SAAA3C,GACf,WAAAiC,GAAAjC,IC3BO4C,GAAoCD,IAE3C,SAAAE,GAAAC,GACAha,KAAAia,OAAAD,EAqBe,SAAAF,GAAAE,GAEf,SAAAE,EAAAhD,GACA,WAAA6C,GAAAC,EAAA9C,IAKA,OAFAgD,EAAAD,OAAAD,EAEAE,EA1BAH,GAAAzgB,UAAA,CACA+f,UAAA,WACArZ,KAAAia,OAAAZ,aAEAE,QAAA,WACAvZ,KAAAia,OAAAV,WAEAE,UAAA,WACAzZ,KAAAia,OAAAR,aAEAE,QAAA,WACA3Z,KAAAia,OAAAN,WAEAC,MAAA,SAAAV,EAAA1gB,GACAwH,KAAAia,OAAAL,MAAAphB,EAAAgC,KAAAsZ,IAAAoF,GAAA1gB,GAAAgC,KAAAoZ,IAAAsF,MCtBOiB,MAAA7gB,UAAA8gB,MCAP5f,KAAAyY,KAAA,KCEe,ICCfoH,GAAA7f,KAAAsZ,IAAkBW,GAAE,IAAAja,KAAAsZ,IAAA,EAAsBW,GAAE,ICH7B6F,IDIf9f,KAAAsZ,IAAkBa,GAAG,IACrBna,KAAAoZ,IAAmBe,GAAG,IELtBna,KAAAyY,KAAA,GCCKzY,KAAAyY,KAAA,GACAzY,KAAAyY,KAAA,IFFU,cGAR,SAAA2G,GAAAW,EAAAtf,EAAAC,GACPqf,EAAAnB,SAAAnH,eACA,EAAAsI,EAAAnJ,IAAAmJ,EAAAjJ,KAAA,GACA,EAAAiJ,EAAAlJ,IAAAkJ,EAAAhJ,KAAA,GACAgJ,EAAAnJ,IAAA,EAAAmJ,EAAAjJ,KAAA,GACAiJ,EAAAlJ,IAAA,EAAAkJ,EAAAhJ,KAAA,GACAgJ,EAAAnJ,IAAA,EAAAmJ,EAAAjJ,IAAArW,GAAA,GACAsf,EAAAlJ,IAAA,EAAAkJ,EAAAhJ,IAAArW,GAAA,GAIO,SAAAsf,GAAAtD,GACPlX,KAAAoZ,SAAAlC,EAGAsD,GAAAlhB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IACAtR,KAAAqR,IAAArR,KAAAuR,IAAAiI,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,WACA,OAAA3Z,KAAA0Z,QACA,OAAAE,GAAA5Z,UAAAsR,IAAAtR,KAAAuR,KACA,OAAAvR,KAAAoZ,SAAAvH,OAAA7R,KAAAsR,IAAAtR,KAAAuR,MAEAvR,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA5W,EAAAC,GAAA8E,KAAAoZ,SAAAzH,OAAA1W,EAAAC,GAAsE,MACpG,OAAA8E,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAoZ,SAAAvH,QAAA,EAAA7R,KAAAoR,IAAApR,KAAAsR,KAAA,KAAAtR,KAAAqR,IAAArR,KAAAuR,KAAA,GAC9B,QAAAqI,GAAA5Z,KAAA/E,EAAAC,GAEA8E,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAArW,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAArW,ICzCA,SAAAuf,GAAAvD,GACAlX,KAAAoZ,SAAAlC,EAGAuD,GAAAnhB,UAAA,CACA+f,UAAaiB,GACbf,QAAWe,GACXb,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA2a,IAAA3a,KAAA4a,IACA5a,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA8a,IAAA9a,KAAA+a,IAAAvB,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,WACA,OAAA3Z,KAAA0Z,QACA,OACA1Z,KAAAoZ,SAAAzH,OAAA3R,KAAA0a,IAAA1a,KAAA6a,KACA7a,KAAAoZ,SAAAxH,YACA,MAEA,OACA5R,KAAAoZ,SAAAzH,QAAA3R,KAAA0a,IAAA,EAAA1a,KAAA2a,KAAA,GAAA3a,KAAA6a,IAAA,EAAA7a,KAAA8a,KAAA,GACA9a,KAAAoZ,SAAAvH,QAAA7R,KAAA2a,IAAA,EAAA3a,KAAA0a,KAAA,GAAA1a,KAAA8a,IAAA,EAAA9a,KAAA6a,KAAA,GACA7a,KAAAoZ,SAAAxH,YACA,MAEA,OACA5R,KAAA4Z,MAAA5Z,KAAA0a,IAAA1a,KAAA6a,KACA7a,KAAA4Z,MAAA5Z,KAAA2a,IAAA3a,KAAA8a,KACA9a,KAAA4Z,MAAA5Z,KAAA4a,IAAA5a,KAAA+a,OAKAnB,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAA0a,IAAAzf,EAAA+E,KAAA6a,IAAA3f,EAA4B,MAC1D,OAAA8E,KAAA0Z,OAAA,EAA8B1Z,KAAA2a,IAAA1f,EAAA+E,KAAA8a,IAAA5f,EAA4B,MAC1D,OAAA8E,KAAA0Z,OAAA,EAA8B1Z,KAAA4a,IAAA3f,EAAA+E,KAAA+a,IAAA7f,EAA4B8E,KAAAoZ,SAAAzH,QAAA3R,KAAAoR,IAAA,EAAApR,KAAAsR,IAAArW,GAAA,GAAA+E,KAAAqR,IAAA,EAAArR,KAAAuR,IAAArW,GAAA,GAA4F,MACtJ,QAAe0e,GAAK5Z,KAAA/E,EAAAC,GAEpB8E,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAArW,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAArW,IC3CA,SAAA8f,GAAA9D,GACAlX,KAAAoZ,SAAAlC,EAGA8D,GAAA1hB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IACAtR,KAAAqR,IAAArR,KAAAuR,IAAAiI,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,YACA3Z,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA8B,IAAArH,GAAArS,KAAAoR,IAAA,EAAApR,KAAAsR,IAAArW,GAAA,EAAAqX,GAAAtS,KAAAqR,IAAA,EAAArR,KAAAuR,IAAArW,GAAA,EAAoF8E,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAAQ,EAAAC,GAAAtS,KAAAoZ,SAAAzH,OAAAU,EAAAC,GAA0E,MAC5L,OAAAtS,KAAA0Z,OAAA,EACA,QAAeE,GAAK5Z,KAAA/E,EAAAC,GAEpB8E,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAArW,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAArW,IC9BA,SAAA+f,GAAA/D,EAAAgE,GACAlb,KAAAmb,OAAA,IAAoBX,GAAKtD,GACzBlX,KAAAob,MAAAF,EAGAD,GAAA3hB,UAAA,CACAmgB,UAAA,WACAzZ,KAAAqb,GAAA,GACArb,KAAAsb,GAAA,GACAtb,KAAAmb,OAAA1B,aAEAE,QAAA,WACA,IAAA1e,EAAA+E,KAAAqb,GACAngB,EAAA8E,KAAAsb,GACAC,EAAAtgB,EAAArB,OAAA,EAEA,GAAA2hB,EAAA,EAQA,IAPA,IAKA3iB,EALAyZ,EAAApX,EAAA,GACAqX,EAAApX,EAAA,GACAyY,EAAA1Y,EAAAsgB,GAAAlJ,EACAwB,EAAA3Y,EAAAqgB,GAAAjJ,EACA5a,GAAA,IAGAA,GAAA6jB,GACA3iB,EAAAlB,EAAA6jB,EACAvb,KAAAmb,OAAAvB,MACA5Z,KAAAob,MAAAngB,EAAAvD,IAAA,EAAAsI,KAAAob,QAAA/I,EAAAzZ,EAAA+a,GACA3T,KAAAob,MAAAlgB,EAAAxD,IAAA,EAAAsI,KAAAob,QAAA9I,EAAA1Z,EAAAib,IAKA7T,KAAAqb,GAAArb,KAAAsb,GAAA,KACAtb,KAAAmb,OAAAxB,WAEAC,MAAA,SAAA3e,EAAAC,GACA8E,KAAAqb,GAAA1W,MAAA1J,GACA+E,KAAAsb,GAAA3W,MAAAzJ,MAIe,SAAAsgB,EAAAN,GAEf,SAAAO,EAAAvE,GACA,WAAAgE,EAAA,IAA4BV,GAAKtD,GAAA,IAAA+D,GAAA/D,EAAAgE,GAOjC,OAJAO,EAAAP,KAAA,SAAAA,GACA,OAAAM,GAAAN,IAGAO,GAVe,CAWd,KCvDM,SAASC,GAAKnB,EAAAtf,EAAAC,GACrBqf,EAAAnB,SAAAnH,cACAsI,EAAAjJ,IAAAiJ,EAAAoB,IAAApB,EAAAG,IAAAH,EAAAnJ,KACAmJ,EAAAhJ,IAAAgJ,EAAAoB,IAAApB,EAAAM,IAAAN,EAAAlJ,KACAkJ,EAAAG,IAAAH,EAAAoB,IAAApB,EAAAjJ,IAAArW,GACAsf,EAAAM,IAAAN,EAAAoB,IAAApB,EAAAhJ,IAAArW,GACAqf,EAAAG,IACAH,EAAAM,KAIO,SAAAe,GAAA1E,EAAA2E,GACP7b,KAAAoZ,SAAAlC,EACAlX,KAAA2b,IAAA,EAAAE,GAAA,EAGAD,GAAAtiB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAA0a,IACA1a,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAA6a,IAAArB,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,WACA,OAAA3Z,KAAA0Z,QACA,OAAA1Z,KAAAoZ,SAAAvH,OAAA7R,KAAA0a,IAAA1a,KAAA6a,KAAuD,MACvD,OAAca,GAAK1b,UAAAsR,IAAAtR,KAAAuR,MAEnBvR,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA5W,EAAAC,GAAA8E,KAAAoZ,SAAAzH,OAAA1W,EAAAC,GAAsE,MACpG,OAAA8E,KAAA0Z,OAAA,EAA8B1Z,KAAAsR,IAAArW,EAAA+E,KAAAuR,IAAArW,EAA4B,MAC1D,OAAA8E,KAAA0Z,OAAA,EACA,QAAegC,GAAK1b,KAAA/E,EAAAC,GAEpB8E,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA0a,IAAAzf,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA6a,IAAA3f,KAIe,SAAAsgB,EAAAK,GAEf,SAAAC,EAAA5E,GACA,WAAA0E,GAAA1E,EAAA2E,GAOA,OAJAC,EAAAD,QAAA,SAAAA,GACA,OAAAL,GAAAK,IAGAC,GAVe,CAWd,GCzDM,SAAAC,GAAA7E,EAAA2E,GACP7b,KAAAoZ,SAAAlC,EACAlX,KAAA2b,IAAA,EAAAE,GAAA,EAGAE,GAAAziB,UAAA,CACA+f,UAAaiB,GACbf,QAAWe,GACXb,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA2a,IAAA3a,KAAA4a,IAAA5a,KAAAgc,IACAhc,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA8a,IAAA9a,KAAA+a,IAAA/a,KAAAic,IAAAzC,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,WACA,OAAA3Z,KAAA0Z,QACA,OACA1Z,KAAAoZ,SAAAzH,OAAA3R,KAAA2a,IAAA3a,KAAA8a,KACA9a,KAAAoZ,SAAAxH,YACA,MAEA,OACA5R,KAAAoZ,SAAAvH,OAAA7R,KAAA2a,IAAA3a,KAAA8a,KACA9a,KAAAoZ,SAAAxH,YACA,MAEA,OACA5R,KAAA4Z,MAAA5Z,KAAA2a,IAAA3a,KAAA8a,KACA9a,KAAA4Z,MAAA5Z,KAAA4a,IAAA5a,KAAA+a,KACA/a,KAAA4Z,MAAA5Z,KAAAgc,IAAAhc,KAAAic,OAKArC,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAA2a,IAAA1f,EAAA+E,KAAA8a,IAAA5f,EAA4B,MAC1D,OAAA8E,KAAA0Z,OAAA,EAA8B1Z,KAAAoZ,SAAAzH,OAAA3R,KAAA4a,IAAA3f,EAAA+E,KAAA+a,IAAA7f,GAAkD,MAChF,OAAA8E,KAAA0Z,OAAA,EAA8B1Z,KAAAgc,IAAA/gB,EAAA+E,KAAAic,IAAA/gB,EAA4B,MAC1D,QAAewgB,GAAK1b,KAAA/E,EAAAC,GAEpB8E,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA0a,IAAAzf,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA6a,IAAA3f,KAIe,SAAAsgB,EAAAK,GAEf,SAAAC,EAAA5E,GACA,WAAA6E,GAAA7E,EAAA2E,GAOA,OAJAC,EAAAD,QAAA,SAAAA,GACA,OAAAL,GAAAK,IAGAC,GAVe,CAWd,GC1DM,SAAAI,GAAAhF,EAAA2E,GACP7b,KAAAoZ,SAAAlC,EACAlX,KAAA2b,IAAA,EAAAE,GAAA,EAGAK,GAAA5iB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAA0a,IACA1a,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAA6a,IAAArB,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,YACA3Z,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA7R,KAAA0a,IAAA1a,KAAA6a,KAAA7a,KAAAoZ,SAAAzH,OAAA3R,KAAA0a,IAAA1a,KAAA6a,KAAkG,MAChI,OAAA7a,KAAA0Z,OAAA,EACA,QAAegC,GAAK1b,KAAA/E,EAAAC,GAEpB8E,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA0a,IAAAzf,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA6a,IAAA3f,KAIe,SAAAsgB,EAAAK,GAEf,SAAAC,EAAA5E,GACA,WAAAgF,GAAAhF,EAAA2E,GAOA,OAJAC,EAAAD,QAAA,SAAAA,GACA,OAAAL,GAAAK,IAGAC,GAVe,CAWd,GC7CM,SAASK,GAAK5B,EAAAtf,EAAAC,GACrB,IAAA6W,EAAAwI,EAAAjJ,IACAU,EAAAuI,EAAAhJ,IACAW,EAAAqI,EAAAG,IACAvI,EAAAoI,EAAAM,IAEA,GAAAN,EAAA6B,OAAoB5H,GAAO,CAC3B,IAAA0E,EAAA,EAAAqB,EAAA8B,QAAA,EAAA9B,EAAA6B,OAAA7B,EAAA+B,OAAA/B,EAAAgC,QACApjB,EAAA,EAAAohB,EAAA6B,QAAA7B,EAAA6B,OAAA7B,EAAA+B,QACAvK,KAAAmH,EAAAqB,EAAAnJ,IAAAmJ,EAAAgC,QAAAhC,EAAAG,IAAAH,EAAA8B,SAAAljB,EACA6Y,KAAAkH,EAAAqB,EAAAlJ,IAAAkJ,EAAAgC,QAAAhC,EAAAM,IAAAN,EAAA8B,SAAAljB,EAGA,GAAAohB,EAAAiC,OAAoBhI,GAAO,CAC3B,IAAAiI,EAAA,EAAAlC,EAAAmC,QAAA,EAAAnC,EAAAiC,OAAAjC,EAAA+B,OAAA/B,EAAAgC,QACAzkB,EAAA,EAAAyiB,EAAAiC,QAAAjC,EAAAiC,OAAAjC,EAAA+B,QACApK,KAAAuK,EAAAlC,EAAAjJ,IAAAiJ,EAAAmC,QAAAzhB,EAAAsf,EAAAgC,SAAAzkB,EACAqa,KAAAsK,EAAAlC,EAAAhJ,IAAAgJ,EAAAmC,QAAAxhB,EAAAqf,EAAAgC,SAAAzkB,EAGAyiB,EAAAnB,SAAAnH,cAAAF,EAAAC,EAAAE,EAAAC,EAAAoI,EAAAG,IAAAH,EAAAM,KAGA,SAAA8B,GAAAzF,EAAA0F,GACA5c,KAAAoZ,SAAAlC,EACAlX,KAAA6c,OAAAD,EAGAD,GAAArjB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAA0a,IACA1a,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAA6a,IAAArB,IACAxZ,KAAAoc,OAAApc,KAAAsc,OAAAtc,KAAAwc,OACAxc,KAAAqc,QAAArc,KAAAuc,QAAAvc,KAAA0c,QACA1c,KAAA0Z,OAAA,GAEAC,QAAA,WACA,OAAA3Z,KAAA0Z,QACA,OAAA1Z,KAAAoZ,SAAAvH,OAAA7R,KAAA0a,IAAA1a,KAAA6a,KAAuD,MACvD,OAAA7a,KAAA4Z,MAAA5Z,KAAA0a,IAAA1a,KAAA6a,MAEA7a,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GAGA,GAFAD,KAAAC,KAEA8E,KAAA0Z,OAAA,CACA,IAAAoD,EAAA9c,KAAA0a,IAAAzf,EACA8hB,EAAA/c,KAAA6a,IAAA3f,EACA8E,KAAAwc,OAAAhiB,KAAAyY,KAAAjT,KAAA0c,QAAAliB,KAAAwiB,IAAAF,IAAAC,IAAA/c,KAAA6c,SAGA,OAAA7c,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA5W,EAAAC,GAAA8E,KAAAoZ,SAAAzH,OAAA1W,EAAAC,GAAsE,MACpG,OAAA8E,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EACA,QAAeyC,GAAKnc,KAAA/E,EAAAC,GAGpB8E,KAAAoc,OAAApc,KAAAsc,OAAAtc,KAAAsc,OAAAtc,KAAAwc,OACAxc,KAAAqc,QAAArc,KAAAuc,QAAAvc,KAAAuc,QAAAvc,KAAA0c,QACA1c,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA0a,IAAAzf,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA6a,IAAA3f,KAIe,SAAAsgB,EAAAoB,GAEf,SAAAK,EAAA/F,GACA,OAAA0F,EAAA,IAAAD,GAAAzF,EAAA0F,GAAA,IAAwDhB,GAAQ1E,EAAA,GAOhE,OAJA+F,EAAAL,MAAA,SAAAA,GACA,OAAApB,GAAAoB,IAGAK,GAVe,CAWd,ICnFD,SAAAC,GAAAhG,EAAA0F,GACA5c,KAAAoZ,SAAAlC,EACAlX,KAAA6c,OAAAD,EAGAM,GAAA5jB,UAAA,CACA+f,UAAaiB,GACbf,QAAWe,GACXb,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA2a,IAAA3a,KAAA4a,IAAA5a,KAAAgc,IACAhc,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA8a,IAAA9a,KAAA+a,IAAA/a,KAAAic,IAAAzC,IACAxZ,KAAAoc,OAAApc,KAAAsc,OAAAtc,KAAAwc,OACAxc,KAAAqc,QAAArc,KAAAuc,QAAAvc,KAAA0c,QACA1c,KAAA0Z,OAAA,GAEAC,QAAA,WACA,OAAA3Z,KAAA0Z,QACA,OACA1Z,KAAAoZ,SAAAzH,OAAA3R,KAAA2a,IAAA3a,KAAA8a,KACA9a,KAAAoZ,SAAAxH,YACA,MAEA,OACA5R,KAAAoZ,SAAAvH,OAAA7R,KAAA2a,IAAA3a,KAAA8a,KACA9a,KAAAoZ,SAAAxH,YACA,MAEA,OACA5R,KAAA4Z,MAAA5Z,KAAA2a,IAAA3a,KAAA8a,KACA9a,KAAA4Z,MAAA5Z,KAAA4a,IAAA5a,KAAA+a,KACA/a,KAAA4Z,MAAA5Z,KAAAgc,IAAAhc,KAAAic,OAKArC,MAAA,SAAA3e,EAAAC,GAGA,GAFAD,KAAAC,KAEA8E,KAAA0Z,OAAA,CACA,IAAAoD,EAAA9c,KAAA0a,IAAAzf,EACA8hB,EAAA/c,KAAA6a,IAAA3f,EACA8E,KAAAwc,OAAAhiB,KAAAyY,KAAAjT,KAAA0c,QAAAliB,KAAAwiB,IAAAF,IAAAC,IAAA/c,KAAA6c,SAGA,OAAA7c,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAA2a,IAAA1f,EAAA+E,KAAA8a,IAAA5f,EAA4B,MAC1D,OAAA8E,KAAA0Z,OAAA,EAA8B1Z,KAAAoZ,SAAAzH,OAAA3R,KAAA4a,IAAA3f,EAAA+E,KAAA+a,IAAA7f,GAAkD,MAChF,OAAA8E,KAAA0Z,OAAA,EAA8B1Z,KAAAgc,IAAA/gB,EAAA+E,KAAAic,IAAA/gB,EAA4B,MAC1D,QAAeihB,GAAKnc,KAAA/E,EAAAC,GAGpB8E,KAAAoc,OAAApc,KAAAsc,OAAAtc,KAAAsc,OAAAtc,KAAAwc,OACAxc,KAAAqc,QAAArc,KAAAuc,QAAAvc,KAAAuc,QAAAvc,KAAA0c,QACA1c,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA0a,IAAAzf,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA6a,IAAA3f,KAIe,SAAAsgB,EAAAoB,GAEf,SAAAK,EAAA/F,GACA,OAAA0F,EAAA,IAAAM,GAAAhG,EAAA0F,GAAA,IAA8Db,GAAc7E,EAAA,GAO5E,OAJA+F,EAAAL,MAAA,SAAAA,GACA,OAAApB,GAAAoB,IAGAK,GAVe,CAWd,ICtED,SAAAE,GAAAjG,EAAA0F,GACA5c,KAAAoZ,SAAAlC,EACAlX,KAAA6c,OAAAD,EAGAO,GAAA7jB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAA0a,IACA1a,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAA6a,IAAArB,IACAxZ,KAAAoc,OAAApc,KAAAsc,OAAAtc,KAAAwc,OACAxc,KAAAqc,QAAArc,KAAAuc,QAAAvc,KAAA0c,QACA1c,KAAA0Z,OAAA,GAEAC,QAAA,YACA3Z,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GAGA,GAFAD,KAAAC,KAEA8E,KAAA0Z,OAAA,CACA,IAAAoD,EAAA9c,KAAA0a,IAAAzf,EACA8hB,EAAA/c,KAAA6a,IAAA3f,EACA8E,KAAAwc,OAAAhiB,KAAAyY,KAAAjT,KAAA0c,QAAAliB,KAAAwiB,IAAAF,IAAAC,IAAA/c,KAAA6c,SAGA,OAAA7c,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA7R,KAAA0a,IAAA1a,KAAA6a,KAAA7a,KAAAoZ,SAAAzH,OAAA3R,KAAA0a,IAAA1a,KAAA6a,KAAkG,MAChI,OAAA7a,KAAA0Z,OAAA,EACA,QAAeyC,GAAKnc,KAAA/E,EAAAC,GAGpB8E,KAAAoc,OAAApc,KAAAsc,OAAAtc,KAAAsc,OAAAtc,KAAAwc,OACAxc,KAAAqc,QAAArc,KAAAuc,QAAAvc,KAAAuc,QAAAvc,KAAA0c,QACA1c,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAAtR,KAAA0a,IAAA1a,KAAA0a,IAAAzf,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAAvR,KAAA6a,IAAA7a,KAAA6a,IAAA3f,KAIe,SAAAsgB,EAAAoB,GAEf,SAAAK,EAAA/F,GACA,OAAA0F,EAAA,IAAAO,GAAAjG,EAAA0F,GAAA,IAA4DV,GAAYhF,EAAA,GAOxE,OAJA+F,EAAAL,MAAA,SAAAA,GACA,OAAApB,GAAAoB,IAGAK,GAVe,CAWd,IC3DD,SAAAG,GAAAlG,GACAlX,KAAAoZ,SAAAlC,EAGAkG,GAAA9jB,UAAA,CACA+f,UAAaiB,GACbf,QAAWe,GACXb,UAAA,WACAzZ,KAAA0Z,OAAA,GAEAC,QAAA,WACA3Z,KAAA0Z,QAAA1Z,KAAAoZ,SAAAxH,aAEAgI,MAAA,SAAA3e,EAAAC,GACAD,KAAAC,KACA8E,KAAA0Z,OAAA1Z,KAAAoZ,SAAAvH,OAAA5W,EAAAC,IACA8E,KAAA0Z,OAAA,EAAA1Z,KAAAoZ,SAAAzH,OAAA1W,EAAAC,MClBA,SAAAmiB,GAAApiB,GACA,OAAAA,EAAA,OAOA,SAAAqiB,GAAA/C,EAAArI,EAAAC,GACA,IAAAoL,EAAAhD,EAAAjJ,IAAAiJ,EAAAnJ,IACAoM,EAAAtL,EAAAqI,EAAAjJ,IACAmM,GAAAlD,EAAAhJ,IAAAgJ,EAAAlJ,MAAAkM,GAAAC,EAAA,OACAE,GAAAvL,EAAAoI,EAAAhJ,MAAAiM,GAAAD,EAAA,OACA/jB,GAAAikB,EAAAD,EAAAE,EAAAH,MAAAC,GACA,OAAAH,GAAAI,GAAAJ,GAAAK,IAAAljB,KAAAmT,IAAAnT,KAAAC,IAAAgjB,GAAAjjB,KAAAC,IAAAijB,GAAA,GAAAljB,KAAAC,IAAAjB,KAAA,EAIA,SAAAmkB,GAAApD,EAAA3hB,GACA,IAAAub,EAAAoG,EAAAjJ,IAAAiJ,EAAAnJ,IACA,OAAA+C,GAAA,GAAAoG,EAAAhJ,IAAAgJ,EAAAlJ,KAAA8C,EAAAvb,GAAA,EAAAA,EAMA,SAASglB,GAAKrD,EAAAhD,EAAAC,GACd,IAAAnF,EAAAkI,EAAAnJ,IACAkB,EAAAiI,EAAAlJ,IACAU,EAAAwI,EAAAjJ,IACAU,EAAAuI,EAAAhJ,IACAoC,GAAA5B,EAAAM,GAAA,EACAkI,EAAAnB,SAAAnH,cAAAI,EAAAsB,EAAArB,EAAAqB,EAAA4D,EAAAxF,EAAA4B,EAAA3B,EAAA2B,EAAA6D,EAAAzF,EAAAC,GAGA,SAAA6L,GAAA3G,GACAlX,KAAAoZ,SAAAlC,EA0CA,SAAA4G,GAAA5G,GACAlX,KAAAoZ,SAAA,IAAA2E,GAAA7G,GAOA,SAAA6G,GAAA7G,GACAlX,KAAAoZ,SAAAlC,ECvFA,SAAA8G,GAAA9G,GACAlX,KAAAoZ,SAAAlC,EA2CA,SAAA+G,GAAAhjB,GACA,IAAAvD,EAEAI,EADAqB,EAAA8B,EAAArB,OAAA,EAEAsf,EAAA,IAAAiB,MAAAhhB,GACAsjB,EAAA,IAAAtC,MAAAhhB,GACAX,EAAA,IAAA2hB,MAAAhhB,GAEA,IADA+f,EAAA,KAAAuD,EAAA,KAAAjkB,EAAA,GAAAyC,EAAA,KAAAA,EAAA,GACAvD,EAAA,EAAaA,EAAAyB,EAAA,IAAWzB,EAAAwhB,EAAAxhB,GAAA,EAAA+kB,EAAA/kB,GAAA,EAAAc,EAAAd,GAAA,EAAAuD,EAAAvD,GAAA,EAAAuD,EAAAvD,EAAA,GAExB,IADAwhB,EAAA/f,EAAA,KAAAsjB,EAAAtjB,EAAA,KAAAX,EAAAW,EAAA,KAAA8B,EAAA9B,EAAA,GAAA8B,EAAA9B,GACAzB,EAAA,EAAaA,EAAAyB,IAAOzB,EAAAI,EAAAohB,EAAAxhB,GAAA+kB,EAAA/kB,EAAA,GAAA+kB,EAAA/kB,IAAAI,EAAAU,EAAAd,IAAAI,EAAAU,EAAAd,EAAA,GAEpB,IADAwhB,EAAA/f,EAAA,GAAAX,EAAAW,EAAA,GAAAsjB,EAAAtjB,EAAA,GACAzB,EAAAyB,EAAA,EAAiBzB,GAAA,IAAQA,EAAAwhB,EAAAxhB,IAAAc,EAAAd,GAAAwhB,EAAAxhB,EAAA,IAAA+kB,EAAA/kB,GAEzB,IADA+kB,EAAAtjB,EAAA,IAAA8B,EAAA9B,GAAA+f,EAAA/f,EAAA,MACAzB,EAAA,EAAaA,EAAAyB,EAAA,IAAWzB,EAAA+kB,EAAA/kB,GAAA,EAAAuD,EAAAvD,EAAA,GAAAwhB,EAAAxhB,EAAA,GACxB,OAAAwhB,EAAAuD,GDpBAoB,GAAAvkB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAoR,IAAApR,KAAAsR,IACAtR,KAAAqR,IAAArR,KAAAuR,IACAvR,KAAAke,IAAA1E,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,WACA,OAAA3Z,KAAA0Z,QACA,OAAA1Z,KAAAoZ,SAAAvH,OAAA7R,KAAAsR,IAAAtR,KAAAuR,KAAuD,MACvD,OAAcqM,GAAK5d,UAAAke,IAAAP,GAAA3d,UAAAke,OAEnBle,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,OAEAM,MAAA,SAAA3e,EAAAC,GACA,IAAAsc,EAAAgC,IAGA,GADAte,MAAAD,QACA+E,KAAAsR,KAAApW,IAAA8E,KAAAuR,IAAA,CACA,OAAAvR,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA5W,EAAAC,GAAA8E,KAAAoZ,SAAAzH,OAAA1W,EAAAC,GAAsE,MACpG,OAAA8E,KAAA0Z,OAAA,EAA8B,MAC9B,OAAA1Z,KAAA0Z,OAAA,EAA+BkE,GAAK5d,KAAA2d,GAAA3d,KAAAwX,EAAA8F,GAAAtd,KAAA/E,EAAAC,IAAAsc,GAAkD,MACtF,QAAeoG,GAAK5d,UAAAke,IAAA1G,EAAA8F,GAAAtd,KAAA/E,EAAAC,IAGpB8E,KAAAoR,IAAApR,KAAAsR,IAAAtR,KAAAsR,IAAArW,EACA+E,KAAAqR,IAAArR,KAAAuR,IAAAvR,KAAAuR,IAAArW,EACA8E,KAAAke,IAAA1G,MAQAsG,GAAAxkB,UAAAlB,OAAAY,OAAA6kB,GAAAvkB,YAAAsgB,MAAA,SAAA3e,EAAAC,GACA2iB,GAAAvkB,UAAAsgB,MAAA/hB,KAAAmI,KAAA9E,EAAAD,IAOA8iB,GAAAzkB,UAAA,CACAqY,OAAA,SAAA1W,EAAAC,GAA0B8E,KAAAoZ,SAAAzH,OAAAzW,EAAAD,IAC1B2W,UAAA,WAAyB5R,KAAAoZ,SAAAxH,aACzBC,OAAA,SAAA5W,EAAAC,GAA0B8E,KAAAoZ,SAAAvH,OAAA3W,EAAAD,IAC1BgX,cAAA,SAAAF,EAAAC,EAAAE,EAAAC,EAAAlX,EAAAC,GAAiD8E,KAAAoZ,SAAAnH,cAAAD,EAAAD,EAAAI,EAAAD,EAAAhX,EAAAD,KC1FjD+iB,GAAA1kB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAqb,GAAA,GACArb,KAAAsb,GAAA,IAEA3B,QAAA,WACA,IAAA1e,EAAA+E,KAAAqb,GACAngB,EAAA8E,KAAAsb,GACAniB,EAAA8B,EAAArB,OAEA,GAAAT,EAEA,GADA6G,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA5W,EAAA,GAAAC,EAAA,IAAA8E,KAAAoZ,SAAAzH,OAAA1W,EAAA,GAAAC,EAAA,IACA,IAAA/B,EACA6G,KAAAoZ,SAAAvH,OAAA5W,EAAA,GAAAC,EAAA,SAIA,IAFA,IAAAijB,EAAAF,GAAAhjB,GACAmjB,EAAAH,GAAA/iB,GACAmjB,EAAA,EAAAC,EAAA,EAAgCA,EAAAnlB,IAAQklB,IAAAC,EACxCte,KAAAoZ,SAAAnH,cAAAkM,EAAA,GAAAE,GAAAD,EAAA,GAAAC,GAAAF,EAAA,GAAAE,GAAAD,EAAA,GAAAC,GAAApjB,EAAAqjB,GAAApjB,EAAAojB,KAKAte,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAngB,IAAA6G,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,MACAtZ,KAAAqb,GAAArb,KAAAsb,GAAA,MAEA1B,MAAA,SAAA3e,EAAAC,GACA8E,KAAAqb,GAAA1W,MAAA1J,GACA+E,KAAAsb,GAAA3W,MAAAzJ,KCvCA,SAAAqjB,GAAArH,EAAAte,GACAoH,KAAAoZ,SAAAlC,EACAlX,KAAAwe,GAAA5lB,EAGA2lB,GAAAjlB,UAAA,CACA+f,UAAA,WACArZ,KAAAsZ,MAAA,GAEAC,QAAA,WACAvZ,KAAAsZ,MAAAE,KAEAC,UAAA,WACAzZ,KAAAqb,GAAArb,KAAAsb,GAAA9B,IACAxZ,KAAA0Z,OAAA,GAEAC,QAAA,WACA,EAAA3Z,KAAAwe,IAAAxe,KAAAwe,GAAA,OAAAxe,KAAA0Z,QAAA1Z,KAAAoZ,SAAAvH,OAAA7R,KAAAqb,GAAArb,KAAAsb,KACAtb,KAAAsZ,OAAA,IAAAtZ,KAAAsZ,OAAA,IAAAtZ,KAAA0Z,SAAA1Z,KAAAoZ,SAAAxH,YACA5R,KAAAsZ,OAAA,IAAAtZ,KAAAwe,GAAA,EAAAxe,KAAAwe,GAAAxe,KAAAsZ,MAAA,EAAAtZ,KAAAsZ,QAEAM,MAAA,SAAA3e,EAAAC,GAEA,OADAD,KAAAC,KACA8E,KAAA0Z,QACA,OAAA1Z,KAAA0Z,OAAA,EAA8B1Z,KAAAsZ,MAAAtZ,KAAAoZ,SAAAvH,OAAA5W,EAAAC,GAAA8E,KAAAoZ,SAAAzH,OAAA1W,EAAAC,GAAsE,MACpG,OAAA8E,KAAA0Z,OAAA,EACA,QACA,GAAA1Z,KAAAwe,IAAA,EACAxe,KAAAoZ,SAAAvH,OAAA7R,KAAAqb,GAAAngB,GACA8E,KAAAoZ,SAAAvH,OAAA5W,EAAAC,OACS,CACT,IAAA6W,EAAA/R,KAAAqb,IAAA,EAAArb,KAAAwe,IAAAvjB,EAAA+E,KAAAwe,GACAxe,KAAAoZ,SAAAvH,OAAAE,EAAA/R,KAAAsb,IACAtb,KAAAoZ,SAAAvH,OAAAE,EAAA7W,IAKA8E,KAAAqb,GAAApgB,EAAA+E,KAAAsb,GAAApgB,ICpCe,iiBCoCf,SAASujB,GACPnf,GAEA,OAAQA,GACN,IAAK,eACL,IAAK,SACL,IAAK,wBACL,IAAK,4BACH,OAAOA,EACT,QACA,OACE,MAAO,eACT,OACE,MAAO,SACT,QACE,MAAO,wBACT,QACE,MAAO,6BAQb,SAASof,GAAiBpO,GACxB,OAAQA,GACN,IAAK,UACL,IAAK,QACH,OAAOA,EACT,QACE,MAAO,WAaN,SAASqO,GACd3jB,GAEA,OAAO4jB,GAAA,GACFxmB,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,EACJuf,eAAgBJ,GAAsBzjB,EAAK6jB,gBAAkB7jB,EAAKsE,MAClEgR,UAAWoO,GAAiB1jB,EAAKsV,WACjCwO,SAAU1mB,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAK8jB,SAAU,MACpCC,SAAU3mB,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAK+jB,SAAU,MACpC3W,MAAOhQ,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKoN,MAAO,MACpC4W,WAAY5mB,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKgkB,WAAY,MAC9CrmB,MAAOP,OAAAmP,EAAA,EAAAnP,CAAa4C,EAAKrC,MAAO,MAChCsmB,KAAM7mB,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKikB,KAAM,OAC/B7mB,OAAAmP,EAAA,EAAAnP,CAAmB4C,GACnB5C,OAAAmP,EAAA,EAAAnP,CAAqB4C,IAI5B,IAAMkkB,GAAQ,gCAEd,SAAAnZ,GAAA,SAAAoZ,mDA0KA,OA1KwCC,GAAAD,EAAApZ,GAC5BoZ,EAAA7lB,UAAAqH,iBAAV,WACE,IAYI0e,EAZEhU,EAAS,CACb1D,WAAY,UACZ2X,SAAUtf,KAAKD,MAAMqI,OAAS,UAC9BvJ,KAAMmB,KAAKD,MAAMif,YAAc,WAG3BM,EAAWtf,KAAKuf,cAEhB1d,EAAUZ,SAASC,cAAc,OAEjCyH,EAAM1H,SAAS2H,gBAAgBsW,GAAO,OAW5C,OARwB,MAApBlf,KAAKD,MAAMpH,QAEX0mB,EADEvhB,KACYA,KAAK0hB,aAAa,SAASrhB,OAAO6B,KAAKD,MAAMpH,OAE7CqH,KAAKD,MAAMpH,OAIrBqH,KAAKD,MAAM8e,gBACjB,IAAK,eAED,IAAMY,EAAiBxe,SAAS2H,gBAAgBsW,GAAO,QACvDO,EAAe3X,aAAa,OAAQuD,EAAO1D,YAC3C8X,EAAe3X,aAAa,eAAgB,OAC5C2X,EAAe3X,aAAa,QAAS,OACrC2X,EAAe3X,aAAa,SAAU,MACtC2X,EAAe3X,aAAa,KAAM,KAClC2X,EAAe3X,aAAa,KAAM,KAClC,IAAM4X,EAAeze,SAAS2H,gBAAgBsW,GAAO,QACrDQ,EAAa5X,aAAa,OAAQuD,EAAOiU,UACzCI,EAAa5X,aAAa,eAAgB,KAC1C4X,EAAa5X,aAAa,QAAS,GAAGwX,GACtCI,EAAa5X,aAAa,SAAU,MACpC4X,EAAa5X,aAAa,KAAM,KAChC4X,EAAa5X,aAAa,KAAM,MAC1BjJ,EAAOoC,SAAS2H,gBAAgBsW,GAAO,SACxCpX,aAAa,cAAe,UACjCjJ,EAAKiJ,aAAa,qBAAsB,UACxCjJ,EAAKiJ,aAAa,YAAa,MAC/BjJ,EAAKiJ,aAAa,cAAe,SACjCjJ,EAAKiJ,aAAa,cAAe,QACjCjJ,EAAKiJ,aAAa,YAAa,oBAC/BjJ,EAAKiJ,aAAa,OAAQuD,EAAOxM,MAEJ,UAAzBmB,KAAKD,MAAMuQ,WACbzR,EAAKwC,MAAMkM,SAAW,MAEtB1O,EAAKiN,YAAc9L,KAAKD,MAAMkf,KACvBI,EAAW,IAAIrf,KAAKD,MAAMkf,KAC7B,GAAGI,GAEPxgB,EAAKiN,YAAiBwT,EAAQ,IAIhC3W,EAAIb,aAAa,UAAW,cAC5Ba,EAAI/H,OAAO6e,EAAgBC,EAAc7gB,GAE3C,MACF,IAAK,SACL,IAAK,wBACL,IAAK,4BAKD,GAFA8J,EAAIb,aAAa,UAAW,eAEM,WAA9B9H,KAAKD,MAAM8e,eAA6B,EAEpCc,EAAmB1e,SAAS2H,gBAAgBsW,GAAO,WACxCpX,aAAa,YAAa,oBAC3C6X,EAAiB7X,aAAa,OAAQuD,EAAO1D,YAC7CgY,EAAiB7X,aAAa,eAAgB,OAC9C6X,EAAiB7X,aAAa,IAAK,OAC7B8X,EAAiB3e,SAAS2H,gBAAgBsW,GAAO,WACxCpX,aAAa,YAAa,oBACzC8X,EAAe9X,aAAa,OAAQuD,EAAOiU,UAC3CM,EAAe9X,aAAa,eAAgB,KAC5C8X,EAAe9X,aAAa,IAAK,GAAGwX,EAAW,GAE/C3W,EAAI/H,OAAO+e,EAAkBC,OACxB,CAEL,IASMD,EAKAC,EAdAC,EAAW,CACf/K,YACgC,0BAA9B9U,KAAKD,MAAM8e,eAA6C,GAAK,EAC/D7J,YAAa,GACbE,WAAY,EACZE,SAAoB,EAAV5a,KAAKwW,IAEXuC,EAAMwD,MAEN4I,EAAmB1e,SAAS2H,gBAAgBsW,GAAO,SACxCpX,aAAa,YAAa,oBAC3C6X,EAAiB7X,aAAa,OAAQuD,EAAO1D,YAC7CgY,EAAiB7X,aAAa,eAAgB,OAC9C6X,EAAiB7X,aAAa,IAAK,GAAGyL,EAAIsM,KACpCD,EAAiB3e,SAAS2H,gBAAgBsW,GAAO,SACxCpX,aAAa,YAAa,oBACzC8X,EAAe9X,aAAa,OAAQuD,EAAOiU,UAC3CM,EAAe9X,aAAa,eAAgB,KAC5C8X,EAAe9X,aACb,IACA,GAAGyL,EAAIqL,GAAA,GACFiB,EAAQ,CACXzK,SAAUyK,EAASzK,UAAYkK,EAAW,SAI9C3W,EAAI/H,OAAO+e,EAAkBC,GAI/B,IAAM/gB,EAQN,IARMA,EAAOoC,SAAS2H,gBAAgBsW,GAAO,SACxCpX,aAAa,cAAe,UACjCjJ,EAAKiJ,aAAa,qBAAsB,UACxCjJ,EAAKiJ,aAAa,YAAa,MAC/BjJ,EAAKiJ,aAAa,cAAe,SACjCjJ,EAAKiJ,aAAa,cAAe,QACjCjJ,EAAKiJ,aAAa,OAAQuD,EAAOxM,MAEJ,UAAzBmB,KAAKD,MAAMuQ,WAA6C,MAApBtQ,KAAKD,MAAMpH,MAEjD,GAAIqH,KAAKD,MAAMkf,MAAQjf,KAAKD,MAAMkf,KAAKrlB,OAAS,EAAG,CACjD,IAAMjB,EAAQsI,SAAS2H,gBAAgBsW,GAAO,SAC9CvmB,EAAMmP,aAAa,IAAK,KACxBnP,EAAMmP,aAAa,KAAM,OACzBnP,EAAMmT,YAAc,GAAGuT,EACvB1mB,EAAM0I,MAAMkM,SAAW,MACvB,IAAM0R,EAAOhe,SAAS2H,gBAAgBsW,GAAO,SAC7CD,EAAKnX,aAAa,IAAK,KACvBmX,EAAKnX,aAAa,KAAM,OACxBmX,EAAKnT,YAAc,GAAG9L,KAAKD,MAAMkf,KACjCA,EAAK5d,MAAMkM,SAAW,MACtB1O,EAAK+B,OAAOjI,EAAOsmB,GACnBpgB,EAAKiJ,aAAa,YAAa,yBAE/BjJ,EAAKiN,YAAc,GAAGuT,EACtBxgB,EAAKwC,MAAMkM,SAAW,MACtB1O,EAAKiJ,aAAa,YAAa,yBAIjCjJ,EAAKiN,YAAiBwT,EAAQ,IAC9BzgB,EAAKiJ,aAAa,YAAa,oBAGjCa,EAAI/H,OAAO/B,GAOjB,OAFAgD,EAAQjB,OAAO+H,GAER9G,GAGDsd,EAAA7lB,UAAAimB,YAAR,WACE,IAAMT,EAAW9e,KAAKD,MAAM+e,UAAY,EAClCC,EAAW/e,KAAKD,MAAMgf,UAAY,IAClCpmB,EAA4B,MAApBqH,KAAKD,MAAMpH,MAAgB,EAAIqH,KAAKD,MAAMpH,MAExD,OAAIA,GAASmmB,EAAiB,EACrBnmB,GAASomB,EAAiB,IACvBvkB,KAAKslB,OAAQnnB,EAAQmmB,IAAaC,EAAWD,GAAa,MAE1EK,EA1KA,CAAwC9X,EAAA,gkBC7EjC,SAAS0Y,GAAoB/kB,GAClC,GAAsB,OAAlBA,EAAKoM,UACP,GACiC,iBAAxBpM,EAAKsM,gBACqB,IAAjCtM,EAAKoM,SAASE,eAEd,MAAM,IAAIhM,UAAU,kCAGtB,GAAIlD,OAAAmP,EAAA,EAAAnP,CAAc4C,EAAKglB,cACrB,MAAM,IAAI1kB,UAAU,kCAIxB,GAAyC,OAArClD,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKilB,UAAW,MAC7B,MAAM,IAAI3kB,UAAU,uBAGtB,OAAO4kB,GAAA,GACF9nB,OAAAiP,EAAA,EAAAjP,CAAqB4C,GAAK,CAC7BsE,KAAI,GACJ2gB,UAAWjlB,EAAKilB,UAChB7Y,SAAUhP,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKoM,SAAU,MAC1CE,eAAgBlP,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKsM,eAAgB,MACtD0Y,aAAc5nB,OAAAmP,EAAA,EAAAnP,CAAiB4C,EAAKglB,aAAc,QAItD,gBAAAja,GAAA,SAAAoa,mDAeA,OAfqCC,GAAAD,EAAApa,GAC5Boa,EAAA7mB,UAAAqH,iBAAP,WACE,IAAMkB,EAAUZ,SAASC,cAAc,OAWvC,OAVAW,EAAQT,UAAY,UAEc,OAA9BpB,KAAKD,MAAMuH,gBACbzF,EAAQR,MAAMsG,WAAa,OAAO3H,KAAKD,MAAMuH,eAAc,cAC3DzF,EAAQR,MAAMuG,eAAiB,UAC/B/F,EAAQR,MAAMwG,mBAAqB,UACE,OAA5B7H,KAAKD,MAAMigB,eACpBne,EAAQO,UAAYhK,OAAAmP,EAAA,EAAAnP,CAAa4H,KAAKD,MAAMigB,eAGvCne,GAEXse,EAfA,CAAqC9Y,EAAA,oNCpBrC,SAASgZ,GAAiBrlB,GACxB,IAAMsE,EAAOlH,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKsE,KAAM,MACnC,GAAY,MAARA,EAAc,MAAM,IAAIhE,UAAU,sBAEtC,OAAQgE,GACN,OACE,OAAO,IAAIghB,EAAYnZ,EAAwBnM,IACjD,OACE,OAAO,IAAI4L,GAAA,EAAYxO,OAAAwO,GAAA,EAAAxO,CAAwB4C,IACjD,OACA,OACA,OACA,OACE,OAAO,IAAIulB,EAAY9P,EAAwBzV,IACjD,OACA,OACA,QACA,QACE,OAAO,IAAIwlB,GAAW7B,GAAuB3jB,IAC/C,OACE,OAAO,IAAIylB,EAAMxQ,EAAkBjV,IACrC,OACE,OAAO,IAAI0lB,EAAK3Y,EAAiB/M,IACnC,QACE,OAAO,IAAI2lB,GAAQZ,GAAoB/kB,IACzC,QACE,OAAO,IAAI4lB,EAAM1X,EAAkBlO,IACrC,QACE,OAAO,IAAI6lB,EAAIrS,EAAgBxT,IACjC,QACE,OAAO,IAAI8lB,EAAK3R,EAAiBnU,IACnC,QACE,OAAO,IAAI8K,EAAA,EAAc1N,OAAA0N,EAAA,EAAA1N,CAA0B4C,IACrD,QACE,OAAO,IAAIwL,GAAA,EAAWpO,OAAAoO,GAAA,EAAApO,CAAuB4C,IAC/C,QACE,OAAO,IAAI0L,GAAA,EAAUtO,OAAAsO,GAAA,EAAAtO,CAAsB4C,IAC7C,QACE,OAAO,IAAIkP,EAAML,EAAkB7O,IACrC,QACE,OAAO,IAAI+lB,EAAW5Y,EAAuBnN,IAC/C,QACE,MAAM,IAAIM,UAAU,mBA4G1B,kBA0CE,SAAA0lB,EACE/d,EACAlD,EACAkhB,GAHF,IAAAjgB,EAAAhB,KApCQA,KAAAkhB,aAEJ,GAEIlhB,KAAAmhB,WAAgC,GAEhCnhB,KAAAohB,UAEJ,GAEaphB,KAAAC,kBAAoB,IAAI6E,GAAA,EAIxB9E,KAAAI,YAA4B,GAMrCJ,KAAAqhB,mBAA6D,SAAA3f,GACnEV,EAAKf,kBAAkB0B,KAAKD,IAQtB1B,KAAAshB,oBAA+D,SAAA5f,GAErEV,EAAKmgB,WAAangB,EAAKmgB,WAAWI,OAAO,SAAAllB,GAAM,OAAAA,IAAOqF,EAAE1G,KAAKqB,YACtD2E,EAAKkgB,aAAaxf,EAAE1G,KAAKqB,IAChC2E,EAAKwgB,eAAe9f,EAAE1G,KAAKqB,KAQ3B2D,KAAKyhB,aAAexe,EACpBjD,KAAK0hB,OApFF,SACL1mB,GAIE,IAAAqB,EAAArB,EAAAqB,GACApE,EAAA+C,EAAA/C,KACAkR,EAAAnO,EAAAmO,QACAwY,EAAA3mB,EAAA2mB,cACA3S,EAAAhU,EAAAgU,gBACA4S,EAAA5mB,EAAA4mB,WACAC,EAAA7mB,EAAA6mB,kBAGF,GAAU,MAANxlB,GAAcxC,MAAMC,SAASuC,IAC/B,MAAM,IAAIf,UAAU,eAEtB,GAAoB,iBAATrD,GAAqC,IAAhBA,EAAK2B,OACnC,MAAM,IAAI0B,UAAU,iBAEtB,GAAe,MAAX6N,GAAmBtP,MAAMC,SAASqP,IACpC,MAAM,IAAI7N,UAAU,qBAGtB,OAAOwmB,GAAA,CACLzlB,GAAIvC,SAASuC,GACbpE,KAAIA,EACJkR,QAASrP,SAASqP,GAClBwY,cAAevpB,OAAAmP,EAAA,EAAAnP,CAAiBupB,EAAe,MAC/C3S,gBAAiB5W,OAAAmP,EAAA,EAAAnP,CAAiB4W,EAAiB,MACnD4S,WAAYxpB,OAAAmP,EAAA,EAAAnP,CAAawpB,GACzBC,kBAAmBzpB,OAAAmP,EAAA,EAAAnP,CAAWypB,EAAmB,IAC9CzpB,OAAAmP,EAAA,EAAAnP,CAAiB4C,IAoDN+mB,CAA0BhiB,GAGxCC,KAAK2C,UAGLse,EAAQA,EAAMe,KAAK,SAAS9I,EAAGuD,GAC7B,OACe,MAAbvD,EAAEvZ,SACW,MAAb8c,EAAE9c,SACM,MAARuZ,EAAE7c,IACM,MAARogB,EAAEpgB,GAEK,EAGL6c,EAAEvZ,UAAY8c,EAAE9c,QAAgB,GAC1BuZ,EAAEvZ,SAAW8c,EAAE9c,SAAiB,EACjCuZ,EAAE7c,GAAKogB,EAAEpgB,GAAW,GAChB,KAIToH,QAAQ,SAAAU,GACZ,IACE,IAAM8d,EAAe5B,GAAiBlc,GAEtCnD,EAAKkgB,aAAae,EAAaliB,MAAM1D,IAAM4lB,EAC3CjhB,EAAKmgB,WAAWxc,KAAKsd,EAAaliB,MAAM1D,IAExC4lB,EAAazd,QAAQxD,EAAKqgB,oBAC1BY,EAAard,SAAS5D,EAAKsgB,qBAE3BtgB,EAAKygB,aAAa7gB,OAAOqhB,EAAa3hB,YACtC,MAAO4hB,GACPC,QAAQC,IAAI,gCAAiCF,EAAMG,YAKvDriB,KAAKsiB,iBA+RT,OAxRElqB,OAAAC,eAAW2oB,EAAA1nB,UAAA,WAAQ,KAAnB,eAAA0H,EAAAhB,KAEE,OAAOA,KAAKmhB,WACToB,IAAI,SAAAlmB,GAAM,OAAA2E,EAAKkgB,aAAa7kB,KAC5BklB,OAAO,SAAA/P,GAAK,OAAK,MAALA,qCAOVwP,EAAA1nB,UAAAkpB,eAAP,SAAsBvB,GAAtB,IAAAjgB,EAAAhB,KACQyiB,EAAUxB,EAAMsB,IAAI,SAAApe,GAAQ,OAAAA,EAAK9H,IAAM,OAAMklB,OAAO,SAAAllB,GAAM,OAAM,MAANA,IAGnC2D,KAAKmhB,WAAWI,OAC3C,SAAAllB,GAAM,OAAAomB,EAAQrd,QAAQ/I,GAAM,IAGnBoH,QAAQ,SAAApH,GACY,MAAzB2E,EAAKkgB,aAAa7kB,KACpB2E,EAAKkgB,aAAa7kB,GAAImH,gBACfxC,EAAKkgB,aAAa7kB,MAI7B2D,KAAKmhB,WAAasB,EAGlBxB,EAAMxd,QAAQ,SAAAU,GACZ,GAAIA,EAAK9H,GACP,GAAkC,MAA9B2E,EAAKkgB,aAAa/c,EAAK9H,IAEzB,IACE,IAAM4lB,EAAe5B,GAAiBlc,GAEtCnD,EAAKkgB,aAAae,EAAaliB,MAAM1D,IAAM4lB,EAE3CA,EAAazd,QAAQxD,EAAKqgB,oBAC1BY,EAAard,SAAS5D,EAAKsgB,qBAE3BtgB,EAAKygB,aAAa7gB,OAAOqhB,EAAa3hB,YACtC,MAAO4hB,GACPC,QAAQC,IAAI,gCAAiCF,EAAMG,cAIrD,IACErhB,EAAKkgB,aAAa/c,EAAK9H,IAAI0D,MArPvC,SAAqB/E,GACnB,IAAMsE,EAAOlH,OAAAmP,EAAA,EAAAnP,CAAW4C,EAAKsE,KAAM,MACnC,GAAY,MAARA,EAAc,MAAM,IAAIhE,UAAU,sBAEtC,OAAQgE,GACN,OACE,OAAO6H,EAAwBnM,GACjC,OACE,OAAO5C,OAAAwO,GAAA,EAAAxO,CAAwB4C,GACjC,OACA,OACA,OACA,OACE,OAAOyV,EAAwBzV,GACjC,OACA,OACA,QACA,QACE,OAAO2jB,GAAuB3jB,GAChC,OACE,OAAOiV,EAAkBjV,GAC3B,OACE,OAAO+M,EAAiB/M,GAC1B,QACE,OAAO+kB,GAAoB/kB,GAC7B,QACE,OAAOkO,EAAkBlO,GAC3B,QACE,OAAOwT,EAAgBxT,GACzB,QACE,OAAOmU,EAAiBnU,GAC1B,QACE,OAAO5C,OAAA0N,EAAA,EAAA1N,CAA0B4C,GACnC,QACE,OAAO5C,OAAAoO,GAAA,EAAApO,CAAuB4C,GAChC,QACE,OAAO5C,OAAAsO,GAAA,EAAAtO,CAAsB4C,GAC/B,QACE,OAAO6O,EAAkB7O,GAC3B,QACE,OAAOmN,EAAuBnN,GAChC,QACE,MAAM,IAAIM,UAAU,sBA2MqBonB,CAAYve,GAC/C,MAAO+d,GACPC,QAAQC,IAAI,6BAA8BF,EAAMG,YAOxDriB,KAAKsiB,kBAOPlqB,OAAAC,eAAW2oB,EAAA1nB,UAAA,QAAK,KAAhB,WACE,OAAOwoB,GAAA,GAAK9hB,KAAK0hB,aASnB,SAAiBlf,GACf,IAAMC,EAAYzC,KAAKD,MAEvBC,KAAK0hB,OAASlf,EAKdxC,KAAK2C,OAAOF,oCAOPue,EAAA1nB,UAAAqJ,OAAP,SAAcF,QAAA,IAAAA,MAAA,MACRA,GACEA,EAAUkf,gBAAkB3hB,KAAKD,MAAM4hB,gBACzC3hB,KAAKyhB,aAAapgB,MAAMshB,gBACO,OAA7B3iB,KAAKD,MAAM4hB,cACP,OAAO3hB,KAAKD,MAAM4hB,cAAa,IAC/B,MAEJlf,EAAUuM,kBAAoBhP,KAAKD,MAAMiP,kBAC3ChP,KAAKyhB,aAAapgB,MAAM2N,gBAAkBhP,KAAKD,MAAMiP,iBAEnDhP,KAAK8C,YAAYL,EAAWzC,KAAKD,QACnCC,KAAKa,cAAcb,KAAKD,MAAM3E,MAAO4E,KAAKD,MAAM1E,UAGlD2E,KAAKyhB,aAAapgB,MAAMshB,gBACO,OAA7B3iB,KAAKD,MAAM4hB,cACP,OAAO3hB,KAAKD,MAAM4hB,cAAa,IAC/B,KAEN3hB,KAAKyhB,aAAapgB,MAAM2N,gBAAkBhP,KAAKD,MAAMiP,gBACrDhP,KAAKa,cAAcb,KAAKD,MAAM3E,MAAO4E,KAAKD,MAAM1E,UAW7C2lB,EAAA1nB,UAAAwJ,YAAP,SAAmBuB,EAAgBC,GACjC,OACED,EAASjJ,QAAUkJ,EAAQlJ,OAASiJ,EAAShJ,SAAWiJ,EAAQjJ,QAS7D2lB,EAAA1nB,UAAAuH,cAAP,SAAqBzF,EAAeC,GAClC2E,KAAKyhB,aAAapgB,MAAMjG,MAAWA,EAAK,KACxC4E,KAAKyhB,aAAapgB,MAAMhG,OAAYA,EAAM,MAQrC2lB,EAAA1nB,UAAAiL,OAAP,SAAcnJ,EAAeC,GAC3B2E,KAAKD,MAAQ+hB,GAAA,GACR9hB,KAAKD,MAAK,CACb3E,MAAKA,EACLC,OAAMA,KAOH2lB,EAAA1nB,UAAAkK,OAAP,WACExD,KAAKI,YAAYqD,QAAQ,SAAAzL,GAAK,OAAAA,EAAE2L,YAChC3D,KAAK4iB,SAASnf,QAAQ,SAAA/B,GAAK,OAAAA,EAAE8B,WAC7BxD,KAAKkhB,aAAe,GACpBlhB,KAAKmhB,WAAa,GAElBnhB,KAAKwhB,iBAELxhB,KAAKyhB,aAAarf,UAAY,IAMxB4e,EAAA1nB,UAAAgpB,eAAR,eAAAthB,EAAAhB,KAEEA,KAAKwhB,iBAELxhB,KAAK4iB,SAASnf,QAAQ,SAAAU,GACpB,GAA4B,OAAxBA,EAAKpE,MAAMH,SAAmB,CAChC,IAAMijB,EAAS7hB,EAAKkgB,aAAa/c,EAAKpE,MAAMH,UACtCkjB,EAAQ9hB,EAAKkgB,aAAa/c,EAAKpE,MAAM1D,IACvCwmB,GAAUC,GAAO9hB,EAAK+hB,gBAAgBF,EAAQC,OAShD9B,EAAA1nB,UAAAkoB,eAAR,SAAuBwB,GACrB,GAAc,MAAVA,EACF,IAAK,IAAI/pB,KAAO+G,KAAKohB,UAAW,CAC9B,IAAM6B,EAAMhqB,EAAIqV,MAAM,KAChB1O,EAAWsjB,OAAOppB,SAASmpB,EAAI,IAC/BE,EAAUD,OAAOppB,SAASmpB,EAAI,IAEhCD,IAAWpjB,GAAYojB,IAAWG,IACpCnjB,KAAKohB,UAAUnoB,GAAKuK,gBACbxD,KAAKohB,UAAUnoB,SAI1B,IAAK,IAAIA,KAAO+G,KAAKohB,UACnBphB,KAAKohB,UAAUnoB,GAAKuK,gBACbxD,KAAKohB,UAAUnoB,IAWpB+nB,EAAA1nB,UAAA8pB,gBAAR,SAAwBxjB,EAAkBujB,GACxC,IAAME,EAAgBzjB,EAAQ,IAAIujB,EAClC,OAAOnjB,KAAKohB,UAAUiC,IAAe,MAS/BrC,EAAA1nB,UAAAypB,gBAAR,SACEO,EACAR,GAEA,IAAMO,EAAgBC,EAAOvjB,MAAM1D,GAAE,IAAIymB,EAAM/iB,MAAM1D,GACnB,MAA9B2D,KAAKohB,UAAUiC,IACjBrjB,KAAKohB,UAAUiC,GAAY7f,SAI7B,IAAM8L,EAASgU,EAAOvjB,MAAM9E,EAAIqoB,EAAOhjB,WAAWijB,YAAc,EAC1DhU,EACJ+T,EAAOvjB,MAAM7E,GACZooB,EAAOhjB,WAAWkjB,aAAeF,EAAO9iB,gBAAgBgjB,cACvD,EACE/T,EAAOqT,EAAM/iB,MAAM9E,EAAI6nB,EAAMxiB,WAAWijB,YAAc,EACtD7T,EACJoT,EAAM/iB,MAAM7E,GACX4nB,EAAMxiB,WAAWkjB,aAAeV,EAAMtiB,gBAAgBgjB,cAAgB,EAEnExT,EAAO,IAAI8Q,EACf3R,EAAiB,CACf9S,GAAI,EACJiD,KAAI,GACJgQ,OAAMA,EACNC,OAAMA,EACNE,KAAIA,EACJC,KAAIA,EACJtU,MAAO,EACPC,OAAQ,EACRsU,UAAW3P,KAAKD,MAAM8hB,kBACtBzZ,MAAO,aAUX,OANApI,KAAKohB,UAAUiC,GAAcrT,EAG7BA,EAAK1P,WAAWe,MAAMC,OAAS,IAC/BtB,KAAKyhB,aAAa7gB,OAAOoP,EAAK1P,YAEvB0P,GAOFgR,EAAA1nB,UAAAkL,QAAP,SAAeC,GAMb,IAAMf,EAAa1D,KAAKC,kBAAkByE,GAAGD,GAG7C,OAFAzE,KAAKI,YAAYuE,KAAKjB,GAEfA,GAEXsd,EAvXA,GC3KAyC,GAAA,WAUE,SAAAC,EAAmBC,GARX3jB,KAAA4jB,YAA2B,CAAEC,OAAQ,cACrC7jB,KAAA8jB,QAA2B,UAGlB9jB,KAAA+jB,yBAA2B,IAAIjf,GAAA,EAE/B9E,KAAAI,YAA4B,GAG3CJ,KAAK2jB,cAAgBA,EAqDzB,OA9CEvrB,OAAAC,eAAWqrB,EAAApqB,UAAA,SAAM,KASjB,WACE,OAAO0G,KAAK8jB,aAVd,SAAkBE,GAChBhkB,KAAK8jB,QAAUE,EACfhkB,KAAK+jB,yBAAyBpiB,KAAKqiB,oCAc9BN,EAAApqB,UAAA2qB,KAAP,eAAAjjB,EAAAhB,KACEA,KAAK4jB,YAAc5jB,KAAK2jB,cAAc,WACpC3iB,EAAKgjB,OAAS,aAEhBhkB,KAAKgkB,OAAS,WAMTN,EAAApqB,UAAAuqB,OAAP,WACE7jB,KAAK4jB,YAAYC,SACjB7jB,KAAKgkB,OAAS,aAOTN,EAAApqB,UAAA4qB,eAAP,SAAsBzf,GAMpB,IAAMf,EAAa1D,KAAK+jB,yBAAyBrf,GAAGD,GAGpD,OAFAzE,KAAKI,YAAYuE,KAAKjB,GAEfA,GAEXggB,EAhEA,GAsGA,2BAAAS,IACUnkB,KAAAokB,MAA6C,GAuDvD,OA7CSD,EAAA7qB,UAAA+qB,IAAP,SACEhB,EACAM,EACAhT,QAAA,IAAAA,MAAA,GAEI3Q,KAAKokB,MAAMf,IAAiD,YAAlCrjB,KAAKokB,MAAMf,GAAYW,QACnDhkB,KAAKokB,MAAMf,GAAYQ,SAGzB,IAAMS,EACJ3T,EAAS,EA/Cf,SAAuB4T,EAAiB5T,GACtC,OAAO,IAAI8S,GAAU,WACnB,IAAIe,EAAqB,KAYzB,OAVAD,EAAKL,eAAe,SAAAF,GACH,aAAXA,IACFQ,EAAM/mB,OAAO0I,WAAW,WACtBoe,EAAKN,QACJtT,MAIP4T,EAAKN,OAEE,CACLJ,OAAQ,WACFW,GAAKC,aAAaD,GACtBD,EAAKV,aA+BHa,CAAc,IAAIjB,GAAUE,GAAgBhT,GAC5C,IAAI8S,GAAUE,GAIpB,OAFA3jB,KAAKokB,MAAMf,GAAciB,EAElBtkB,KAAKokB,MAAMf,IAQbc,EAAA7qB,UAAA2qB,KAAP,SAAYZ,IAERrjB,KAAKokB,MAAMf,IACwB,YAAlCrjB,KAAKokB,MAAMf,GAAYW,QACY,cAAlChkB,KAAKokB,MAAMf,GAAYW,QACW,aAAlChkB,KAAKokB,MAAMf,GAAYW,QAEzBhkB,KAAKokB,MAAMf,GAAYY,QASpBE,EAAA7qB,UAAAuqB,OAAP,SAAcR,GACRrjB,KAAKokB,MAAMf,IAAiD,YAAlCrjB,KAAKokB,MAAMf,GAAYW,QACnDhkB,KAAKokB,MAAMf,GAAYQ,UAG7BM,EAxDA,GCtGC1mB,OAAeujB,cAAgB2D,GAI/BlnB,OAAe0mB,iBAAmBS","file":"vc.main.min.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 9);\n","import {\n UnknownObject,\n Position,\n Size,\n WithAgentProps,\n WithModuleProps,\n LinkedVisualConsoleProps,\n LinkedVisualConsolePropsStatus\n} from \"../types\";\n\n/**\n * Return a number or a default value from a raw value.\n * @param value Raw value from which we will try to extract a valid number.\n * @param defaultValue Default value to use if we cannot extract a valid number.\n * @return A valid number or the default value.\n */\nexport function parseIntOr(value: unknown, defaultValue: T): number | T {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\" && value.length > 0 && !isNaN(parseInt(value)))\n return parseInt(value);\n else return defaultValue;\n}\n\n/**\n * Return a number or a default value from a raw value.\n * @param value Raw value from which we will try to extract a valid number.\n * @param defaultValue Default value to use if we cannot extract a valid number.\n * @return A valid number or the default value.\n */\nexport function parseFloatOr(value: unknown, defaultValue: T): number | T {\n if (typeof value === \"number\") return value;\n if (\n typeof value === \"string\" &&\n value.length > 0 &&\n !isNaN(parseFloat(value))\n )\n return parseFloat(value);\n else return defaultValue;\n}\n\n/**\n * Check if a string exists and it's not empty.\n * @param value Value to check.\n * @return The check result.\n */\nexport function stringIsEmpty(value?: string | null): boolean {\n return value == null || value.length === 0;\n}\n\n/**\n * Return a not empty string or a default value from a raw value.\n * @param value Raw value from which we will try to extract a non empty string.\n * @param defaultValue Default value to use if we cannot extract a non empty string.\n * @return A non empty string or the default value.\n */\nexport function notEmptyStringOr(\n value: unknown,\n defaultValue: T\n): string | T {\n return typeof value === \"string\" && value.length > 0 ? value : defaultValue;\n}\n\n/**\n * Return a boolean from a raw value.\n * @param value Raw value from which we will try to extract the boolean.\n * @return A valid boolean value. false by default.\n */\nexport function parseBoolean(value: unknown): boolean {\n if (typeof value === \"boolean\") return value;\n else if (typeof value === \"number\") return value > 0;\n else if (typeof value === \"string\") return value === \"1\" || value === \"true\";\n else return false;\n}\n\n/**\n * Pad the current string with another string (multiple times, if needed)\n * until the resulting string reaches the given length.\n * The padding is applied from the start (left) of the current string.\n * @param value Text that needs to be padded.\n * @param length Length of the returned text.\n * @param pad Text to add.\n * @return Padded text.\n */\nexport function leftPad(\n value: string | number,\n length: number,\n pad: string | number = \" \"\n): string {\n if (typeof value === \"number\") value = `${value}`;\n if (typeof pad === \"number\") pad = `${pad}`;\n\n const diffLength = length - value.length;\n if (diffLength === 0) return value;\n if (diffLength < 0) return value.substr(Math.abs(diffLength));\n\n if (diffLength === pad.length) return `${pad}${value}`;\n if (diffLength < pad.length) return `${pad.substring(0, diffLength)}${value}`;\n\n const repeatTimes = Math.floor(diffLength / pad.length);\n const restLength = diffLength - pad.length * repeatTimes;\n\n let newPad = \"\";\n for (let i = 0; i < repeatTimes; i++) newPad += pad;\n\n if (restLength === 0) return `${newPad}${value}`;\n return `${newPad}${pad.substring(0, restLength)}${value}`;\n}\n\n/* Decoders */\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the position.\n */\nexport function positionPropsDecoder(data: UnknownObject): Position {\n return {\n x: parseIntOr(data.x, 0),\n y: parseIntOr(data.y, 0)\n };\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the size.\n * @throws Will throw a TypeError if the width and height are not valid numbers.\n */\nexport function sizePropsDecoder(data: UnknownObject): Size | never {\n if (\n data.width == null ||\n isNaN(parseInt(data.width)) ||\n data.height == null ||\n isNaN(parseInt(data.height))\n ) {\n throw new TypeError(\"invalid size.\");\n }\n\n return {\n width: parseInt(data.width),\n height: parseInt(data.height)\n };\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the agent properties.\n */\nexport function agentPropsDecoder(data: UnknownObject): WithAgentProps {\n const agentProps: WithAgentProps = {\n agentId: parseIntOr(data.agent, null),\n agentName: notEmptyStringOr(data.agentName, null),\n agentAlias: notEmptyStringOr(data.agentAlias, null),\n agentDescription: notEmptyStringOr(data.agentDescription, null),\n agentAddress: notEmptyStringOr(data.agentAddress, null)\n };\n\n return data.metaconsoleId != null\n ? {\n metaconsoleId: data.metaconsoleId,\n ...agentProps // Object spread: http://es6-features.org/#SpreadOperator\n }\n : agentProps;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the module and agent properties.\n */\nexport function modulePropsDecoder(data: UnknownObject): WithModuleProps {\n return {\n moduleId: parseIntOr(data.moduleId, null),\n moduleName: notEmptyStringOr(data.moduleName, null),\n moduleDescription: notEmptyStringOr(data.moduleDescription, null),\n ...agentPropsDecoder(data) // Object spread: http://es6-features.org/#SpreadOperator\n };\n}\n\n/**\n * Build a valid typed object from a raw object.\n * @param data Raw object.\n * @return An object representing the linked visual console properties.\n * @throws Will throw a TypeError if the status calculation properties are invalid.\n */\nexport function linkedVCPropsDecoder(\n data: UnknownObject\n): LinkedVisualConsoleProps | never {\n // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation\n const {\n metaconsoleId,\n linkedLayoutId: id,\n linkedLayoutAgentId: agentId\n } = data;\n\n let linkedLayoutStatusProps: LinkedVisualConsolePropsStatus = {\n linkedLayoutStatusType: \"default\"\n };\n switch (data.linkedLayoutStatusType) {\n case \"weight\": {\n const weight = parseIntOr(data.linkedLayoutStatusTypeWeight, null);\n if (weight == null)\n throw new TypeError(\"invalid status calculation properties.\");\n\n if (data.linkedLayoutStatusTypeWeight)\n linkedLayoutStatusProps = {\n linkedLayoutStatusType: \"weight\",\n linkedLayoutStatusTypeWeight: weight\n };\n break;\n }\n case \"service\": {\n const warningThreshold = parseIntOr(\n data.linkedLayoutStatusTypeWarningThreshold,\n null\n );\n const criticalThreshold = parseIntOr(\n data.linkedLayoutStatusTypeCriticalThreshold,\n null\n );\n if (warningThreshold == null || criticalThreshold == null) {\n throw new TypeError(\"invalid status calculation properties.\");\n }\n\n linkedLayoutStatusProps = {\n linkedLayoutStatusType: \"service\",\n linkedLayoutStatusTypeWarningThreshold: warningThreshold,\n linkedLayoutStatusTypeCriticalThreshold: criticalThreshold\n };\n break;\n }\n }\n\n const linkedLayoutBaseProps = {\n linkedLayoutId: parseIntOr(id, null),\n linkedLayoutAgentId: parseIntOr(agentId, null),\n ...linkedLayoutStatusProps // Object spread: http://es6-features.org/#SpreadOperator\n };\n\n return metaconsoleId != null\n ? {\n metaconsoleId,\n ...linkedLayoutBaseProps // Object spread: http://es6-features.org/#SpreadOperator\n }\n : linkedLayoutBaseProps;\n}\n\n/**\n * To get a CSS rule with the most used prefixes.\n * @param ruleName Name of the CSS rule.\n * @param ruleValue Value of the CSS rule.\n * @return An array of rules with the prefixes applied.\n */\nexport function prefixedCssRules(\n ruleName: string,\n ruleValue: string\n): string[] {\n const rule = `${ruleName}: ${ruleValue};`;\n return [\n `-webkit-${rule}`,\n `-moz-${rule}`,\n `-ms-${rule}`,\n `-o-${rule}`,\n `${rule}`\n ];\n}\n\n/**\n * Decode a base64 string.\n * @param input Data encoded using base64.\n * @return Decoded data.\n */\nexport function decodeBase64(input: string): string {\n return decodeURIComponent(escape(window.atob(input)));\n}\n\n/**\n * Generate a date representation with the format 'd/m/Y'.\n * @param initialDate Date to be used instead of a generated one.\n * @param locale Locale to use if localization is required and available.\n * @example 24/02/2020.\n * @return Date representation.\n */\nexport function humanDate(date: Date, locale: string | null = null): string {\n if (locale && Intl && Intl.DateTimeFormat) {\n // Format using the user locale.\n const options: Intl.DateTimeFormatOptions = {\n day: \"2-digit\",\n month: \"2-digit\",\n year: \"numeric\"\n };\n return Intl.DateTimeFormat(locale, options).format(date);\n } else {\n // Use getDate, getDay returns the week day.\n const day = leftPad(date.getDate(), 2, 0);\n // The getMonth function returns the month starting by 0.\n const month = leftPad(date.getMonth() + 1, 2, 0);\n const year = leftPad(date.getFullYear(), 4, 0);\n\n // Format: 'd/m/Y'.\n return `${day}/${month}/${year}`;\n }\n}\n\n/**\n * Generate a time representation with the format 'hh:mm:ss'.\n * @param initialDate Date to be used instead of a generated one.\n * @example 01:34:09.\n * @return Time representation.\n */\nexport function humanTime(date: Date): string {\n const hours = leftPad(date.getHours(), 2, 0);\n const minutes = leftPad(date.getMinutes(), 2, 0);\n const seconds = leftPad(date.getSeconds(), 2, 0);\n\n return `${hours}:${minutes}:${seconds}`;\n}\n\ninterface Macro {\n macro: string | RegExp;\n value: string;\n}\n/**\n * Replace the macros of a text.\n * @param macros List of macros and their replacements.\n * @param text Text in which we need to replace the macros.\n */\nexport function replaceMacros(macros: Macro[], text: string): string {\n return macros.reduce(\n (acc, { macro, value }) => acc.replace(macro, value),\n text\n );\n}\n","import { Position, Size, UnknownObject, WithModuleProps } from \"./types\";\nimport {\n sizePropsDecoder,\n positionPropsDecoder,\n parseIntOr,\n parseBoolean,\n notEmptyStringOr,\n replaceMacros,\n humanDate,\n humanTime\n} from \"./lib\";\nimport TypedEvent, { Listener, Disposable } from \"./TypedEvent\";\n\n// Enum: https://www.typescriptlang.org/docs/handbook/enums.html.\nexport const enum ItemType {\n STATIC_GRAPH = 0,\n MODULE_GRAPH = 1,\n SIMPLE_VALUE = 2,\n PERCENTILE_BAR = 3,\n LABEL = 4,\n ICON = 5,\n SIMPLE_VALUE_MAX = 6,\n SIMPLE_VALUE_MIN = 7,\n SIMPLE_VALUE_AVG = 8,\n PERCENTILE_BUBBLE = 9,\n SERVICE = 10,\n GROUP_ITEM = 11,\n BOX_ITEM = 12,\n LINE_ITEM = 13,\n AUTO_SLA_GRAPH = 14,\n CIRCULAR_PROGRESS_BAR = 15,\n CIRCULAR_INTERIOR_PROGRESS_BAR = 16,\n DONUT_GRAPH = 17,\n BARS_GRAPH = 18,\n CLOCK = 19,\n COLOR_CLOUD = 20\n}\n\n// Base item properties. This interface should be extended by the item implementations.\nexport interface ItemProps extends Position, Size {\n readonly id: number;\n readonly type: ItemType;\n label: string | null;\n labelPosition: \"up\" | \"right\" | \"down\" | \"left\";\n isLinkEnabled: boolean;\n link: string | null;\n isOnTop: boolean;\n parentId: number | null;\n aclGroupId: number | null;\n}\n\n// FIXME: Fix type compatibility.\nexport interface ItemClickEvent {\n // data: Props;\n data: UnknownObject;\n nativeEvent: Event;\n}\n\n// FIXME: Fix type compatibility.\nexport interface ItemRemoveEvent {\n // data: Props;\n data: UnknownObject;\n}\n\n/**\n * Extract a valid enum value from a raw label positi9on value.\n * @param labelPosition Raw value.\n */\nconst parseLabelPosition = (\n labelPosition: unknown\n): ItemProps[\"labelPosition\"] => {\n switch (labelPosition) {\n case \"up\":\n case \"right\":\n case \"down\":\n case \"left\":\n return labelPosition;\n default:\n return \"down\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the item props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function itemBasePropsDecoder(data: UnknownObject): ItemProps | never {\n if (data.id == null || isNaN(parseInt(data.id))) {\n throw new TypeError(\"invalid id.\");\n }\n if (data.type == null || isNaN(parseInt(data.type))) {\n throw new TypeError(\"invalid type.\");\n }\n\n return {\n id: parseInt(data.id),\n type: parseInt(data.type),\n label: notEmptyStringOr(data.label, null),\n labelPosition: parseLabelPosition(data.labelPosition),\n isLinkEnabled: parseBoolean(data.isLinkEnabled),\n link: notEmptyStringOr(data.link, null),\n isOnTop: parseBoolean(data.isOnTop),\n parentId: parseIntOr(data.parentId, null),\n aclGroupId: parseIntOr(data.aclGroupId, null),\n ...sizePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...positionPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\n/**\n * Base class of the visual console items. Should be extended to use its capabilities.\n */\nabstract class VisualConsoleItem {\n // Properties of the item.\n private itemProps: Props;\n // Reference to the DOM element which will contain the item.\n public elementRef: HTMLElement;\n public readonly labelElementRef: HTMLElement;\n // Reference to the DOM element which will contain the view of the item which extends this class.\n protected readonly childElementRef: HTMLElement;\n // Event manager for click events.\n private readonly clickEventManager = new TypedEvent>();\n // Event manager for remove events.\n private readonly removeEventManager = new TypedEvent<\n ItemRemoveEvent\n >();\n // List of references to clean the event listeners.\n private readonly disposables: Disposable[] = [];\n\n /**\n * To create a new element which will be inside the item box.\n * @return Item.\n */\n protected abstract createDomElement(): HTMLElement;\n\n public constructor(props: Props) {\n this.itemProps = props;\n\n /*\n * Get a HTMLElement which represents the container box\n * of the Visual Console item. This element will manage\n * all the common things like click events, show a border\n * when hovered, etc.\n */\n this.elementRef = this.createContainerDomElement();\n this.labelElementRef = this.createLabelDomElement();\n\n /*\n * Get a HTMLElement which represents the custom view\n * of the Visual Console item. This element will be\n * different depending on the item implementation.\n */\n this.childElementRef = this.createDomElement();\n\n // Insert the elements into the container.\n this.elementRef.append(this.childElementRef, this.labelElementRef);\n\n // Resize element.\n this.resizeElement(props.width, props.height);\n // Set label position.\n this.changeLabelPosition(props.labelPosition);\n }\n\n /**\n * To create a new box for the visual console item.\n * @return Item box.\n */\n private createContainerDomElement(): HTMLElement {\n let box;\n if (this.props.isLinkEnabled) {\n box = document.createElement(\"a\");\n box as HTMLAnchorElement;\n if (this.props.link) box.href = this.props.link;\n } else {\n box = document.createElement(\"div\");\n box as HTMLDivElement;\n }\n\n box.className = \"visual-console-item\";\n box.style.zIndex = this.props.isOnTop ? \"2\" : \"1\";\n box.style.left = `${this.props.x}px`;\n box.style.top = `${this.props.y}px`;\n box.onclick = e =>\n this.clickEventManager.emit({ data: this.props, nativeEvent: e });\n\n return box;\n }\n\n /**\n * To create a new label for the visual console item.\n * @return Item label.\n */\n protected createLabelDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"visual-console-item-label\";\n // Add the label if it exists.\n const label = this.getLabelWithMacrosReplaced();\n if (label.length > 0) {\n // Ugly table we need to use to replicate the legacy style.\n const table = document.createElement(\"table\");\n const row = document.createElement(\"tr\");\n const emptyRow1 = document.createElement(\"tr\");\n const emptyRow2 = document.createElement(\"tr\");\n const cell = document.createElement(\"td\");\n\n cell.innerHTML = label;\n row.append(cell);\n table.append(emptyRow1, row, emptyRow2);\n table.style.textAlign = \"center\";\n\n // Change the table size depending on its position.\n switch (this.props.labelPosition) {\n case \"up\":\n case \"down\":\n if (this.props.width > 0) {\n table.style.width = `${this.props.width}px`;\n table.style.height = null;\n }\n break;\n case \"left\":\n case \"right\":\n if (this.props.height > 0) {\n table.style.width = null;\n table.style.height = `${this.props.height}px`;\n }\n break;\n }\n\n // element.innerHTML = this.props.label;\n element.append(table);\n }\n\n return element;\n }\n\n /**\n * Return the label stored into the props with some macros replaced.\n */\n protected getLabelWithMacrosReplaced(): string {\n // We assert that the props may have some needed properties.\n const props = this.props as Partial;\n\n return replaceMacros(\n [\n {\n macro: \"_date_\",\n value: humanDate(new Date())\n },\n {\n macro: \"_time_\",\n value: humanTime(new Date())\n },\n {\n macro: \"_agent_\",\n value: props.agentAlias != null ? props.agentAlias : \"\"\n },\n {\n macro: \"_agentdescription_\",\n value: props.agentDescription != null ? props.agentDescription : \"\"\n },\n {\n macro: \"_address_\",\n value: props.agentAddress != null ? props.agentAddress : \"\"\n },\n {\n macro: \"_module_\",\n value: props.moduleName != null ? props.moduleName : \"\"\n },\n {\n macro: \"_moduledescription_\",\n value: props.moduleDescription != null ? props.moduleDescription : \"\"\n }\n ],\n this.props.label || \"\"\n );\n }\n\n /**\n * To update the content element.\n * @return Item.\n */\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.createDomElement().innerHTML;\n }\n\n /**\n * Public accessor of the `props` property.\n * @return Properties.\n */\n public get props(): Props {\n return { ...this.itemProps }; // Return a copy.\n }\n\n /**\n * Public setter of the `props` property.\n * If the new props are different enough than the\n * stored props, a render would be fired.\n * @param newProps\n */\n public set props(newProps: Props) {\n const prevProps = this.props;\n // Update the internal props.\n this.itemProps = newProps;\n\n // From this point, things which rely on this.props can access to the changes.\n\n // Check if we should re-render.\n if (this.shouldBeUpdated(prevProps, newProps)) this.render(prevProps);\n }\n\n /**\n * To compare the previous and the new props and returns a boolean value\n * in case the difference is meaningfull enough to perform DOM changes.\n *\n * Here, the only comparision is done by reference.\n *\n * Override this function to perform a different comparision depending on the item needs.\n *\n * @param prevProps\n * @param newProps\n * @return Whether the difference is meaningful enough to perform DOM changes or not.\n */\n protected shouldBeUpdated(prevProps: Props, newProps: Props): boolean {\n return prevProps !== newProps;\n }\n\n /**\n * To recreate or update the HTMLElement which represents the item into the DOM.\n * @param prevProps If exists it will be used to only perform DOM updates instead of a full replace.\n */\n public render(prevProps: Props | null = null): void {\n this.updateDomElement(this.childElementRef);\n\n // Move box.\n if (!prevProps || this.positionChanged(prevProps, this.props)) {\n this.moveElement(this.props.x, this.props.y);\n }\n // Resize box.\n if (!prevProps || this.sizeChanged(prevProps, this.props)) {\n this.resizeElement(this.props.width, this.props.height);\n }\n // Change label.\n const oldLabelHtml = this.labelElementRef.innerHTML;\n const newLabelHtml = this.createLabelDomElement().innerHTML;\n if (oldLabelHtml !== newLabelHtml) {\n this.labelElementRef.innerHTML = newLabelHtml;\n }\n // Change label position.\n if (!prevProps || prevProps.labelPosition !== this.props.labelPosition) {\n this.changeLabelPosition(this.props.labelPosition);\n }\n // Change link.\n if (\n prevProps &&\n (prevProps.isLinkEnabled !== this.props.isLinkEnabled ||\n (this.props.isLinkEnabled && prevProps.link !== this.props.link))\n ) {\n const container = this.createContainerDomElement();\n // Add the children of the old element.\n container.innerHTML = this.elementRef.innerHTML;\n // Copy the attributes.\n const attrs = this.elementRef.attributes;\n for (let i = 0; i < attrs.length; i++) {\n if (attrs[i].nodeName !== \"id\") {\n container.setAttributeNode(attrs[i]);\n }\n }\n // Replace the reference.\n if (this.elementRef.parentNode !== null) {\n this.elementRef.parentNode.replaceChild(container, this.elementRef);\n }\n\n // Changed the reference to the main element. It's ugly, but needed.\n this.elementRef = container;\n }\n }\n\n /**\n * To remove the event listeners and the elements from the DOM.\n */\n public remove(): void {\n // Call the remove event.\n this.removeEventManager.emit({ data: this.props });\n // Event listeners.\n this.disposables.forEach(disposable => {\n try {\n disposable.dispose();\n } catch (ignored) {} // eslint-disable-line no-empty\n });\n // VisualConsoleItem DOM element.\n this.elementRef.remove();\n }\n\n /**\n * Compare the previous and the new position and return\n * a boolean value in case the position changed.\n * @param prevPosition\n * @param newPosition\n * @return Whether the position changed or not.\n */\n protected positionChanged(\n prevPosition: Position,\n newPosition: Position\n ): boolean {\n return prevPosition.x !== newPosition.x || prevPosition.y !== newPosition.y;\n }\n\n /**\n * Move the label around the item content.\n * @param position Label position.\n */\n protected changeLabelPosition(position: Props[\"labelPosition\"]): void {\n switch (position) {\n case \"up\":\n this.elementRef.style.flexDirection = \"column-reverse\";\n break;\n case \"left\":\n this.elementRef.style.flexDirection = \"row-reverse\";\n break;\n case \"right\":\n this.elementRef.style.flexDirection = \"row\";\n break;\n case \"down\":\n default:\n this.elementRef.style.flexDirection = \"column\";\n break;\n }\n\n // Ugly table to show the label as its legacy counterpart.\n const tables = this.labelElementRef.getElementsByTagName(\"table\");\n const table = tables.length > 0 ? tables.item(0) : null;\n // Change the table size depending on its position.\n if (table) {\n switch (this.props.labelPosition) {\n case \"up\":\n case \"down\":\n if (this.props.width > 0) {\n table.style.width = `${this.props.width}px`;\n table.style.height = null;\n }\n break;\n case \"left\":\n case \"right\":\n if (this.props.height > 0) {\n table.style.width = null;\n table.style.height = `${this.props.height}px`;\n }\n break;\n }\n }\n }\n\n /**\n * Move the DOM container.\n * @param x Horizontal axis position.\n * @param y Vertical axis position.\n */\n protected moveElement(x: number, y: number): void {\n this.elementRef.style.left = `${x}px`;\n this.elementRef.style.top = `${y}px`;\n }\n\n /**\n * Update the position into the properties and move the DOM container.\n * @param x Horizontal axis position.\n * @param y Vertical axis position.\n */\n public move(x: number, y: number): void {\n this.moveElement(x, y);\n this.itemProps = {\n ...this.props, // Object spread: http://es6-features.org/#SpreadOperator\n x,\n y\n };\n }\n\n /**\n * Compare the previous and the new size and return\n * a boolean value in case the size changed.\n * @param prevSize\n * @param newSize\n * @return Whether the size changed or not.\n */\n protected sizeChanged(prevSize: Size, newSize: Size): boolean {\n return (\n prevSize.width !== newSize.width || prevSize.height !== newSize.height\n );\n }\n\n /**\n * Resize the DOM content container.\n * @param width\n * @param height\n */\n protected resizeElement(width: number, height: number): void {\n // The most valuable size is the content size.\n this.childElementRef.style.width = width > 0 ? `${width}px` : null;\n this.childElementRef.style.height = height > 0 ? `${height}px` : null;\n }\n\n /**\n * Update the size into the properties and resize the DOM container.\n * @param width\n * @param height\n */\n public resize(width: number, height: number): void {\n this.resizeElement(width, height);\n this.itemProps = {\n ...this.props, // Object spread: http://es6-features.org/#SpreadOperator\n width,\n height\n };\n }\n\n /**\n * To add an event handler to the click of the linked visual console elements.\n * @param listener Function which is going to be executed when a linked console is clicked.\n */\n public onClick(listener: Listener>): Disposable {\n /*\n * The '.on' function returns a function which will clean the event\n * listener when executed. We store all the 'dispose' functions to\n * call them when the item should be cleared.\n */\n const disposable = this.clickEventManager.on(listener);\n this.disposables.push(disposable);\n\n return disposable;\n }\n\n /**\n * To add an event handler to the removal of the item.\n * @param listener Function which is going to be executed when a item is removed.\n */\n public onRemove(listener: Listener>): Disposable {\n /*\n * The '.on' function returns a function which will clean the event\n * listener when executed. We store all the 'dispose' functions to\n * call them when the item should be cleared.\n */\n const disposable = this.removeEventManager.on(listener);\n this.disposables.push(disposable);\n\n return disposable;\n }\n}\n\nexport default VisualConsoleItem;\n","export interface Listener {\n (event: T): void;\n}\n\nexport interface Disposable {\n dispose: () => void;\n}\n\n/** passes through events as they happen. You will not get events from before you start listening */\nexport default class TypedEvent {\n private listeners: Listener[] = [];\n private listenersOncer: Listener[] = [];\n\n public on = (listener: Listener): Disposable => {\n this.listeners.push(listener);\n return {\n dispose: () => this.off(listener)\n };\n };\n\n public once = (listener: Listener): void => {\n this.listenersOncer.push(listener);\n };\n\n public off = (listener: Listener): void => {\n const callbackIndex = this.listeners.indexOf(listener);\n if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);\n };\n\n public emit = (event: T): void => {\n /** Update any general listeners */\n this.listeners.forEach(listener => listener(event));\n\n /** Clear the `once` queue */\n this.listenersOncer.forEach(listener => listener(event));\n this.listenersOncer = [];\n };\n\n public pipe = (te: TypedEvent): Disposable => this.on(e => te.emit(e));\n}\n","import { UnknownObject, WithModuleProps } from \"../types\";\nimport {\n modulePropsDecoder,\n parseIntOr,\n decodeBase64,\n stringIsEmpty\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type EventsHistoryProps = {\n type: ItemType.AUTO_SLA_GRAPH;\n maxTime: number | null;\n html: string;\n} & ItemProps &\n WithModuleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the events history props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function eventsHistoryPropsDecoder(\n data: UnknownObject\n): EventsHistoryProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.AUTO_SLA_GRAPH,\n maxTime: parseIntOr(data.maxTime, null),\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class EventsHistory extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"events-history\";\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n setTimeout(() => {\n try {\n eval(scripts[i].innerHTML.trim());\n } catch (ignored) {} // eslint-disable-line no-empty\n }, 0);\n }\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n modulePropsDecoder,\n decodeBase64,\n stringIsEmpty\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type DonutGraphProps = {\n type: ItemType.DONUT_GRAPH;\n html: string;\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the donut graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function donutGraphPropsDecoder(\n data: UnknownObject\n): DonutGraphProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.DONUT_GRAPH,\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class DonutGraph extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"donut-graph\";\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n setTimeout(() => {\n if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim());\n }, 0);\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import { UnknownObject, WithModuleProps } from \"../types\";\nimport { modulePropsDecoder, decodeBase64, stringIsEmpty } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type BarsGraphProps = {\n type: ItemType.BARS_GRAPH;\n html: string;\n} & ItemProps &\n WithModuleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the bars graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function barsGraphPropsDecoder(\n data: UnknownObject\n): BarsGraphProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.BARS_GRAPH,\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class BarsGraph extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"bars-graph\";\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n setTimeout(() => {\n if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim());\n }, 0);\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n modulePropsDecoder,\n decodeBase64,\n stringIsEmpty\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type ModuleGraphProps = {\n type: ItemType.MODULE_GRAPH;\n html: string;\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the module graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function moduleGraphPropsDecoder(\n data: UnknownObject\n): ModuleGraphProps | never {\n if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {\n throw new TypeError(\"missing html content.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.MODULE_GRAPH,\n html: !stringIsEmpty(data.html)\n ? data.html\n : decodeBase64(data.encodedHtml),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class ModuleGraph extends Item {\n /**\n * @override Item.resizeElement.\n * Resize the DOM content container.\n * We need to override the resize function cause this item's height\n * is larger than the configured and the graph is over the label.\n * @param width\n * @param height\n */\n protected resizeElement(width: number): void {\n super.resizeElement(width, 0);\n }\n\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"module-graph\";\n element.innerHTML = this.props.html;\n\n // Remove the overview graph.\n const legendP = element.getElementsByTagName(\"p\");\n for (let i = 0; i < legendP.length; i++) {\n legendP[i].style.margin = \"0px\";\n }\n\n // Remove the overview graph.\n const overviewGraphs = element.getElementsByClassName(\"overview_graph\");\n for (let i = 0; i < overviewGraphs.length; i++) {\n overviewGraphs[i].remove();\n }\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const scripts = element.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n setTimeout(() => {\n try {\n eval(scripts[i].innerHTML.trim());\n } catch (ignored) {} // eslint-disable-line no-empty\n }, 0);\n }\n }\n\n return element;\n }\n\n protected updateDomElement(element: HTMLElement): void {\n element.innerHTML = this.props.html;\n\n // Remove the overview graph.\n const legendP = element.getElementsByTagName(\"p\");\n for (let i = 0; i < legendP.length; i++) {\n legendP[i].style.margin = \"0px\";\n }\n\n // Remove the overview graph.\n const overviewGraphs = element.getElementsByClassName(\"overview_graph\");\n for (let i = 0; i < overviewGraphs.length; i++) {\n overviewGraphs[i].remove();\n }\n\n // Hack to execute the JS after the HTML is added to the DOM.\n const aux = document.createElement(\"div\");\n aux.innerHTML = this.props.html;\n const scripts = aux.getElementsByTagName(\"script\");\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src.length === 0) {\n eval(scripts[i].innerHTML.trim());\n }\n }\n }\n}\n","import {\n WithModuleProps,\n LinkedVisualConsoleProps,\n UnknownObject\n} from \"../types\";\n\nimport {\n modulePropsDecoder,\n linkedVCPropsDecoder,\n notEmptyStringOr\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type StaticGraphProps = {\n type: ItemType.STATIC_GRAPH;\n imageSrc: string; // URL?\n showLastValueTooltip: \"default\" | \"enabled\" | \"disabled\";\n statusImageSrc: string | null; // URL?\n lastValue: string | null;\n} & ItemProps &\n (WithModuleProps | LinkedVisualConsoleProps);\n\n/**\n * Extract a valid enum value from a raw unknown value.\n * @param showLastValueTooltip Raw value.\n */\nconst parseShowLastValueTooltip = (\n showLastValueTooltip: unknown\n): StaticGraphProps[\"showLastValueTooltip\"] => {\n switch (showLastValueTooltip) {\n case \"default\":\n case \"enabled\":\n case \"disabled\":\n return showLastValueTooltip;\n default:\n return \"default\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the static graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function staticGraphPropsDecoder(\n data: UnknownObject\n): StaticGraphProps | never {\n if (typeof data.imageSrc !== \"string\" || data.imageSrc.length === 0) {\n throw new TypeError(\"invalid image src.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.STATIC_GRAPH,\n imageSrc: data.imageSrc,\n showLastValueTooltip: parseShowLastValueTooltip(data.showLastValueTooltip),\n statusImageSrc: notEmptyStringOr(data.statusImageSrc, null),\n lastValue: notEmptyStringOr(data.lastValue, null),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class StaticGraph extends Item {\n protected createDomElement(): HTMLElement {\n const imgSrc = this.props.statusImageSrc || this.props.imageSrc;\n const element = document.createElement(\"div\");\n element.className = \"static-graph\";\n element.style.background = `url(${imgSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n\n // Show last value in a tooltip.\n if (\n this.props.lastValue !== null &&\n this.props.showLastValueTooltip !== \"disabled\"\n ) {\n element.className = \"static-graph image forced_title\";\n element.setAttribute(\"data-use_title_for_force_title\", \"1\");\n element.setAttribute(\"data-title\", this.props.lastValue);\n }\n\n return element;\n }\n}\n","import { LinkedVisualConsoleProps, UnknownObject } from \"../types\";\nimport { linkedVCPropsDecoder } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type IconProps = {\n type: ItemType.ICON;\n imageSrc: string; // URL?\n} & ItemProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the icon props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function iconPropsDecoder(data: UnknownObject): IconProps | never {\n if (typeof data.imageSrc !== \"string\" || data.imageSrc.length === 0) {\n throw new TypeError(\"invalid image src.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.ICON,\n imageSrc: data.imageSrc,\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Icon extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"icon\";\n element.style.background = `url(${this.props.imageSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n\n return element;\n }\n}\n","import {\n WithModuleProps,\n LinkedVisualConsoleProps,\n UnknownObject\n} from \"../types\";\nimport { modulePropsDecoder, linkedVCPropsDecoder } from \"../lib\";\nimport Item, { itemBasePropsDecoder, ItemType, ItemProps } from \"../Item\";\n\nexport type ColorCloudProps = {\n type: ItemType.COLOR_CLOUD;\n color: string;\n // TODO: Add the rest of the color cloud values?\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the static graph props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function colorCloudPropsDecoder(\n data: UnknownObject\n): ColorCloudProps | never {\n // TODO: Validate the color.\n if (typeof data.color !== \"string\" || data.color.length === 0) {\n throw new TypeError(\"invalid color.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.COLOR_CLOUD,\n color: data.color,\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nconst svgNS = \"http://www.w3.org/2000/svg\";\n\nexport default class ColorCloud extends Item {\n protected createDomElement(): HTMLElement {\n const container: HTMLDivElement = document.createElement(\"div\");\n container.className = \"color-cloud\";\n\n // Add the SVG.\n container.append(this.createSvgElement());\n\n return container;\n }\n\n public createSvgElement(): SVGSVGElement {\n const gradientId = `grad_${this.props.id}`;\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 100\");\n\n // Defs.\n const defs = document.createElementNS(svgNS, \"defs\");\n // Radial gradient.\n const radialGradient = document.createElementNS(svgNS, \"radialGradient\");\n radialGradient.setAttribute(\"id\", gradientId);\n radialGradient.setAttribute(\"cx\", \"50%\");\n radialGradient.setAttribute(\"cy\", \"50%\");\n radialGradient.setAttribute(\"r\", \"50%\");\n radialGradient.setAttribute(\"fx\", \"50%\");\n radialGradient.setAttribute(\"fy\", \"50%\");\n // Stops.\n const stop0 = document.createElementNS(svgNS, \"stop\");\n stop0.setAttribute(\"offset\", \"0%\");\n stop0.setAttribute(\n \"style\",\n `stop-color:${this.props.color};stop-opacity:0.9`\n );\n const stop100 = document.createElementNS(svgNS, \"stop\");\n stop100.setAttribute(\"offset\", \"100%\");\n stop100.setAttribute(\n \"style\",\n `stop-color:${this.props.color};stop-opacity:0`\n );\n // Circle.\n const circle = document.createElementNS(svgNS, \"circle\");\n circle.setAttribute(\"fill\", `url(#${gradientId})`);\n circle.setAttribute(\"cx\", \"50%\");\n circle.setAttribute(\"cy\", \"50%\");\n circle.setAttribute(\"r\", \"50%\");\n\n // Append elements.\n radialGradient.append(stop0, stop100);\n defs.append(radialGradient);\n svg.append(defs, circle);\n\n return svg;\n }\n}\n","import { LinkedVisualConsoleProps, UnknownObject } from \"../types\";\nimport {\n linkedVCPropsDecoder,\n parseIntOr,\n notEmptyStringOr,\n stringIsEmpty,\n decodeBase64,\n parseBoolean\n} from \"../lib\";\nimport Item, { ItemProps, itemBasePropsDecoder, ItemType } from \"../Item\";\n\nexport type GroupProps = {\n type: ItemType.GROUP_ITEM;\n groupId: number;\n imageSrc: string | null; // URL?\n statusImageSrc: string | null;\n showStatistics: boolean;\n html?: string | null;\n} & ItemProps &\n LinkedVisualConsoleProps;\n\nfunction extractHtml(data: UnknownObject): string | null {\n if (!stringIsEmpty(data.html)) return data.html;\n if (!stringIsEmpty(data.encodedHtml)) return decodeBase64(data.encodedHtml);\n return null;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the group props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function groupPropsDecoder(data: UnknownObject): GroupProps | never {\n if (\n (typeof data.imageSrc !== \"string\" || data.imageSrc.length === 0) &&\n data.encodedHtml === null\n ) {\n throw new TypeError(\"invalid image src.\");\n }\n if (parseIntOr(data.groupId, null) === null) {\n throw new TypeError(\"invalid group Id.\");\n }\n\n const showStatistics = parseBoolean(data.showStatistics);\n const html = showStatistics ? extractHtml(data) : null;\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.GROUP_ITEM,\n groupId: parseInt(data.groupId),\n imageSrc: notEmptyStringOr(data.imageSrc, null),\n statusImageSrc: notEmptyStringOr(data.statusImageSrc, null),\n showStatistics,\n html,\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Group extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"group\";\n\n if (!this.props.showStatistics && this.props.statusImageSrc !== null) {\n // Icon with status.\n element.style.background = `url(${this.props.statusImageSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n } else if (this.props.showStatistics && this.props.html != null) {\n // Stats table.\n element.innerHTML = this.props.html;\n }\n\n return element;\n }\n}\n","import \"./styles.css\";\n\nimport { LinkedVisualConsoleProps, UnknownObject, Size } from \"../../types\";\nimport {\n linkedVCPropsDecoder,\n parseIntOr,\n parseBoolean,\n prefixedCssRules,\n notEmptyStringOr,\n humanDate,\n humanTime\n} from \"../../lib\";\nimport Item, { ItemProps, itemBasePropsDecoder, ItemType } from \"../../Item\";\n\nexport type ClockProps = {\n type: ItemType.CLOCK;\n clockType: \"analogic\" | \"digital\";\n clockFormat: \"datetime\" | \"time\";\n clockTimezone: string;\n clockTimezoneOffset: number; // Offset of the timezone to UTC in seconds.\n showClockTimezone: boolean;\n color?: string | null;\n} & ItemProps &\n LinkedVisualConsoleProps;\n\n/**\n * Extract a valid enum value from a raw unknown value.\n * @param clockType Raw value.\n */\nconst parseClockType = (clockType: unknown): ClockProps[\"clockType\"] => {\n switch (clockType) {\n case \"analogic\":\n case \"digital\":\n return clockType;\n default:\n return \"analogic\";\n }\n};\n\n/**\n * Extract a valid enum value from a raw unknown value.\n * @param clockFormat Raw value.\n */\nconst parseClockFormat = (clockFormat: unknown): ClockProps[\"clockFormat\"] => {\n switch (clockFormat) {\n case \"datetime\":\n case \"time\":\n return clockFormat;\n default:\n return \"datetime\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the clock props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function clockPropsDecoder(data: UnknownObject): ClockProps | never {\n if (\n typeof data.clockTimezone !== \"string\" ||\n data.clockTimezone.length === 0\n ) {\n throw new TypeError(\"invalid timezone.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.CLOCK,\n clockType: parseClockType(data.clockType),\n clockFormat: parseClockFormat(data.clockFormat),\n clockTimezone: data.clockTimezone,\n clockTimezoneOffset: parseIntOr(data.clockTimezoneOffset, 0),\n showClockTimezone: parseBoolean(data.showClockTimezone),\n color: notEmptyStringOr(data.color, null),\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Clock extends Item {\n public static readonly TICK_INTERVAL = 1000; // In ms.\n private intervalRef: number | null = null;\n\n public constructor(props: ClockProps) {\n // Call the superclass constructor.\n super(props);\n\n /* The item is already loaded and inserted into the DOM.\n * The class properties are now initialized.\n * Now you can modify the item, add event handlers, timers, etc.\n */\n\n /* The use of the arrow function is important here. startTick will\n * use the function passed as an argument to call the global setInterval\n * function. The interval, timeout or event functions, among other, are\n * called into another execution loop and using a different context.\n * The arrow functions, unlike the classic functions, doesn't create\n * their own context (this), so their context at execution time will be\n * use the current context at the declaration time.\n * http://es6-features.org/#Lexicalthis\n */\n this.startTick(\n () => {\n // Replace the old element with the updated date.\n this.childElementRef.innerHTML = this.createClock().innerHTML;\n },\n /* The analogic clock doesn't need to tick,\n * but it will be refreshed every 20 seconds\n * to avoid a desync caused by page freezes.\n */\n this.props.clockType === \"analogic\" ? 20000 : Clock.TICK_INTERVAL\n );\n }\n\n /**\n * Wrap a window.clearInterval call.\n */\n private stopTick(): void {\n if (this.intervalRef !== null) {\n window.clearInterval(this.intervalRef);\n this.intervalRef = null;\n }\n }\n\n /**\n * Wrap a window.setInterval call.\n * @param handler Function to be called every time the interval\n * timer is reached.\n * @param interval Number in milliseconds for the interval timer.\n */\n private startTick(\n handler: TimerHandler,\n interval: number = Clock.TICK_INTERVAL\n ): void {\n this.stopTick();\n this.intervalRef = window.setInterval(handler, interval);\n }\n\n /**\n * Create a element which contains the DOM representation of the item.\n * @return DOM Element.\n * @override\n */\n protected createDomElement(): HTMLElement | never {\n return this.createClock();\n }\n\n /**\n * To remove the event listeners and the elements from the DOM.\n * @override\n */\n public remove(): void {\n // Clear the interval.\n this.stopTick();\n // Call to the parent clean function.\n super.remove();\n }\n\n /**\n * @override Item.resizeElement\n * Resize the DOM content container.\n * @param width\n * @param height\n */\n protected resizeElement(width: number, height: number): void {\n const { width: newWidth, height: newHeight } = this.getElementSize(\n width,\n height\n ); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation\n super.resizeElement(newWidth, newHeight);\n // Re-render the item to force it calculate a new font size.\n if (this.props.clockType === \"digital\") {\n // Replace the old element with the updated date.\n this.childElementRef.innerHTML = this.createClock().innerHTML;\n }\n }\n\n /**\n * Create a element which contains a representation of a clock.\n * It choose between the clock types.\n * @return DOM Element.\n * @throws Error.\n */\n private createClock(): HTMLElement | never {\n switch (this.props.clockType) {\n case \"analogic\":\n return this.createAnalogicClock();\n case \"digital\":\n return this.createDigitalClock();\n default:\n throw new Error(\"invalid clock type.\");\n }\n }\n\n /**\n * Create a element which contains a representation of an analogic clock.\n * @return DOM Element.\n */\n private createAnalogicClock(): HTMLElement {\n const svgNS = \"http://www.w3.org/2000/svg\";\n const colors = {\n watchFace: \"#FFFFF0\",\n watchFaceBorder: \"#242124\",\n mark: \"#242124\",\n handDark: \"#242124\",\n handLight: \"#525252\",\n secondHand: \"#DC143C\"\n };\n\n const { width, height } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation\n\n // Calculate font size to adapt the font to the item size.\n const baseTimeFontSize = 20; // Per 100px of width.\n const dateFontSizeMultiplier = 0.5;\n const dateFontSize =\n (baseTimeFontSize * dateFontSizeMultiplier * width) / 100;\n\n const div = document.createElement(\"div\");\n div.className = \"analogic-clock\";\n div.style.width = `${width}px`;\n div.style.height = `${height}px`;\n\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 100\");\n\n // Clock face.\n const clockFace = document.createElementNS(svgNS, \"g\");\n clockFace.setAttribute(\"class\", \"clockface\");\n const clockFaceBackground = document.createElementNS(svgNS, \"circle\");\n clockFaceBackground.setAttribute(\"cx\", \"50\");\n clockFaceBackground.setAttribute(\"cy\", \"50\");\n clockFaceBackground.setAttribute(\"r\", \"48\");\n clockFaceBackground.setAttribute(\"fill\", colors.watchFace);\n clockFaceBackground.setAttribute(\"stroke\", colors.watchFaceBorder);\n clockFaceBackground.setAttribute(\"stroke-width\", \"2\");\n clockFaceBackground.setAttribute(\"stroke-linecap\", \"round\");\n // Insert the clockface background into the clockface group.\n clockFace.append(clockFaceBackground);\n\n // Timezone complication.\n const city = this.getHumanTimezone();\n if (city.length > 0) {\n const timezoneComplication = document.createElementNS(svgNS, \"text\");\n timezoneComplication.setAttribute(\"text-anchor\", \"middle\");\n timezoneComplication.setAttribute(\"font-size\", \"8\");\n timezoneComplication.setAttribute(\n \"transform\",\n \"translate(30 50) rotate(90)\" // Rotate to counter the clock rotation.\n );\n timezoneComplication.setAttribute(\"fill\", colors.mark);\n timezoneComplication.textContent = city;\n clockFace.append(timezoneComplication);\n }\n\n // Marks group.\n const marksGroup = document.createElementNS(svgNS, \"g\");\n marksGroup.setAttribute(\"class\", \"marks\");\n // Build the 12 hours mark.\n const mainMarkGroup = document.createElementNS(svgNS, \"g\");\n mainMarkGroup.setAttribute(\"class\", \"mark\");\n mainMarkGroup.setAttribute(\"transform\", \"translate(50 50)\");\n const mark1a = document.createElementNS(svgNS, \"line\");\n mark1a.setAttribute(\"x1\", \"36\");\n mark1a.setAttribute(\"y1\", \"0\");\n mark1a.setAttribute(\"x2\", \"46\");\n mark1a.setAttribute(\"y2\", \"0\");\n mark1a.setAttribute(\"stroke\", colors.mark);\n mark1a.setAttribute(\"stroke-width\", \"5\");\n const mark1b = document.createElementNS(svgNS, \"line\");\n mark1b.setAttribute(\"x1\", \"36\");\n mark1b.setAttribute(\"y1\", \"0\");\n mark1b.setAttribute(\"x2\", \"46\");\n mark1b.setAttribute(\"y2\", \"0\");\n mark1b.setAttribute(\"stroke\", colors.watchFace);\n mark1b.setAttribute(\"stroke-width\", \"1\");\n // Insert the 12 mark lines into their group.\n mainMarkGroup.append(mark1a, mark1b);\n // Insert the main mark into the marks group.\n marksGroup.append(mainMarkGroup);\n // Build the rest of the marks.\n for (let i = 1; i < 60; i++) {\n const mark = document.createElementNS(svgNS, \"line\");\n mark.setAttribute(\"y1\", \"0\");\n mark.setAttribute(\"y2\", \"0\");\n mark.setAttribute(\"stroke\", colors.mark);\n mark.setAttribute(\"transform\", `translate(50 50) rotate(${i * 6})`);\n\n if (i % 5 === 0) {\n mark.setAttribute(\"x1\", \"38\");\n mark.setAttribute(\"x2\", \"46\");\n mark.setAttribute(\"stroke-width\", i % 15 === 0 ? \"2\" : \"1\");\n } else {\n mark.setAttribute(\"x1\", \"42\");\n mark.setAttribute(\"x2\", \"46\");\n mark.setAttribute(\"stroke-width\", \"0.5\");\n }\n\n // Insert the mark into the marks group.\n marksGroup.append(mark);\n }\n\n /* Clock hands */\n\n // Hour hand.\n const hourHand = document.createElementNS(svgNS, \"g\");\n hourHand.setAttribute(\"class\", \"hour-hand\");\n hourHand.setAttribute(\"transform\", \"translate(50 50)\");\n // This will go back and will act like a border.\n const hourHandA = document.createElementNS(svgNS, \"line\");\n hourHandA.setAttribute(\"class\", \"hour-hand-a\");\n hourHandA.setAttribute(\"x1\", \"0\");\n hourHandA.setAttribute(\"y1\", \"0\");\n hourHandA.setAttribute(\"x2\", \"30\");\n hourHandA.setAttribute(\"y2\", \"0\");\n hourHandA.setAttribute(\"stroke\", colors.handLight);\n hourHandA.setAttribute(\"stroke-width\", \"4\");\n hourHandA.setAttribute(\"stroke-linecap\", \"round\");\n // This will go in front of the previous line.\n const hourHandB = document.createElementNS(svgNS, \"line\");\n hourHandB.setAttribute(\"class\", \"hour-hand-b\");\n hourHandB.setAttribute(\"x1\", \"0\");\n hourHandB.setAttribute(\"y1\", \"0\");\n hourHandB.setAttribute(\"x2\", \"29.9\");\n hourHandB.setAttribute(\"y2\", \"0\");\n hourHandB.setAttribute(\"stroke\", colors.handDark);\n hourHandB.setAttribute(\"stroke-width\", \"3.1\");\n hourHandB.setAttribute(\"stroke-linecap\", \"round\");\n // Append the elements to finish the hour hand.\n hourHand.append(hourHandA, hourHandB);\n\n // Minute hand.\n const minuteHand = document.createElementNS(svgNS, \"g\");\n minuteHand.setAttribute(\"class\", \"minute-hand\");\n minuteHand.setAttribute(\"transform\", \"translate(50 50)\");\n // This will go back and will act like a border.\n const minuteHandA = document.createElementNS(svgNS, \"line\");\n minuteHandA.setAttribute(\"class\", \"minute-hand-a\");\n minuteHandA.setAttribute(\"x1\", \"0\");\n minuteHandA.setAttribute(\"y1\", \"0\");\n minuteHandA.setAttribute(\"x2\", \"40\");\n minuteHandA.setAttribute(\"y2\", \"0\");\n minuteHandA.setAttribute(\"stroke\", colors.handLight);\n minuteHandA.setAttribute(\"stroke-width\", \"2\");\n minuteHandA.setAttribute(\"stroke-linecap\", \"round\");\n // This will go in front of the previous line.\n const minuteHandB = document.createElementNS(svgNS, \"line\");\n minuteHandB.setAttribute(\"class\", \"minute-hand-b\");\n minuteHandB.setAttribute(\"x1\", \"0\");\n minuteHandB.setAttribute(\"y1\", \"0\");\n minuteHandB.setAttribute(\"x2\", \"39.9\");\n minuteHandB.setAttribute(\"y2\", \"0\");\n minuteHandB.setAttribute(\"stroke\", colors.handDark);\n minuteHandB.setAttribute(\"stroke-width\", \"1.5\");\n minuteHandB.setAttribute(\"stroke-linecap\", \"round\");\n const minuteHandPin = document.createElementNS(svgNS, \"circle\");\n minuteHandPin.setAttribute(\"r\", \"3\");\n minuteHandPin.setAttribute(\"fill\", colors.handDark);\n // Append the elements to finish the minute hand.\n minuteHand.append(minuteHandA, minuteHandB, minuteHandPin);\n\n // Second hand.\n const secondHand = document.createElementNS(svgNS, \"g\");\n secondHand.setAttribute(\"class\", \"second-hand\");\n secondHand.setAttribute(\"transform\", \"translate(50 50)\");\n const secondHandBar = document.createElementNS(svgNS, \"line\");\n secondHandBar.setAttribute(\"x1\", \"0\");\n secondHandBar.setAttribute(\"y1\", \"0\");\n secondHandBar.setAttribute(\"x2\", \"46\");\n secondHandBar.setAttribute(\"y2\", \"0\");\n secondHandBar.setAttribute(\"stroke\", colors.secondHand);\n secondHandBar.setAttribute(\"stroke-width\", \"1\");\n secondHandBar.setAttribute(\"stroke-linecap\", \"round\");\n const secondHandPin = document.createElementNS(svgNS, \"circle\");\n secondHandPin.setAttribute(\"r\", \"2\");\n secondHandPin.setAttribute(\"fill\", colors.secondHand);\n // Append the elements to finish the second hand.\n secondHand.append(secondHandBar, secondHandPin);\n\n // Pin.\n const pin = document.createElementNS(svgNS, \"circle\");\n pin.setAttribute(\"cx\", \"50\");\n pin.setAttribute(\"cy\", \"50\");\n pin.setAttribute(\"r\", \"0.3\");\n pin.setAttribute(\"fill\", colors.handDark);\n\n // Get the hand angles.\n const date = this.getOriginDate();\n const seconds = date.getSeconds();\n const minutes = date.getMinutes();\n const hours = date.getHours();\n const secAngle = (360 / 60) * seconds;\n const minuteAngle = (360 / 60) * minutes + (360 / 60) * (seconds / 60);\n const hourAngle = (360 / 12) * hours + (360 / 12) * (minutes / 60);\n // Set the clock time by moving the hands.\n hourHand.setAttribute(\"transform\", `translate(50 50) rotate(${hourAngle})`);\n minuteHand.setAttribute(\n \"transform\",\n `translate(50 50) rotate(${minuteAngle})`\n );\n secondHand.setAttribute(\n \"transform\",\n `translate(50 50) rotate(${secAngle})`\n );\n\n // Build the clock\n svg.append(clockFace, marksGroup, hourHand, minuteHand, secondHand, pin);\n // Rotate the clock to its normal position.\n svg.setAttribute(\"transform\", \"rotate(-90)\");\n\n /* Add the animation declaration to the container.\n * Since the animation keyframes need to know the\n * start angle, this angle is dynamic (current time),\n * and we can't edit keyframes through javascript\n * safely and with backwards compatibility, we need\n * to inject it.\n */\n div.innerHTML = `\n \n `;\n // Add the clock to the container\n div.append(svg);\n\n // Date.\n if (this.props.clockFormat === \"datetime\") {\n const dateElem: HTMLSpanElement = document.createElement(\"span\");\n dateElem.className = \"date\";\n dateElem.textContent = humanDate(date, \"default\");\n dateElem.style.fontSize = `${dateFontSize}px`;\n if (this.props.color) dateElem.style.color = this.props.color;\n div.append(dateElem);\n }\n\n return div;\n }\n\n /**\n * Create a element which contains a representation of a digital clock.\n * @return DOM Element.\n */\n private createDigitalClock(): HTMLElement {\n const element: HTMLDivElement = document.createElement(\"div\");\n element.className = \"digital-clock\";\n\n const { width } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation\n\n // Calculate font size to adapt the font to the item size.\n const baseTimeFontSize = 20; // Per 100px of width.\n const dateFontSizeMultiplier = 0.5;\n const tzFontSizeMultiplier = 6 / this.props.clockTimezone.length;\n const timeFontSize = (baseTimeFontSize * width) / 100;\n const dateFontSize =\n (baseTimeFontSize * dateFontSizeMultiplier * width) / 100;\n const tzFontSize = Math.min(\n (baseTimeFontSize * tzFontSizeMultiplier * width) / 100,\n (width / 100) * 10\n );\n\n // Date calculated using the original timezone.\n const date = this.getOriginDate();\n\n // Date.\n if (this.props.clockFormat === \"datetime\") {\n const dateElem: HTMLSpanElement = document.createElement(\"span\");\n dateElem.className = \"date\";\n dateElem.textContent = humanDate(date, \"default\");\n dateElem.style.fontSize = `${dateFontSize}px`;\n if (this.props.color) dateElem.style.color = this.props.color;\n element.append(dateElem);\n }\n\n // Time.\n const timeElem: HTMLSpanElement = document.createElement(\"span\");\n timeElem.className = \"time\";\n timeElem.textContent = humanTime(date);\n timeElem.style.fontSize = `${timeFontSize}px`;\n if (this.props.color) timeElem.style.color = this.props.color;\n element.append(timeElem);\n\n // City name.\n const city = this.getHumanTimezone();\n if (city.length > 0) {\n const tzElem: HTMLSpanElement = document.createElement(\"span\");\n tzElem.className = \"timezone\";\n tzElem.textContent = city;\n tzElem.style.fontSize = `${tzFontSize}px`;\n if (this.props.color) tzElem.style.color = this.props.color;\n element.append(tzElem);\n }\n\n return element;\n }\n\n /**\n * Generate the current date using the timezone offset stored into the properties.\n * @return The current date.\n */\n private getOriginDate(initialDate: Date | null = null): Date {\n const d = initialDate ? initialDate : new Date();\n const targetTZOffset = this.props.clockTimezoneOffset * 1000; // In ms.\n const localTZOffset = d.getTimezoneOffset() * 60 * 1000; // In ms.\n const utimestamp = d.getTime() + targetTZOffset + localTZOffset;\n\n return new Date(utimestamp);\n }\n\n /**\n * Extract a human readable city name from the timezone text.\n * @param timezone Timezone text.\n */\n public getHumanTimezone(timezone: string = this.props.clockTimezone): string {\n const [, city = \"\"] = timezone.split(\"/\");\n return city.replace(\"_\", \" \");\n }\n\n /**\n * Generate a element size using the current size and the default values.\n * @return The size.\n */\n private getElementSize(\n width: number = this.props.width,\n height: number = this.props.height\n ): Size {\n switch (this.props.clockType) {\n case \"analogic\": {\n let diameter = 100; // Default value.\n\n if (width > 0 && height > 0) {\n diameter = Math.min(width, height);\n } else if (width > 0) {\n diameter = width;\n } else if (height > 0) {\n diameter = height;\n }\n\n return {\n width: diameter,\n height: diameter\n };\n }\n case \"digital\": {\n if (width > 0 && height > 0) {\n // The proportion of the clock should be (width = height / 2) aproximately.\n height = width / 2 < height ? width / 2 : height;\n } else if (width > 0) {\n height = width / 2;\n } else if (height > 0) {\n // The proportion of the clock should be (height * 2 = width) aproximately.\n width = height * 2;\n } else {\n width = 100; // Default value.\n height = 50; // Default value.\n }\n\n return {\n width,\n height\n };\n }\n default:\n throw new Error(\"invalid clock type.\");\n }\n }\n}\n","import { UnknownObject } from \"../types\";\nimport { parseIntOr, notEmptyStringOr } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\ninterface BoxProps extends ItemProps {\n // Overrided properties.\n readonly type: ItemType.BOX_ITEM;\n label: null;\n isLinkEnabled: false;\n parentId: null;\n aclGroupId: null;\n // Custom properties.\n borderWidth: number;\n borderColor: string | null;\n fillColor: string | null;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the item props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function boxPropsDecoder(data: UnknownObject): BoxProps | never {\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.BOX_ITEM,\n label: null,\n isLinkEnabled: false,\n parentId: null,\n aclGroupId: null,\n // Custom properties.\n borderWidth: parseIntOr(data.borderWidth, 0),\n borderColor: notEmptyStringOr(data.borderColor, null),\n fillColor: notEmptyStringOr(data.fillColor, null)\n };\n}\n\nexport default class Box extends Item {\n protected createDomElement(): HTMLElement {\n const box: HTMLDivElement = document.createElement(\"div\");\n box.className = \"box\";\n // To prevent this item to expand beyond its parent.\n box.style.boxSizing = \"border-box\";\n\n if (this.props.fillColor) {\n box.style.backgroundColor = this.props.fillColor;\n }\n\n // Border.\n if (this.props.borderWidth > 0) {\n box.style.borderStyle = \"solid\";\n // Control the max width to prevent this item to expand beyond its parent.\n const maxBorderWidth = Math.min(this.props.width, this.props.height) / 2;\n const borderWidth = Math.min(this.props.borderWidth, maxBorderWidth);\n box.style.borderWidth = `${borderWidth}px`;\n\n if (this.props.borderColor) {\n box.style.borderColor = this.props.borderColor;\n }\n }\n\n return box;\n }\n}\n","import { UnknownObject, Position, Size } from \"../types\";\nimport { parseIntOr, notEmptyStringOr } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\ninterface LineProps extends ItemProps {\n // Overrided properties.\n readonly type: ItemType.LINE_ITEM;\n label: null;\n isLinkEnabled: false;\n parentId: null;\n aclGroupId: null;\n // Custom properties.\n startPosition: Position;\n endPosition: Position;\n lineWidth: number;\n color: string | null;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the item props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function linePropsDecoder(data: UnknownObject): LineProps | never {\n const props: LineProps = {\n ...itemBasePropsDecoder({ ...data, width: 1, height: 1 }), // Object spread. It will merge the properties of the two objects.\n type: ItemType.LINE_ITEM,\n label: null,\n isLinkEnabled: false,\n parentId: null,\n aclGroupId: null,\n // Initialize Position & Size.\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n // Custom properties.\n startPosition: {\n x: parseIntOr(data.startX, 0),\n y: parseIntOr(data.startY, 0)\n },\n endPosition: {\n x: parseIntOr(data.endX, 0),\n y: parseIntOr(data.endY, 0)\n },\n lineWidth: parseIntOr(data.lineWidth || data.borderWidth, 1),\n color: notEmptyStringOr(data.borderColor || data.color, null)\n };\n\n /*\n * We need to enhance the props with the extracted size and position\n * of the box cause there are missing at the props update. A better\n * solution would be overriding the props setter to do it there, but\n * the language doesn't allow it while targetting ES5.\n * TODO: We need to figure out a more consistent solution.\n */\n\n return {\n ...props,\n // Enhance the props extracting the box size and position.\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n ...Line.extractBoxSizeAndPosition(props)\n };\n}\n\nexport default class Line extends Item {\n /**\n * @override\n */\n public constructor(props: LineProps) {\n /*\n * We need to override the constructor cause we need to obtain\n * the\n * box size and position from the start and finish points\n * of the line.\n */\n super({\n ...props,\n ...Line.extractBoxSizeAndPosition(props)\n });\n }\n\n /**\n * @override\n * To create the item's DOM representation.\n * @return Item.\n */\n protected createDomElement(): HTMLElement {\n const element: HTMLDivElement = document.createElement(\"div\");\n element.className = \"line\";\n\n const svgNS = \"http://www.w3.org/2000/svg\";\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n // Set SVG size.\n svg.setAttribute(\n \"width\",\n (this.props.width + this.props.lineWidth).toString()\n );\n svg.setAttribute(\n \"height\",\n (this.props.height + this.props.lineWidth).toString()\n );\n const line = document.createElementNS(svgNS, \"line\");\n line.setAttribute(\n \"x1\",\n `${this.props.startPosition.x - this.props.x + this.props.lineWidth / 2}`\n );\n line.setAttribute(\n \"y1\",\n `${this.props.startPosition.y - this.props.y + this.props.lineWidth / 2}`\n );\n line.setAttribute(\n \"x2\",\n `${this.props.endPosition.x - this.props.x + this.props.lineWidth / 2}`\n );\n line.setAttribute(\n \"y2\",\n `${this.props.endPosition.y - this.props.y + this.props.lineWidth / 2}`\n );\n line.setAttribute(\"stroke\", this.props.color || \"black\");\n line.setAttribute(\"stroke-width\", this.props.lineWidth.toString());\n\n svg.append(line);\n element.append(svg);\n\n return element;\n }\n\n /**\n * Extract the size and position of the box from\n * the start and the finish of the line.\n * @param props Item properties.\n */\n public static extractBoxSizeAndPosition(props: LineProps): Size & Position {\n return {\n width: Math.abs(props.startPosition.x - props.endPosition.x),\n height: Math.abs(props.startPosition.y - props.endPosition.y),\n x: Math.min(props.startPosition.x, props.endPosition.x),\n y: Math.min(props.startPosition.y, props.endPosition.y)\n };\n }\n}\n","import { LinkedVisualConsoleProps, UnknownObject } from \"../types\";\nimport { linkedVCPropsDecoder } from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type LabelProps = {\n type: ItemType.LABEL;\n} & ItemProps &\n LinkedVisualConsoleProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the label props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function labelPropsDecoder(data: UnknownObject): LabelProps | never {\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.LABEL,\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class Label extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"label\";\n element.innerHTML = this.getLabelWithMacrosReplaced();\n\n return element;\n }\n\n /**\n * @override Item.createLabelDomElement\n * Create a new label for the visual console item.\n * @return Item label.\n */\n public createLabelDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"visual-console-item-label\";\n // Always return an empty label.\n return element;\n }\n}\n","import {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n parseIntOr,\n modulePropsDecoder,\n replaceMacros\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type SimpleValueProps = {\n type: ItemType.SIMPLE_VALUE;\n valueType: \"string\" | \"image\";\n value: string;\n} & (\n | {\n processValue: \"none\";\n }\n | {\n processValue: \"avg\" | \"max\" | \"min\";\n period: number;\n }) &\n ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Extract a valid enum value from a raw value type.\n * @param valueType Raw value.\n */\nconst parseValueType = (valueType: unknown): SimpleValueProps[\"valueType\"] => {\n switch (valueType) {\n case \"string\":\n case \"image\":\n return valueType;\n default:\n return \"string\";\n }\n};\n\n/**\n * Extract a valid enum value from a raw process value.\n * @param processValue Raw value.\n */\nconst parseProcessValue = (\n processValue: unknown\n): SimpleValueProps[\"processValue\"] => {\n switch (processValue) {\n case \"none\":\n case \"avg\":\n case \"max\":\n case \"min\":\n return processValue;\n default:\n return \"none\";\n }\n};\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the simple value props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function simpleValuePropsDecoder(\n data: UnknownObject\n): SimpleValueProps | never {\n if (typeof data.value !== \"string\" || data.value.length === 0) {\n throw new TypeError(\"invalid value\");\n }\n\n const processValue = parseProcessValue(data.processValue);\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.SIMPLE_VALUE,\n valueType: parseValueType(data.valueType),\n value: data.value,\n ...(processValue === \"none\"\n ? { processValue }\n : { processValue, period: parseIntOr(data.period, 0) }), // Object spread. It will merge the properties of the two objects.\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nexport default class SimpleValue extends Item {\n protected createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"simple-value\";\n\n if (this.props.valueType === \"image\") {\n const img = document.createElement(\"img\");\n img.src = this.props.value;\n element.append(img);\n } else {\n // Add the value to the label and show it.\n let text = this.props.value;\n let label = this.getLabelWithMacrosReplaced();\n if (label.length > 0) {\n text = replaceMacros([{ macro: /\\(?_VALUE_\\)?/i, value: text }], label);\n }\n\n element.innerHTML = text;\n }\n\n return element;\n }\n\n /**\n * @override Item.createLabelDomElement\n * Create a new label for the visual console item.\n * @return Item label.\n */\n protected createLabelDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"visual-console-item-label\";\n // Always return an empty label.\n return element;\n }\n}\n","var pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction Path() {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n}\n\nfunction path() {\n return new Path;\n}\n\nPath.prototype = path.prototype = {\n constructor: Path,\n moveTo: function(x, y) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n },\n closePath: function() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._ += \"Z\";\n }\n },\n lineTo: function(x, y) {\n this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n quadraticCurveTo: function(x1, y1, x, y) {\n this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n arcTo: function(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n var x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon));\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Otherwise, draw an arc!\n else {\n var x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n }\n\n this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n }\n },\n arc: function(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r;\n var dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._ += \"M\" + x0 + \",\" + y0;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._ += \"L\" + x0 + \",\" + y0;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n }\n },\n rect: function(x, y, w, h) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n },\n toString: function() {\n return this._;\n }\n};\n\nexport default path;\n","export default function(x) {\n return function constant() {\n return x;\n };\n}\n","export var abs = Math.abs;\nexport var atan2 = Math.atan2;\nexport var cos = Math.cos;\nexport var max = Math.max;\nexport var min = Math.min;\nexport var sin = Math.sin;\nexport var sqrt = Math.sqrt;\n\nexport var epsilon = 1e-12;\nexport var pi = Math.PI;\nexport var halfPi = pi / 2;\nexport var tau = 2 * pi;\n\nexport function acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nexport function asin(x) {\n return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);\n}\n","import {path} from \"d3-path\";\nimport constant from \"./constant\";\nimport {abs, acos, asin, atan2, cos, epsilon, halfPi, max, min, pi, sin, sqrt, tau} from \"./math\";\n\nfunction arcInnerRadius(d) {\n return d.innerRadius;\n}\n\nfunction arcOuterRadius(d) {\n return d.outerRadius;\n}\n\nfunction arcStartAngle(d) {\n return d.startAngle;\n}\n\nfunction arcEndAngle(d) {\n return d.endAngle;\n}\n\nfunction arcPadAngle(d) {\n return d && d.padAngle; // Note: optional!\n}\n\nfunction intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n var x10 = x1 - x0, y10 = y1 - y0,\n x32 = x3 - x2, y32 = y3 - y2,\n t = y32 * x10 - x32 * y10;\n if (t * t < epsilon) return;\n t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;\n return [x0 + t * x10, y0 + t * y10];\n}\n\n// Compute perpendicular offset line of length rc.\n// http://mathworld.wolfram.com/Circle-LineIntersection.html\nfunction cornerTangents(x0, y0, x1, y1, r1, rc, cw) {\n var x01 = x0 - x1,\n y01 = y0 - y1,\n lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),\n ox = lo * y01,\n oy = -lo * x01,\n x11 = x0 + ox,\n y11 = y0 + oy,\n x10 = x1 + ox,\n y10 = y1 + oy,\n x00 = (x11 + x10) / 2,\n y00 = (y11 + y10) / 2,\n dx = x10 - x11,\n dy = y10 - y11,\n d2 = dx * dx + dy * dy,\n r = r1 - rc,\n D = x11 * y10 - x10 * y11,\n d = (dy < 0 ? -1 : 1) * sqrt(max(0, r * r * d2 - D * D)),\n cx0 = (D * dy - dx * d) / d2,\n cy0 = (-D * dx - dy * d) / d2,\n cx1 = (D * dy + dx * d) / d2,\n cy1 = (-D * dx + dy * d) / d2,\n dx0 = cx0 - x00,\n dy0 = cy0 - y00,\n dx1 = cx1 - x00,\n dy1 = cy1 - y00;\n\n // Pick the closer of the two intersection points.\n // TODO Is there a faster way to determine which intersection to use?\n if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;\n\n return {\n cx: cx0,\n cy: cy0,\n x01: -ox,\n y01: -oy,\n x11: cx0 * (r1 / r - 1),\n y11: cy0 * (r1 / r - 1)\n };\n}\n\nexport default function() {\n var innerRadius = arcInnerRadius,\n outerRadius = arcOuterRadius,\n cornerRadius = constant(0),\n padRadius = null,\n startAngle = arcStartAngle,\n endAngle = arcEndAngle,\n padAngle = arcPadAngle,\n context = null;\n\n function arc() {\n var buffer,\n r,\n r0 = +innerRadius.apply(this, arguments),\n r1 = +outerRadius.apply(this, arguments),\n a0 = startAngle.apply(this, arguments) - halfPi,\n a1 = endAngle.apply(this, arguments) - halfPi,\n da = abs(a1 - a0),\n cw = a1 > a0;\n\n if (!context) context = buffer = path();\n\n // Ensure that the outer radius is always larger than the inner radius.\n if (r1 < r0) r = r1, r1 = r0, r0 = r;\n\n // Is it a point?\n if (!(r1 > epsilon)) context.moveTo(0, 0);\n\n // Or is it a circle or annulus?\n else if (da > tau - epsilon) {\n context.moveTo(r1 * cos(a0), r1 * sin(a0));\n context.arc(0, 0, r1, a0, a1, !cw);\n if (r0 > epsilon) {\n context.moveTo(r0 * cos(a1), r0 * sin(a1));\n context.arc(0, 0, r0, a1, a0, cw);\n }\n }\n\n // Or is it a circular or annular sector?\n else {\n var a01 = a0,\n a11 = a1,\n a00 = a0,\n a10 = a1,\n da0 = da,\n da1 = da,\n ap = padAngle.apply(this, arguments) / 2,\n rp = (ap > epsilon) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),\n rc = min(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),\n rc0 = rc,\n rc1 = rc,\n t0,\n t1;\n\n // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.\n if (rp > epsilon) {\n var p0 = asin(rp / r0 * sin(ap)),\n p1 = asin(rp / r1 * sin(ap));\n if ((da0 -= p0 * 2) > epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;\n else da0 = 0, a00 = a10 = (a0 + a1) / 2;\n if ((da1 -= p1 * 2) > epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;\n else da1 = 0, a01 = a11 = (a0 + a1) / 2;\n }\n\n var x01 = r1 * cos(a01),\n y01 = r1 * sin(a01),\n x10 = r0 * cos(a10),\n y10 = r0 * sin(a10);\n\n // Apply rounded corners?\n if (rc > epsilon) {\n var x11 = r1 * cos(a11),\n y11 = r1 * sin(a11),\n x00 = r0 * cos(a00),\n y00 = r0 * sin(a00),\n oc;\n\n // Restrict the corner radius according to the sector angle.\n if (da < pi && (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10))) {\n var ax = x01 - oc[0],\n ay = y01 - oc[1],\n bx = x11 - oc[0],\n by = y11 - oc[1],\n kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),\n lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);\n rc0 = min(rc, (r0 - lc) / (kc - 1));\n rc1 = min(rc, (r1 - lc) / (kc + 1));\n }\n }\n\n // Is the sector collapsed to a line?\n if (!(da1 > epsilon)) context.moveTo(x01, y01);\n\n // Does the sector’s outer ring have rounded corners?\n else if (rc1 > epsilon) {\n t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);\n t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);\n\n context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);\n context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the outer ring just a circular arc?\n else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);\n\n // Is there no inner ring, and it’s a circular sector?\n // Or perhaps it’s an annular sector collapsed due to padding?\n if (!(r0 > epsilon) || !(da0 > epsilon)) context.lineTo(x10, y10);\n\n // Does the sector’s inner ring (or point) have rounded corners?\n else if (rc0 > epsilon) {\n t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);\n t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);\n\n context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);\n context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the inner ring just a circular arc?\n else context.arc(0, 0, r0, a10, a00, cw);\n }\n\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n arc.centroid = function() {\n var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,\n a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2;\n return [cos(a) * r, sin(a) * r];\n };\n\n arc.innerRadius = function(_) {\n return arguments.length ? (innerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : innerRadius;\n };\n\n arc.outerRadius = function(_) {\n return arguments.length ? (outerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : outerRadius;\n };\n\n arc.cornerRadius = function(_) {\n return arguments.length ? (cornerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : cornerRadius;\n };\n\n arc.padRadius = function(_) {\n return arguments.length ? (padRadius = _ == null ? null : typeof _ === \"function\" ? _ : constant(+_), arc) : padRadius;\n };\n\n arc.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : startAngle;\n };\n\n arc.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : endAngle;\n };\n\n arc.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : padAngle;\n };\n\n arc.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), arc) : context;\n };\n\n return arc;\n}\n","function Linear(context) {\n this._context = context;\n}\n\nLinear.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: this._context.lineTo(x, y); break;\n }\n }\n};\n\nexport default function(context) {\n return new Linear(context);\n}\n","import curveLinear from \"./linear\";\n\nexport var curveRadialLinear = curveRadial(curveLinear);\n\nfunction Radial(curve) {\n this._curve = curve;\n}\n\nRadial.prototype = {\n areaStart: function() {\n this._curve.areaStart();\n },\n areaEnd: function() {\n this._curve.areaEnd();\n },\n lineStart: function() {\n this._curve.lineStart();\n },\n lineEnd: function() {\n this._curve.lineEnd();\n },\n point: function(a, r) {\n this._curve.point(r * Math.sin(a), r * -Math.cos(a));\n }\n};\n\nexport default function curveRadial(curve) {\n\n function radial(context) {\n return new Radial(curve(context));\n }\n\n radial._curve = curve;\n\n return radial;\n}\n","export var slice = Array.prototype.slice;\n","var tan30 = Math.sqrt(1 / 3),\n tan30_2 = tan30 * 2;\n\nexport default {\n draw: function(context, size) {\n var y = Math.sqrt(size / tan30_2),\n x = y * tan30;\n context.moveTo(0, -y);\n context.lineTo(x, 0);\n context.lineTo(0, y);\n context.lineTo(-x, 0);\n context.closePath();\n }\n};\n","import {pi, tau} from \"../math\";\n\nexport default {\n draw: function(context, size) {\n var r = Math.sqrt(size / pi);\n context.moveTo(r, 0);\n context.arc(0, 0, r, 0, tau);\n }\n};\n","import {pi, tau} from \"../math\";\n\nvar ka = 0.89081309152928522810,\n kr = Math.sin(pi / 10) / Math.sin(7 * pi / 10),\n kx = Math.sin(tau / 10) * kr,\n ky = -Math.cos(tau / 10) * kr;\n\nexport default {\n draw: function(context, size) {\n var r = Math.sqrt(size * ka),\n x = kx * r,\n y = ky * r;\n context.moveTo(0, -r);\n context.lineTo(x, y);\n for (var i = 1; i < 5; ++i) {\n var a = tau * i / 5,\n c = Math.cos(a),\n s = Math.sin(a);\n context.lineTo(s * r, -c * r);\n context.lineTo(c * x - s * y, s * x + c * y);\n }\n context.closePath();\n }\n};\n","export default function() {}\n","var sqrt3 = Math.sqrt(3);\n\nexport default {\n draw: function(context, size) {\n var y = -Math.sqrt(size / (sqrt3 * 3));\n context.moveTo(0, y * 2);\n context.lineTo(-sqrt3 * y, -y);\n context.lineTo(sqrt3 * y, -y);\n context.closePath();\n }\n};\n","var c = -0.5,\n s = Math.sqrt(3) / 2,\n k = 1 / Math.sqrt(12),\n a = (k / 2 + 1) * 3;\n\nexport default {\n draw: function(context, size) {\n var r = Math.sqrt(size / a),\n x0 = r / 2,\n y0 = r * k,\n x1 = x0,\n y1 = r * k + r,\n x2 = -x1,\n y2 = y1;\n context.moveTo(x0, y0);\n context.lineTo(x1, y1);\n context.lineTo(x2, y2);\n context.lineTo(c * x0 - s * y0, s * x0 + c * y0);\n context.lineTo(c * x1 - s * y1, s * x1 + c * y1);\n context.lineTo(c * x2 - s * y2, s * x2 + c * y2);\n context.lineTo(c * x0 + s * y0, c * y0 - s * x0);\n context.lineTo(c * x1 + s * y1, c * y1 - s * x1);\n context.lineTo(c * x2 + s * y2, c * y2 - s * x2);\n context.closePath();\n }\n};\n","export function point(that, x, y) {\n that._context.bezierCurveTo(\n (2 * that._x0 + that._x1) / 3,\n (2 * that._y0 + that._y1) / 3,\n (that._x0 + 2 * that._x1) / 3,\n (that._y0 + 2 * that._y1) / 3,\n (that._x0 + 4 * that._x1 + x) / 6,\n (that._y0 + 4 * that._y1 + y) / 6\n );\n}\n\nexport function Basis(context) {\n this._context = context;\n}\n\nBasis.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 3: point(this, this._x1, this._y1); // proceed\n case 2: this._context.lineTo(this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\nexport default function(context) {\n return new Basis(context);\n}\n","import noop from \"../noop\";\nimport {point} from \"./basis\";\n\nfunction BasisClosed(context) {\n this._context = context;\n}\n\nBasisClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x2, this._y2);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);\n this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x2, this._y2);\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x2 = x, this._y2 = y; break;\n case 1: this._point = 2; this._x3 = x, this._y3 = y; break;\n case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\nexport default function(context) {\n return new BasisClosed(context);\n}\n","import {point} from \"./basis\";\n\nfunction BasisOpen(context) {\n this._context = context;\n}\n\nBasisOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;\n case 3: this._point = 4; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\nexport default function(context) {\n return new BasisOpen(context);\n}\n","import {Basis} from \"./basis\";\n\nfunction Bundle(context, beta) {\n this._basis = new Basis(context);\n this._beta = beta;\n}\n\nBundle.prototype = {\n lineStart: function() {\n this._x = [];\n this._y = [];\n this._basis.lineStart();\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n j = x.length - 1;\n\n if (j > 0) {\n var x0 = x[0],\n y0 = y[0],\n dx = x[j] - x0,\n dy = y[j] - y0,\n i = -1,\n t;\n\n while (++i <= j) {\n t = i / j;\n this._basis.point(\n this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),\n this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)\n );\n }\n }\n\n this._x = this._y = null;\n this._basis.lineEnd();\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\nexport default (function custom(beta) {\n\n function bundle(context) {\n return beta === 1 ? new Basis(context) : new Bundle(context, beta);\n }\n\n bundle.beta = function(beta) {\n return custom(+beta);\n };\n\n return bundle;\n})(0.85);\n","export function point(that, x, y) {\n that._context.bezierCurveTo(\n that._x1 + that._k * (that._x2 - that._x0),\n that._y1 + that._k * (that._y2 - that._y0),\n that._x2 + that._k * (that._x1 - x),\n that._y2 + that._k * (that._y1 - y),\n that._x2,\n that._y2\n );\n}\n\nexport function Cardinal(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinal.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: point(this, this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; this._x1 = x, this._y1 = y; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(tension) {\n\n function cardinal(context) {\n return new Cardinal(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0);\n","import noop from \"../noop\";\nimport {point} from \"./cardinal\";\n\nexport function CardinalClosed(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(tension) {\n\n function cardinal(context) {\n return new CardinalClosed(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0);\n","import {point} from \"./cardinal\";\n\nexport function CardinalOpen(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(tension) {\n\n function cardinal(context) {\n return new CardinalOpen(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0);\n","import {epsilon} from \"../math\";\nimport {Cardinal} from \"./cardinal\";\n\nexport function point(that, x, y) {\n var x1 = that._x1,\n y1 = that._y1,\n x2 = that._x2,\n y2 = that._y2;\n\n if (that._l01_a > epsilon) {\n var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,\n n = 3 * that._l01_a * (that._l01_a + that._l12_a);\n x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;\n y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;\n }\n\n if (that._l23_a > epsilon) {\n var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,\n m = 3 * that._l23_a * (that._l23_a + that._l12_a);\n x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;\n y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;\n }\n\n that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);\n}\n\nfunction CatmullRom(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRom.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: this.point(this._x2, this._y2); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5);\n","import {CardinalClosed} from \"./cardinalClosed\";\nimport noop from \"../noop\";\nimport {point} from \"./catmullRom\";\n\nfunction CatmullRomClosed(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5);\n","import {CardinalOpen} from \"./cardinalOpen\";\nimport {point} from \"./catmullRom\";\n\nfunction CatmullRomOpen(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\nexport default (function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5);\n","import noop from \"../noop\";\n\nfunction LinearClosed(context) {\n this._context = context;\n}\n\nLinearClosed.prototype = {\n areaStart: noop,\n areaEnd: noop,\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._point) this._context.closePath();\n },\n point: function(x, y) {\n x = +x, y = +y;\n if (this._point) this._context.lineTo(x, y);\n else this._point = 1, this._context.moveTo(x, y);\n }\n};\n\nexport default function(context) {\n return new LinearClosed(context);\n}\n","function sign(x) {\n return x < 0 ? -1 : 1;\n}\n\n// Calculate the slopes of the tangents (Hermite-type interpolation) based on\n// the following paper: Steffen, M. 1990. A Simple Method for Monotonic\n// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.\n// NOV(II), P. 443, 1990.\nfunction slope3(that, x2, y2) {\n var h0 = that._x1 - that._x0,\n h1 = x2 - that._x1,\n s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),\n s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),\n p = (s0 * h1 + s1 * h0) / (h0 + h1);\n return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;\n}\n\n// Calculate a one-sided slope.\nfunction slope2(that, t) {\n var h = that._x1 - that._x0;\n return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;\n}\n\n// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations\n// \"you can express cubic Hermite interpolation in terms of cubic Bézier curves\n// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1\".\nfunction point(that, t0, t1) {\n var x0 = that._x0,\n y0 = that._y0,\n x1 = that._x1,\n y1 = that._y1,\n dx = (x1 - x0) / 3;\n that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);\n}\n\nfunction MonotoneX(context) {\n this._context = context;\n}\n\nMonotoneX.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 =\n this._t0 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x1, this._y1); break;\n case 3: point(this, this._t0, slope2(this, this._t0)); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n var t1 = NaN;\n\n x = +x, y = +y;\n if (x === this._x1 && y === this._y1) return; // Ignore coincident points.\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; point(this, slope2(this, t1 = slope3(this, x, y)), t1); break;\n default: point(this, this._t0, t1 = slope3(this, x, y)); break;\n }\n\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n this._t0 = t1;\n }\n}\n\nfunction MonotoneY(context) {\n this._context = new ReflectContext(context);\n}\n\n(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {\n MonotoneX.prototype.point.call(this, y, x);\n};\n\nfunction ReflectContext(context) {\n this._context = context;\n}\n\nReflectContext.prototype = {\n moveTo: function(x, y) { this._context.moveTo(y, x); },\n closePath: function() { this._context.closePath(); },\n lineTo: function(x, y) { this._context.lineTo(y, x); },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }\n};\n\nexport function monotoneX(context) {\n return new MonotoneX(context);\n}\n\nexport function monotoneY(context) {\n return new MonotoneY(context);\n}\n","function Natural(context) {\n this._context = context;\n}\n\nNatural.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = [];\n this._y = [];\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n n = x.length;\n\n if (n) {\n this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);\n if (n === 2) {\n this._context.lineTo(x[1], y[1]);\n } else {\n var px = controlPoints(x),\n py = controlPoints(y);\n for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {\n this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);\n }\n }\n }\n\n if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();\n this._line = 1 - this._line;\n this._x = this._y = null;\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\n// See https://www.particleincell.com/2012/bezier-splines/ for derivation.\nfunction controlPoints(x) {\n var i,\n n = x.length - 1,\n m,\n a = new Array(n),\n b = new Array(n),\n r = new Array(n);\n a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];\n for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];\n a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];\n for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];\n a[n - 1] = r[n - 1] / b[n - 1];\n for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];\n b[n - 1] = (x[n] + a[n - 1]) / 2;\n for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];\n return [a, b];\n}\n\nexport default function(context) {\n return new Natural(context);\n}\n","function Step(context, t) {\n this._context = context;\n this._t = t;\n}\n\nStep.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = this._y = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: {\n if (this._t <= 0) {\n this._context.lineTo(this._x, y);\n this._context.lineTo(x, y);\n } else {\n var x1 = this._x * (1 - this._t) + x * this._t;\n this._context.lineTo(x1, this._y);\n this._context.lineTo(x1, y);\n }\n break;\n }\n }\n this._x = x, this._y = y;\n }\n};\n\nexport default function(context) {\n return new Step(context, 0.5);\n}\n\nexport function stepBefore(context) {\n return new Step(context, 0);\n}\n\nexport function stepAfter(context) {\n return new Step(context, 1);\n}\n","import ascending from \"./ascending\";\n\nexport default function(series) {\n return ascending(series).reverse();\n}\n","import { arc as arcFactory } from \"d3-shape\";\n\nimport {\n LinkedVisualConsoleProps,\n UnknownObject,\n WithModuleProps\n} from \"../types\";\nimport {\n linkedVCPropsDecoder,\n modulePropsDecoder,\n notEmptyStringOr,\n parseIntOr,\n parseFloatOr\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type PercentileProps = {\n type: ItemType.PERCENTILE_BAR;\n percentileType:\n | \"progress-bar\"\n | \"bubble\"\n | \"circular-progress-bar\"\n | \"circular-progress-bar-alt\";\n valueType: \"percent\" | \"value\";\n minValue: number | null;\n maxValue: number | null;\n color: string | null;\n labelColor: string | null;\n value: number | null;\n unit: string | null;\n} & ItemProps &\n WithModuleProps &\n LinkedVisualConsoleProps;\n\n/**\n * Extract a valid enum value from a raw type value.\n * @param type Raw value.\n */\nfunction extractPercentileType(\n type: unknown\n): PercentileProps[\"percentileType\"] {\n switch (type) {\n case \"progress-bar\":\n case \"bubble\":\n case \"circular-progress-bar\":\n case \"circular-progress-bar-alt\":\n return type;\n default:\n case ItemType.PERCENTILE_BAR:\n return \"progress-bar\";\n case ItemType.PERCENTILE_BUBBLE:\n return \"bubble\";\n case ItemType.CIRCULAR_PROGRESS_BAR:\n return \"circular-progress-bar\";\n case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:\n return \"circular-progress-bar-alt\";\n }\n}\n\n/**\n * Extract a valid enum value from a raw value type value.\n * @param type Raw value.\n */\nfunction extractValueType(valueType: unknown): PercentileProps[\"valueType\"] {\n switch (valueType) {\n case \"percent\":\n case \"value\":\n return valueType;\n default:\n return \"percent\";\n }\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the percentile props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function percentilePropsDecoder(\n data: UnknownObject\n): PercentileProps | never {\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.PERCENTILE_BAR,\n percentileType: extractPercentileType(data.percentileType || data.type),\n valueType: extractValueType(data.valueType),\n minValue: parseIntOr(data.minValue, null),\n maxValue: parseIntOr(data.maxValue, null),\n color: notEmptyStringOr(data.color, null),\n labelColor: notEmptyStringOr(data.labelColor, null),\n value: parseFloatOr(data.value, null),\n unit: notEmptyStringOr(data.unit, null),\n ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.\n };\n}\n\nconst svgNS = \"http://www.w3.org/2000/svg\";\n\nexport default class Percentile extends Item {\n protected createDomElement(): HTMLElement {\n const colors = {\n background: \"#000000\",\n progress: this.props.color || \"#F0F0F0\",\n text: this.props.labelColor || \"#444444\"\n };\n // Progress.\n const progress = this.getProgress();\n // Main element.\n const element = document.createElement(\"div\");\n // SVG container.\n const svg = document.createElementNS(svgNS, \"svg\");\n\n var formatValue;\n if (this.props.value != null) {\n if (Intl) {\n formatValue = Intl.NumberFormat(\"en-EN\").format(this.props.value);\n } else {\n formatValue = this.props.value;\n }\n }\n\n switch (this.props.percentileType) {\n case \"progress-bar\":\n {\n const backgroundRect = document.createElementNS(svgNS, \"rect\");\n backgroundRect.setAttribute(\"fill\", colors.background);\n backgroundRect.setAttribute(\"fill-opacity\", \"0.5\");\n backgroundRect.setAttribute(\"width\", \"100\");\n backgroundRect.setAttribute(\"height\", \"20\");\n backgroundRect.setAttribute(\"rx\", \"5\");\n backgroundRect.setAttribute(\"ry\", \"5\");\n const progressRect = document.createElementNS(svgNS, \"rect\");\n progressRect.setAttribute(\"fill\", colors.progress);\n progressRect.setAttribute(\"fill-opacity\", \"1\");\n progressRect.setAttribute(\"width\", `${progress}`);\n progressRect.setAttribute(\"height\", \"20\");\n progressRect.setAttribute(\"rx\", \"5\");\n progressRect.setAttribute(\"ry\", \"5\");\n const text = document.createElementNS(svgNS, \"text\");\n text.setAttribute(\"text-anchor\", \"middle\");\n text.setAttribute(\"alignment-baseline\", \"middle\");\n text.setAttribute(\"font-size\", \"12\");\n text.setAttribute(\"font-family\", \"arial\");\n text.setAttribute(\"font-weight\", \"bold\");\n text.setAttribute(\"transform\", \"translate(50 11)\");\n text.setAttribute(\"fill\", colors.text);\n\n if (this.props.valueType === \"value\") {\n text.style.fontSize = \"6pt\";\n\n text.textContent = this.props.unit\n ? `${formatValue} ${this.props.unit}`\n : `${formatValue}`;\n } else {\n text.textContent = `${progress}%`;\n }\n\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 20\");\n svg.append(backgroundRect, progressRect, text);\n }\n break;\n case \"bubble\":\n case \"circular-progress-bar\":\n case \"circular-progress-bar-alt\":\n {\n // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/\n svg.setAttribute(\"viewBox\", \"0 0 100 100\");\n\n if (this.props.percentileType === \"bubble\") {\n // Create and append the circles.\n const backgroundCircle = document.createElementNS(svgNS, \"circle\");\n backgroundCircle.setAttribute(\"transform\", \"translate(50 50)\");\n backgroundCircle.setAttribute(\"fill\", colors.background);\n backgroundCircle.setAttribute(\"fill-opacity\", \"0.5\");\n backgroundCircle.setAttribute(\"r\", \"50\");\n const progressCircle = document.createElementNS(svgNS, \"circle\");\n progressCircle.setAttribute(\"transform\", \"translate(50 50)\");\n progressCircle.setAttribute(\"fill\", colors.progress);\n progressCircle.setAttribute(\"fill-opacity\", \"1\");\n progressCircle.setAttribute(\"r\", `${progress / 2}`);\n\n svg.append(backgroundCircle, progressCircle);\n } else {\n // Create and append the circles.\n const arcProps = {\n innerRadius:\n this.props.percentileType === \"circular-progress-bar\" ? 30 : 0,\n outerRadius: 50,\n startAngle: 0,\n endAngle: Math.PI * 2\n };\n const arc = arcFactory();\n\n const backgroundCircle = document.createElementNS(svgNS, \"path\");\n backgroundCircle.setAttribute(\"transform\", \"translate(50 50)\");\n backgroundCircle.setAttribute(\"fill\", colors.background);\n backgroundCircle.setAttribute(\"fill-opacity\", \"0.5\");\n backgroundCircle.setAttribute(\"d\", `${arc(arcProps)}`);\n const progressCircle = document.createElementNS(svgNS, \"path\");\n progressCircle.setAttribute(\"transform\", \"translate(50 50)\");\n progressCircle.setAttribute(\"fill\", colors.progress);\n progressCircle.setAttribute(\"fill-opacity\", \"1\");\n progressCircle.setAttribute(\n \"d\",\n `${arc({\n ...arcProps,\n endAngle: arcProps.endAngle * (progress / 100)\n })}`\n );\n\n svg.append(backgroundCircle, progressCircle);\n }\n\n // Create and append the text.\n const text = document.createElementNS(svgNS, \"text\");\n text.setAttribute(\"text-anchor\", \"middle\");\n text.setAttribute(\"alignment-baseline\", \"middle\");\n text.setAttribute(\"font-size\", \"16\");\n text.setAttribute(\"font-family\", \"arial\");\n text.setAttribute(\"font-weight\", \"bold\");\n text.setAttribute(\"fill\", colors.text);\n\n if (this.props.valueType === \"value\" && this.props.value != null) {\n // Show value and unit in 1 (no unit) or 2 lines.\n if (this.props.unit && this.props.unit.length > 0) {\n const value = document.createElementNS(svgNS, \"tspan\");\n value.setAttribute(\"x\", \"0\");\n value.setAttribute(\"dy\", \"1em\");\n value.textContent = `${formatValue}`;\n value.style.fontSize = \"8pt\";\n const unit = document.createElementNS(svgNS, \"tspan\");\n unit.setAttribute(\"x\", \"0\");\n unit.setAttribute(\"dy\", \"1em\");\n unit.textContent = `${this.props.unit}`;\n unit.style.fontSize = \"8pt\";\n text.append(value, unit);\n text.setAttribute(\"transform\", \"translate(50 33)\");\n } else {\n text.textContent = `${formatValue}`;\n text.style.fontSize = \"8pt\";\n text.setAttribute(\"transform\", \"translate(50 50)\");\n }\n } else {\n // Percentage.\n text.textContent = `${progress}%`;\n text.setAttribute(\"transform\", \"translate(50 50)\");\n }\n\n svg.append(text);\n }\n break;\n }\n\n element.append(svg);\n\n return element;\n }\n\n private getProgress(): number {\n const minValue = this.props.minValue || 0;\n const maxValue = this.props.maxValue || 100;\n const value = this.props.value == null ? 0 : this.props.value;\n\n if (value <= minValue) return 0;\n else if (value >= maxValue) return 100;\n else return Math.trunc(((value - minValue) / (maxValue - minValue)) * 100);\n }\n}\n","import { UnknownObject } from \"../types\";\nimport {\n stringIsEmpty,\n notEmptyStringOr,\n decodeBase64,\n parseIntOr\n} from \"../lib\";\nimport Item, { ItemType, ItemProps, itemBasePropsDecoder } from \"../Item\";\n\nexport type ServiceProps = {\n type: ItemType.SERVICE;\n serviceId: number;\n imageSrc: string | null;\n statusImageSrc: string | null;\n encodedTitle: string | null;\n} & ItemProps;\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the service props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function servicePropsDecoder(data: UnknownObject): ServiceProps | never {\n if (data.imageSrc !== null) {\n if (\n typeof data.statusImageSrc !== \"string\" ||\n data.imageSrc.statusImageSrc === 0\n ) {\n throw new TypeError(\"invalid status image src.\");\n }\n } else {\n if (stringIsEmpty(data.encodedTitle)) {\n throw new TypeError(\"missing encode tittle content.\");\n }\n }\n\n if (parseIntOr(data.serviceId, null) === null) {\n throw new TypeError(\"invalid service id.\");\n }\n\n return {\n ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.\n type: ItemType.SERVICE,\n serviceId: data.serviceId,\n imageSrc: notEmptyStringOr(data.imageSrc, null),\n statusImageSrc: notEmptyStringOr(data.statusImageSrc, null),\n encodedTitle: notEmptyStringOr(data.encodedTitle, null)\n };\n}\n\nexport default class Service extends Item {\n public createDomElement(): HTMLElement {\n const element = document.createElement(\"div\");\n element.className = \"service\";\n\n if (this.props.statusImageSrc !== null) {\n element.style.background = `url(${this.props.statusImageSrc}) no-repeat`;\n element.style.backgroundSize = \"contain\";\n element.style.backgroundPosition = \"center\";\n } else if (this.props.encodedTitle !== null) {\n element.innerHTML = decodeBase64(this.props.encodedTitle);\n }\n\n return element;\n }\n}\n","import { UnknownObject, Size } from \"./types\";\nimport {\n parseBoolean,\n sizePropsDecoder,\n parseIntOr,\n notEmptyStringOr\n} from \"./lib\";\nimport Item, {\n ItemType,\n ItemProps,\n ItemClickEvent,\n ItemRemoveEvent\n} from \"./Item\";\nimport StaticGraph, { staticGraphPropsDecoder } from \"./items/StaticGraph\";\nimport Icon, { iconPropsDecoder } from \"./items/Icon\";\nimport ColorCloud, { colorCloudPropsDecoder } from \"./items/ColorCloud\";\nimport Group, { groupPropsDecoder } from \"./items/Group\";\nimport Clock, { clockPropsDecoder } from \"./items/Clock\";\nimport Box, { boxPropsDecoder } from \"./items/Box\";\nimport Line, { linePropsDecoder } from \"./items/Line\";\nimport Label, { labelPropsDecoder } from \"./items/Label\";\nimport SimpleValue, { simpleValuePropsDecoder } from \"./items/SimpleValue\";\nimport EventsHistory, {\n eventsHistoryPropsDecoder\n} from \"./items/EventsHistory\";\nimport Percentile, { percentilePropsDecoder } from \"./items/Percentile\";\nimport TypedEvent, { Disposable, Listener } from \"./TypedEvent\";\nimport DonutGraph, { donutGraphPropsDecoder } from \"./items/DonutGraph\";\nimport BarsGraph, { barsGraphPropsDecoder } from \"./items/BarsGraph\";\nimport ModuleGraph, { moduleGraphPropsDecoder } from \"./items/ModuleGraph\";\nimport Service, { servicePropsDecoder } from \"./items/Service\";\n\n// TODO: Document.\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nfunction itemInstanceFrom(data: UnknownObject) {\n const type = parseIntOr(data.type, null);\n if (type == null) throw new TypeError(\"missing item type.\");\n\n switch (type as ItemType) {\n case ItemType.STATIC_GRAPH:\n return new StaticGraph(staticGraphPropsDecoder(data));\n case ItemType.MODULE_GRAPH:\n return new ModuleGraph(moduleGraphPropsDecoder(data));\n case ItemType.SIMPLE_VALUE:\n case ItemType.SIMPLE_VALUE_MAX:\n case ItemType.SIMPLE_VALUE_MIN:\n case ItemType.SIMPLE_VALUE_AVG:\n return new SimpleValue(simpleValuePropsDecoder(data));\n case ItemType.PERCENTILE_BAR:\n case ItemType.PERCENTILE_BUBBLE:\n case ItemType.CIRCULAR_PROGRESS_BAR:\n case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:\n return new Percentile(percentilePropsDecoder(data));\n case ItemType.LABEL:\n return new Label(labelPropsDecoder(data));\n case ItemType.ICON:\n return new Icon(iconPropsDecoder(data));\n case ItemType.SERVICE:\n return new Service(servicePropsDecoder(data));\n case ItemType.GROUP_ITEM:\n return new Group(groupPropsDecoder(data));\n case ItemType.BOX_ITEM:\n return new Box(boxPropsDecoder(data));\n case ItemType.LINE_ITEM:\n return new Line(linePropsDecoder(data));\n case ItemType.AUTO_SLA_GRAPH:\n return new EventsHistory(eventsHistoryPropsDecoder(data));\n case ItemType.DONUT_GRAPH:\n return new DonutGraph(donutGraphPropsDecoder(data));\n case ItemType.BARS_GRAPH:\n return new BarsGraph(barsGraphPropsDecoder(data));\n case ItemType.CLOCK:\n return new Clock(clockPropsDecoder(data));\n case ItemType.COLOR_CLOUD:\n return new ColorCloud(colorCloudPropsDecoder(data));\n default:\n throw new TypeError(\"item not found\");\n }\n}\n\n// TODO: Document.\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nfunction decodeProps(data: UnknownObject) {\n const type = parseIntOr(data.type, null);\n if (type == null) throw new TypeError(\"missing item type.\");\n\n switch (type as ItemType) {\n case ItemType.STATIC_GRAPH:\n return staticGraphPropsDecoder(data);\n case ItemType.MODULE_GRAPH:\n return moduleGraphPropsDecoder(data);\n case ItemType.SIMPLE_VALUE:\n case ItemType.SIMPLE_VALUE_MAX:\n case ItemType.SIMPLE_VALUE_MIN:\n case ItemType.SIMPLE_VALUE_AVG:\n return simpleValuePropsDecoder(data);\n case ItemType.PERCENTILE_BAR:\n case ItemType.PERCENTILE_BUBBLE:\n case ItemType.CIRCULAR_PROGRESS_BAR:\n case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR:\n return percentilePropsDecoder(data);\n case ItemType.LABEL:\n return labelPropsDecoder(data);\n case ItemType.ICON:\n return iconPropsDecoder(data);\n case ItemType.SERVICE:\n return servicePropsDecoder(data);\n case ItemType.GROUP_ITEM:\n return groupPropsDecoder(data);\n case ItemType.BOX_ITEM:\n return boxPropsDecoder(data);\n case ItemType.LINE_ITEM:\n return linePropsDecoder(data);\n case ItemType.AUTO_SLA_GRAPH:\n return eventsHistoryPropsDecoder(data);\n case ItemType.DONUT_GRAPH:\n return donutGraphPropsDecoder(data);\n case ItemType.BARS_GRAPH:\n return barsGraphPropsDecoder(data);\n case ItemType.CLOCK:\n return clockPropsDecoder(data);\n case ItemType.COLOR_CLOUD:\n return colorCloudPropsDecoder(data);\n default:\n throw new TypeError(\"decoder not found\");\n }\n}\n\n// Base properties.\nexport interface VisualConsoleProps extends Size {\n readonly id: number;\n name: string;\n groupId: number;\n backgroundURL: string | null; // URL?\n backgroundColor: string | null;\n isFavorite: boolean;\n relationLineWidth: number;\n}\n\n/**\n * Build a valid typed object from a raw object.\n * This will allow us to ensure the type safety.\n *\n * @param data Raw object.\n * @return An object representing the Visual Console props.\n * @throws Will throw a TypeError if some property\n * is missing from the raw object or have an invalid type.\n */\nexport function visualConsolePropsDecoder(\n data: UnknownObject\n): VisualConsoleProps | never {\n // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation\n const {\n id,\n name,\n groupId,\n backgroundURL,\n backgroundColor,\n isFavorite,\n relationLineWidth\n } = data;\n\n if (id == null || isNaN(parseInt(id))) {\n throw new TypeError(\"invalid Id.\");\n }\n if (typeof name !== \"string\" || name.length === 0) {\n throw new TypeError(\"invalid name.\");\n }\n if (groupId == null || isNaN(parseInt(groupId))) {\n throw new TypeError(\"invalid group Id.\");\n }\n\n return {\n id: parseInt(id),\n name,\n groupId: parseInt(groupId),\n backgroundURL: notEmptyStringOr(backgroundURL, null),\n backgroundColor: notEmptyStringOr(backgroundColor, null),\n isFavorite: parseBoolean(isFavorite),\n relationLineWidth: parseIntOr(relationLineWidth, 0),\n ...sizePropsDecoder(data)\n };\n}\n\nexport default class VisualConsole {\n // Reference to the DOM element which will contain the items.\n private readonly containerRef: HTMLElement;\n // Properties.\n private _props: VisualConsoleProps;\n // Visual Console Item instances by their Id.\n private elementsById: {\n [key: number]: Item;\n } = {};\n // Visual Console Item Ids.\n private elementIds: ItemProps[\"id\"][] = [];\n // Dictionary which store the created lines.\n private relations: {\n [key: string]: Line;\n } = {};\n // Event manager for click events.\n private readonly clickEventManager = new TypedEvent<\n ItemClickEvent\n >();\n // List of references to clean the event listeners.\n private readonly disposables: Disposable[] = [];\n\n /**\n * React to a click on an element.\n * @param e Event object.\n */\n private handleElementClick: (e: ItemClickEvent) => void = e => {\n this.clickEventManager.emit(e);\n // console.log(`Clicked element #${e.data.id}`, e);\n };\n\n /**\n * Clear some element references.\n * @param e Event object.\n */\n private handleElementRemove: (e: ItemRemoveEvent) => void = e => {\n // Remove the element from the list and its relations.\n this.elementIds = this.elementIds.filter(id => id !== e.data.id);\n delete this.elementsById[e.data.id];\n this.clearRelations(e.data.id);\n };\n\n public constructor(\n container: HTMLElement,\n props: UnknownObject,\n items: UnknownObject[]\n ) {\n this.containerRef = container;\n this._props = visualConsolePropsDecoder(props);\n\n // Force the first render.\n this.render();\n\n // Sort by isOnTop, id ASC\n items = items.sort(function(a, b) {\n if (\n a.isOnTop == null ||\n b.isOnTop == null ||\n a.id == null ||\n b.id == null\n ) {\n return 0;\n }\n\n if (a.isOnTop && !b.isOnTop) return 1;\n else if (!a.isOnTop && b.isOnTop) return -1;\n else if (a.id > b.id) return 1;\n else return -1;\n });\n\n // Initialize the items.\n items.forEach(item => {\n try {\n const itemInstance = itemInstanceFrom(item);\n // Add the item to the list.\n this.elementsById[itemInstance.props.id] = itemInstance;\n this.elementIds.push(itemInstance.props.id);\n // Item event handlers.\n itemInstance.onClick(this.handleElementClick);\n itemInstance.onRemove(this.handleElementRemove);\n // Add the item to the DOM.\n this.containerRef.append(itemInstance.elementRef);\n } catch (error) {\n console.log(\"Error creating a new element:\", error.message);\n }\n });\n\n // Create lines.\n this.buildRelations();\n }\n\n /**\n * Public accessor of the `elements` property.\n * @return Properties.\n */\n public get elements(): Item[] {\n // Ensure the type cause Typescript doesn't know the filter removes null items.\n return this.elementIds\n .map(id => this.elementsById[id])\n .filter(_ => _ != null) as Item[];\n }\n\n /**\n * Public setter of the `elements` property.\n * @param items.\n */\n public updateElements(items: UnknownObject[]): void {\n const itemIds = items.map(item => item.id || null).filter(id => id != null);\n itemIds as number[]; // Tell the type system to rely on us.\n // Get the elements we should delete.\n const deletedIds: number[] = this.elementIds.filter(\n id => itemIds.indexOf(id) < 0\n );\n // Delete the elements.\n deletedIds.forEach(id => {\n if (this.elementsById[id] != null) {\n this.elementsById[id].remove();\n delete this.elementsById[id];\n }\n });\n // Replace the element ids.\n this.elementIds = itemIds;\n\n // Initialize the items.\n items.forEach(item => {\n if (item.id) {\n if (this.elementsById[item.id] == null) {\n // New item.\n try {\n const itemInstance = itemInstanceFrom(item);\n // Add the item to the list.\n this.elementsById[itemInstance.props.id] = itemInstance;\n // Item event handlers.\n itemInstance.onClick(this.handleElementClick);\n itemInstance.onRemove(this.handleElementRemove);\n // Add the item to the DOM.\n this.containerRef.append(itemInstance.elementRef);\n } catch (error) {\n console.log(\"Error creating a new element:\", error.message);\n }\n } else {\n // Update item.\n try {\n this.elementsById[item.id].props = decodeProps(item);\n } catch (error) {\n console.log(\"Error updating an element:\", error.message);\n }\n }\n }\n });\n\n // Re-build relations.\n this.buildRelations();\n }\n\n /**\n * Public accessor of the `props` property.\n * @return Properties.\n */\n public get props(): VisualConsoleProps {\n return { ...this._props }; // Return a copy.\n }\n\n /**\n * Public setter of the `props` property.\n * If the new props are different enough than the\n * stored props, a render would be fired.\n * @param newProps\n */\n public set props(newProps: VisualConsoleProps) {\n const prevProps = this.props;\n // Update the internal props.\n this._props = newProps;\n\n // From this point, things which rely on this.props can access to the changes.\n\n // Re-render.\n this.render(prevProps);\n }\n\n /**\n * Recreate or update the HTMLElement which represents the Visual Console into the DOM.\n * @param prevProps If exists it will be used to only DOM updates instead of a full replace.\n */\n public render(prevProps: VisualConsoleProps | null = null): void {\n if (prevProps) {\n if (prevProps.backgroundURL !== this.props.backgroundURL) {\n this.containerRef.style.backgroundImage =\n this.props.backgroundURL !== null\n ? `url(${this.props.backgroundURL})`\n : null;\n }\n if (prevProps.backgroundColor !== this.props.backgroundColor) {\n this.containerRef.style.backgroundColor = this.props.backgroundColor;\n }\n if (this.sizeChanged(prevProps, this.props)) {\n this.resizeElement(this.props.width, this.props.height);\n }\n } else {\n this.containerRef.style.backgroundImage =\n this.props.backgroundURL !== null\n ? `url(${this.props.backgroundURL})`\n : null;\n\n this.containerRef.style.backgroundColor = this.props.backgroundColor;\n this.resizeElement(this.props.width, this.props.height);\n }\n }\n\n /**\n * Compare the previous and the new size and return\n * a boolean value in case the size changed.\n * @param prevSize\n * @param newSize\n * @return Whether the size changed or not.\n */\n public sizeChanged(prevSize: Size, newSize: Size): boolean {\n return (\n prevSize.width !== newSize.width || prevSize.height !== newSize.height\n );\n }\n\n /**\n * Resize the DOM container.\n * @param width\n * @param height\n */\n public resizeElement(width: number, height: number): void {\n this.containerRef.style.width = `${width}px`;\n this.containerRef.style.height = `${height}px`;\n }\n\n /**\n * Update the size into the properties and resize the DOM container.\n * @param width\n * @param height\n */\n public resize(width: number, height: number): void {\n this.props = {\n ...this.props, // Object spread: http://es6-features.org/#SpreadOperator\n width,\n height\n };\n }\n\n /**\n * To remove the event listeners and the elements from the DOM.\n */\n public remove(): void {\n this.disposables.forEach(d => d.dispose()); // Arrow function.\n this.elements.forEach(e => e.remove()); // Arrow function.\n this.elementsById = {};\n this.elementIds = [];\n // Clear relations.\n this.clearRelations();\n // Clean container.\n this.containerRef.innerHTML = \"\";\n }\n\n /**\n * Create line elements which connect the elements with their parents.\n */\n private buildRelations(): void {\n // Clear relations.\n this.clearRelations();\n // Add relations.\n this.elements.forEach(item => {\n if (item.props.parentId !== null) {\n const parent = this.elementsById[item.props.parentId];\n const child = this.elementsById[item.props.id];\n if (parent && child) this.addRelationLine(parent, child);\n }\n });\n }\n\n /**\n * @param itemId Optional identifier of a parent or child item.\n * Remove the line elements which connect the elements with their parents.\n */\n private clearRelations(itemId?: number): void {\n if (itemId != null) {\n for (let key in this.relations) {\n const ids = key.split(\"|\");\n const parentId = Number.parseInt(ids[0]);\n const childId = Number.parseInt(ids[1]);\n\n if (itemId === parentId || itemId === childId) {\n this.relations[key].remove();\n delete this.relations[key];\n }\n }\n } else {\n for (let key in this.relations) {\n this.relations[key].remove();\n delete this.relations[key];\n }\n }\n }\n\n /**\n * Retrieve the line element which represent the relation between items.\n * @param parentId Identifier of the parent item.\n * @param childId Itentifier of the child item.\n * @return The line element or nothing.\n */\n private getRelationLine(parentId: number, childId: number): Line | null {\n const identifier = `${parentId}|${childId}`;\n return this.relations[identifier] || null;\n }\n\n /**\n * Add a new line item to represent a relation between the items.\n * @param parent Parent item.\n * @param child Child item.\n * @return Whether the line was added or not.\n */\n private addRelationLine(\n parent: Item,\n child: Item\n ): Line {\n const identifier = `${parent.props.id}|${child.props.id}`;\n if (this.relations[identifier] != null) {\n this.relations[identifier].remove();\n }\n\n // Get the items center.\n const startX = parent.props.x + parent.elementRef.clientWidth / 2;\n const startY =\n parent.props.y +\n (parent.elementRef.clientHeight - parent.labelElementRef.clientHeight) /\n 2;\n const endX = child.props.x + child.elementRef.clientWidth / 2;\n const endY =\n child.props.y +\n (child.elementRef.clientHeight - child.labelElementRef.clientHeight) / 2;\n\n const line = new Line(\n linePropsDecoder({\n id: 0,\n type: ItemType.LINE_ITEM,\n startX,\n startY,\n endX,\n endY,\n width: 0,\n height: 0,\n lineWidth: this.props.relationLineWidth,\n color: \"#CCCCCC\"\n })\n );\n // Save a reference to the line item.\n this.relations[identifier] = line;\n\n // Add the line to the DOM.\n line.elementRef.style.zIndex = \"0\";\n this.containerRef.append(line.elementRef);\n\n return line;\n }\n\n /**\n * Add an event handler to the click of the linked visual console elements.\n * @param listener Function which is going to be executed when a linked console is clicked.\n */\n public onClick(listener: Listener>): Disposable {\n /*\n * The '.on' function returns a function which will clean the event\n * listener when executed. We store all the 'dispose' functions to\n * call them when the item should be cleared.\n */\n const disposable = this.clickEventManager.on(listener);\n this.disposables.push(disposable);\n\n return disposable;\n }\n}\n","import TypedEvent, { Disposable, Listener } from \"../TypedEvent\";\n\ninterface Cancellable {\n cancel(): void;\n}\n\ntype AsyncTaskStatus = \"waiting\" | \"started\" | \"cancelled\" | \"finished\";\ntype AsyncTaskInitiator = (done: () => void) => Cancellable;\n\n/**\n * Defines an async task which can be started and cancelled.\n * It's possible to observe the status changes of the task.\n */\nclass AsyncTask {\n private readonly taskInitiator: AsyncTaskInitiator;\n private cancellable: Cancellable = { cancel: () => {} };\n private _status: AsyncTaskStatus = \"waiting\";\n\n // Event manager for status change events.\n private readonly statusChangeEventManager = new TypedEvent();\n // List of references to clean the event listeners.\n private readonly disposables: Disposable[] = [];\n\n public constructor(taskInitiator: AsyncTaskInitiator) {\n this.taskInitiator = taskInitiator;\n }\n\n /**\n * Public setter of the `status` property.\n * @param status.\n */\n public set status(status: AsyncTaskStatus) {\n this._status = status;\n this.statusChangeEventManager.emit(status);\n }\n\n /**\n * Public accessor of the `status` property.\n * @return status.\n */\n public get status() {\n return this._status;\n }\n\n /**\n * Start the async task.\n */\n public init(): void {\n this.cancellable = this.taskInitiator(() => {\n this.status = \"finished\";\n });\n this.status = \"started\";\n }\n\n /**\n * Cancel the async task.\n */\n public cancel(): void {\n this.cancellable.cancel();\n this.status = \"cancelled\";\n }\n\n /**\n * Add an event handler to the status change.\n * @param listener Function which is going to be executed when the status changes.\n */\n public onStatusChange(listener: Listener): Disposable {\n /*\n * The '.on' function returns a function which will clean the event\n * listener when executed. We store all the 'dispose' functions to\n * call them when the item should be cleared.\n */\n const disposable = this.statusChangeEventManager.on(listener);\n this.disposables.push(disposable);\n\n return disposable;\n }\n}\n\n/**\n * Wrap an async task into another which will execute that task indefinitely\n * every time the tash finnish and the chosen period ends.\n * Will last until cancellation.\n *\n * @param task Async task to execute.\n * @param period Time in milliseconds to wait until the next async esecution.\n *\n * @return A new async task.\n */\nfunction asyncPeriodic(task: AsyncTask, period: number): AsyncTask {\n return new AsyncTask(() => {\n let ref: number | null = null;\n\n task.onStatusChange(status => {\n if (status === \"finished\") {\n ref = window.setTimeout(() => {\n task.init();\n }, period);\n }\n });\n\n task.init();\n\n return {\n cancel: () => {\n if (ref) clearTimeout(ref);\n task.cancel();\n }\n };\n });\n}\n\n/**\n * Manages a list of async tasks.\n */\nexport default class AsyncTaskManager {\n private tasks: { [identifier: string]: AsyncTask } = {};\n\n /**\n * Adds an async task to the manager.\n *\n * @param identifier Unique identifier.\n * @param taskInitiator Function to initialize the async task.\n * Should return a structure to cancel the task.\n * @param period Optional period to repeat the task indefinitely.\n */\n public add(\n identifier: string,\n taskInitiator: AsyncTaskInitiator,\n period: number = 0\n ): AsyncTask {\n if (this.tasks[identifier] && this.tasks[identifier].status === \"started\") {\n this.tasks[identifier].cancel();\n }\n\n const asyncTask =\n period > 0\n ? asyncPeriodic(new AsyncTask(taskInitiator), period)\n : new AsyncTask(taskInitiator);\n\n this.tasks[identifier] = asyncTask;\n\n return this.tasks[identifier];\n }\n\n /**\n * Starts an async task.\n *\n * @param identifier Unique identifier.\n */\n public init(identifier: string) {\n if (\n this.tasks[identifier] &&\n (this.tasks[identifier].status === \"waiting\" ||\n this.tasks[identifier].status === \"cancelled\" ||\n this.tasks[identifier].status === \"finished\")\n ) {\n this.tasks[identifier].init();\n }\n }\n\n /**\n * Cancel a running async task.\n *\n * @param identifier Unique identifier.\n */\n public cancel(identifier: string) {\n if (this.tasks[identifier] && this.tasks[identifier].status === \"started\") {\n this.tasks[identifier].cancel();\n }\n }\n}\n","/*\n * Useful resources.\n * http://es6-features.org/\n * http://exploringjs.com/es6\n * https://www.typescriptlang.org/\n */\n\nimport \"./main.css\"; // CSS import.\nimport VisualConsole from \"./VisualConsole\";\nimport AsyncTaskManager from \"./lib/AsyncTaskManager\";\n\n// Export the VisualConsole class to the global object.\n// eslint-disable-next-line\n(window as any).VisualConsole = VisualConsole;\n\n// Export the AsyncTaskManager class to the global object.\n// eslint-disable-next-line\n(window as any).AsyncTaskManager = AsyncTaskManager;\n"],"sourceRoot":""} \ No newline at end of file diff --git a/pandora_console/index.php b/pandora_console/index.php index f0f67ec445..06793c1a87 100755 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -173,8 +173,8 @@ if (!empty($config['https']) && empty($_SERVER['HTTPS'])) { // Pure mode (without menu, header and footer). $config['pure'] = (bool) get_parameter('pure'); -// Auto Refresh page (can now be disabled anywhere in the script) -if (get_parameter('refr')) { +// Auto Refresh page (can now be disabled anywhere in the script). +if (get_parameter('refr') != null) { $config['refr'] = (int) get_parameter('refr'); } @@ -244,6 +244,10 @@ if (strlen($search) > 0) { // Login process if (! isset($config['id_user'])) { + // Clear error messages. + unset($_COOKIE['errormsg']); + setcookie('errormsg', null, -1); + if (isset($_GET['login'])) { include_once 'include/functions_db.php'; // Include it to use escape_string_sql function @@ -619,10 +623,9 @@ if (! isset($config['id_user'])) { header('Location: '.$config['homeurl'].'index.php'.$redirect_url); exit; - // Always exit after sending location headers - } - // Hash login process - else if (isset($_GET['loginhash'])) { + // Always exit after sending location headers. + } else if (isset($_GET['loginhash'])) { + // Hash login process $loginhash_data = get_parameter('loginhash_data', ''); $loginhash_user = str_rot13(get_parameter('loginhash_user', '')); @@ -638,9 +641,8 @@ if (! isset($config['id_user'])) { exit(''); } - } - // There is no user connected - else { + } else { + // There is no user connected. if ($config['enterprise_installed']) { enterprise_include_once('include/functions_reset_pass.php'); } @@ -722,64 +724,55 @@ if (! isset($config['id_user'])) { $show_error = false; if (!$first) { - if ($reset) { - if ($user_reset_pass == '') { + if ($user_reset_pass == '') { + $reset = false; + $error = __('Id user cannot be empty'); + $show_error = true; + } else { + $check_user = check_user_id($user_reset_pass); + + if (!$check_user) { $reset = false; - $error = __('Id user cannot be empty'); + register_pass_change_try($user_reset_pass, 0); + $error = __('Error in reset password request'); $show_error = true; } else { - $check_user = check_user_id($user_reset_pass); + $check_mail = check_user_have_mail($user_reset_pass); - if (!$check_user) { + if (!$check_mail) { $reset = false; register_pass_change_try($user_reset_pass, 0); - $error = __('Error in reset password request'); + $error = __('This user doesn\'t have a valid email address'); $show_error = true; } else { - $check_mail = check_user_have_mail($user_reset_pass); - - if (!$check_mail) { - $reset = false; - register_pass_change_try($user_reset_pass, 0); - $error = __('This user doesn\'t have a valid email address'); - $show_error = true; - } else { - $mail = $check_mail; - } + $mail = $check_mail; } } } - if (!$reset) { - if ($config['enterprise_installed']) { - include_once 'enterprise/include/reset_pass.php'; - } + $cod_hash = $user_reset_pass.'::::'.md5(rand(10, 1000000).rand(10, 1000000).rand(10, 1000000)); + + $subject = '['.io_safe_output(get_product_name()).'] '.__('Reset password'); + $body = __('This is an automatically sent message for user '); + $body .= ' "'.$user_reset_pass.'"'; + $body .= '

    '; + $body .= __('Please click the link below to reset your password'); + $body .= '

    '; + $body .= ''.__('Reset your password').''; + $body .= '

    '; + $body .= get_product_name(); + $body .= '

    '; + $body .= ''.__('Please do not reply to this email.').''; + + $result = send_email_to_user($mail, $body, $subject); + + if (!$result) { + $process_error_message = __('Error at sending the email'); } else { - $cod_hash = $user_reset_pass.'::::'.md5(rand(10, 1000000).rand(10, 1000000).rand(10, 1000000)); - - $subject = '['.get_product_name().'] '.__('Reset password'); - $body = __('This is an automatically sent message for user '); - $body .= ' "'.$user_reset_pass.'"'; - $body .= '

    '; - $body .= __('Please click the link below to reset your password'); - $body .= '

    '; - $body .= ''.__('Reset your password').''; - $body .= '

    '; - $body .= get_product_name(); - $body .= '

    '; - $body .= ''.__('Please do not reply to this email.').''; - - $result = send_email_to_user($mail, $body, $subject); - - $process_error_message = ''; - if (!$result) { - $process_error_message = __('Error at sending the email'); - } else { - send_token_to_db($user_reset_pass, $cod_hash); - } - - include_once 'general/login_page.php'; + send_token_to_db($user_reset_pass, $cod_hash); } + + include_once 'general/login_page.php'; } else { include_once 'enterprise/include/reset_pass.php'; } @@ -898,15 +891,6 @@ clear_pandora_error_for_header(); $config['logged'] = false; extensions_load_extensions($process_login); -// Check for update manager messages -if (license_free() && is_user_admin($config['id_user']) - && (($config['last_um_check'] < time()) - || (!isset($config['last_um_check']))) -) { - include_once 'include/functions_update_manager.php'; - update_manager_download_messages(); -} - if ($process_login) { // Call all extensions login function extensions_call_login_function(); @@ -982,31 +966,9 @@ if ($old_global_counter_chat != $now_global_counter_chat) { } } -// Pop-ups display order: -// 1) login_required (timezone and email) -// 2) identification (newsletter and register) -// 3) last_message (update manager message popup -// 4) login_help (online help, enterpirse version, forums, documentation) -if (is_user_admin($config['id_user']) - && (!isset($config['initial_wizard']) || $config['initial_wizard'] != 1) -) { - include_once 'general/login_required.php'; -} +require_once 'general/register.php'; if (get_parameter('login', 0) !== 0) { - // Display news dialog - include_once 'general/news_dialog.php'; - - // Display login help info dialog - // If it's configured to not skip this - $display_previous_popup = false; - if (license_free() && is_user_admin($config['id_user']) && $config['initial_wizard'] == 1) { - $display_previous_popup = include_once 'general/login_identification_wizard.php'; - if ($display_previous_popup === false) { - $display_previous_popup = include_once 'general/last_message.php'; - } - } - if ((!isset($config['skip_login_help_dialog']) || $config['skip_login_help_dialog'] == 0) && $display_previous_popup === false && $config['initial_wizard'] == 1 @@ -1025,7 +987,13 @@ if (get_parameter('login', 0) !== 0) { if ($config['pure'] == 0) { echo '

    '; echo ''; @@ -1057,6 +1025,9 @@ if ($searchPage) { if ($main_sec == false) { if ($sec == 'extensions') { $main_sec = get_parameter('extension_in_menu'); + if (empty($main_sec) === true) { + $main_sec = $sec; + } } else if ($sec == 'gextensions') { $main_sec = get_parameter('extension_in_menu'); } else { @@ -1210,6 +1181,11 @@ if ($config['pure'] == 0) { // main_pure } +echo '
    '; +echo '
    '; + +echo '
    '; +echo '
    '; if ($config['pure'] == 0) { echo '
    '; @@ -1241,6 +1217,11 @@ require 'include/php_to_js_values.php'; \ No newline at end of file + diff --git a/pandora_console/operation/agentes/custom_fields.php b/pandora_console/operation/agentes/custom_fields.php index a0c123ea5e..a98f92d4ee 100644 --- a/pandora_console/operation/agentes/custom_fields.php +++ b/pandora_console/operation/agentes/custom_fields.php @@ -60,7 +60,7 @@ if ($fields === false) { } else { $table = new stdClass(); $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->head = []; $table->head[0] = __('Field'); $table->size[0] = '20%'; diff --git a/pandora_console/operation/agentes/ehorus.php b/pandora_console/operation/agentes/ehorus.php index 51fca3a801..2091aa5d60 100644 --- a/pandora_console/operation/agentes/ehorus.php +++ b/pandora_console/operation/agentes/ehorus.php @@ -58,6 +58,12 @@ $curl_timeout = $config['ehorus_req_timeout']; $base_url = 'https://'.$hostname.':'.$port; +// If user login is enabled, fetch user and pass from user info +if ($config['ehorus_user_level_conf']) { + $user = $user_info['ehorus_user_level_user']; + $password = io_safe_output(io_output_password($user_info['ehorus_user_level_pass'])); +} + // Get the login auth token $login_path = '/api/login'; $body = [ diff --git a/pandora_console/operation/agentes/estado_agente.php b/pandora_console/operation/agentes/estado_agente.php index b823ad02d9..bb9dc7cef7 100644 --- a/pandora_console/operation/agentes/estado_agente.php +++ b/pandora_console/operation/agentes/estado_agente.php @@ -264,17 +264,19 @@ if ($search != '') { $sortField = get_parameter('sort_field'); $sort = get_parameter('sort', 'none'); -$selected = 'border: 1px solid black;'; -$selectNameUp = ''; -$selectNameDown = ''; -$selectOsUp = ''; -$selectOsDown = ''; -$selectIntervalUp = ''; -$selectIntervalDown = ''; -$selectGroupUp = ''; -$selectGroupDown = ''; -$selectLastContactUp = ''; -$selectLastContactDown = ''; +$selected = true; +$selectNameUp = false; +$selectNameDown = false; +$selectOsUp = false; +$selectOsDown = false; +$selectIntervalUp = false; +$selectIntervalDown = false; +$selectGroupUp = false; +$selectGroupDown = false; +$selectDescriptionUp = false; +$selectDescriptionDown = false; +$selectLastContactUp = false; +$selectLastContactDown = false; $order = null; @@ -428,7 +430,7 @@ switch ($sortField) { case 'description': switch ($sort) { case 'up': - $selectLastContactUp = $selected; + $selectDescriptionUp = $selected; $order = [ 'field' => 'comentarios', 'field2' => 'alias'.$order_collation, @@ -437,7 +439,7 @@ switch ($sortField) { break; case 'down': - $selectLastContactDown = $selected; + $selectDescriptionDown = $selected; $order = [ 'field' => 'comentarios', 'field2' => 'alias'.$order_collation, @@ -449,15 +451,17 @@ switch ($sortField) { default: $selectNameUp = $selected; - $selectNameDown = ''; - $selectOsUp = ''; - $selectOsDown = ''; - $selectIntervalUp = ''; - $selectIntervalDown = ''; - $selectGroupUp = ''; - $selectGroupDown = ''; - $selectLastContactUp = ''; - $selectLastContactDown = ''; + $selectNameDown = false; + $selectOsUp = false; + $selectOsDown = false; + $selectIntervalUp = false; + $selectIntervalDown = false; + $selectGroupUp = false; + $selectGroupDown = false; + $selectDescriptionUp = false; + $selectDescriptionDown = false; + $selectLastContactUp = false; + $selectLastContactDown = false; $order = [ 'field' => 'alias'.$order_collation, 'field2' => 'alias'.$order_collation, @@ -620,6 +624,23 @@ if (empty($agents)) { $agents = []; } +// Urls to sort the table. +$url_up_agente = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=name&sort=up'; +$url_down_agente = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=name&sort=down'; +$url_up_description = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=description&sort=up'; +$url_down_description = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=description&sort=down'; +$url_up_remote = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=remote&sort=up'; +$url_down_remote = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=remote&sort=down'; +$url_up_os = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=os&sort=up'; +$url_down_os = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=os&sort=down'; +$url_up_interval = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=interval&sort=up'; +$url_down_interval = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=interval&sort=down'; +$url_up_group = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=group&sort=up'; +$url_down_group = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=group&sort=down'; +$url_up_last = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=last_contact&sort=up'; +$url_down_last = 'index.php?sec=view&sec2=operation/agentes/estado_agente&refr='.$refr.'&offset='.$offset.'&group_id='.$group_id.'&recursion='.$recursion.'&search='.$search.'&status='.$status.'&sort_field=last_contact&sort=down'; + + // Prepare pagination ui_pagination( $total_agents, @@ -631,27 +652,25 @@ $table = new stdClass(); $table->cellpadding = 0; $table->cellspacing = 0; $table->width = '100%'; -$table->class = 'databox data'; +$table->class = 'info_table'; $table->head = []; -$table->head[0] = __('Agent').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectNameUp, 'alt' => 'up']).''.'
    '.html_print_image('images/sort_down.png', true, ['style' => $selectNameDown, 'alt' => 'down']).''; +$table->head[0] = __('Agent').ui_get_sorting_arrows($url_up_agente, $url_down_agente, $selectNameUp, $selectNameDown); $table->size[0] = '10%'; -$table->head[1] = __('Description').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectNameUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectNameDown, 'alt' => 'down']).''; - +$table->head[1] = __('Description').ui_get_sorting_arrows($url_up_description, $url_down_description, $selectDescriptionUp, $selectDescriptionDown); $table->size[1] = '16%'; -$table->head[10] = __('Remote').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectRemoteUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectRemoteDown, 'alt' => 'down']).''; - +$table->head[10] = __('Remote').ui_get_sorting_arrows($url_up_remote, $url_down_remote, $selectRemoteUp, $selectRemoteDown); $table->size[10] = '9%'; -$table->head[2] = __('OS').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectOsUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectOsDown, 'alt' => 'down']).''; +$table->head[2] = __('OS').ui_get_sorting_arrows($url_up_os, $url_down_os, $selectOsUp, $selectOsDown); $table->size[2] = '8%'; -$table->head[3] = __('Interval').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectIntervalUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectIntervalDown, 'alt' => 'down']).''; +$table->head[3] = __('Interval').ui_get_sorting_arrows($url_up_interval, $url_down_interval, $selectIntervalUp, $selectIntervalDown); $table->size[3] = '10%'; -$table->head[4] = __('Group').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectGroupUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectGroupDown, 'alt' => 'down']).''; +$table->head[4] = __('Group').ui_get_sorting_arrows($url_up_group, $url_down_group, $selectGroupUp, $selectGroupDown); $table->size[4] = '8%'; $table->head[5] = __('Type'); @@ -666,7 +685,7 @@ $table->size[7] = '4%'; $table->head[8] = __('Alerts'); $table->size[8] = '4%'; -$table->head[9] = __('Last contact').' '.''.html_print_image('images/sort_up.png', true, ['style' => $selectLastContactUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectLastContactDown, 'alt' => 'down']).''; +$table->head[9] = __('Last contact').ui_get_sorting_arrows($url_up_last, $url_down_last, $selectLastContactUp, $selectLastContactDown); $table->size[9] = '15%'; $table->align = []; @@ -795,14 +814,6 @@ foreach ($agents as $agent) { if (!empty($table->data)) { html_print_table($table); - if (check_acl($config['id_user'], 0, 'AW') || check_acl($config['id_user'], 0, 'AM')) { - echo '
    '; - echo '
    '; - html_print_input_hidden('new_agent', 1); - html_print_submit_button(__('Create agent'), 'crt', false, 'class="sub next"'); - echo '
    '; - echo '
    '; - } ui_pagination( $total_agents, @@ -819,8 +830,19 @@ if (!empty($table->data)) { 0, false, 'offset', - false + true, + 'pagination-bottom' ); + + if (check_acl($config['id_user'], 0, 'AW') || check_acl($config['id_user'], 0, 'AM')) { + echo '
    '; + echo '
    '; + html_print_input_hidden('new_agent', 1); + html_print_submit_button(__('Create agent'), 'crt', false, 'class="sub next"'); + echo '
    '; + echo '
    '; + } + unset($table); } else { ui_print_info_message([ 'no_close' => true, 'message' => __('There are no defined agents') ]); diff --git a/pandora_console/operation/agentes/estado_generalagente.php b/pandora_console/operation/agentes/estado_generalagente.php index 18cb29027a..360a7fd332 100755 --- a/pandora_console/operation/agentes/estado_generalagente.php +++ b/pandora_console/operation/agentes/estado_generalagente.php @@ -1,17 +1,32 @@ id = 'agent_details_main'; -$table_agent->width = '95%'; -$table_agent->cellspacing = 0; -$table_agent->cellpadding = 0; -$table_agent->class = 'databox filters'; -$table_agent->style[0] = 'width: 16px; text-align:center; padding: 0px;'; -$table_agent->style[5] = 'width: 16px; text-align:center; padding: 0px;'; -$table_agent->styleTable = 'padding:0px;'; -$table_agent->data = []; -$data = []; -$agent_name = ui_print_agent_name($agent['id_agente'], true, 500, 'font-size: medium;font-weight:bold', true); +/* + * START: TABLE AGENT BUILD. + */ + +$agent_name = ui_print_agent_name( + $agent['id_agente'], + true, + 500, + 'font-size: medium;font-weight:bold', + true +); $in_planned_downtime = db_get_sql( 'SELECT executed FROM tplanned_downtime INNER JOIN tplanned_downtime_agents @@ -80,7 +101,6 @@ $in_planned_downtime = db_get_sql( WHERE tplanned_downtime_agents.id_agent = '.$agent['id_agente'].' AND tplanned_downtime.executed = 1' ); - if ($agent['disabled']) { if ($in_planned_downtime) { $agent_name = ''.$agent_name.ui_print_help_tip(__('Disabled'), true); @@ -98,24 +118,35 @@ if ($agent['disabled']) { } if ($in_planned_downtime && !$agent['disabled'] && !$agent['quiet']) { - $agent_name .= ''.' '.ui_print_help_tip(__('Agent in planned downtime'), true, 'images/minireloj-16.png').''; -} else if (($in_planned_downtime && !$agent['disabled']) || ($in_planned_downtime && !$agent['quiet'])) { - $agent_name .= ' '.ui_print_help_tip(__('Agent in planned downtime'), true, 'images/minireloj-16.png').''; + $agent_name .= ' '.ui_print_help_tip( + __('Agent in planned downtime'), + true, + 'images/minireloj-16.png' + ).''; +} else if (($in_planned_downtime && !$agent['disabled']) + || ($in_planned_downtime && !$agent['quiet']) +) { + $agent_name .= ' '.ui_print_help_tip( + __('Agent in planned downtime'), + true, + 'images/minireloj-16.png' + ).''; } +$table_agent_header = '
    '; +$table_agent_header .= $agent_name; +$table_agent_header .= '
    '; +$table_agent_header .= '
    '; if (!$config['show_group_name']) { - $data[0] = ui_print_group_icon($agent['id_grupo'], true); -} else { - $data[0] = ''; + $table_agent_header .= ui_print_group_icon( + $agent['id_grupo'], + true, + 'groups_small', + 'padding-right: 6px;' + ); } -$table_agent->cellstyle[count($table_agent->data)][0] = 'width: 16px; text-align:center; padding: 0px;'; - -$data[2] = $agent_name; -$table_agent->colspan[count($table_agent->data)][2] = 3; - -$table_agent->cellstyle[count($table_agent->data)][2] = 'width: 100px; word-break: break-all;'; - +$table_agent_header .= '
    '; $status_img = agents_detail_view_status_img( $agent['critical_count'], @@ -124,39 +155,36 @@ $status_img = agents_detail_view_status_img( $agent['total_count'], $agent['notinit_count'] ); -$data[2] .= '  '.$status_img; -$table_agent->data[] = $data; -$table_agent->rowclass[] = ''; +$table_agent_header .= '
    '.$status_img.'
    '; +// Fixed width non interactive charts. +$status_chart_width = 180; +$graph_width = 180; -$data = []; +$table_agent_graph = '
    '; +$table_agent_graph .= graph_agent_status( + $id_agente, + $graph_width, + $graph_width, + true, + false, + false, + true +); +$table_agent_graph .= '
    '; -// $data[0] = reporting_tiny_stats ($agent, true, 'agent', '
    '); -// $table_agent->rowspan[count($table_agent->data)][0] = 6; -// Fixed width non interactive charts -$status_chart_width = 150; -$graph_width = 150; - -$data[0] = '
    '; -$data[0] .= '
    '; -$data[0] .= graph_agent_status($id_agente, $graph_width, 120, true); -$data[0] .= '
    '; -$data[0] .= '
    '.reporting_tiny_stats($agent, true); -$data[0] .= ui_print_help_tip(__('Agent statuses are re-calculated by the server, they are not shown in real time.'), true); -$data[0] .= '
    '; -$table_agent->rowspan[count($table_agent->data)][0] = 6; -$table_agent->colspan[count($table_agent->data)][0] = 2; -$table_agent->cellstyle[count($table_agent->data)][0] = 'width: 150px; text-align:center; padding: 0px; vertical-align: top;'; - - -$data[2] = ui_print_os_icon($agent['id_os'], false, true, true, false, false, false, ['title' => __('OS').': '.get_os_name($agent['id_os'])]); -$table_agent->cellstyle[count($table_agent->data)][2] = 'width: 16px; text-align: right; padding: 0px;'; -$data[3] = empty($agent['os_version']) ? get_os_name((int) $agent['id_os']) : $agent['os_version']; -$table_agent->colspan[count($table_agent->data)][3] = 2; - -$table_agent->data[] = $data; -$table_agent->rowclass[] = ''; +$table_agent_os = '

    '.ui_print_os_icon( + $agent['id_os'], + false, + true, + true, + false, + false, + false, + ['title' => __('OS').': '.get_os_name($agent['id_os'])] +); +$table_agent_os .= (empty($agent['os_version'])) ? get_os_name((int) $agent['id_os']) : $agent['os_version'].'

    '; $addresses = agents_get_addresses($id_agente); $address = agents_get_address($id_agente); @@ -168,55 +196,95 @@ foreach ($addresses as $k => $add) { } if (!empty($address)) { - $data = []; - $data[2] = html_print_image('images/world.png', true, ['title' => __('IP address')]); - $table_agent->cellstyle[count($table_agent->data)][2] = 'width: 16px; text-align: right; padding: 0px;'; - $data[3] = ''; - $data[3] .= empty($address) ? ''.__('N/A').'' : $address; - $data[3] .= ''; - $table_agent->colspan[count($table_agent->data)][3] = 2; - $table_agent->data[] = $data; - $table_agent->rowclass[] = ''; + $table_agent_ip = '

    '.html_print_image('images/world.png', true, ['title' => __('IP address')]); + $table_agent_ip .= ''; + $table_agent_ip .= empty($address) ? ''.__('N/A').'' : $address; + $table_agent_ip .= '

    '; } -$data = []; -$data[2] = html_print_image('images/version.png', true, ['title' => __('Agent Version')]); -$table_agent->cellstyle[count($table_agent->data)][2] = 'width: 16px; text-align: right; padding: 0px;'; -$data[3] = ''; -$data[3] .= empty($agent['agent_version']) ? ''.__('N/A').'' : $agent['agent_version']; -$data[3] .= ''; -$table_agent->colspan[count($table_agent->data)][3] = 2; -$table_agent->data[] = $data; -$table_agent->rowclass[] = ''; +$table_agent_version = '

    '.html_print_image('images/version.png', true, ['title' => __('Agent Version')]); +$table_agent_version .= ''; +$table_agent_version .= empty($agent['agent_version']) ? ''.__('N/A').'' : $agent['agent_version']; +$table_agent_version .= '

    '; -$data = []; -$data[2] = html_print_image( +$table_agent_description = '

    '.html_print_image( 'images/default_list.png', true, ['title' => __('Description')] ); -$table_agent->cellstyle[count($table_agent->data)][2] = 'width: 16px; text-align: right; padding: 0px;'; -$data[3] = ''; -$data[3] .= empty($agent['comentarios']) ? ''.__('N/A').'' : $agent['comentarios']; -$data[3] .= ''; -$table_agent->colspan[count($table_agent->data)][3] = 2; +$table_agent_description .= ''; +$table_agent_description .= empty($agent['comentarios']) ? ''.__('N/A').'' : $agent['comentarios']; +$table_agent_description .= '

    '; -$table_agent->data[] = $data; -$table_agent->rowclass[] = ''; +$table_agent_count_modules = reporting_tiny_stats( + $agent, + true, + 'agent', + // Useless. + ':', + true +); + +$has_remote_conf = enterprise_hook( + 'config_agents_has_remote_configuration', + [$agent['id_agente']] +); + +if ($has_remote_conf) { + $remote_cfg = '

    '.html_print_image('images/remote_configuration.png', true); + $remote_cfg .= __('Remote configuration enabled').'

    '; +} else { + $remote_cfg = ''; +} + + + +// $table_agent_count_modules .= ui_print_help_tip(__('Agent statuses are re-calculated by the server, they are not shown in real time.'), true); +$table_agent = ' +
    + '.$table_agent_header.' +
    +
    +
    + '.$table_agent_graph.' +
    + '.$table_agent_count_modules.' +
    +
    +
    + '.$table_agent_os.$table_agent_ip.$table_agent_version.$table_agent_description.$remote_cfg.' +
    +
    '; + +/* + * END: TABLE AGENT BUILD. + */ + +/* + *START: TABLE CONTACT BUILD. + */ -// END: TABLE AGENT BUILD -// START: TABLE CONTACT BUILD $table_contact = new stdClass(); $table_contact->id = 'agent_contact_main'; $table_contact->width = '100%'; $table_contact->cellspacing = 0; $table_contact->cellpadding = 0; -$table_contact->class = 'databox data'; -$table_contact->style[0] = 'width: 30%;height:30px;'; +$table_contact->class = 'white_table white_table_no_border'; +$table_contact->style[0] = 'width: 30%;'; $table_contact->style[1] = 'width: 70%;'; +$table_contact->headstyle[1] = 'padding-top:6px; padding-bottom:6px;padding-right: 10px;'; $table_contact->head[0] = ' '.__('Agent contact').''; -$table_contact->head_colspan[0] = 2; + +$buttons_refresh_agent_view = '
    '; +$buttons_refresh_agent_view .= ''.html_print_image('images/refresh.png', true, ['title' => __('Refresh data'), 'alt' => '']).'
    '; +if (check_acl_one_of_groups($config['id_user'], $all_groups, 'AW')) { + $buttons_refresh_agent_view .= ''.html_print_image('images/target.png', true, ['title' => __('Force remote checks'), 'alt' => '']).''; +} + +$buttons_refresh_agent_view .= '
    '; + +$table_contact->head[1] = $buttons_refresh_agent_view; $data = []; $data[0] = ''.__('Interval').''; @@ -236,140 +304,141 @@ if ($agent['ultimo_contacto_remoto'] == '01-01-1970 00:00:00') { $table_contact->data[] = $data; + +$data = []; $data[0] = ''.__('Next contact').''; $progress = agents_get_next_contact($id_agente); -$data[1] = progress_bar($progress, 200, 20, '', 1, false, '#666666'); +$data[1] = ui_progress( + $progress, + '100%', + 1.8, + '#BBB', + true, + ($agent['intervalo'] * (100 - $progress) / 100).' s' +); if ($progress > 100) { - $data[1] .= clippy_context_help('agent_out_of_limits'); + $data[0] .= clippy_context_help('agent_out_of_limits'); } $table_contact->data[] = $data; -// END: TABLE CONTACT BUILD -// START: TABLE DATA BUILD -$table_data = new stdClass(); -$table_data->id = 'agent_data_main'; -$table_data->width = '100%'; -$table_data->styleTable = 'height:180px'; -$table_data->cellspacing = 0; -$table_data->cellpadding = 0; -$table_data->class = 'databox data'; -$table_data->style[0] = 'width: 30%;'; -$table_data->style[1] = 'width: 40%;'; - -$table_data->head[0] = ' '.__('Agent info').''; -$table_data->head_colspan[0] = 3; - $data = []; $data[0] = ''.__('Group').''; $data[1] = ''.groups_get_name($agent['id_grupo']).''; -// ACCESS RATE GRAPH -$access_agent = db_get_value_sql( - 'SELECT COUNT(id_agent) - FROM tagent_access - WHERE id_agent = '.$id_agente -); -if ($config['agentaccess'] && $access_agent > 0) { - $data[2] = '
    - '.__('Agent access rate (24h)').''.graphic_agentaccess($id_agente, '95%', 100, SECONDS_1DAY, true).'
    '; - $table_data->style[0] = 'width: 20%;'; - $table_data->style[1] = 'width: 30%;'; - $table_data->style[2] = 'width: 50%;'; - $table_data->rowspan[0][2] = 5; -} - -$table_data->data[] = $data; - -if (!empty($addresses)) { - $data = []; - $data[0] = ''.__('Other IP addresses').''; - $data[1] = '
    '.implode('
    ', $addresses).'
    '; - $table_data->data[] = $data; -} +$table_contact->data[] = $data; $data = []; -$data[0] = ''.__('Parent').''; -if ($agent['id_parent'] == 0) { +$data[0] = ''.__('Secondary groups').''; +$secondary_groups = enterprise_hook('agents_get_secondary_groups', [$id_agente]); +if (!$secondary_groups) { $data[1] = ''.__('N/A').''; } else { - $data[1] = ''.agents_get_alias($agent['id_parent']).''; + $secondary_links = []; + foreach ($secondary_groups['for_select'] as $id => $name) { + $secondary_links[] = ''.$name.''; + } + + $data[1] = implode(', ', $secondary_links); } -$table_data->data[] = $data; - -$has_remote_conf = enterprise_hook('config_agents_has_remote_configuration', [$agent['id_agente']]); +$table_contact->data[] = $data; if (enterprise_installed()) { $data = []; - $data[0] = ''.__('Remote configuration').''; - if (!$has_remote_conf) { - $data[1] = __('Disabled'); - } else { - $data[1] = __('Enabled'); - } - - $table_data->data[] = $data; - - $data = []; - $data[0] = ''.__('Secondary groups').''; - $secondary_groups = enterprise_hook('agents_get_secondary_groups', [$id_agente]); - if (!$secondary_groups) { + $data[0] = ''.__('Parent').''; + if ($agent['id_parent'] == 0) { $data[1] = ''.__('N/A').''; } else { - $secondary_links = []; - foreach ($secondary_groups['for_select'] as $id => $name) { - $secondary_links[] = ''.$name.''; - } - - $data[1] = implode(', ', $secondary_links); + $data[1] = ''.agents_get_alias($agent['id_parent']).''; } - $table_data->data[] = $data; + $table_contact->data[] = $data; } -if ($config['activate_gis'] || $agent['url_address'] != '') { - $data = []; - // Position Information - if ($config['activate_gis']) { - $dataPositionAgent = gis_get_data_last_position_agent($agent['id_agente']); +/* + * END: TABLE CONTACT BUILD + */ - $data[0] = ''.__('Position (Long, Lat)').''; +/* + * START: TABLE DATA BUILD + */ - if ($dataPositionAgent === false) { - $data[1] = __('There is no GIS data.'); +$table_data = new stdClass(); +$table_data->id = 'agent_data_main'; +$table_data->width = '100%'; +$table_data->cellspacing = 0; +$table_data->cellpadding = 0; +$table_data->class = 'box-shadow white_table white_table_droppable align-top'; +$table_data->style = array_fill(0, 3, 'width: 25%;'); + +$table_data->head[0] = html_print_image( + 'images/arrow_down_green.png', + true, + $options +); +$table_data->head[0] .= ' '.__('Agent info').''; +$table_data->head_colspan[0] = 4; + +// Gis and url address. +$data_opcional = []; +// Position Information. +if ($config['activate_gis']) { + $data_opcional[] = ''.__('Position (Long, Lat)').''; + $dataPositionAgent = gis_get_data_last_position_agent( + $agent['id_agente'] + ); + + if ($dataPositionAgent === false) { + $data_opcional[] = __('There is no GIS data.'); + } else { + $data_opcional[] = ''; + if ($dataPositionAgent['description'] != '') { + $data_opcional[] .= $dataPositionAgent['description']; } else { - $data[1] = ''; - if ($dataPositionAgent['description'] != '') { - $data[1] .= $dataPositionAgent['description']; - } else { - $data[1] .= $dataPositionAgent['stored_longitude'].', '.$dataPositionAgent['stored_latitude']; - } - - $data[1] .= ''; + $data_opcional[] .= $dataPositionAgent['stored_longitude'].', '.$dataPositionAgent['stored_latitude']; } - $table_data->data[] = $data; + $data_opcional[] .= ''; } - // If the url description is setted + array_push($data_opcional); +} + +// If the url description is set. +if ($agent['url_address'] != '') { + // $data_opcional = []; + $data_opcional[] = ''.__('Url address').''; if ($agent['url_address'] != '') { - $data = []; - $data[0] = ''.__('Url address').''; - $data[1] = ''.$agent['url_address'].''; - $table_data->data[] = $data; + $data_opcional[] = ''.$agent['url_address'].''; } } -// Timezone Offset -if ($agent['timezone_offset'] != 0) { - $data = []; - $data[0] = ''.__('Timezone Offset').''; - $data[1] = $agent['timezone_offset']; - $table->data[] = $data; + +// Other IP address and timezone offset. +if (!empty($addresses)) { + // $data_opcional = []; + $data_opcional[] = ''.__('Other IP addresses').''; + if (!empty($addresses)) { + $data_opcional[] = '
    '.implode('
    ', $addresses).'
    '; + } } -// Custom fields +// Timezone Offset. +if ($agent['timezone_offset'] != 0) { + $data_opcional[] = ''.__('Timezone Offset').''; + if ($agent['timezone_offset'] != 0) { + $data_opcional[] = $agent['timezone_offset']; + } +} + + +$data_opcional = array_chunk($data_opcional, 4); +foreach ($data_opcional as $key => $value) { + $table_data->data[] = $data_opcional[$key]; +} + +// Custom fields. $fields = db_get_all_rows_filter( 'tagent_custom_fields', ['display_on_front' => 1] @@ -378,6 +447,7 @@ if ($fields === false) { $fields = []; } +$custom_fields = []; foreach ($fields as $field) { $data = []; $data[0] = ''.$field['name'].ui_print_help_tip(__('Custom field'), true).''; @@ -398,17 +468,75 @@ foreach ($fields as $field) { $data[1] = $custom_value[0]['description']; } - $table_data->data[] = $data; + $custom_fields[] = $data; } -// END: TABLE DATA BUILD -// START: TABLE INCIDENTS +$custom_fields_count = count($custom_fields); +for ($i = 0; $i < $custom_fields_count; $i++) { + $first_column = $custom_fields[$i]; + $j = ($i + 1); + $second_column = $custom_fields[$j]; + + if (is_array($second_column)) { + $columns = array_merge($first_column, $second_column); + } else { + $columns = $first_column; + $filas = count($table_data->data); + $table_data->colspan[$filas][1] = 3; + } + + $table_data->data[] = $columns; + + $i++; +} + +/* + * END: TABLE DATA BUILD + */ + +/* + * START: ACCESS RATE GRAPH + */ + +$access_agent = db_get_value_sql( + 'SELECT COUNT(id_agent) + FROM tagent_access + WHERE id_agent = '.$id_agente +); + +if ($config['agentaccess'] && $access_agent > 0) { + $table_access_rate = '
    +
    '.html_print_image( + 'images/arrow_down_green.png', + true + ).''.__('Agent access rate (24h)').'
    +
    +'.graphic_agentaccess( + $id_agente, + '95%', + 100, + SECONDS_1DAY, + true + ).'
    +
    '; +} + +/* + * END: ACCESS RATE GRAPH + */ + +/* + * START: TABLE INCIDENTS + */ + $last_incident = db_get_row_sql( - " - SELECT * FROM tincidencia - WHERE estado IN (0,1) - AND id_agent = $id_agente - ORDER BY actualizacion DESC" + sprintf( + 'SELECT * FROM tincidencia + WHERE estado IN (0,1) + AND id_agent = %d + ORDER BY actualizacion DESC', + $id_agente + ) ); if ($last_incident != false) { @@ -416,36 +544,35 @@ if ($last_incident != false) { $table_incident->width = '100%'; $table_incident->cellspacing = 0; $table_incident->cellpadding = 0; - $table_incident->class = 'databox'; - $table_incident->style[0] = 'width: 30%;'; - $table_incident->style[1] = 'width: 70%;'; + $table_incident->class = 'white_table'; + $table_incident->style = array_fill(0, 3, 'width: 25%;'); - $table_incident->head[0] = ' '.''.__('Active incident on this agent').''.''; - $table_incident->head_colspan[0] = 2; + $table_incident->head[0] = ' '.__('Active incident on this agent').''; + $table_incident->head_colspan[0] = 4; $data = []; $data[0] = ''.__('Author').''; $data[1] = $last_incident['id_creator']; + $data[2] = ''.__('Timestamp').''; + $data[3] = $last_incident['inicio']; $table_incident->data[] = $data; $data = []; $data[0] = ''.__('Title').''; $data[1] = ''.$last_incident['titulo'].''; - $table_incident->data[] = $data; - - $data = []; - $data[0] = ''.__('Timestamp').''; - $data[1] = $last_incident['inicio']; - $table_incident->data[] = $data; - - $data = []; - $data[0] = ''.__('Priority').''; - $data[1] = incidents_print_priority_img($last_incident['prioridad'], true); + $data[2] = ''.__('Priority').''; + $data[3] = incidents_print_priority_img($last_incident['prioridad'], true); $table_incident->data[] = $data; } -// END: TABLE INCIDENTS -// START: TABLE INTERFACES +/* + * END: TABLE INCIDENTS + */ + +/* + * START: TABLE INTERFACES + */ + $network_interfaces_by_agents = agents_get_network_interfaces([$agent]); $network_interfaces = []; @@ -456,8 +583,8 @@ if (!empty($network_interfaces_by_agents) && !empty($network_interfaces_by_agent if (!empty($network_interfaces)) { $table_interface = new stdClass(); $table_interface->id = 'agent_interface_info'; - $table_interface->class = 'databox data'; - $table_interface->width = '98%'; + $table_interface->class = 'info_table'; + $table_interface->width = '100%'; $table_interface->style = []; $table_interface->style['interface_status'] = 'width: 30px;padding-top:0px;padding-bottom:0px;'; $table_interface->style['interface_graph'] = 'width: 20px;padding-top:0px;padding-bottom:0px;'; @@ -476,12 +603,8 @@ if (!empty($network_interfaces)) { $table_interface->head = []; $options = [ 'class' => 'closed', - 'style' => 'vertical-align:righ; cursor:pointer;', + 'style' => 'cursor:pointer;', ]; - $table_interface->head[0] = html_print_image('images/graphmenu_arrow.png', true, $options).'  '; - $table_interface->head[0] .= ''.__('Interface information').' (SNMP)'; - $table_interface->head_colspan = []; - $table_interface->head_colspan[0] = 8; $table_interface->data = []; $event_text_cont = 0; @@ -499,7 +622,14 @@ if (!empty($network_interfaces)) { $params_json = json_encode($params); $params_encoded = base64_encode($params_json); $win_handle = dechex(crc32($interface['status_module_id'].$interface_name)); - $graph_link = "".html_print_image('images/chart_curve.png', true, ['title' => __('Interface traffic')]).''; + $graph_link = ""; + $graph_link .= html_print_image( + 'images/chart_curve.png', + true, + ['title' => __('Interface traffic')] + ).''; } else { $graph_link = ''; } @@ -522,9 +652,9 @@ if (!empty($network_interfaces)) { $status_condition = ' AND (estado = 0 OR estado = 1) '; $unixtime = (get_system_time() - SECONDS_1DAY); - // last hour + // Last hour. $time_condition = 'AND (utimestamp > '.$unixtime.')'; - // Tags ACLS + // Tags ACLs. if ($id_group > 0 && in_array(0, $user_groups_ids)) { $group_array = (array) $id_group; } else { @@ -547,25 +677,23 @@ if (!empty($network_interfaces)) { $id_modules_array[] = $interface['status_module_id']; $unixtime = (get_system_time() - SECONDS_1DAY); - // last hour + // Last hour. $time_condition = 'WHERE (te.utimestamp > '.$unixtime.')'; $sqlEvents = sprintf( - ' - SELECT * + 'SELECT * FROM tevento te INNER JOIN tagente_estado tae ON te.id_agentmodule = tae.id_agente_modulo AND tae.id_agente_modulo IN (%s) - %s - ', + %s', implode(',', $id_modules_array), $time_condition ); $sqlLast_contact = sprintf( ' - SELECT last_try + SELECT timestamp FROM tagente_estado WHERE id_agente_modulo = '.$interface['status_module_id'] ); @@ -600,69 +728,112 @@ if (!empty($network_interfaces)) { $table_interface->data[] = $data; } +} - // This javascript piece of code is used to make expandible the body of the table - ?> +/* + * END: TABLE INTERFACES + */ + + // This javascript piece of code is used to make expandible + // the body of the table. +?> - 0) { + $extra_class = 'min-height-100'; +} else { + $extra_class = ''; } -// END: TABLE INTERFACES -$table = new stdClass(); -$table->id = 'agent_details'; -$table->width = '100%'; -$table->cellspacing = 0; -$table->cellpadding = 0; -$table->class = 'agents'; -$table->style = array_fill(0, 3, 'vertical-align: top;'); +$table_events = '
    +
    '.html_print_image( + 'images/arrow_down_green.png', + true +).''.__('Events (24h)').'
    +
    +'.graph_graphic_agentevents( + $id_agente, + 100, + 45, + SECONDS_1DAY, + '', + true, + true +).'
    +
    '; -$data = []; -$data[0][0] = html_print_table($table_agent, true); -$data[0][0] .= '
    - '.''.'
    '.__('Events (24h)').'

    '.graph_graphic_agentevents($id_agente, 100, 45, SECONDS_1DAY, '', true, true).'
    '; +/* + * EVENTS TABLE END. + */ -$table->style[0] = 'width:40%; vertical-align:top;'; -$data[0][1] = html_print_table($table_contact, true); -$data[0][1] .= empty($table_data->data) ? '' : '
    '.html_print_table($table_data, true); -$data[0][1] .= !isset($table_incident) ? '' : '
    '.html_print_table($table_incident, true); +$agent_contact = html_print_table($table_contact, true); -$table->rowspan[1][0] = 0; +$agent_info = empty($table_data->data) ? '' : html_print_table($table_data, true); -$data[0][2] = '
    '; -$data[0][2] .= ''.html_print_image('images/refresh.png', true, ['border' => '0', 'title' => __('Refresh data'), 'alt' => '']).'
    '; -if (check_acl_one_of_groups($config['id_user'], $all_groups, 'AW')) { - $data[0][2] .= ''.html_print_image('images/target.png', true, ['border' => '0', 'title' => __('Force remote checks'), 'alt' => '']).''; +$agent_incidents = !isset($table_incident) ? '' : html_print_table($table_incident, true); + +echo '
    +
    '.$table_agent.'
    +
    '.$agent_contact.'
    +
    '.$agent_info; + +// Show both graphs, events and access rate. +if ($table_access_rate) { + echo '
    '.$table_access_rate.$table_events.'
    '; +} else { + echo '
    '.$table_events.'
    '; } -$data[0][2] .= '
    '; - -$table->data = $data; -$table->rowclass[] = ''; - -$table->cellstyle[1][0] = 'text-align:center;'; - -html_print_table($table); -$data2[1][0] = !isset($table_interface) ? '' : html_print_table($table_interface, true); -$table->data = $data2; -$table->styleTable = ''; -html_print_table($table); - -unset($table); +echo $agent_incidents; +if (isset($table_interface)) { + ui_toggle( + html_print_table($table_interface, true), + ''.__('Interface information (SNMP)').'' + ); +} diff --git a/pandora_console/operation/agentes/estado_monitores.php b/pandora_console/operation/agentes/estado_monitores.php index b311885acb..7536d294ae 100755 --- a/pandora_console/operation/agentes/estado_monitores.php +++ b/pandora_console/operation/agentes/estado_monitores.php @@ -1,17 +1,32 @@ ".ui_print_truncate_text(modules_get_agentmodule_name($id_module), 'module_medium', true, true, true, '[…]').''; + &id_agente=".$id_agent.'&tab=module&edit_module=1&id_agent_module='.$id_module."'>".ui_print_truncate_text(modules_get_agentmodule_name($id_module), 'module_medium', true, true, true, '[…]').''; $table_relations->data[] = $data; } @@ -132,33 +147,46 @@ $sort_field = get_parameter('sort_field', 'name'); $sort = get_parameter('sort', 'up'); -echo "

    "; - -echo ui_print_help_tip(__('To see the list of modules paginated, enable this option in the Styles Configuration.'), true).__('Full list of monitors').' '.reporting_tiny_stats($agent, true, 'modules'); - $modules_not_init = agents_monitor_notinit($id_agente); if (!empty($modules_not_init)) { - echo clippy_context_help('modules_not_init'); + $help_not_init = clippy_context_help('modules_not_init'); +} else { + $help_not_init = ''; } -echo '

    '; - - ob_start(); -print_form_filter_monitors($id_agente, $status_filter_monitor, $status_text_monitor, $status_hierachy_mode); +print_form_filter_monitors( + $id_agente, + $status_filter_monitor, + $status_text_monitor, + $status_hierachy_mode +); -echo "
    ".html_print_image('images/spinner.gif', true).'
    '; -echo "
    ".'
    '; +echo '
    '; $html_toggle = ob_get_clean(); + ui_toggle( $html_toggle, - __('List of modules'), + __('List of modules').$help_not_init.ui_print_help_tip( + __('To see the list of modules paginated, enable this option in the Styles Configuration.'), + true + ).reporting_tiny_stats( + $agent, + true, + 'modules', + ':', + true + ), 'status_monitor_agent', - false + false, + false, + '', + 'white_table_graph_content no-padding-imp' ); + ?> class = 'databox filters'; + $table->class = 'info_table'; + $table->styleTable = 'border: 1px solid #ebebeb;border-radius: 0;padding: 0;margin: 0;margin-top: -1px;'; $table->width = '100%'; $table->style[0] = 'font-weight: bold;'; $table->style[2] = 'font-weight: bold;'; $table->style[4] = 'font-weight: bold;'; + $table->style[6] = 'font-weight: bold;'; + $table->style[6] = 'min-width: 150px;'; $table->data[0][0] = html_print_input_hidden('filter_monitors', 1, true); $table->data[0][0] .= html_print_input_hidden('monitors_change_filter', 1, true); $table->data[0][0] .= __('Status:'); @@ -488,9 +531,26 @@ function print_form_filter_monitors( true ); - $table->data[0][3] = html_print_input_text('status_text_monitor', $status_text_monitor, '', 30, 100, true); + $table->data[0][3] = html_print_input_text( + 'status_text_monitor', + $status_text_monitor, + '', + '', + 100, + true + ); $table->data[0][4] = __('Module group'); - $rows = db_get_all_rows_sql("SELECT * FROM tmodule_group where id_mg in (SELECT id_module_group from tagente_modulo where id_agente = $id_agent ) ORDER BY name"); + $rows = db_get_all_rows_sql( + sprintf( + 'SELECT * + FROM tmodule_group + WHERE id_mg IN (SELECT id_module_group + FROM tagente_modulo + WHERE id_agente = %d ) + ORDER BY name', + $id_agent + ) + ); $rows_select[-1] = __('All'); if (!empty($rows)) { @@ -499,11 +559,43 @@ function print_form_filter_monitors( } } - $table->data[0][5] = html_print_select($rows_select, 'status_module_group', $status_module_group, '', '', 0, true); - $table->data[0][6] = __('Show in hierachy mode'); - $table->data[0][6] .= html_print_checkbox('status_hierachy_mode', '', false, true, false, 'onChange=change_module_filter();'); - $table->data[0][7] = html_print_button(__('Filter'), 'filter', false, 'filter_modules();', 'class="sub search"', true); - $table->data[0][8] = ' '.html_print_button(__('Reset'), 'filter', false, 'reset_filter_modules();', 'class="sub upd" style="margin-top:0px;"', true); + $table->data[0][5] = html_print_select( + $rows_select, + 'status_module_group', + $status_module_group, + '', + '', + 0, + true + ); + $table->data[0][6] = '
    '; + $table->data[0][6] .= __('Show in hierachy mode'); + $table->data[0][6] .= html_print_switch( + [ + 'name' => 'status_hierachy_mode', + 'value' => $all_events_24h, + 'onchange' => 'change_module_filter()', + 'id' => 'checkbox-status_hierachy_mode', + 'style' => 'margin-left: 1em;', + ] + ); + $table->data[0][6] .= '
    '; + $table->data[0][7] = html_print_button( + __('Filter'), + 'filter', + false, + 'filter_modules();', + 'class="sub search"', + true + ); + $table->data[0][8] = html_print_button( + __('Reset'), + 'filter', + false, + 'reset_filter_modules();', + 'class="sub upd" style="margin-top:0px;"', + true + ); $form_text .= html_print_table($table, true); $filter_hidden = false; @@ -514,4 +606,3 @@ function print_form_filter_monitors( echo $form_text; } - diff --git a/pandora_console/operation/agentes/group_view.php b/pandora_console/operation/agentes/group_view.php index c4e98bc986..080e7ffbaf 100644 --- a/pandora_console/operation/agentes/group_view.php +++ b/pandora_console/operation/agentes/group_view.php @@ -65,8 +65,15 @@ if ($config['realtimestats'] == 0) { $updated_info = ''; } -// Header -ui_print_page_header(__('Group view'), 'images/group.png', false, '', false, $updated_time); +// Header. +ui_print_page_header( + __('Group view'), + 'images/group.png', + false, + 'group_view', + false, + $updated_time +); $total_agentes = 0; $monitor_ok = 0; diff --git a/pandora_console/operation/agentes/interface_traffic_graph_win.php b/pandora_console/operation/agentes/interface_traffic_graph_win.php index 72e8f8989f..c02cbbcb31 100644 --- a/pandora_console/operation/agentes/interface_traffic_graph_win.php +++ b/pandora_console/operation/agentes/interface_traffic_graph_win.php @@ -96,7 +96,7 @@ if ($refresh > 0) { --> - + $now) { $urlImage = ui_get_full_url(false); - echo '
    '; + // Graph. + echo '
    '; $height = 400; $width = '90%'; @@ -177,27 +178,14 @@ if ($date > $now) { echo '
    '; - // - // SIDE MENU - // - $side_layer_params = []; - // TOP TEXT - $side_layer_params['top_text'] = "
    ".html_print_image('/images/config.disabled.png', true, ['width' => '16px'], false, false, false, true).' '.__('Graph configuration menu').'
    '; - $side_layer_params['body_text'] = "'; - // outer - // ICONS - $side_layer_params['icon_closed'] = '/images/graphmenu_arrow_hide.png'; - $side_layer_params['icon_open'] = '/images/graphmenu_arrow.png'; - - // SIZE - $side_layer_params['width'] = 500; - - // POSITION - $side_layer_params['position'] = 'left'; - - html_print_side_layer($side_layer_params); + echo $menu_form; + echo '
    +
    + '.html_print_image('images/arrow_down_green.png', true, ['class' => 'module_graph_menu_arrow', 'float' => 'left'], false, false, true).' + '.__('Graph configuration menu').ui_print_help_icon('graphs', true, $config['homeurl'], 'images/help_g.png', true).' + '.html_print_image('images/config.png', true, ['float' => 'right'], false, false, true).' +
    + +
    '; + echo ''; // Hidden div to forced title html_print_div(['id' => 'forced_title_layer', 'class' => 'forced_title_layer', 'hidden' => true]); @@ -341,4 +321,22 @@ ui_include_time_picker(true); $.datepicker.setDefaults($.datepicker.regional[""]); forced_title_callback(); + + // Menu. + $('#module_graph_menu_header').on('click', function(){ + var arrow = $('#module_graph_menu_header .module_graph_menu_arrow'); + var arrow_up = 'arrow_up_green'; + var arrow_down = 'arrow_down_green'; + if( $('.module_graph_menu_content').hasClass('module_graph_menu_content_closed')){ + $('.module_graph_menu_content').show(); + $('.module_graph_menu_content').removeClass('module_graph_menu_content_closed'); + arrow.attr('src',arrow.attr('src').replace(arrow_down, arrow_up)); + } + else{ + $('.module_graph_menu_content').hide(); + $('.module_graph_menu_content').addClass('module_graph_menu_content_closed'); + arrow.attr('src',arrow.attr('src').replace(arrow_up, arrow_down)); + } + }); + diff --git a/pandora_console/operation/agentes/networkmap.dinamic.php b/pandora_console/operation/agentes/networkmap.dinamic.php index ae4b696aba..3afd34728a 100644 --- a/pandora_console/operation/agentes/networkmap.dinamic.php +++ b/pandora_console/operation/agentes/networkmap.dinamic.php @@ -127,7 +127,7 @@ ui_print_page_header( io_safe_output($networkmap['name']), 'images/bricks.png', false, - 'network_map_enterprise', + 'network_map_enterprise_list', false, $buttons, false, @@ -504,9 +504,9 @@ function create_loading_tooltip(d, x, y) { } function show_tooltip(d) { - x = d3.event.clientX + 10; - y = d3.event.clientY + 10; - + x = d3.event.clientX 100; + y = d3.event.clientY 100; + if (d.default_tooltip) { create_loading_tooltip(d, x, y); diff --git a/pandora_console/operation/agentes/pandora_networkmap.editor.php b/pandora_console/operation/agentes/pandora_networkmap.editor.php index e43f26c6eb..4ed526a404 100644 --- a/pandora_console/operation/agentes/pandora_networkmap.editor.php +++ b/pandora_console/operation/agentes/pandora_networkmap.editor.php @@ -49,18 +49,18 @@ if ($new_networkmap) { $id_group = 0; $node_radius = 40; $description = ''; - $method = 'fdp'; + $method = 'neato'; $recon_task_id = 0; $source = 'group'; $ip_mask = ''; - $dont_show_subgroups = false; + $dont_show_subgroups = 0; $offset_x = ''; $offset_y = ''; $scale_z = 0.5; - $node_sep = 0.25; + $node_sep = 0.1; $rank_sep = 1.0; $mindist = 1.0; - $kval = 0.3; + $kval = 0.1; } $disabled_generation_method_select = false; @@ -198,7 +198,7 @@ ui_print_page_header( __('Networkmap'), 'images/bricks.png', false, - 'network_map_enterprise', + 'network_map_enterprise_edit', false ); @@ -330,7 +330,13 @@ if ($not_found) { $table->data['source_data_ip_mask'][1] = html_print_input_text('ip_mask', $ip_mask, '', 20, 255, true, $disabled_source); $table->data['source_data_dont_show_subgroups'][0] = __('Don\'t show subgroups:'); - $table->data['source_data_dont_show_subgroups'][1] = html_print_checkbox('dont_show_subgroups', '1', $dont_show_subgroups, true, $disabled_source); + $table->data['source_data_dont_show_subgroups'][1] = html_print_checkbox( + 'dont_show_subgroups', + '1', + $dont_show_subgroups, + true, + $disabled_source + ); $methods = [ 'twopi' => 'radial', @@ -348,7 +354,7 @@ if ($not_found) { $method, '', '', - 'twopi', + 'neato', true, false, true, diff --git a/pandora_console/operation/agentes/pandora_networkmap.php b/pandora_console/operation/agentes/pandora_networkmap.php index 604d800307..35eb712146 100644 --- a/pandora_console/operation/agentes/pandora_networkmap.php +++ b/pandora_console/operation/agentes/pandora_networkmap.php @@ -30,7 +30,6 @@ if (!$networkmaps_read && !$networkmaps_write && !$networkmaps_manage) { } require_once 'include/functions_networkmap.php'; -require_once 'include/functions_pandora_networkmap.php'; $new_networkmap = (bool) get_parameter('new_networkmap', false); $save_networkmap = (bool) get_parameter('save_networkmap', false); @@ -45,8 +44,9 @@ $new_empty_networkmap = get_parameter('new_empty_networkmap', false); if (enterprise_installed()) { if ($new_empty_networkmap) { if ($networkmaps_write || $networkmaps_manage) { - include $config['homedir'].'/enterprise/godmode/agentes/pandora_networkmap_empty.editor.php'; - include 'pandora_networkmap_empty.editor.php'; + enterprise_include( + 'godmode/agentes/pandora_networkmap_empty.editor.php' + ); return; } } @@ -71,12 +71,15 @@ if (enterprise_installed()) { $name = (string) get_parameter('name', ''); // Default size values - $width = 4000; - $height = 4000; + $width = $config['networkmap_max_width']; + $height = $config['networkmap_max_width']; $method = (string) get_parameter('method', 'fdp'); - $dont_show_subgroups = 0; + $dont_show_subgroups = (int) get_parameter_checkbox( + 'dont_show_subgroups', + 0 + ); $node_radius = (int) get_parameter('node_radius', 40); $description = get_parameter('description', ''); @@ -126,7 +129,14 @@ if (enterprise_installed()) { $id = $result; define('_id_', $id); - $tab = 'view'; + if ($result !== false) { + $tab = 'view'; + header( + 'Location: '.ui_get_full_url( + 'index.php?sec=network&sec2=operation/agentes/pandora_networkmap&tab='.$tab.'&id_networkmap='.$id + ) + ); + } } else if ($update_empty_networkmap) { $id_group = (int) get_parameter('id_group', 0); @@ -161,7 +171,10 @@ if (enterprise_installed()) { $description = get_parameter('description', ''); $values['description'] = $description; - $dont_show_subgroups = 0; + $dont_show_subgroups = (int) get_parameter_checkbox( + 'dont_show_subgroups', + 0 + ); $node_radius = (int) get_parameter('node_radius', 40); $row = db_get_row('tmap', 'id', $id); $filter = json_decode($row['filter'], true); @@ -189,9 +202,9 @@ if (enterprise_installed()) { } } -$result_txt = ''; // The networkmap doesn't exist yet if ($new_networkmap || $save_networkmap) { + $result_txt = ''; if ($new_networkmap) { if ($networkmaps_write || $networkmaps_manage) { include 'pandora_networkmap.editor.php'; @@ -226,8 +239,8 @@ if ($new_networkmap || $save_networkmap) { $name = (string) get_parameter('name', ''); // Default size values - $width = 4000; - $height = 4000; + $width = $config['networkmap_max_width']; + $height = $config['networkmap_max_width']; $method = (string) get_parameter('method', 'fdp'); @@ -240,12 +253,15 @@ if ($new_networkmap || $save_networkmap) { '' ); $source = (string) get_parameter('source', 'group'); - $dont_show_subgroups = (int) get_parameter('dont_show_subgroups', 0); + $dont_show_subgroups = (int) get_parameter_checkbox( + 'dont_show_subgroups', + 0 + ); $node_radius = (int) get_parameter('node_radius', 40); $description = get_parameter('description', ''); - $offset_x = get_parameter('pos_x'); - $offset_y = get_parameter('pos_y'); + $offset_x = get_parameter('pos_x', 0); + $offset_y = get_parameter('pos_y', 0); $scale_z = get_parameter('scale_z', 0.5); $node_sep = get_parameter('node_sep', '0.25'); @@ -269,31 +285,31 @@ if ($new_networkmap || $save_networkmap) { switch ($method) { case 'twopi': - $values['generation_method'] = 2; + $values['generation_method'] = LAYOUT_RADIAL; break; case 'dot': - $values['generation_method'] = 1; + $values['generation_method'] = LAYOUT_FLAT; break; case 'circo': - $values['generation_method'] = 0; + $values['generation_method'] = LAYOUT_CIRCULAR; break; case 'neato': - $values['generation_method'] = 3; + $values['generation_method'] = LAYOUT_SPRING1; break; case 'fdp': - $values['generation_method'] = 4; + $values['generation_method'] = LAYOUT_SPRING2; break; case 'radial_dinamic': - $values['generation_method'] = 6; + $values['generation_method'] = LAYOUT_RADIAL_DYNAMIC; break; default: - $values['generation_method'] = 2; + $values['generation_method'] = LAYOUT_RADIAL; break; } @@ -349,11 +365,25 @@ if ($new_networkmap || $save_networkmap) { $id = $result; define('_id_', $id); - $tab = 'view'; - - if ($values['generation_method'] == 6) { - $tab = 'r_dinamic'; - define('_activeTab_', 'radial_dynamic'); + if ($result !== false) { + $tab = 'view'; + if ($values['generation_method'] == LAYOUT_RADIAL_DYNAMIC) { + $tab = 'r_dinamic'; + define('_activeTab_', 'radial_dynamic'); + $url = 'index.php?sec=network&sec2=operation/agentes/networkmap.dinamic&activeTab=radial_dynamic'; + header( + 'Location: '.ui_get_full_url( + $url.'&id_networkmap='.$id + ) + ); + } else { + $url = 'index.php?sec=network&sec2=operation/agentes/pandora_networkmap'; + header( + 'Location: '.ui_get_full_url( + $url.'&tab='.$tab.'&id_networkmap='.$id + ) + ); + } } } } @@ -419,8 +449,8 @@ else if ($update_networkmap || $copy_networkmap || $delete) { $source = (string) get_parameter('source', 'group'); - $offset_x = get_parameter('pos_x'); - $offset_y = get_parameter('pos_y'); + $offset_x = get_parameter('pos_x', 0); + $offset_y = get_parameter('pos_y', 0); $scale_z = get_parameter('scale_z', 0.5); $values = []; @@ -604,7 +634,7 @@ switch ($tab) { __('Networkmap'), 'images/op_network.png', false, - 'network_map_enterprise', + 'network_map_enterprise_list', false ); @@ -612,7 +642,9 @@ switch ($tab) { $table = new stdClass(); $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; + $table->cellpadding = 0; + $table->cellspacing = 0; $table->headstyle['copy'] = 'text-align: center;'; $table->headstyle['edit'] = 'text-align: center;'; @@ -657,11 +689,24 @@ switch ($tab) { $id_groups = array_keys(users_get_groups()); - $network_maps = db_get_all_rows_filter( + // Prepare pagination. + $offset = (int) get_parameter('offset'); + $limit = $config['block_size']; + $count_maps = db_get_value_filter( + 'count(*)', 'tmap', ['id_group' => $id_groups] ); + $network_maps = db_get_all_rows_filter( + 'tmap', + [ + 'id_group' => $id_groups, + 'limit' => $limit, + 'offset' => $offset, + ] + ); + if ($network_maps !== false) { $table->data = []; @@ -712,6 +757,8 @@ switch ($tab) { if (($count == 0) && ($network_map['source'] != 'empty')) { if ($network_map['generated']) { $data['nodes'] = __('Empty map'); + } else if ($network_map['generation_method'] == LAYOUT_RADIAL_DYNAMIC) { + $data['nodes'] = __('Dynamic'); } else { $data['nodes'] = __('Pending to generate'); } @@ -724,6 +771,11 @@ switch ($tab) { $data['groups'] = ui_print_group_icon($network_map['id_group'], true); if ($networkmap_write || $networkmap_manage) { + $table->cellclass[] = [ + 'copy' => 'action_buttons', + 'edit' => 'action_buttons', + 'delete' => 'action_buttons', + ]; $data['copy'] = ''.html_print_image('images/copy.png', true).''; $data['edit'] = ''.html_print_image('images/config.png', true).''; $data['delete'] = ''.html_print_image('images/cross.png', true).''; @@ -732,7 +784,9 @@ switch ($tab) { $table->data[] = $data; } + ui_pagination($count_maps, false, $offset); html_print_table($table); + ui_pagination($count_maps, false, 0, 0, false, 'offset', true, 'pagination-bottom'); } else { ui_print_info_message(['no_close' => true, 'message' => __('There are no maps defined.') ]); } @@ -756,4 +810,3 @@ switch ($tab) { } break; } - diff --git a/pandora_console/operation/agentes/pandora_networkmap.view.php b/pandora_console/operation/agentes/pandora_networkmap.view.php index df97e7742b..77f3d2c8a5 100644 --- a/pandora_console/operation/agentes/pandora_networkmap.view.php +++ b/pandora_console/operation/agentes/pandora_networkmap.view.php @@ -29,7 +29,7 @@ global $config; -require_once 'include/functions_pandora_networkmap.php'; +require_once 'include/functions_networkmap.php'; enterprise_include_once('include/functions_policies.php'); enterprise_include_once('include/functions_dashboard.php'); require_once 'include/functions_modules.php'; @@ -80,6 +80,10 @@ if (is_ajax()) { if ($module_get_status) { $id = (int) get_parameter('id', 0); + if ($id == 0) { + return; + } + $return = []; $return['correct'] = true; $return['status'] = modules_get_agentmodule_status( @@ -756,6 +760,7 @@ html_print_input_hidden('center_logo', ui_get_full_url(ui_get_logo_to_center_net $dash_mode = 0; $map_dash_details = []; $networkmap = db_get_row('tmap', 'id', $id); + if (enterprise_installed()) { include_once 'enterprise/dashboard/widgets/network_map.php'; if ($id_networkmap) { @@ -767,7 +772,6 @@ if (enterprise_installed()) { $map_dash_details['x_offs'] = $x_offs; $map_dash_details['y_offs'] = $y_offs; $map_dash_details['z_dash'] = $z_dash; - $networkmap = db_get_row('tmap', 'id', $id); } else { $networkmap_filter = json_decode($networkmap['filter'], true); @@ -796,7 +800,7 @@ if ($networkmap === false) { __('Networkmap'), 'images/bricks.png', false, - 'network_map_enterprise', + 'network_map_enterprise_view', false ); ui_print_error_message(__('Not found networkmap.')); @@ -857,7 +861,7 @@ if ($networkmap === false) { $networkmap['name'], 'images/bricks.png', false, - 'network_map_enterprise', + 'network_map_enterprise_view', false, $buttons, false, @@ -866,9 +870,13 @@ if ($networkmap === false) { ); } - $nodes_and_relations = networkmap_process_networkmap($id); + include_once $config['homedir'].'/include/class/NetworkMap.class.php'; - show_networkmap($id, $user_readonly, $nodes_and_relations, $dash_mode, $map_dash_details); + $map_manager = new NetworkMap( + [ 'id_map' => $networkmap['id']] + ); + + $map_manager->printMap(); } ?> diff --git a/pandora_console/operation/agentes/stat_win.php b/pandora_console/operation/agentes/stat_win.php index 78cd56cfcc..70af429f6d 100644 --- a/pandora_console/operation/agentes/stat_win.php +++ b/pandora_console/operation/agentes/stat_win.php @@ -89,7 +89,7 @@ $alias = db_get_value('alias', 'tagente', 'id_agente', $id_agent); }; - + "; @@ -175,8 +175,9 @@ $alias = db_get_value('alias', 'tagente', 'id_agente', $id_agent); $unit = db_get_value('unit', 'tagente_modulo', 'id_agente_modulo', $id); - echo '
    '; + // Graph. + echo '
    '; $width = '90%'; $height = '450'; @@ -228,39 +229,16 @@ $alias = db_get_value('alias', 'tagente', 'id_agente', $id_agent); echo '
    '; - // - // SIDE MENU - // - $params = []; - // TOP TEXT - // Use the no_meta parameter because this image is only in the base console - $params['top_text'] = "
    ".html_print_image('images/wrench_blanco.png', true, ['width' => '16px'], false, false, true).' '.__('Graph configuration menu').ui_print_help_icon('graphs', true, $config['homeurl'], 'images/help_w.png', true).'
    '; - $params['body_text'] = "'; - unset($table); - $table = new stdClass(); - $table->id = 'stat_win_form'; - $table->width = '100%'; - $table->cellspacing = 2; - $table->cellpadding = 2; - $table->class = 'databox'; + // Menu. + $menu_form = "".html_print_input_hidden('id', $id, true).html_print_input_hidden('label', $label, true); - $data = []; - $data[0] = html_print_div( - [ - 'id' => 'field_list', - 'content' => $form_table, - 'style' => 'overflow: auto; height: 220px', - ], - true - ); - $table->data[] = $data; - $table->rowclass[] = ''; + if (!empty($server_id)) { + $menu_form .= html_print_input_hidden('server', $server_id, true); + } - $params['body_text'] .= html_print_table($table, true); - $params['body_text'] .= ''; - $params['body_text'] .= '
    '; - // outer - // ICONS - $params['icon_closed'] = '/images/graphmenu_arrow_hide.png'; - $params['icon_open'] = '/images/graphmenu_arrow.png'; + if (isset($_GET['type'])) { + $type = get_parameter_get('type'); + $menu_form .= html_print_input_hidden('type', $type, true); + } - // SIZE - $params['width'] = 500; - - // POSITION - $params['position'] = 'left'; - - html_print_side_layer($params); + echo $menu_form; + echo '
    +
    + '.html_print_image('images/arrow_down_green.png', true, ['class' => 'module_graph_menu_arrow', 'float' => 'left'], false, false, true).' + '.__('Graph configuration menu').ui_print_help_icon('graphs', true, $config['homeurl'], 'images/help_g.png', true).' + '.html_print_image('images/config.png', true, ['float' => 'right'], false, false, true).' +
    + +
    '; + echo ''; // Hidden div to forced title html_print_div( @@ -491,11 +456,21 @@ ui_include_time_picker(true); $.datepicker.setDefaults($.datepicker.regional[""]); - $(window).ready(function() { - $("#field_list").css('height', ($(window).height() - 160) + 'px'); + // Menu. + $('#module_graph_menu_header').on('click', function(){ + var arrow = $('#module_graph_menu_header .module_graph_menu_arrow'); + var arrow_up = 'arrow_up_green'; + var arrow_down = 'arrow_down_green'; + if( $('.module_graph_menu_content').hasClass('module_graph_menu_content_closed')){ + $('.module_graph_menu_content').show(); + $('.module_graph_menu_content').removeClass('module_graph_menu_content_closed'); + arrow.attr('src',arrow.attr('src').replace(arrow_down, arrow_up)); + } + else{ + $('.module_graph_menu_content').hide(); + $('.module_graph_menu_content').addClass('module_graph_menu_content_closed'); + arrow.attr('src',arrow.attr('src').replace(arrow_up, arrow_down)); + } }); - $(window).resize(function() { - $("#field_list").css('height', ($(document).height() - 160) + 'px'); - }); diff --git a/pandora_console/operation/agentes/status_events.php b/pandora_console/operation/agentes/status_events.php index 825a4c2c42..5e6e060f82 100755 --- a/pandora_console/operation/agentes/status_events.php +++ b/pandora_console/operation/agentes/status_events.php @@ -20,12 +20,15 @@ if (!isset($id_agente)) { } require_once 'include/functions_events.php'; - +ui_require_css_file('events'); ui_toggle( - "
    ".html_print_image('images/spinner.gif', true).'
    ', + "
    ".html_print_image('images/spinner.gif', true).'
    ', __('Latest events for this agent'), __('Latest events for this agent'), - false + false, + false, + '', + 'white_table_graph_content no-padding-imp' ); ?> diff --git a/pandora_console/operation/agentes/status_monitor.php b/pandora_console/operation/agentes/status_monitor.php index fa0473c771..447baa3556 100644 --- a/pandora_console/operation/agentes/status_monitor.php +++ b/pandora_console/operation/agentes/status_monitor.php @@ -62,7 +62,7 @@ if (! defined('METACONSOLE')) { break; } - ui_print_page_header(__('Monitor detail').$subpage, '', false, '', true, $buttons); + ui_print_page_header(__('Monitor detail').$subpage, '', false, 'monitor_detail_view', true, $buttons); if ($section == 'fields') { include_once $config['homedir'].'/godmode/agentes/status_monitor_custom_fields.php'; @@ -735,21 +735,23 @@ unset($table); // End Build Search Form // // Sort functionality -$selected = 'border: 1px solid black;'; -$selectAgentNameUp = ''; -$selectAgentNameDown = ''; -$selectTypeUp = ''; -$selectTypeDown = ''; -$selectModuleNameUp = ''; -$selectModuleNameDown = ''; -$selectIntervalUp = ''; -$selectIntervalDown = ''; -$selectStatusUp = ''; -$selectStatusDown = ''; -$selectDataUp = ''; -$selectDataDown = ''; -$selectTimestampUp = ''; -$selectTimestampDown = ''; +$selected = true; +$selectAgentNameUp = false; +$selectAgentNameDown = false; +$selectDataTypeUp = false; +$selectDataTypeDown = false; +$selectTypeUp = false; +$selectTypeDown = false; +$selectModuleNameUp = false; +$selectModuleNameDown = false; +$selectIntervalUp = false; +$selectIntervalDown = false; +$selectStatusUp = false; +$selectStatusDown = false; +$selectDataUp = false; +$selectDataDown = false; +$selectTimestampUp = false; +$selectTimestampDown = false; $order = null; switch ($sortField) { @@ -776,7 +778,7 @@ switch ($sortField) { case 'type': switch ($sort) { case 'up': - $selectTypeUp = $selected; + $selectDataTypeUp = $selected; $order = [ 'field' => 'tagente_modulo.id_tipo_modulo', 'order' => 'ASC', @@ -784,7 +786,7 @@ switch ($sortField) { break; case 'down': - $selectTypeDown = $selected; + $selectDataTypeDown = $selected; $order = [ 'field' => 'tagente_modulo.id_tipo_modulo', 'order' => 'DESC', @@ -896,7 +898,7 @@ switch ($sortField) { case 'data': switch ($sort) { case 'up': - $selectTimestampUp = $selected; + $selectDataUp = $selected; $order = [ 'field' => 'tagente_estado.datos', 'order' => 'ASC', @@ -904,7 +906,7 @@ switch ($sortField) { break; case 'down': - $selectTimestampDown = $selected; + $selectDataDown = $selected; $order = [ 'field' => 'tagente_estado.datos', 'order' => 'DESC', @@ -915,19 +917,21 @@ switch ($sortField) { default: $selectAgentNameUp = $selected; - $selectAgentNameDown = ''; - $selectTypeUp = ''; - $selectTypeDown = ''; - $selectModuleNameUp = ''; - $selectModuleNameDown = ''; - $selectIntervalUp = ''; - $selectIntervalDown = ''; - $selectStatusUp = ''; - $selectStatusDown = ''; - $selectDataUp = ''; - $selectDataDown = ''; - $selectTimestampUp = ''; - $selectTimestampDown = ''; + $selectAgentNameDown = false; + $selectDataTypeUp = false; + $selectDataTypeDown = false; + $selectTypeUp = false; + $selectTypeDown = false; + $selectModuleNameUp = false; + $selectModuleNameDown = false; + $selectIntervalUp = false; + $selectIntervalDown = false; + $selectStatusUp = false; + $selectStatusDown = false; + $selectDataUp = false; + $selectDataDown = false; + $selectTimestampUp = false; + $selectTimestampDown = false; $order = [ 'field' => 'tagente.alias', 'order' => 'ASC', @@ -987,7 +991,7 @@ if ($autosearch) { if ($result === false) { $result = []; } else { - ui_pagination($count, false, $offset); + ui_pagination($count, false, $offset, 0, false, 'offset', true); } } else { // For each server defined and not disabled: @@ -1066,14 +1070,25 @@ if (($config['dbtype'] == 'oracle') && ($result !== false)) { } +// Urls to sort the table. +$url_agent_name = 'index.php?sec=view&sec2=operation/agentes/status_monitor&refr='.$refr.'&datatype='.$datatype.'&moduletype='.$moduletype.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=agent_alias&sort='; +$url_type = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=type&sort='; +$url_module_name = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=module_name&sort='; +$url_server_type = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=moduletype&sort='; +$url_interval = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=interval&sort='; +$url_status = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=status&sort='; +$url_data = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=data&sort='; +$url_timestamp_up = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=timestamp&sort=up'; +$url_timestamp_down = 'index.php?sec=view&sec2=operation/agentes/status_monitor&datatype='.$datatype.'&moduletype='.$moduletype.'&refr='.$refr.'&modulegroup='.$modulegroup.'&offset='.$offset.'&ag_group='.$ag_group.'&ag_freestring='.$ag_freestring.'&ag_modulename='.$ag_modulename.'&status='.$status.$ag_custom_fields_params.'&sort_field=timestamp&sort=down'; + + // Start Build List Result -// if (!empty($result)) { $table = new StdClass(); $table->cellpadding = 0; $table->cellspacing = 0; $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->head = []; $table->data = []; $table->size = []; @@ -1090,36 +1105,34 @@ if (!empty($result)) { if (in_array('agent', $show_fields) || is_metaconsole()) { $table->head[1] = __('Agent'); - $table->head[1] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectAgentNameUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectAgentNameDown, 'alt' => 'down']).''; + $table->head[1] .= ui_get_sorting_arrows($url_agent_name.'up', $url_agent_name.'down', $selectAgentNameUp, $selectAgentNameDown); } if (in_array('data_type', $show_fields) || is_metaconsole()) { $table->head[2] = __('Data Type'); - $table->head[2] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectTypeUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectTypeDown, 'alt' => 'down']).''; - + $table->head[2] .= ui_get_sorting_arrows($url_type.'up', $url_type.'down', $selectDataTypeUp, $selectDataTypeDown); $table->align[2] = 'left'; } if (in_array('module_name', $show_fields) || is_metaconsole()) { $table->head[3] = __('Module name'); - $table->head[3] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectModuleNameUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectModuleNameDown, 'alt' => 'down']).''; + $table->head[3] .= ui_get_sorting_arrows($url_module_name.'up', $url_module_name.'down', $selectModuleNameUp, $selectModuleNameDown); } if (in_array('server_type', $show_fields) || is_metaconsole()) { $table->head[4] = __('Server type'); - $table->head[4] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectModuleNameUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectModuleNameDown, 'alt' => 'down']).''; + $table->head[4] .= ui_get_sorting_arrows($url_server_type.'up', $url_server_type.'down', $selectTypeUp, $selectTypeDown); } if (in_array('interval', $show_fields) || is_metaconsole()) { $table->head[5] = __('Interval'); - $table->head[5] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectIntervalUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectIntervalDown, 'alt' => 'down']).''; + $table->head[5] .= ui_get_sorting_arrows($url_interval.'up', $url_interval.'down', $selectIntervalUp, $selectIntervalDown); $table->align[5] = 'left'; } if (in_array('status', $show_fields) || is_metaconsole()) { $table->head[6] = __('Status'); - $table->head[6] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectStatusUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectStatusDown, 'alt' => 'down']).''; - + $table->head[6] .= ui_get_sorting_arrows($url_status.'up', $url_status.'down', $selectStatusUp, $selectStatusDown); $table->align[6] = 'left'; } @@ -1137,13 +1150,13 @@ if (!empty($result)) { $table->head[9] = __('Data'); $table->align[9] = 'left'; if (is_metaconsole()) { - $table->head[9] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectStatusUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectStatusDown, 'alt' => 'down']).''; + $table->head[9] .= ui_get_sorting_arrows($url_data.'up', $url_data.'down', $selectDataUp, $selectDataDown); } } if (in_array('timestamp', $show_fields) || is_metaconsole()) { $table->head[10] = __('Timestamp'); - $table->head[10] .= ' '.html_print_image('images/sort_up.png', true, ['style' => $selectTimestampUp, 'alt' => 'up']).''.''.html_print_image('images/sort_down.png', true, ['style' => $selectTimestampDown, 'alt' => 'down']).''; + $table->head[10] .= ui_get_sorting_arrows($url_timestamp_up, $url_timestamp_down, $selectTimestampUp, $selectTimestampDown); $table->align[10] = 'left'; } @@ -1284,6 +1297,7 @@ if (!empty($result)) { } if ($show_edit_icon) { + $table->cellclass[][2] = 'action_buttons'; $data[2] .= ''.html_print_image( 'images/config.png', true, @@ -1659,6 +1673,8 @@ if (!empty($result)) { } html_print_table($table); + + ui_pagination($count, false, $offset, 0, false, 'offset', true, 'pagination-bottom'); } else { if ($first_interaction) { ui_print_info_message(['no_close' => true, 'message' => __('This group doesn\'t have any monitor')]); diff --git a/pandora_console/operation/agentes/tactical.php b/pandora_console/operation/agentes/tactical.php index a4d3e6c98e..10e4ff5736 100755 --- a/pandora_console/operation/agentes/tactical.php +++ b/pandora_console/operation/agentes/tactical.php @@ -49,8 +49,15 @@ if ($config['realtimestats'] == 0) { $updated_info = ''; } -// Header -ui_print_page_header(__('Tactical view'), '', false, '', false, $updated_time); +// Header. +ui_print_page_header( + __('Tactical view'), + '', + false, + 'tactical_view', + false, + $updated_time +); // Currently this function makes loading this page is impossible. Change // and create new function. @@ -115,7 +122,7 @@ echo ''; foreach ($stats as $stat) { @@ -180,7 +187,12 @@ if (check_acl($config['id_user'], 0, 'ER')) { } $events = events_print_event_table($event_filter, 10, '100%', true, false, true); - ui_toggle($events, __('Latest events'), false, false); + ui_toggle( + $events, + __('Latest events'), + false, + false + ); } // --------------------------------------------------------------------- @@ -226,4 +238,4 @@ echo ''; } }); }); - \ No newline at end of file + diff --git a/pandora_console/operation/agentes/ver_agente.php b/pandora_console/operation/agentes/ver_agente.php index 5578b9182f..9bed24a3db 100644 --- a/pandora_console/operation/agentes/ver_agente.php +++ b/pandora_console/operation/agentes/ver_agente.php @@ -37,6 +37,7 @@ require_once $config['homedir'].'/include/functions_users.php'; enterprise_include_once('include/functions_metaconsole.php'); ui_require_javascript_file('openlayers.pandora'); +ui_require_css_file('agent_view'); enterprise_include_once('operation/agentes/ver_agente.php'); @@ -1260,38 +1261,47 @@ if (enterprise_installed() && $config['log_collector']) { // EHorus tab. if ($config['ehorus_enabled'] && !empty($config['ehorus_custom_field']) - && (check_acl_one_of_groups($config['id_user'], $all_groups, 'AW') || is_user_admin($config['id_user'])) + && (check_acl_one_of_groups( + $config['id_user'], + $all_groups, + 'AW' + ) || is_user_admin($config['id_user'])) ) { - $ehorus_agent_id = agents_get_agent_custom_field($id_agente, $config['ehorus_custom_field']); - if (!empty($ehorus_agent_id)) { - $tab_url = 'index.php?sec=estado&sec2=operation/agentes/ver_agente&tab=ehorus&id_agente='.$id_agente; - $ehorus_tab['text'] = ''.html_print_image('images/ehorus/ehorus.png', true, [ 'title' => __('eHorus')]).''; + $user_info = users_get_user_by_id($config['id_user']); + if ($config['ehorus_user_level_conf'] && !$user_info['ehorus_user_level_enabled']) { + // If ehorus user configuration is enabled, and userr acces level is disabled do not show eHorus tab. + } else { + $ehorus_agent_id = agents_get_agent_custom_field($id_agente, $config['ehorus_custom_field']); + if (!empty($ehorus_agent_id)) { + $tab_url = 'index.php?sec=estado&sec2=operation/agentes/ver_agente&tab=ehorus&id_agente='.$id_agente; + $ehorus_tab['text'] = ''.html_print_image('images/ehorus/ehorus.png', true, [ 'title' => __('eHorus')]).''; - // Hidden subtab layer. - $ehorus_tab['sub_menu'] = ''; + // Hidden subtab layer. + $ehorus_tab['sub_menu'] = ''; - $ehorus_tab['active'] = $tab == 'ehorus'; + $ehorus_tab['active'] = $tab == 'ehorus'; + } } } @@ -1331,6 +1341,7 @@ if (isset($ehorus_tab) && !empty($ehorus_tab)) { } // Tabs for extensions. +$tab_name_extensions = ''; foreach ($config['extensions'] as $extension) { if (isset($extension['extension_ope_tab']) && !isset($extension['extension_god_tab'])) { if (check_acl_one_of_groups($config['id_user'], $all_groups, $extension['extension_ope_tab']['acl'])) { @@ -1373,6 +1384,7 @@ foreach ($config['extensions'] as $extension) { $image = $extension['extension_ope_tab']['icon']; $name = $extension['extension_ope_tab']['name']; $id = $extension['extension_ope_tab']['id']; + $tab_name_extensions = $name; $id_extension = get_parameter('id_extension', ''); @@ -1394,10 +1406,85 @@ foreach ($config['extensions'] as $extension) { } } -if ($tab == 'wux_console_tab') { - $help_header = 'wux_console'; -} else { - $help_header = ''; +switch ($tab) { + case 'custom_fields': + $tab_name = 'Custom fields'; + break; + + case 'gis': + $tab_name = 'GIS data'; + break; + + case 'manage': + $tab_name = 'Manage'; + break; + + case 'main': + $tab_name = 'Main'; + $help_header = 'agent_'.$tab.'_tab'; + break; + + case 'data_view': + $tab_name = ''; + break; + + case 'alert': + $tab_name = 'Alerts'; + break; + + case 'inventory': + $tab_name = 'Inventory'; + break; + + case 'collection': + $tab_name = 'Collection'; + break; + + case 'policy': + $tab_name = 'Policies'; + break; + + case 'ux_console_tab': + $tab_name = 'UX Console'; + break; + + case 'wux_console_tab': + $tab_name = 'WUX Console'; + $help_header = 'wux_console_tab'; + break; + + case 'url_route_analyzer_tab': + $tab_name = 'URL Route Analyzer'; + break; + + case 'graphs'; + $tab_name = 'Graphs'; + break; + + case 'incident': + $tab_name = 'Incidents'; + break; + + case 'url_address': + $tab_name = 'Url address'; + break; + + case 'log_viewer': + $tab_name = 'Log viewer'; + break; + + case 'ehorus': + $tab_name = 'eHorus'; + break; + + case 'extension': + $tab_name = $tab_name_extensions; + break; + + default: + $tab_name = ''; + $help_header = ''; + break; } ui_print_page_header( @@ -1409,7 +1496,15 @@ ui_print_page_header( $onheader, false, '', - $config['item_title_size_text'] + $config['item_title_size_text'], + '', + ui_print_breadcrums( + [ + __('Monitoring'), + __('View'), + ''.$tab_name.'', + ] + ) ); @@ -1427,6 +1522,7 @@ switch ($tab) { break; case 'main': + default: include 'estado_generalagente.php'; echo ""; include 'estado_monitores.php'; diff --git a/pandora_console/operation/events/events.build_query.php b/pandora_console/operation/events/events.build_query.php index 2c4d1fae07..f915a5093b 100755 --- a/pandora_console/operation/events/events.build_query.php +++ b/pandora_console/operation/events/events.build_query.php @@ -83,22 +83,43 @@ if (($date_from === '') && ($date_to === '')) { } } +$is_using_secondary_group = enterprise_hook('agents_is_using_secondary_groups'); + // Group selection. if ($id_group > 0 && in_array($id_group, array_keys($groups))) { if ($propagate) { $childrens_str = implode(',', $childrens_ids); - $sql_post .= " AND (id_grupo IN ($childrens_str) OR id_group IN ($childrens_str))"; + $sql_post .= " AND (id_grupo IN ($childrens_str)"; + + if ($is_using_secondary_group === 1) { + $sql_post .= " OR id_group IN ($childrens_str)"; + } + + $sql_post .= ')'; } else { // If a group is selected and it's in the groups allowed. - $sql_post .= " AND (id_grupo = $id_group OR id_group = $id_group)"; + $sql_post .= " AND (id_grupo = $id_group"; + + if ($is_using_secondary_group === 1) { + $sql_post .= " OR id_group = $id_group"; + } + + $sql_post .= ')'; } } else { if (!users_is_admin() && !users_can_manage_group_all('ER')) { - $sql_post .= sprintf( - ' AND (id_grupo IN (%s) OR id_group IN (%s)) ', - implode(',', array_keys($groups)), - implode(',', array_keys($groups)) - ); + if ($is_using_secondary_group === 1) { + $sql_post .= sprintf( + ' AND (id_grupo IN (%s) OR id_group IN (%s)) ', + implode(',', array_keys($groups)), + implode(',', array_keys($groups)) + ); + } else { + $sql_post .= sprintf( + ' AND (id_grupo IN (%s)) ', + implode(',', array_keys($groups)) + ); + } } } diff --git a/pandora_console/operation/events/events.build_table.php b/pandora_console/operation/events/events.build_table.php index d38bd1918e..3e112f3fb8 100644 --- a/pandora_console/operation/events/events.build_table.php +++ b/pandora_console/operation/events/events.build_table.php @@ -896,7 +896,7 @@ if ($group_rep == 2) { // Checkbox $data[$i] = html_print_checkbox_extended('validate_ids[]', $event['id_evento'], false, false, false, 'class="chk_val"', true); } else if (isset($table->header[$i]) || true) { - $data[$i] = ''; + $data[$i] = html_print_checkbox_extended('validate_ids[]', $event['id_evento'], false, false, false, 'class="chk_val"', true); } } @@ -929,6 +929,13 @@ if ($group_rep == 2) { } html_print_table($table); + if ($allow_pagination) { + $params_to_paginate = $params; + unset($params_to_paginate['offset']); + $url_paginate = 'index.php?'.http_build_query($params_to_paginate, '', '&'); + ui_pagination($total_events, $url_paginate, $offset, $pagination, false, 'offset', true, 'pagination-bottom'); + } + echo '
    '; if ($allow_action) { diff --git a/pandora_console/operation/events/events.php b/pandora_console/operation/events/events.php index de7a02074a..09f952108c 100644 --- a/pandora_console/operation/events/events.php +++ b/pandora_console/operation/events/events.php @@ -1,33 +1,52 @@ 0) { - // Agent name + // Agent name. echo agents_get_alias($event['id_agente']); } else { echo ''; @@ -104,7 +123,7 @@ if (is_ajax()) { $comment = (string) get_parameter('comment'); $new_status = get_parameter('new_status'); - // Set off the standby mode when close an event + // Set off the standby mode when close an event. if ($new_status == 1) { $event = events_get_event($id); alerts_agent_module_standby($event['id_alert_am'], 0); @@ -251,7 +270,14 @@ if (!$meta) { 'ACL Violation', 'Trying to access event viewer. View disabled due event replication.' ); - ui_print_info_message(['message' => __('Event viewer is disabled due event replication. For more information, please contact with the administrator'), 'no_close' => true]); + ui_print_info_message( + [ + 'message' => __( + 'Event viewer is disabled due event replication. For more information, please contact with the administrator' + ), + 'no_close' => true, + ] + ); return; } else { $readonly = true; @@ -259,8 +285,38 @@ if (!$meta) { } } +$id_filter = db_get_value( + 'id_filter', + 'tusuario', + 'id_user', + $config['id_user'] +); + +// If user has event filter retrieve filter values. +if (!empty($id_filter)) { + $apply_filter = true; + + $event_filter = events_get_event_filter($id_filter); + + $event_filter['search'] = io_safe_output($event_filter['search']); + $event_filter['id_name'] = io_safe_output($event_filter['id_name']); + $event_filter['tag_with'] = base64_encode( + io_safe_output($event_filter['tag_with']) + ); + $event_filter['tag_without'] = base64_encode( + io_safe_output($event_filter['tag_without']) + ); +} + +$is_filtered = get_parameter('is_filtered', false); $offset = (int) get_parameter('offset', 0); + +if ($event_filter['id_group'] == '') { + $event_filter['id_group'] = 0; +} + $id_group = (int) get_parameter('id_group', 0); + // 0 all // ********************************************************************** // TODO @@ -270,35 +326,60 @@ $id_group = (int) get_parameter('id_group', 0); // $recursion = (bool)get_parameter('recursion', false); //Flag show in child groups // ********************************************************************** $recursion = (bool) get_parameter('recursion', true); -// Flag show in child groups -$event_type = get_parameter('event_type', ''); -// 0 all -$severity = (int) get_parameter('severity', -1); -// -1 all -$status = (int) get_parameter('status', 3); -// -1 all, 0 only new, 1 only validated, 2 only in process, 3 only not validated, -$id_agent = (int) get_parameter('id_agent', 0); -$pagination = (int) get_parameter('pagination', $config['block_size']); -$event_view_hr = (int) get_parameter('event_view_hr', $history ? 0 : $config['event_view_hr']); -$id_user_ack = get_parameter('id_user_ack', 0); -$group_rep = (int) get_parameter('group_rep', 1); +// Flag show in child groups. +if (empty($event_filter['event_type'])) { + $event_filter['event_type'] = ''; +} + +$event_type = ($apply_filter === true && $is_filtered === false) ? $event_filter['event_type'] : get_parameter('event_type', ''); + +// 0 all. +$severity = ($apply_filter === true && $is_filtered === false) ? (int) $event_filter['severity'] : (int) get_parameter('severity', -1); +// -1 all. +if ($event_filter['status'] == -1) { + $event_filter['status'] = 3; +} + +$status = ($apply_filter === true && $is_filtered === false) ? (int) $event_filter['status'] : (int) get_parameter('status', 3); +// -1 all, 0 only new, 1 only validated, +// 2 only in process, 3 only not validated. +$id_agent = ($apply_filter === true && $is_filtered === false) ? (int) $event_filter['id_agent'] : (int) get_parameter('id_agent', 0); +$pagination = ($apply_filter === true && $is_filtered === false) ? (int) $event_filter['pagination'] : (int) get_parameter('pagination', $config['block_size']); + +if (empty($event_filter['event_view_hr'])) { + $event_filter['event_view_hr'] = ($history) ? 0 : $config['event_view_hr']; +} + +$event_view_hr = ($apply_filter === true && $is_filtered === false) ? (int) $event_filter['event_view_hr'] : (int) get_parameter( + 'event_view_hr', + ($history) ? 0 : $config['event_view_hr'] +); + + +$id_user_ack = ($apply_filter === true && $is_filtered === false) ? $event_filter['id_user_ack'] : get_parameter('id_user_ack', 0); +$group_rep = ($apply_filter === true && $is_filtered === false) ? (int) $event_filter['group_rep'] : (int) get_parameter('group_rep', 1); $delete = (bool) get_parameter('delete'); $validate = (bool) get_parameter('validate', 0); $section = (string) get_parameter('section', 'list'); -$filter_only_alert = (int) get_parameter('filter_only_alert', -1); +$filter_only_alert = ($apply_filter === true && $is_filtered === false) ? (int) $event_filter['filter_only_alert'] : (int) get_parameter('filter_only_alert', -1); $filter_id = (int) get_parameter('filter_id', 0); -$id_name = (string) get_parameter('id_name', ''); + +if (empty($event_filter['id_name'])) { + $event_filter['id_name'] = ''; +} + +$id_name = ($apply_filter === true && $is_filtered === false) ? (string) $event_filter['id_name'] : (string) get_parameter('id_name', ''); $open_filter = (int) get_parameter('open_filter', 0); -$date_from = (string) get_parameter('date_from', ''); -$date_to = (string) get_parameter('date_to', ''); +$date_from = ($apply_filter === true && $is_filtered === false) ? (string) $event_filter['date_from'] : (string) get_parameter('date_from', ''); +$date_to = ($apply_filter === true && $is_filtered === false) ? (string) $event_filter['date_to'] : (string) get_parameter('date_to', ''); $time_from = (string) get_parameter('time_from', ''); $time_to = (string) get_parameter('time_to', ''); $server_id = (int) get_parameter('server_id', 0); -$text_agent = (string) get_parameter('text_agent'); +$text_agent = ($apply_filter === true && $is_filtered === false) ? (string) $event_filter['text_agent'] : (string) get_parameter('text_agent'); $refr = (int) get_parameter('refresh'); -$id_extra = (string) get_parameter('id_extra'); -$user_comment = (string) get_parameter('user_comment'); -$source = (string) get_parameter('source'); +$id_extra = ($apply_filter === true && $is_filtered === false) ? (string) $event_filter['id_extra'] : (string) get_parameter('id_extra'); +$user_comment = ($apply_filter === true && $is_filtered === false) ? (string) $event_filter['user_comment'] : (string) get_parameter('user_comment'); +$source = ($apply_filter === true && $is_filtered === false) ? (string) $event_filter['source'] : (string) get_parameter('source'); if ($id_agent != 0) { $text_agent = agents_get_alias($id_agent); @@ -313,9 +394,17 @@ if ($id_agent != 0) { } $text_module = (string) get_parameter('module_search', ''); -$id_agent_module = get_parameter('module_search_hidden', get_parameter('id_agent_module', 0)); +$id_agent_module = ($apply_filter === true && $is_filtered === false) ? $event_filter['id_agent_module'] : get_parameter( + 'module_search_hidden', + get_parameter('id_agent_module', 0) +); if ($id_agent_module != 0) { - $text_module = db_get_value('nombre', 'tagente_modulo', 'id_agente_modulo', $id_agent_module); + $text_module = db_get_value( + 'nombre', + 'tagente_modulo', + 'id_agente_modulo', + $id_agent_module + ); if ($text_module == false) { $text_module = ''; } @@ -325,7 +414,7 @@ if ($id_agent_module != 0) { -$tag_with_json = base64_decode(get_parameter('tag_with', '')); +$tag_with_json = ($apply_filter === true && $is_filtered === false) ? base64_decode($event_filter['tag_with']) : base64_decode(get_parameter('tag_with', '')); $tag_with_json_clean = io_safe_output($tag_with_json); $tag_with_base64 = base64_encode($tag_with_json_clean); $tag_with = json_decode($tag_with_json_clean, true); @@ -335,7 +424,7 @@ if (empty($tag_with)) { $tag_with = array_diff($tag_with, [0 => 0]); -$tag_without_json = base64_decode(get_parameter('tag_without', '')); +$tag_without_json = ($apply_filter === true && $is_filtered === false) ? base64_decode($event_filter['tag_without']) : base64_decode(get_parameter('tag_without', '')); $tag_without_json_clean = io_safe_output($tag_without_json); $tag_without_base64 = base64_encode($tag_without_json_clean); $tag_without = json_decode($tag_without_json_clean, true); @@ -362,42 +451,42 @@ $url = 'index.php?sec=eventos&sec2=operation/events/events&'.$params; -// Header +// Header. if ($config['pure'] == 0 || $meta) { $pss = get_user_info($config['id_user']); $hashup = md5($config['id_user'].$pss['password']); - // Fullscreen + // Fullscreen. $fullscreen['active'] = false; $fullscreen['text'] = ''.html_print_image('images/full_screen.png', true, ['title' => __('Full screen')]).''; - // Event list + // Event list. $list['active'] = false; $list['text'] = ''.html_print_image('images/events_list.png', true, ['title' => __('Event list')]).''; - // History event list + // History event list. $history_list['active'] = false; $history_list['text'] = ''.html_print_image('images/books.png', true, ['title' => __('History event list')]).''; - // RSS + // RSS. $rss['active'] = false; $rss['text'] = ''.html_print_image('images/rss.png', true, ['title' => __('RSS Events')]).''; - // Marquee + // Marquee. $marquee['active'] = false; $marquee['text'] = ''.html_print_image('images/heart.png', true, ['title' => __('Marquee display')]).''; - // CSV + // CSV. $csv['active'] = false; $csv['text'] = ''.html_print_image('images/csv_mc.png', true, ['title' => __('Export to CSV file')]).''; - // Sound events + // Sound events. $sound_event['active'] = false; $sound_event['text'] = ''.html_print_image('images/sound.png', true, ['title' => __('Sound events')]).''; - // If the user has administrator permission display manage tab + // If the user has administrator permission display manage tab. if (check_acl($config['id_user'], 0, 'EW') || check_acl($config['id_user'], 0, 'EM')) { - // Manage events + // Manage events. $manage_events['active'] = false; $manage_events['text'] = ''.html_print_image('images/setup.png', true, ['title' => __('Manage events')]).''; @@ -425,7 +514,7 @@ if ($config['pure'] == 0 || $meta) { ]; } - // If the history event is not ebabled, dont show the history tab + // If the history event is not ebabled, dont show the history tab. if (!isset($config['metaconsole_events_history']) || $config['metaconsole_events_history'] != 1) { unset($onheader['history']); } @@ -447,16 +536,6 @@ if ($config['pure'] == 0 || $meta) { break; } - - /* - Hello there! :) - - We added some of what seems to be "buggy" messages to the openSource version recently. This is not to force open-source users to move to the enterprise version, this is just to inform people using Pandora FMS open source that it requires skilled people to maintain and keep it running smoothly without professional support. This does not imply open-source version is limited in any way. If you check the recently added code, it contains only warnings and messages, no limitations except one: we removed the option to add custom logo in header. In the Update Manager section, it warns about the 'danger’ of applying automated updates without a proper backup, remembering in the process that the Enterprise version comes with a human-tested package. Maintaining an OpenSource version with more than 500 agents is not so easy, that's why someone using a Pandora with 8000 agents should consider asking for support. It's not a joke, we know of many setups with a huge number of agents, and we hate to hear that “its becoming unstable and slow” :( - - You can of course remove the warnings, that's why we include the source and do not use any kind of trick. And that's why we added here this comment, to let you know this does not reflect any change in our opensource mentality of does the last 14 years. - - */ - if (! defined('METACONSOLE')) { unset($onheader['history']); ui_print_page_header( @@ -482,37 +561,55 @@ if ($config['pure'] == 0 || $meta) { '; echo ''; echo '
    '; - // Floating menu - End + // Floating menu - End. ui_require_jquery_file('countdown'); } -// Error div for ajax messages +// Error div for ajax messages. echo "
    "; echo '
    '; @@ -535,7 +632,7 @@ if (($section == 'validate') && ($ids[0] == -1)) { ui_print_error_message(__('No events selected')); } -// Process validation (pass array or single value) +// Process validation (pass array or single value). if ($validate) { $ids = get_parameter('eventid', -1); $comment = get_parameter('comment', ''); @@ -543,7 +640,7 @@ if ($validate) { $ids = explode(',', $ids); $standby_alert = (bool) get_parameter('standby-alert'); - // Avoid to re-set inprocess events + // Avoid to re-set inprocess events. if ($new_status == 2) { foreach ($ids as $key => $id) { $event = events_get_event($id); @@ -572,11 +669,11 @@ if ($validate) { } } -// Process deletion (pass array or single value) +// Process deletion (pass array or single value). if ($delete) { $ids = (array) get_parameter('validate_ids', -1); - // Discard deleting in progress events + // Discard deleting in progress events. $in_process_status = db_get_all_rows_sql( ' SELECT id_evento @@ -629,7 +726,6 @@ $(document).ready( function() { var refr = ; var pure = ; var href = ""; - // alert($(location).attr('href')); if (pure) { var startCountDown = function (duration, cb) { $('div.vc-countdown').countdown('destroy'); @@ -663,9 +759,10 @@ $(document).ready( function() { }); } else { - $('#refresh').change(function () { - $('#hidden-vc_refr').val($('#refresh option:selected').val()); + $('#hidden-vc_refr').val( + $('#refresh option:selected').val() + ); }); } @@ -840,11 +937,11 @@ $(document).ready( function() { // Remove delete link (if event is not grouped and there is more than one event) if ($("#group_rep").val() == 1) { if (parseInt($("#count_event_group_"+id).text()) <= 1) { - $("#delete-"+id).replaceWith('' + <?php echo + '" title="' + + '" src="images/cross.disabled.png">'); + $("#delete-"+id).replaceWith(' <?php echo addslashes(__('Is not allowed delete events in process')); ?>'); } } else { // Remove delete link (if event is not grouped) - $("#delete-"+id).replaceWith('' + <?php echo + '" title="' + + '" src="images/cross.disabled.png">'); + $("#delete-"+id).replaceWith('<?php echo addslashes(__('Is not allowed delete events in process')); ?> '); } // Change state image @@ -930,7 +1027,7 @@ $(document).ready( function() { {"page" : "operation/events/events", "delete_event" : 1, "id" : id, - "similars" : , + "similars" : , "meta" : meta, "history" : history_var }, @@ -1045,10 +1142,10 @@ function validate_event_advanced(id, new_status) { $("#in-progress-"+id).remove(); // Format the new disabled delete icon. $("#validate-"+id).parent().append(""); - $("#delete-"+id).attr ("data-title", ); - $("#delete-"+id).attr ("alt", ); + $("#delete-"+id).attr ("data-title", ""); + $("#delete-"+id).attr ("alt"," "); $("#delete-"+id).attr ("data-use_title_for_force_title", 1); - $("#delete-"+id).attr ("class", "forced_title"); + $("#delete-"+id).attr ("class", "forced_title"); // Remove row due to new state if (($("#status").val() == 0) @@ -1096,4 +1193,4 @@ if ($load_event) { } ?> /* ]]> */ - + \ No newline at end of file diff --git a/pandora_console/operation/events/events_list.php b/pandora_console/operation/events/events_list.php index 92ef7bff6d..8333ca5b0d 100644 --- a/pandora_console/operation/events/events_list.php +++ b/pandora_console/operation/events/events_list.php @@ -1,28 +1,44 @@ $user]); -$update_from_filter_table = (bool) get_parameter('update_from_filter_table', false); +$user_filter = db_get_value_filter( + 'default_event_filter', + 'tusuario', + ['id_user' => $user] +); +$update_from_filter_table = (bool) get_parameter( + 'update_from_filter_table', + false +); if ($user_filter != 0 && empty($id_name) && !$update_from_filter_table) { - $user_default_filter = db_get_all_rows_filter('tevent_filter', ['id_filter' => $user_filter]); + $user_default_filter = db_get_all_rows_filter( + 'tevent_filter', + ['id_filter' => $user_filter] + ); $user_default_filter = $user_default_filter[0]; - // FORM - $id_name = $user_default_filter['id_name']; - $id_group = $user_default_filter['id_group']; - if ($user_default_filter['event_type'] != '') { - $event_type = $user_default_filter['event_type']; - } + if (!empty($user_default_filter)) { + // FORM. + $id_name = $user_default_filter['id_name']; + $id_group = $user_default_filter['id_group']; + if ($user_default_filter['event_type'] != '') { + $event_type = $user_default_filter['event_type']; + } - $severity = $user_default_filter['severity']; - $status = $user_default_filter['status']; - $event_view_hr = $user_default_filter['event_view_hr']; - $group_rep = $user_default_filter['group_rep']; - $id_extra = $user_default_filter['id_extra']; - $user_comment = $user_default_filter['user_comment']; - $source = $user_default_filter['source']; + $severity = $user_default_filter['severity']; + $status = $user_default_filter['status']; + $event_view_hr = $user_default_filter['event_view_hr']; + $group_rep = $user_default_filter['group_rep']; + $id_extra = $user_default_filter['id_extra']; + $user_comment = $user_default_filter['user_comment']; + $source = $user_default_filter['source']; - if ($user_default_filter['search'] != '') { - $search = $user_default_filter['search']; - } + if ($user_default_filter['search'] != '') { + $search = $user_default_filter['search']; + } - if ($user_default_filter['id_user_ack'] != 0) { - $id_user_ack = $user_default_filter['id_user_ack']; - } + if ($user_default_filter['id_user_ack'] != 0) { + $id_user_ack = $user_default_filter['id_user_ack']; + } - if ($user_default_filter['id_agent_module'] != 0) { - $id_agent_module = $user_default_filter['id_agent_module']; - } + if ($user_default_filter['id_agent_module'] != 0) { + $id_agent_module = $user_default_filter['id_agent_module']; + } - if ($user_default_filter['id_agent'] != 0) { - $id_agent = $user_default_filter['id_agent']; - $text_agent = agents_get_alias($id_agent); - } + if ($user_default_filter['id_agent'] != 0) { + $id_agent = $user_default_filter['id_agent']; + $text_agent = agents_get_alias($id_agent); + } - if ($user_default_filter['filter_only_alert'] != -1) { - $filter_only_alert = $user_default_filter['filter_only_alert']; - } + if ($user_default_filter['filter_only_alert'] != -1) { + $filter_only_alert = $user_default_filter['filter_only_alert']; + } - if ($user_default_filter['pagination'] != 20) { - $pagination = $user_default_filter['pagination']; - } + if ($user_default_filter['pagination'] != 20) { + $pagination = $user_default_filter['pagination']; + } - if ($user_default_filter['date_from'] != '0000-00-00') { - $date_from = $user_default_filter['date_from']; - } + if ($user_default_filter['date_from'] != '0000-00-00') { + $date_from = $user_default_filter['date_from']; + } - if ($user_default_filter['date_to'] != '0000-00-00') { - $date_to = $user_default_filter['date_to']; - } + if ($user_default_filter['date_to'] != '0000-00-00') { + $date_to = $user_default_filter['date_to']; + } - if (io_safe_output($user_default_filter['tag_with']) != '[]' && io_safe_output($user_default_filter['tag_with']) != '["0"]') { - $tag_with = $user_default_filter['tag_with']; - $tag_with_clean = io_safe_output($tag_with); - $tag_with = json_decode($tag_with_clean, true); - } + if (io_safe_output($user_default_filter['tag_with']) != '[]' + && io_safe_output($user_default_filter['tag_with']) != '["0"]' + ) { + $tag_with = $user_default_filter['tag_with']; + $tag_with_clean = io_safe_output($tag_with); + $tag_with = json_decode($tag_with_clean, true); + } - if (io_safe_output($user_default_filter['tag_without']) != '[]' && io_safe_output($user_default_filter['tag_without']) != '["0"]') { - $tag_without = $user_default_filter['tag_without']; - $tag_without_clear = io_safe_output($tag_without); - $tag_without = json_decode($tag_without_clear, true); + if (io_safe_output($user_default_filter['tag_without']) != '[]' + && io_safe_output($user_default_filter['tag_without']) != '["0"]' + ) { + $tag_without = $user_default_filter['tag_without']; + $tag_without_clear = io_safe_output($tag_without); + $tag_without = json_decode($tag_without_clear, true); + } } } -// -------------------------------------------------------------------------- -// -// Build the condition of the events query +// Build the condition of the events query. $sql_post = ''; $id_user = $config['id_user']; @@ -265,10 +307,10 @@ $id_user = $config['id_user']; $filter_resume = []; require 'events.build_query.php'; -// Now $sql_post have all the where condition -// +// Now $sql_post have all the where condition. // Trick to catch if any filter button has been pushed (don't collapse filter) -// or the filter was open before click or autorefresh is in use (collapse filter) +// or the filter was open before click +// or autorefresh is in use (collapse filter). $update_pressed = get_parameter_post('update', ''); $update_pressed = (int) !empty($update_pressed); @@ -279,9 +321,15 @@ if ($update_pressed || $open_filter) { unset($table); $filters = events_get_event_filter_select(); -$user_groups_array = users_get_groups_for_select($config['id_user'], $access, true, true, false); +$user_groups_array = users_get_groups_for_select( + $config['id_user'], + $access, + true, + true, + false +); -// Some translated words to be used from javascript +// Some translated words to be used from javascript. html_print_div( [ 'hidden' => true, @@ -289,6 +337,7 @@ html_print_div( 'content' => __('No filter loaded'), ] ); + html_print_div( [ 'hidden' => true, @@ -296,6 +345,7 @@ html_print_div( 'content' => __('Filter loaded'), ] ); + html_print_div( [ 'hidden' => true, @@ -303,6 +353,7 @@ html_print_div( 'content' => __('Save filter'), ] ); + html_print_div( [ 'hidden' => true, @@ -311,8 +362,10 @@ html_print_div( ] ); -if (check_acl($config['id_user'], 0, 'EW') || check_acl($config['id_user'], 0, 'EM')) { - // Save filter div for dialog +if (check_acl($config['id_user'], 0, 'EW') + || check_acl($config['id_user'], 0, 'EM') +) { + // Save filter div for dialog. echo ''; } -// Load filter div for dialog +// Load filter div for dialog. echo ''; -// TAGS +// TAGS. $tags_select_with = []; $tags_select_without = []; $tag_with_temp = []; $tag_without_temp = []; foreach ($tags as $id_tag => $tag) { - if ((array_search($id_tag, $tag_with) === false) || (array_search($id_tag, $tag_with) === null)) { + if ((array_search($id_tag, $tag_with) === false) + || (array_search($id_tag, $tag_with) === null) + ) { $tags_select_with[$id_tag] = ui_print_truncate_text($tag, 50, true); } else { $tag_with_temp[$id_tag] = ui_print_truncate_text($tag, 50, true); } - if ((array_search($id_tag, $tag_without) === false) || (array_search($id_tag, $tag_without) === null)) { + if ((array_search($id_tag, $tag_without) === false) + || (array_search($id_tag, $tag_without) === null) + ) { $tags_select_without[$id_tag] = ui_print_truncate_text($tag, 50, true); } else { $tag_without_temp[$id_tag] = ui_print_truncate_text($tag, 50, true); @@ -476,9 +581,31 @@ $data[0] = html_print_select( 'width: 200px;' ); -$data[1] = html_print_image('images/darrowright.png', true, ['id' => 'button-add_with', 'style' => 'cursor: pointer;', 'title' => __('Add')]); -$data[1] .= html_print_input_hidden('tag_with', $tag_with_base64, true); -$data[1] .= '

    '.html_print_image('images/darrowleft.png', true, ['id' => 'button-remove_with', 'style' => 'cursor: pointer;', 'title' => __('Remove')]); +$data[1] = html_print_image( + 'images/darrowright.png', + true, + [ + 'id' => 'button-add_with', + 'style' => 'cursor: pointer;', + 'title' => __('Add'), + ] +); + +$data[1] .= html_print_input_hidden( + 'tag_with', + $tag_with_base64, + true +); + +$data[1] .= '

    '.html_print_image( + 'images/darrowleft.png', + true, + [ + 'id' => 'button-remove_with', + 'style' => 'cursor: pointer;', + 'title' => __('Remove'), + ] +); $data[2] = html_print_select( $tag_with_temp, @@ -528,9 +655,29 @@ $data[0] = html_print_select( false, 'width: 200px;' ); -$data[1] = html_print_image('images/darrowright.png', true, ['id' => 'button-add_without', 'style' => 'cursor: pointer;', 'title' => __('Add')]); -$data[1] .= html_print_input_hidden('tag_without', $tag_without_base64, true); -$data[1] .= '

    '.html_print_image('images/darrowleft.png', true, ['id' => 'button-remove_without', 'style' => 'cursor: pointer;', 'title' => __('Remove')]); +$data[1] = html_print_image( + 'images/darrowright.png', + true, + [ + 'id' => 'button-add_without', + 'style' => 'cursor: pointer;', + 'title' => __('Add'), + ] +); +$data[1] .= html_print_input_hidden( + 'tag_without', + $tag_without_base64, + true +); +$data[1] .= '

    '.html_print_image( + 'images/darrowleft.png', + true, + [ + 'id' => 'button-remove_without', + 'style' => 'cursor: pointer;', + 'title' => __('Remove'), + ] +); $data[2] = html_print_select( $tag_without_temp, 'tag_without_temp', @@ -549,24 +696,35 @@ $tabletags_without->data[] = $data; $tabletags_without->rowclass[] = ''; -// END OF TAGS -// EVENTS FILTER -// Table for filter controls +// END OF TAGS. +// EVENTS FILTER. +// Table for filter controls. if (is_metaconsole()) { - $events_filter = '
    '; + $events_filter = ''; } else { - $events_filter = ''; + $events_filter = ''; } -// Hidden field with the loaded filter name +// Hidden field with the loaded filter name. $events_filter .= html_print_input_hidden('id_name', $id_name, true); +// Hidden field that signals filter has been applied. +$events_filter .= html_print_input_hidden('is_filtered', 'true', true); + // Hidden open filter flag -// If autoupdate is in use collapse filter +// If autoupdate is in use collapse filter. if ($open_filter) { - $events_filter .= html_print_input_hidden('open_filter', 'true', true); + $events_filter .= html_print_input_hidden( + 'open_filter', + 'true', + true + ); } else { - $events_filter .= html_print_input_hidden('open_filter', 'false', true); + $events_filter .= html_print_input_hidden( + 'open_filter', + 'false', + true + ); } // ---------------------------------------------------------------------- @@ -606,11 +764,11 @@ $table_advanced->rowclass[] = ''; $data = []; $data[0] = __('User ack.').$jump; -if ($strict_user) { - $user_users = [$config['id_user'] => $config['id_user']]; -} else { - $user_users = users_get_user_users($config['id_user'], $access, users_can_manage_group_all()); -} +$user_users = users_get_user_users( + $config['id_user'], + $access, + users_can_manage_group_all() +); $data[0] .= html_print_select( $user_users, @@ -621,6 +779,7 @@ $data[0] .= html_print_select( 0, true ); + if (!$meta) { $data[1] = __('Module search').$jump; $data[1] .= html_print_autocomplete_modules( @@ -635,27 +794,15 @@ if (!$meta) { ); } else { $data[1] = __('Server').$jump; - if ($strict_user) { - $data[1] .= html_print_select( - '', - 'server_id', - $server_id, - 'script', - __('All'), - '0', - true - ); - } else { - $data[1] .= html_print_select_from_sql( - 'SELECT id, server_name FROM tmetaconsole_setup', - 'server_id', - $server_id, - 'script', - __('All'), - '0', - true - ); - } + $data[1] .= html_print_select_from_sql( + 'SELECT id, server_name FROM tmetaconsole_setup', + 'server_id', + $server_id, + 'script', + __('All'), + '0', + true + ); } $table_advanced->data[] = $data; @@ -668,14 +815,30 @@ $alert_events_titles = [ '0' => __('Filter alert events'), '1' => __('Only alert events'), ]; -$data[0] .= html_print_select($alert_events_titles, 'filter_only_alert', $filter_only_alert, '', '', '', true); +$data[0] .= html_print_select( + $alert_events_titles, + 'filter_only_alert', + $filter_only_alert, + '', + '', + '', + true +); $data[1] = __('Block size for pagination').$jump; $lpagination[25] = 25; $lpagination[50] = 50; $lpagination[100] = 100; $lpagination[200] = 200; $lpagination[500] = 500; -$data[1] .= html_print_select($lpagination, 'pagination', $pagination, '', __('Default'), $config['block_size'], true); +$data[1] .= html_print_select( + $lpagination, + 'pagination', + $pagination, + '', + __('Default'), + $config['block_size'], + true +); $table_advanced->data[] = $data; $table_advanced->rowclass[] = ''; @@ -701,11 +864,11 @@ $table_advanced->rowclass[] = ''; $data = []; if (defined('METACONSOLE')) { - $data[0] = '
    '.''.__('Events with following tags').''.html_print_table($tabletags_with, true).'
    '; - $data2[1] = '
    '.''.__('Events without following tags').''.html_print_table($tabletags_without, true).'
    '; + $data[0] = '
    '.__('Events with following tags').''.html_print_table($tabletags_with, true).'
    '; + $data2[1] = '
    '.__('Events without following tags').''.html_print_table($tabletags_without, true).'
    '; } else { - $data[0] = '
    '.''.__('Events with following tags').''.html_print_table($tabletags_with, true).'
    '; - $data[1] = '
    '.''.__('Events without following tags').''.html_print_table($tabletags_without, true).'
    '; + $data[0] = '
    '.__('Events with following tags').''.html_print_table($tabletags_with, true).'
    '; + $data[1] = '
    '.__('Events without following tags').''.html_print_table($tabletags_without, true).'
    '; } $table_advanced->data[] = $data; @@ -714,7 +877,7 @@ if (defined('METACONSOLE')) { } $table_advanced->rowclass[] = ''; -// - END ADVANCE FILTER ------------------------------------------------- +// END ADVANCE FILTER -------------------------------------------------. $table = new stdClass(); $table->id = 'events_filter_form'; $table->width = '100%'; @@ -748,13 +911,33 @@ $data[0] .= html_print_select( // ********************************************************************** $data[1] = __('Event type').$jump; $types = get_event_types(); -// Expand standard array to add not_normal (not exist in the array, used only for searches) + +// Expand standard array to add not_normal +// (not exist in the array, used only for searches). $types['not_normal'] = __('Not normal'); -$data[1] .= html_print_select($types, 'event_type', $event_type, '', __('All'), '', true); +$data[1] .= html_print_select( + $types, + 'event_type', + $event_type, + '', + __('All'), + '', + true +); $data[2] = __('Severity').$jump; $severities = get_priorities(); -$data[2] .= html_print_select($severities, 'severity', $severity, '', __('All'), '-1', true, false, false); +$data[2] .= html_print_select( + $severities, + 'severity', + $severity, + '', + __('All'), + '-1', + true, + false, + false +); $table->data[] = $data; $table->rowclass[] = ''; @@ -763,12 +946,27 @@ $data[0] = __('Event status').$jump; $fields = events_get_all_status(); $data[0] .= html_print_select($fields, 'status', $status, '', '', '', true); $data[1] = __('Max. hours old').$jump; -$data[1] .= html_print_input_text('event_view_hr', $event_view_hr, '', 5, 255, true); +$data[1] .= html_print_input_text( + 'event_view_hr', + $event_view_hr, + '', + 5, + 255, + true +); $data[2] = __('Repeated').$jump; $repeated_sel[0] = __('All events'); $repeated_sel[1] = __('Group events'); $repeated_sel[2] = __('Group agents'); -$data[2] .= html_print_select($repeated_sel, 'group_rep', $group_rep, '', '', 0, true); +$data[2] .= html_print_select( + $repeated_sel, + 'group_rep', + $group_rep, + '', + '', + 0, + true +); $table->data[] = $data; $table->rowclass[] = ''; @@ -778,7 +976,14 @@ $data[0] .= html_print_input_text('source', $source, '', 35, 255, true); $data[1] = __('Extra ID').$jump; $data[1] .= html_print_input_text('id_extra', $id_extra, '', 11, 255, true); $data[2] = __('Comment').$jump; -$data[2] .= html_print_input_text('user_comment', $user_comment, '', 35, 255, true); +$data[2] .= html_print_input_text( + 'user_comment', + $user_comment, + '', + 35, + 255, + true +); $table->data[] = $data; $table->rowclass[] = ''; @@ -800,7 +1005,7 @@ $data = []; $table->data[] = $data; $table->rowclass[] = ''; -// The buttons +// The buttons. $data = []; $data[0] = '
    '; if ($event_w || $event_m) { @@ -826,13 +1031,33 @@ $events_filter .= html_print_table($table, true); unset($table); $botom_update = "
    "; -$botom_update .= html_print_submit_button(__('Update'), 'update', false, 'class="sub upd" style="float:right;"', true); +$botom_update .= html_print_submit_button( + __('Update'), + 'update', + false, + 'class="sub upd" style="float:right;"', + true +); $botom_update .= '
    '; $events_filter .= $botom_update; $events_filter .= ''; -// This is the filter div +// This is the filter div. +$user_filter = db_get_value( + 'default_event_filter', + 'tusuario', + 'id_user', + $config['id_user'] +); +$user_default_filter = db_get_all_rows_filter( + 'tevent_filter', + ['id_filter' => $user_filter] +); +$user_default_filter = $user_default_filter[0]; + +$id_name = $user_default_filter['id_name']; + $filter_resume['title'] = empty($id_name) ? __('No filter loaded') : __('Filter loaded').': '.$id_name; if (is_metaconsole()) { @@ -846,14 +1071,29 @@ if (is_metaconsole()) { } -// Error div for ajax messages +// Error div for ajax messages. echo "'; ?> @@ -877,7 +1117,7 @@ if ($group_rep == 0) { WHERE 1=1 ".$sql_post." $order_sql LIMIT ".$offset.','.$pagination; - // Extract the events by filter (or not) from db + // Extract the events by filter (or not) from db. $result = db_get_all_rows_sql($sql); } else if ($group_rep == 1) { $filter_resume['duplicate'] = $group_rep; @@ -905,7 +1145,7 @@ if ($group_rep == 0) { // Active filter tag view call (only enterprise version) // It is required to pass some references to enterprise function -// to translate the active filters +// to translate the active filters. enterprise_hook( 'print_event_tags_active_filters', [ @@ -922,8 +1162,9 @@ enterprise_hook( ); if (!empty($result)) { - // ~ Checking the event tags exactly. The event query filters approximated tags to keep events - // ~ with several tags + // Checking the event tags exactly. + // The event query filters approximated tags to keep events + // with several tags. $acltags = tags_get_user_groups_and_tags($config['id_user'], $access, true); foreach ($result as $key => $event_data) { @@ -936,7 +1177,7 @@ if (!empty($result)) { -// Delete rnum field generated by oracle_recode_query() function +// Delete rnum field generated by oracle_recode_query() function. if (($config['dbtype'] == 'oracle') && ($result !== false)) { for ($i = 0; $i < count($result); $i++) { unset($result[$i]['rnum']); @@ -980,7 +1221,7 @@ enterprise_hook('close_meta_frame'); unset($table); -// Values to be used from javascript library +// Values to be used from javascript library. html_print_input_hidden( 'ajax_file', ui_get_full_url('ajax.php', false, false, false) @@ -993,8 +1234,6 @@ ui_require_jquery_file('json'); ui_include_time_picker(); ?> + \ No newline at end of file diff --git a/pandora_console/operation/network/network_explorer.php b/pandora_console/operation/network/network_explorer.php new file mode 100644 index 0000000000..ced3706172 --- /dev/null +++ b/pandora_console/operation/network/network_explorer.php @@ -0,0 +1,40 @@ +class = 'databox'; +$table->styleTable = 'width: 100%'; +$table->data['0']['0'] = __('Data to show').'  '; +$table->data['0']['0'] .= html_print_select( + network_get_report_actions($is_network), + 'action', + $action, + '', + '', + 0, + true +); + +$table->data['0']['1'] = __('Number of result to show').'  '; +$table->data['0']['1'] .= html_print_select( + [ + '5' => 5, + '10' => 10, + '15' => 15, + '20' => 20, + '25' => 25, + '50' => 50, + '100' => 100, + '250' => 250, + ], + 'top', + $top, + '', + '', + 0, + true +); + +$table->data['0']['2'] = ''; + +$table->data['1']['0'] = '
    '; +$table->data['1']['0'] .= '
    '; +$table->data['1']['0'] .= __('Start date').'  '; +$table->data['1']['0'] .= html_print_input_text('date_lower', $date_lower, '', 10, 7, true); +$table->data['1']['0'] .= '  '; +$table->data['1']['0'] .= html_print_input_text('time_lower', $time_lower, '', 7, 8, true); +$table->data['1']['0'] .= '
    '; + +$table->data['1']['0'] .= '
    '; +$table->data['1']['0'] .= __('Time Period').'  '; +$table->data['1']['0'] .= html_print_extended_select_for_time('period', $period, '', '', 0, false, true); +$table->data['1']['0'] .= '
    '; +$table->data['1']['0'] .= html_print_checkbox( + 'is_period', + 1, + ($is_period === true) ? 1 : 0, + true, + false, + 'network_report_click_period(event)' +); +$table->data['1']['0'] .= ui_print_help_tip( + __('Select this checkbox to write interval instead a date.'), + true +); +$table->data['1']['0'] .= '
    '; + +$table->data['1']['1'] = __('End date').'  '; +$table->data['1']['1'] .= html_print_input_text('date_greater', $date_greater, '', 10, 7, true); +$table->data['1']['1'] .= '  '; +$table->data['1']['1'] .= html_print_input_text('time_greater', $time_greater, '', 7, 8, true); + +$table->data['1']['2'] = html_print_submit_button( + __('Update'), + 'update', + false, + 'class="sub upd"', + true +); +$table->data['1']['2'] .= '  '; +$table->data['1']['2'] .= html_print_submit_button( + __('Export to CSV'), + 'export_csv', + false, + 'class="sub next"', + true +); + +echo '
    '; +html_print_input_hidden('order_by', $order_by); +if (!empty($main_value)) { + html_print_input_hidden('main_value', $main_value); +} + +html_print_table($table); +echo '
    '; + +// Print the data. +$data = []; +if ($is_network) { + $data = network_matrix_get_top( + $top, + $action === 'talkers', + $utimestamp_lower, + $utimestamp_greater, + $main_value, + $order_by !== 'pkts' + ); +} else { + $data = netflow_get_top_summary( + $top, + $action, + $utimestamp_lower, + $utimestamp_greater, + $main_value, + $order_by + ); +} + +// Get the params to return the builder. +$hidden_main_link = [ + 'time_greater' => $time_greater, + 'date_greater' => $date_greater, + 'is_period' => $is_period, + 'period' => $period, + 'time_lower' => $time_lower, + 'date_lower' => $date_lower, + 'top' => $top, + 'action' => $action, +]; + +unset($table); +$table = new stdClass(); +$table->styleTable = 'width: 60%'; +// Print the header. +$table->head = []; +$table->head['main'] = __('IP'); +if (!$is_network) { + $table->head['flows'] = network_print_explorer_header( + __('Flows'), + 'flows', + $order_by, + array_merge( + $hidden_main_link, + ['main_value' => $main_value] + ) + ); +} + +$table->head['pkts'] = network_print_explorer_header( + __('Packets'), + 'pkts', + $order_by, + array_merge( + $hidden_main_link, + ['main_value' => $main_value] + ) +); +$table->head['bytes'] = network_print_explorer_header( + __('Bytes'), + 'bytes', + $order_by, + array_merge( + $hidden_main_link, + ['main_value' => $main_value] + ) +); + +// Add the order. +$hidden_main_link['order_by'] = $order_by; + +if (get_parameter('export_csv')) { + // Clean the buffer. + while (ob_get_level()) { + ob_end_clean(); + } + + // Write metadata. + header('Content-type: text/csv;'); + header('Content-Disposition: attachment; filename="network_data.csv"'); + + $div = $config['csv_divider']; + $nl = "\n"; + + // Print the header. + echo reset($table->head).$div; + if (!$is_network) { + echo __('Flows').$div; + } + + echo __('Packets').$div; + echo __('Bytes').$div; + echo $nl; + + // Print the data. + foreach ($data as $row) { + echo $row['host'].$div; + if (isset($row['sum_flows'])) { + echo $row['sum_flows'].$div; + } + + echo $row['sum_pkts'].$div; + echo $row['sum_bytes'].$nl; + } + + exit; +} + +// Print the filter remove link. +if (!empty($main_value)) { + echo html_print_link_with_params( + in_array($action, ['udp', 'tcp']) ? __('Filtered by port %s. Click here to remove the filter.', $main_value) : __('Filtered by IP %s. Click here to remove the filter.', $main_value), + array_merge( + $hidden_main_link, + [ + 'main_value' => $main_value, + 'remove_filter' => 1, + ] + ) + ); +} + +// Print the data and build the chart. +$table->data = []; +$chart_data = []; +$hide_filter = !empty($main_value) && ($action === 'udp' || $action === 'tcp'); +foreach ($data as $item) { + $row = []; + $row['main'] = '
    '; + $row['main'] .= $item['host']; + if (!$hide_filter) { + $row['main'] .= html_print_link_with_params( + 'images/filter.png', + array_merge($hidden_main_link, ['main_value' => $item['host']]), + 'image' + ); + } + + $row['main'] .= '
    '; + if (!$is_network) { + $row['flows'] = format_for_graph($item['sum_flows'], 2); + $row['flows'] .= ' ('.$item['pct_flows'].'%)'; + } + + $row['pkts'] = format_for_graph($item['sum_pkts'], 2); + if (!$is_network) { + $row['pkts'] .= ' ('.$item['pct_pkts'].'%)'; + } + + $row['bytes'] = network_format_bytes($item['sum_bytes']); + if (!$is_network) { + $row['bytes'] .= ' ('.$item['pct_bytes'].'%)'; + } + + $table->data[] = $row; + + // Build the pie graph data structure. + switch ($order_by) { + case 'pkts': + $chart_data[$item['host']] = $item['sum_bytes']; + break; + + case 'flows': + $chart_data[$item['host']] = $item['sum_flows']; + break; + + case 'bytes': + default: + $chart_data[$item['host']] = $item['sum_bytes']; + break; + } +} + +if (empty($data)) { + ui_print_info_message(__('No data found')); +} else { + echo '
    '; + html_print_table($table); + + // Print the graph. + echo '
    '; + echo pie_graph( + $chart_data, + 320, + 200, + __('Others') + ); + echo '
    '; + echo '
    '; +} + +?> + diff --git a/pandora_console/operation/network/network_usage_map.php b/pandora_console/operation/network/network_usage_map.php new file mode 100644 index 0000000000..046cc1fcc4 --- /dev/null +++ b/pandora_console/operation/network/network_usage_map.php @@ -0,0 +1,236 @@ +class = 'databox'; +$table->styleTable = 'width: 100%'; + +$table->data['0']['0'] = '
    '; +$table->data['0']['0'] .= '
    '; +$table->data['0']['0'] .= __('Start date').'  '; +$table->data['0']['0'] .= html_print_input_text('date_lower', $date_lower, '', 10, 7, true); +$table->data['0']['0'] .= '  '; +$table->data['0']['0'] .= html_print_input_text('time_lower', $time_lower, '', 7, 8, true); +$table->data['0']['0'] .= '
    '; + +$table->data['0']['0'] .= '
    '; +$table->data['0']['0'] .= __('Time Period').'  '; +$table->data['0']['0'] .= html_print_extended_select_for_time('period', $period, '', '', 0, false, true); +$table->data['0']['0'] .= '
    '; +$table->data['0']['0'] .= html_print_checkbox( + 'is_period', + 1, + ($is_period === true) ? 1 : 0, + true, + false, + 'network_report_click_period(event)' +); +$table->data['0']['0'] .= ui_print_help_tip( + __('Select this checkbox to write interval instead a date.'), + true +); +$table->data['0']['0'] .= '
    '; + +$table->data['0']['1'] = __('End date').'  '; +$table->data['0']['1'] .= html_print_input_text('date_greater', $date_greater, '', 10, 7, true); +$table->data['0']['1'] .= '  '; +$table->data['0']['1'] .= html_print_input_text('time_greater', $time_greater, '', 7, 8, true); + +$table->data['0']['2'] = __('Number of result to show').'  '; +$table->data['0']['2'] .= html_print_select( + [ + '5' => 5, + '10' => 10, + '15' => 15, + '20' => 20, + '25' => 25, + '50' => 50, + '100' => 100, + '250' => 250, + ], + 'top', + $top, + '', + '', + 0, + true +); + + +$table->data['1']['0'] = __('Data to show').'  '; +$table->data['1']['0'] .= html_print_select( + network_get_report_actions(), + 'action', + $action, + '', + '', + 0, + true +); +$table->data['1']['1'] = ''; + +$netflow_button = ''; +if ((bool) $config['activate_netflow'] === true) { + $netflow_button = html_print_submit_button( + __('Show netflow map'), + 'update_netflow', + false, + 'class="sub upd"', + true + ); +} + +$nta_button = ''; +if ((bool) $config['activate_nta'] === true) { + $nta_button = html_print_submit_button( + __('Show NTA map'), + 'update_nta', + false, + 'class="sub upd"', + true + ); +} + +$table->data['1']['2'] .= implode( + '  ', + [ + $netflow_button, + $nta_button, + ] +); + +echo '
    '; +html_print_input_hidden('order_by', $order_by); + +html_print_table($table); +echo '
    '; + +$has_data = false; +$first_load = true; +if ((bool) get_parameter('update_netflow') === true) { + $map_data = netflow_build_map_data( + $utimestamp_lower, + $utimestamp_greater, + $top, + ($action === 'talkers') ? 'srcip' : 'dstip' + ); + $has_data = !empty($map_data['nodes']); + $first_load = false; +} else if ((bool) get_parameter('update_nta') === true) { + $map_data = network_build_map_data( + $utimestamp_lower, + $utimestamp_greater, + $top, + $action === 'talkers' + ); + $has_data = !empty($map_data['nodes']); + $first_load = false; +} + +if ($has_data === true) { + $map_manager = new NetworkMap($map_data); + $map_manager->printMap(); +} else if (!$first_load) { + ui_print_info_message(__('No data retrieved')); +} + +?> + + + diff --git a/pandora_console/operation/search_agents.getdata.php b/pandora_console/operation/search_agents.getdata.php index 5038800cf7..c754628731 100644 --- a/pandora_console/operation/search_agents.getdata.php +++ b/pandora_console/operation/search_agents.getdata.php @@ -182,7 +182,8 @@ if ($searchAgents) { } $sql = " - FROM tagente t1 + FROM tagente t1 LEFT JOIN tagent_secondary_group tasg + ON t1.id_agente = tasg.id_agent INNER JOIN tgrupo t2 ON t2.id_grupo = t1.id_grupo WHERE ( @@ -191,9 +192,11 @@ if ($searchAgents) { FROM tusuario WHERE id_user = '".$config['id_user']."' ) - OR t1.id_grupo IN ( - ".implode(',', $id_userGroups)." - ) OR 0 IN ( + OR ( + t1.id_grupo IN (".implode(',', $id_userGroups).') + OR tasg.id_group IN ('.implode(',', $id_userGroups).") + ) + OR 0 IN ( SELECT id_grupo FROM tusuario_perfil WHERE id_usuario = '".$config['id_user']."' @@ -208,7 +211,7 @@ if ($searchAgents) { ) '; - $select = 'SELECT t1.id_agente, t1.ultimo_contacto, t1.nombre, t1.id_os, t1.intervalo, t1.id_grupo, t1.disabled, t1.alias, t1.quiet'; + $select = 'SELECT DISTINCT(t1.id_agente), t1.ultimo_contacto, t1.nombre, t1.id_os, t1.intervalo, t1.id_grupo, t1.disabled, t1.alias, t1.quiet'; if ($only_count) { $limit = ' ORDER BY '.$order['field'].' '.$order['order'].' LIMIT '.$config['block_size'].' OFFSET 0'; } else { diff --git a/pandora_console/operation/search_modules.php b/pandora_console/operation/search_modules.php index 687dea13e2..bb86159035 100644 --- a/pandora_console/operation/search_modules.php +++ b/pandora_console/operation/search_modules.php @@ -108,6 +108,12 @@ if (!$modules || !$searchModules) { __('WARNING').': '.modules_get_last_value($module['id_agente_modulo']), true ); + } else if ($status_sql['estado'] == 3) { + $statusCell = ui_print_status_image( + STATUS_MODULE_UNKNOWN, + __('UNKNOWN').': '.modules_get_last_value($module['id_agente_modulo']), + true + ); } else { $last_status = modules_get_agentmodule_last_status($module['id_agente_modulo']); switch ($last_status) { diff --git a/pandora_console/operation/servers/recon_view.php b/pandora_console/operation/servers/recon_view.php index 84a057d69c..5401f6e307 100644 --- a/pandora_console/operation/servers/recon_view.php +++ b/pandora_console/operation/servers/recon_view.php @@ -76,10 +76,10 @@ if ($servers === false) { } $table = new StdClass(); - $table->cellpadding = 4; - $table->cellspacing = 4; + $table->cellpadding = 0; + $table->cellspacing = 0; $table->width = '100%'; - $table->class = 'databox data'; + $table->class = 'info_table'; $table->head = []; $table->data = []; $table->align = []; @@ -161,6 +161,7 @@ if ($servers === false) { $data[7] = ui_print_timestamp($task['utimestamp'], true); if (check_acl($config['id_user'], $task['id_group'], 'PM')) { + $table->cellclass[][8] = 'action_buttons'; $data[8] = ''.html_print_image('images/wrench_orange.png', true).''; } else { $data[8] = ''; diff --git a/pandora_console/operation/snmpconsole/snmp_browser.php b/pandora_console/operation/snmpconsole/snmp_browser.php index bae4f9e06f..b68f2d4da5 100644 --- a/pandora_console/operation/snmpconsole/snmp_browser.php +++ b/pandora_console/operation/snmpconsole/snmp_browser.php @@ -68,7 +68,27 @@ if (is_ajax()) { if (! is_array($snmp_tree)) { echo $snmp_tree; } else { - snmp_browser_print_tree($snmp_tree); + snmp_browser_print_tree( + $snmp_tree, + // Id. + 0, + // Depth. + 0, + // Last. + 0, + // Last_array. + [], + // Sufix. + false, + // Checked. + [], + // Return. + false, + // Descriptive_ids. + false, + // Previous_id. + '' + ); echo html_print_submit_button( __('Create network components'), 'create_network_component', @@ -172,7 +192,7 @@ ui_print_page_header( __('SNMP Browser'), 'images/op_snmp.png', false, - '', + 'snmp_browser_view', false, [$link] ); diff --git a/pandora_console/operation/snmpconsole/snmp_mib_uploader.php b/pandora_console/operation/snmpconsole/snmp_mib_uploader.php index 7754fe3743..1fbe2b1823 100644 --- a/pandora_console/operation/snmpconsole/snmp_mib_uploader.php +++ b/pandora_console/operation/snmpconsole/snmp_mib_uploader.php @@ -69,9 +69,6 @@ $real_directory = realpath($config['homedir'].'/'.$directory); ui_print_info_message(__('MIB files will be installed on the system. Please note that a MIB may depend on other MIB. To customize trap definitions use the SNMP trap editor.')); -// echo '

    ' . __('Index of %s', $directory) . '

    '; -$homedir_filemanager = isset($config['homedir_filemanager']) ? $config['homedir_filemanager'] : false; - filemanager_file_explorer( $real_directory, $directory, @@ -82,5 +79,5 @@ filemanager_file_explorer( '', false, '', - $homedir_filemanager + false ); diff --git a/pandora_console/operation/tree.php b/pandora_console/operation/tree.php index b2344c13ad..41a6b9c5d2 100755 --- a/pandora_console/operation/tree.php +++ b/pandora_console/operation/tree.php @@ -134,7 +134,7 @@ if (!is_metaconsole()) { $header_title = $header_title.' - '.$header_sub_title; } - ui_print_page_header($header_title, 'images/extensions.png', false, '', false, $tabs); + ui_print_page_header($header_title, 'images/extensions.png', false, 'tree_view', false, $tabs); } // ---------------------Tabs ------------------------------------------- diff --git a/pandora_console/operation/users/user_edit.php b/pandora_console/operation/users/user_edit.php index 3e3338ff96..9e267c5c2d 100644 --- a/pandora_console/operation/users/user_edit.php +++ b/pandora_console/operation/users/user_edit.php @@ -29,10 +29,38 @@ global $config; -// Load the header +// Load the header. require $config['homedir'].'/operation/users/user_edit_header.php'; -// Update user info +if (!is_metaconsole()) { + date_default_timezone_set('UTC'); + include 'include/javascript/timezonepicker/includes/parser.inc'; + + // Read in options for map builder. + $bases = [ + 'gray' => 'Gray', + 'blue-marble' => 'Blue marble', + 'night-electric' => 'Night Electric', + 'living' => 'Living Earth', + ]; + + $local_file = 'include/javascript/timezonepicker/images/gray-400.png'; + + // Dimensions must always be exact since the imagemap does not scale. + $array_size = getimagesize($local_file); + + $map_width = $array_size[0]; + $map_height = $array_size[1]; + + $timezones = timezone_picker_parse_files( + $map_width, + $map_height, + 'include/javascript/timezonepicker/tz_world.txt', + 'include/javascript/timezonepicker/tz_islands.txt' + ); +} + +// Update user info. if (isset($_GET['modified']) && !$view_mode) { if (html_print_csrf_error()) { return; @@ -52,17 +80,40 @@ if (isset($_GET['modified']) && !$view_mode) { $upd_info['id_skin'] = get_parameter('skin', $user_info['id_skin']); $upd_info['default_event_filter'] = get_parameter('event_filter', null); $upd_info['block_size'] = get_parameter('block_size', $config['block_size']); - $upd_info['firstname'] = get_parameter('newsletter_reminder', $user_info['first_name']); + $upd_info['middlename'] = get_parameter_switch('newsletter_reminder', $user_info['middlename']); $default_block_size = get_parameter('default_block_size', 0); if ($default_block_size) { $upd_info['block_size'] = 0; } + if ($upd_info['middlename'] == 1) { + // User wants to enable newsletter reminders. + if ($user_info['middlename'] > 0) { + // User has already registered!. No sense. + $upd_info['middlename'] = $user_info['middlename']; + } else { + // Force subscription reminder. + $upd_info['middlename'] = 0; + } + } + + if ($upd_info['middlename'] == 0 || $upd_info['middlename'] == 0) { + // Switch is ON. user had not registered. + $newsletter_reminder_value = 1; + } else if ($upd_info['middlename'] < 1) { + // Switch is OFF. User do not want to register. + $newsletter_reminder_value = 0; + } else if ($upd_info['middlename'] > 0) { + // Switc is OFF. User is already registered! + $newsletter_reminder_value = 0; + } + $upd_info['section'] = get_parameter('section', $user_info['section']); $upd_info['data_section'] = get_parameter('data_section', ''); $dashboard = get_parameter('dashboard', ''); $visual_console = get_parameter('visual_console', ''); + // Save autorefresh list. $autorefresh_list = get_parameter_post('autorefresh_list'); if (($autorefresh_list[0] === '') || ($autorefresh_list[0] === '0')) { @@ -72,6 +123,11 @@ if (isset($_GET['modified']) && !$view_mode) { } $upd_info['time_autorefresh'] = (int) get_parameter('time_autorefresh', 0); + $upd_info['ehorus_user_level_user'] = get_parameter('ehorus_user_level_user'); + $upd_info['ehorus_user_level_pass'] = get_parameter('ehorus_user_level_pass'); + $upd_info['ehorus_user_level_enabled'] = get_parameter('ehorus_user_level_enabled', 0); + + $is_admin = db_get_value('is_admin', 'tusuario', 'id_user', $id); @@ -179,29 +235,15 @@ if ($status != -1) { ); } -$jump = '  '; -$table = new stdClass(); -$table->id = 'user_form'; -$table->width = '100%'; -$table->cellspacing = 4; -$table->cellpadding = 4; -$table->class = 'databox filters'; if (defined('METACONSOLE')) { - $table->head[0] = __('Edit my User'); - $table->head_colspan[0] = 5; - $table->headstyle[0] = 'text-align: center'; + echo '
    '.__('Edit my User').'
    '; } -$table->style[0] = 'min-width: 320px;width: 320px;margin-right:0px;padding-right:0px;'; -$table->style[1] = 'min-width: 280px;width: 280px;margin-right:0px;padding-right:0px;'; -$table->style[2] = 'min-width: 150px;width: 150px;margin-right:0px;margin-left:0px;padding-left:0px;padding-right:0px;'; -$data = []; -$data[0] = ''.__('User ID').''; -$data[0] .= $jump.''.$id.''; -$data[1] = ''.__('Full (display) name').''; -$data[1] .= $jump.''; -$data[1] .= html_print_input_text_extended( +$user_id = '

    '.__('User ID').':

    '; +$user_id .= ''.$id.'
    '; + +$full_name = '
    '.html_print_input_text_extended( 'fullname', $user_info['fullname'], 'fullname', @@ -210,15 +252,18 @@ $data[1] .= html_print_input_text_extended( 100, $view_mode, '', - 'class="input"', + [ + 'class' => 'input', + 'placeholder' => __('Full (display) name'), + ], true -).''; +).'
    '; // Show "Picture" (in future versions, why not, allow users to upload it's own avatar here. if (is_user_admin($id)) { - $data[2] = html_print_image('images/people_1.png', true); + $avatar = html_print_image('images/people_1.png', true, ['class' => 'user_avatar']); } else { - $data[2] = html_print_image('images/people_2.png', true); + $avatar = html_print_image('images/people_2.png', true, ['class' => 'user_avatar']); } if ($view_mode === false) { @@ -227,50 +272,31 @@ if ($view_mode === false) { $table->rowspan[0][2] = 2; } -$table->rowclass[] = ''; -$table->rowstyle[] = 'font-weight: bold;'; -$table->data[] = $data; -$data = []; -$data[0] = ''.__('E-mail').''; -$data[0] .= $jump.''.html_print_input_text_extended('email', $user_info['email'], 'email', '', '25', '100', $view_mode, '', 'class="input"', true).''; -$data[1] = ''.__('Phone number').''; -$data[1] .= $jump.'
    '.html_print_input_text_extended('phone', $user_info['phone'], 'phone', '', '20', '30', $view_mode, '', 'class="input"', true).'
    '; -$table->rowclass[] = ''; -$table->rowstyle[] = 'font-weight: bold;'; -$table->data[] = $data; +$email = '
    '.html_print_input_text_extended('email', $user_info['email'], 'email', '', '25', '100', $view_mode, '', ['class' => 'input', 'placeholder' => __('E-mail')], true).'
    '; + +$phone = '
    '.html_print_input_text_extended('phone', $user_info['phone'], 'phone', '', '20', '30', $view_mode, '', ['class' => 'input', 'placeholder' => __('Phone number')], true).'
    '; if ($view_mode === false) { if ($config['user_can_update_password']) { - $data = []; - $data[0] = ''.__('New Password').''; - $data[0] .= $jump.''.html_print_input_text_extended('password_new', '', 'password_new', '', '25', '45', $view_mode, '', 'class="input"', true, true).''; - $data[1] = ''.__('Password confirmation').''; - $data[1] .= $jump.''.html_print_input_text_extended('password_conf', '', 'password_conf', '', '20', '45', $view_mode, '', 'class="input"', true, true).''; - $table->rowclass[] = ''; - $table->rowstyle[] = 'font-weight: bold;'; - $table->data[] = $data; + $new_pass = '
    '.html_print_input_text_extended('password_new', '', 'password_new', '', '25', '45', $view_mode, '', ['class' => 'input', 'placeholder' => __('New Password')], true, true).'
    '; + $new_pass_confirm = '
    '.html_print_input_text_extended('password_conf', '', 'password_conf', '', '20', '45', $view_mode, '', ['class' => 'input', 'placeholder' => __('Password confirmation')], true, true).'
    '; } else { - $data = []; - $data[0] = ''.__('You cannot change your password under the current authentication scheme').''; - $table->rowclass[] = ''; - $table->rowstyle[] = ''; - $table->colspan[count($table - data)][0] = 2; - $table->data[] = $data; + $new_pass = ''.__('You cannot change your password under the current authentication scheme').''; + $new_pass_confirm = ''; } } -$data = []; -$data[0] = ''.__('Block size for pagination').ui_print_help_tip(__('If checkbox is clicked then block size global configuration is used'), true).''; +$size_pagination = '

    '.__('Block size for pagination').'

    '; if ($user_info['block_size'] == 0) { $block_size = $config['global_block_size']; } else { $block_size = $user_info['block_size']; } -$data[0] .= $jump.''.html_print_input_text('block_size', $block_size, '', 5, 5, true).''; -$data[0] .= $jump.''.html_print_checkbox('default_block_size', 1, $user_info['block_size'] == 0, true).''; -$data[0] .= __('Default').' ('.$config['global_block_size'].')'; +$size_pagination .= html_print_input_text('block_size', $block_size, '', 5, 5, true); +$size_pagination .= html_print_checkbox_switch('default_block_size', 1, $user_info['block_size'] == 0, true); +$size_pagination .= ''.__('Default').' ('.$config['global_block_size'].')'.ui_print_help_tip(__('If checkbox is clicked then block size global configuration is used'), true).'
    '; $values = [ -1 => __('Default'), @@ -278,9 +304,8 @@ $values = [ 0 => __('No'), ]; - -$data[2] = ''.__('Language').''; -$data[2] .= $jump.html_print_select_from_sql( +$language = '

    '.__('Language').':

    '; +$language .= html_print_select_from_sql( 'SELECT id_language, name FROM tlanguage', 'language', $user_info['language'], @@ -294,11 +319,7 @@ $data[2] .= $jump.html_print_select_from_sql( '', '', 10 -); - -$table->rowclass[] = ''; -$table->rowstyle[] = 'font-weight: bold;'; -$table->data[] = $data; +).'
    '; $own_info = get_user_info($config['id_user']); if ($own_info['is_admin'] || check_acl($config['id_user'], 0, 'PM')) { @@ -311,10 +332,8 @@ $usr_groups = (users_get_groups($config['id_user'], 'AR', $display_all_group)); $id_usr = $config['id_user']; -$data = []; - if (!$meta) { - $data[0] = ''.__('Home screen').ui_print_help_tip(__('User can customize the home page. By default, will display \'Agent Detail\'. Example: Select \'Other\' and type sec=estado&sec2=operation/agentes/estado_agente to show agent detail view'), true).''; + $home_screen = '

    '.__('Home screen').ui_print_help_tip(__('User can customize the home page. By default, will display \'Agent Detail\'. Example: Select \'Other\' and type sec=estado&sec2=operation/agentes/estado_agente to show agent detail view'), true).'

    '; $values = [ 'Default' => __('Default'), 'Visual console' => __('Visual console'), @@ -328,7 +347,7 @@ if (!$meta) { $values['Dashboard'] = __('Dashboard'); } - $data[0] .= $jump.''.html_print_select($values, 'section', io_safe_output($user_info['section']), 'show_data_section();', '', -1, true, false, false).''; + $home_screen .= html_print_select($values, 'section', io_safe_output($user_info['section']), 'show_data_section();', '', -1, true, false, false).'
    '; if (enterprise_installed()) { $dashboards = get_user_dashboards($config['id_user']); @@ -342,7 +361,7 @@ if (!$meta) { } } - $data[0] .= html_print_select($dashboards_aux, 'dashboard', $user_info['data_section'], '', '', '', true); + $home_screen .= html_print_select($dashboards_aux, 'dashboard', $user_info['data_section'], '', '', '', true); } $layouts = visual_map_get_user_layouts($config['id_user'], true); @@ -355,51 +374,48 @@ if (!$meta) { } } - $data[0] .= html_print_select($layouts_aux, 'visual_console', $user_info['data_section'], '', '', '', true); - $data[0] .= html_print_input_text('data_section', $user_info['data_section'], '', 60, 255, true, false); + $home_screen .= html_print_select($layouts_aux, 'visual_console', $user_info['data_section'], '', '', '', true); + $home_screen .= html_print_input_text('data_section', $user_info['data_section'], '', 60, 255, true, false); // User only can change skins if has more than one group. - $data[1] = ''; + $skin = ''; if (function_exists('skins_print_select')) { if (count($usr_groups) > 1) { - $data[1] = ''.__('Skin').''; - $data[1] .= $jump.''.skins_print_select($id_usr, 'skin', $user_info['id_skin'], '', __('None'), 0, true).''; + $skin = '

    '.__('Skin').':

    '; + $skin .= skins_print_select($id_usr, 'skin', $user_info['id_skin'], '', __('None'), 0, true).'
    '; } } } else { - $data[0] = ''; - $data[1] = ''; + $home_screen = ''; + $skin = ''; } -$data[2] = ''.__('Timezone').''; -$data[2] .= $jump.html_print_timezone_select('timezone', $user_info['timezone']); - -$table->rowclass[] = ''; -$table->rowstyle[] = 'font-weight: bold;'; -$table->data[] = $data; +$timezone = '

    '.__('Timezone').':

    '; +$timezone .= html_print_timezone_select('timezone', $user_info['timezone']).'
    '; // Double auth. $double_auth_enabled = (bool) db_get_value('id', 'tuser_double_auth', 'id_user', $config['id_user']); -$data = []; + if ($config['double_auth_enabled']) { - $data[0] = ''.__('Double authentication').''; - $data[0] .= $jump; - $data[0] .= ''.html_print_checkbox('double_auth', 1, $double_auth_enabled, true).''; + $double_authentication = '

    '.__('Double authentication').'

    '; + $double_authentication .= html_print_checkbox_switch('double_auth', 1, $double_auth_enabled, true); + // Dialog. + $double_authentication .= ''; } if ($double_auth_enabled) { - $data[0] .= $jump; - $data[0] .= html_print_button(__('Show information'), 'show_info', false, 'javascript:show_double_auth_info();', '', true); + $double_authentication .= html_print_button(__('Show information'), 'show_info', false, 'javascript:show_double_auth_info();', '', true); } -// Dialog. -$data[0] .= '
    '; +if (isset($double_authentication)) { + $double_authentication .= '
    '; +} if (check_acl($config['id_user'], 0, 'ER')) { - $data[1] = ''.__('Event filter').''; - $data[1] .= $jump.''.html_print_select_from_sql( + $event_filter = '

    '.__('Event filter').'

    '; + $event_filter .= html_print_select_from_sql( 'SELECT id_filter, id_name FROM tevent_filter', 'event_filter', $user_info['default_event_filter'], @@ -407,50 +423,49 @@ if (check_acl($config['id_user'], 0, 'ER')) { __('None'), null, true - ).''; -} else if (license_free()) { - $data[1] = __('Newsletter Subscribed').':'; - if ($user_info['middlename']) { - $data[1] .= $jump.''.__('Already subscribed to %s newsletter', get_product_name()).''; - } else { - $data[1] .= $jump.''.__('Subscribe to our newsletter').''; - } - - $data[2] = __('Newsletter Reminder').' '; - if ($user_info['firstname'] != 0) { - $user_info['firstname'] = 1; - } - - $data[2] .= html_print_checkbox('newsletter_reminder', 1, $user_info['firstname'], true); -} else { - $table->colspan[count($table->data)][0] = 3; + ).'
    '; } -$table->rowclass[] = ''; -$table->rowstyle[] = 'font-weight: bold;'; -$table->data[] = $data; -$data = []; + +$newsletter = '

    '.__('Newsletter Subscribed').':

    '; +if ($user_info['middlename'] > 0) { + $newsletter .= ''.__('Already subscribed to %s newsletter', get_product_name()).''; +} else { + $newsletter .= ''.__('Subscribe to our newsletter').'
    '; + $newsletter_reminder = '

    '.__('Newsletter Reminder').':

    '; + $newsletter_reminder .= html_print_switch( + [ + 'name' => 'newsletter_reminder', + 'value' => $newsletter_reminder_value, + 'disabled' => false, + ] + ); +} + +$newsletter_reminder .= '
    '; + + $autorefresh_list_out = []; if (is_metaconsole()) { - $autorefresh_list_out['monitoring/tactical'] = 'tactical'; - $autorefresh_list_out['monitoring/group_view'] = 'group_view'; + $autorefresh_list_out['monitoring/tactical'] = 'Tactical view'; + $autorefresh_list_out['monitoring/group_view'] = 'Group view'; } else { - $autorefresh_list_out['operation/agentes/tactical'] = 'tactical'; - $autorefresh_list_out['operation/agentes/group_view'] = 'group_view'; + $autorefresh_list_out['operation/agentes/tactical'] = 'Tactical view'; + $autorefresh_list_out['operation/agentes/group_view'] = 'Group view'; } -$autorefresh_list_out['operation/agentes/estado_agente'] = 'agent_status'; -$autorefresh_list_out['operation/agentes/alerts_status'] = 'alerts_status'; -$autorefresh_list_out['operation/agentes/status_monitor'] = 'status_monitor'; -$autorefresh_list_out['enterprise/operation/services/services'] = 'services'; -$autorefresh_list_out['enterprise/dashboard/main_dashboard'] = 'main_dashboard'; -$autorefresh_list_out['operation/reporting/graph_viewer'] = 'graph_viewer'; -$autorefresh_list_out['operation/snmpconsole/snmp_view'] = 'snmp_view'; -$autorefresh_list_out['operation/agentes/pandora_networkmap'] = 'networkmap'; -$autorefresh_list_out['operation/visual_console/render_view'] = 'render_view'; -$autorefresh_list_out['operation/events/events'] = 'events'; -$autorefresh_list_out['enterprise/godmode/reporting/cluster_view'] = 'cluster_view'; +$autorefresh_list_out['operation/agentes/estado_agente'] = 'Agent detail'; +$autorefresh_list_out['operation/agentes/alerts_status'] = 'Alert detail'; +$autorefresh_list_out['operation/agentes/status_monitor'] = 'Monitor detail'; +$autorefresh_list_out['enterprise/operation/services/services'] = 'Services'; +$autorefresh_list_out['enterprise/dashboard/main_dashboard'] = 'Dashboard'; +$autorefresh_list_out['operation/reporting/graph_viewer'] = 'Graph Viewer'; +$autorefresh_list_out['operation/snmpconsole/snmp_view'] = 'SNMP console'; +$autorefresh_list_out['operation/agentes/pandora_networkmap'] = 'Network map'; +$autorefresh_list_out['operation/visual_console/render_view'] = 'Visual console'; +$autorefresh_list_out['operation/events/events'] = 'Events'; +$autorefresh_list_out['enterprise/godmode/reporting/cluster_view'] = 'Cluster view'; if (!isset($autorefresh_list)) { $select = db_process_sql("SELECT autorefresh_white_list FROM tusuario WHERE id_user = '".$config['id_user']."'"); @@ -484,10 +499,10 @@ if (!isset($autorefresh_list)) { } } -$data[0] = _('Autorefresh').ui_print_help_tip( +$autorefresh_show = '

    '._('Autorefresh').ui_print_help_tip( __('This will activate autorefresh in selected pages'), true -); +).'

    '; $select_out = html_print_select( $autorefresh_list_out, 'autorefresh_list_out[]', @@ -500,7 +515,7 @@ $select_out = html_print_select( true, '', false, - 'width:200px' + 'width:100%' ); $arrows = ' '; $select_in = html_print_select( @@ -515,20 +530,17 @@ $select_in = html_print_select( true, '', false, - 'width:200px' + 'width:100%' ); -$table_ichanges = ' - - - - - - - -
    '.__('Full list of pages').''.__('List of pages with autorefresh').'
    '.$select_out.' - '.html_print_image( - 'images/darrowright.png', +$table_ichanges = ''; + +$autorefresh_show .= $table_ichanges; // Time autorefresh. $times = get_refresh_time_array(); -$data[1] = ''.__('Time autorefresh'); -$data[1] .= ui_print_help_tip( +$time_autorefresh = '

    '.__('Time autorefresh'); +$time_autorefresh .= ui_print_help_tip( __('Interval of autorefresh of the elements, by default they are 30 seconds, needing to enable the autorefresh first'), true -).''; -$data[1] .= $jump.''; -$data[1] .= html_print_select( +).'

    '; +$time_autorefresh .= html_print_select( $times, 'time_autorefresh', $user_info['time_autorefresh'], @@ -570,22 +583,11 @@ $data[1] .= html_print_select( true, false, false -).''; +).'
    '; -$table->rowclass[] = ''; -$table->rowstyle[] = 'font-weight: bold;vertical-align: top'; -$table->data[] = $data; -$data = []; -$data[0] = __('Comments'); -$table->colspan[count($table->data)][0] = 3; -$table->rowclass[] = ''; -$table->rowstyle[] = 'font-weight: bold;'; -$table->data[] = $data; - -$data = []; -$data[0] = '
    '; -$data[0] .= html_print_textarea( +$comments = '

    '.__('Comments').':

    '; +$comments .= html_print_textarea( 'comments', 2, 60, @@ -593,18 +595,117 @@ $data[0] .= html_print_textarea( (($view_mode) ? 'readonly="readonly"' : ''), true ); -$data[0] .= '
    '; -$data[0] .= html_print_input_hidden('quick_language_change', 1, true); -$table->colspan[count($table->data)][0] = 3; -$table->rowclass[] = ''; -$table->rowstyle[] = ''; -$table->data[] = $data; +$comments .= html_print_input_hidden('quick_language_change', 1, true); -echo '
    '; -html_print_table($table); +foreach ($timezones as $timezone_name => $tz) { + if ($timezone_name == 'America/Montreal') { + $timezone_name = 'America/Toronto'; + } else if ($timezone_name == 'Asia/Chongqing') { + $timezone_name = 'Asia/Shanghai'; + } -echo '
    '; + $area_data_timezone_polys .= ''; + foreach ($tz['polys'] as $coords) { + $area_data_timezone_polys .= ''; + } + + $area_data_timezone_rects .= ''; + foreach ($tz['rects'] as $coords) { + $area_data_timezone_rects .= ''; + } +} + +if (is_metaconsole()) { + echo ''; +} else { + echo ''; +} + + echo '
    +
    + +
    '.$autorefresh_show.$time_autorefresh.'
    +
    +
    +
    '.$language.$size_pagination.$skin.$home_screen.$event_filter.$newsletter.$newsletter_reminder.$double_authentication.'
    +
    '.$timezone; + + + +if (!is_metaconsole()) { + echo '
    + + + '.$area_data_timezone_polys.$area_data_timezone_rects.' +
    '; +} + + echo '
    +
    +
    +
    '.$comments.'
    +
    +
    '; + +if ($config['ehorus_enabled'] && $config['ehorus_user_level_conf']) { + // eHorus user remote login + $table_remote = new StdClass(); + $table_remote->data = []; + $table_remote->width = '100%'; + $table_remote->id = 'ehorus-remote-setup'; + $table_remote->class = 'white_box'; + $table_remote->size['name'] = '30%'; + $table_remote->style['name'] = 'font-weight: bold'; + + + // Title + $row = []; + $row['control'] = '

    '.__('eHorus user configuration').':

    '; + $table_remote->data['ehorus_user_level_conf'] = $row; + + // Enable/disable eHorus for this user + $row = []; + $row['name'] = __('eHorus user acces enabled'); + $row['control'] = html_print_checkbox_switch('ehorus_user_level_enabled', 1, $user_info['ehorus_user_level_enabled'], true); + $table_remote->data['ehorus_user_level_enabled'] = $row; + + // User. + $row = []; + $row['name'] = __('User'); + $row['control'] = html_print_input_text('ehorus_user_level_user', $user_info['ehorus_user_level_user'], '', 30, 100, true); + $table_remote->data['ehorus_user_level_user'] = $row; + + // Pass. + $row = []; + $row['name'] = __('Password'); + $row['control'] = html_print_input_password('ehorus_user_level_pass', io_output_password($user_info['ehorus_user_level_pass']), '', 30, 100, true); + $table_remote->data['ehorus_user_level_pass'] = $row; + + // Test. + $ehorus_port = db_get_value('value', 'tconfig', 'token', 'ehorus_port'); + $ehorus_host = db_get_value('value', 'tconfig', 'token', 'ehorus_hostname'); + + $row = []; + $row['name'] = __('Test'); + $row['control'] = html_print_button(__('Start'), 'test-ehorus', false, 'ehorus_connection_test("'.$ehorus_host.'",'.$ehorus_port.')', 'class="sub next"', true); + $row['control'] .= ' '; + $row['control'] .= ' '; + $row['control'] .= ' '; + $row['control'] .= ''; + $table_remote->data['ehorus_test'] = $row; + + echo '
    '; + + html_print_table($table_remote); + echo '
    '; +} + + +echo '
    '; if (!$config['user_can_update_info']) { echo ''.__('You can not change your user info under the current authentication scheme').''; } else { @@ -612,17 +713,17 @@ if (!$config['user_can_update_info']) { html_print_submit_button(__('Update'), 'uptbutton', $view_mode, 'class="sub upd"'); } -echo '
    '; - -unset($table); + echo '
    '; + echo ''; +echo '
    '; if (!defined('METACONSOLE')) { - echo '

    '.__('Profiles/Groups assigned to this user').'

    '; + echo '

    '.__('Profiles/Groups assigned to this user').'

    '; } $table = new stdClass(); $table->width = '100%'; -$table->class = 'databox data'; +$table->class = 'info_table'; if (defined('METACONSOLE')) { $table->width = '100%'; $table->class = 'databox data'; @@ -684,11 +785,45 @@ if (!empty($table->data)) { ui_print_info_message(['no_close' => true, 'message' => __('This user doesn\'t have any assigned profile/group.') ]); } +// Close edit_user_profiles. +echo '
    '; + enterprise_hook('close_meta_frame'); +if (!defined('METACONSOLE')) { + ?> + + + + + '."\n\t"; + echo ''."\n\t"; + // Closes no meta condition. +} + ?> diff --git a/pandora_console/operation/users/user_edit_header.php b/pandora_console/operation/users/user_edit_header.php index 4b329eee77..fd3e04188b 100644 --- a/pandora_console/operation/users/user_edit_header.php +++ b/pandora_console/operation/users/user_edit_header.php @@ -1,17 +1,32 @@ ', ], ]; + $tab_name = 'User Management'; + + $helpers = ''; + if ($_GET['sec2'] === 'operation/users/user_edit_notifications') { + $helpers = 'user_edit_notifications'; + $tab_name = 'User Notifications'; + } ui_print_page_header( __('User detail editor'), 'images/op_workspace.png', false, - '', + $helpers, false, - $buttons + $buttons, + false, + '', + GENERIC_SIZE_TEXT, + '', + __('Workspace').ui_print_breadcrums($tab_name) ); } diff --git a/pandora_console/operation/users/user_edit_notifications.php b/pandora_console/operation/users/user_edit_notifications.php index 6ffbf87976..03df1b9d22 100644 --- a/pandora_console/operation/users/user_edit_notifications.php +++ b/pandora_console/operation/users/user_edit_notifications.php @@ -1,23 +1,38 @@ data = []; -$table_content->width = '100%'; -$table_content->id = 'user-notifications-wrapper'; -$table_content->class = 'databox filters'; -$table_content->size[0] = '33%'; -$table_content->size[1] = '33%'; -$table_content->size[2] = '33%'; -// Print the header. -$table_content->data[] = [ - '', - __('Enable'), - __('Also receive an email'), -]; +echo '
    +
    +
    +
    '.__('Enable').'
    +
    '.__('Also receive an email').'
    +
    '; -$sources = notifications_get_all_sources(); + $sources = notifications_get_all_sources(); foreach ($sources as $source) { - $table_content->data[] = [ + echo '
    '; + $table_content = [ $source['description'], notifications_print_user_switch($source, $id, 'enabled'), notifications_print_user_switch($source, $id, 'also_mail'), ]; + echo '
    '.$source['description'].'
    '; + echo '
    '.notifications_print_user_switch($source, $id, 'enabled').'
    '; + echo '
    '.notifications_print_user_switch($source, $id, 'also_mail').'
    '; + echo '
    '; } -html_print_table($table_content); +echo '
    '; // Print id user to handle it on js. html_print_input_hidden('id_user', $id); ?> diff --git a/pandora_console/operation/visual_console/legacy_view.php b/pandora_console/operation/visual_console/legacy_view.php new file mode 100644 index 0000000000..f5eec24096 --- /dev/null +++ b/pandora_console/operation/visual_console/legacy_view.php @@ -0,0 +1,386 @@ +'.html_print_image( + 'images/visual_console.png', + true, + ['title' => __('Visual consoles list')] +).''; + +if ($vconsole_write || $vconsole_manage) { + $url_base = 'index.php?sec=network&sec2=godmode/reporting/visual_console_builder&action='; + + $hash = md5($config['dbpass'].$id_layout.$config['id_user']); + + $options['public_link']['text'] = ''.html_print_image( + 'images/camera_mc.png', + true, + ['title' => __('Show link to public Visual Console')] + ).''; + $options['public_link']['active'] = false; + + $options['data']['text'] = ''.html_print_image( + 'images/op_reporting.png', + true, + ['title' => __('Main data')] + ).''; + $options['list_elements']['text'] = ''.html_print_image( + 'images/list.png', + true, + ['title' => __('List elements')] + ).''; + + if (enterprise_installed()) { + $options['wizard_services']['text'] = ''.html_print_image( + 'images/wand_services.png', + true, + ['title' => __('Services wizard')] + ).''; + } + + $options['wizard']['text'] = ''.html_print_image( + 'images/wand.png', + true, + ['title' => __('Wizard')] + ).''; + $options['editor']['text'] = ''.html_print_image( + 'images/builder.png', + true, + ['title' => __('Builder')] + ).''; +} + +$options['view']['text'] = ''.html_print_image('images/operation.png', true, ['title' => __('View')]).''; +$options['view']['active'] = true; + +if (!is_metaconsole()) { + if (!$config['pure']) { + $options['pure']['text'] = ''.html_print_image('images/full_screen.png', true, ['title' => __('Full screen mode')]).''; + ui_print_page_header($layout_name, 'images/visual_console.png', false, '', false, $options); + } + + // Set the hidden value for the javascript + html_print_input_hidden('metaconsole', 0); +} else { + // Set the hidden value for the javascript + html_print_input_hidden('metaconsole', 1); +} + +if ($config['pure']) { + // Container of the visual map (ajax loaded) + echo '
    '.visual_map_print_visual_map( + $id_layout, + true, + true, + null, + null, + '', + false, + true + ).'
    '; + + // Floating menu - Start + echo '
    '; + + echo ''; + + echo '
    '; + // Floating menu - End + ui_require_jquery_file('countdown'); + + ?> + + + + + + \ No newline at end of file diff --git a/pandora_console/operation/visual_console/public_console.php b/pandora_console/operation/visual_console/public_console.php index bc5b3d2a20..8a6ff462fc 100755 --- a/pandora_console/operation/visual_console/public_console.php +++ b/pandora_console/operation/visual_console/public_console.php @@ -15,245 +15,9 @@ // The session is configured and started inside the config process. require_once '../../include/config.php'; -// Set root on homedir, as defined in setup -chdir($config['homedir']); - -ob_start(); -// Enterprise support -if (file_exists(ENTERPRISE_DIR.'/load_enterprise.php')) { - include_once ENTERPRISE_DIR.'/load_enterprise.php'; -} - -if (file_exists(ENTERPRISE_DIR.'/include/functions_login.php')) { - include_once ENTERPRISE_DIR.'/include/functions_login.php'; -} - -echo ''."\n"; -echo ''."\n"; -echo ''; - -global $vc_public_view; -$vc_public_view = true; -// This starts the page head. In the call back function, -// things from $page['head'] array will be processed into the head -ob_start('ui_process_page_head'); -// Enterprise main -enterprise_include('index.php'); - -require_once 'include/functions_visual_map.php'; - -$hash = get_parameter('hash'); -$id_layout = (int) get_parameter('id_layout'); -$graph_javascript = (bool) get_parameter('graph_javascript'); -$config['id_user'] = get_parameter('id_user'); - -$myhash = md5($config['dbpass'].$id_layout.$config['id_user']); - -// Check input hash -if ($myhash != $hash) { - exit; -} - -$refr = (int) get_parameter('refr', 0); -$layout = db_get_row('tlayout', 'id', $id_layout); - -if (! $layout) { - db_pandora_audit('ACL Violation', 'Trying to access visual console without id layout'); - include $config['homedir'].'/general/noaccess.php'; - exit; -} - -if (!isset($config['pure'])) { - $config['pure'] = 0; -} - -// ~ $xhr = (bool) get_parameter('xhr'); -if ($layout) { - $id_group = $layout['id_group']; - $layout_name = $layout['name']; - $background = $layout['background']; - $bwidth = $layout['width']; - $bheight = $layout['height']; - // ~ $width = (int) get_parameter('width'); - // ~ if ($width <= 0) $width = null; - // ~ $height = (int) get_parameter('height'); - // ~ if ($height <= 0) $height = null; - // ~ ob_start(); - // ~ // Render map - visual_map_print_visual_map( - $id_layout, - true, - true, - $width, - $height, - '../../', - true, - true, - true - ); - // ~ return; +$legacy = (bool) get_parameter('legacy', $config['legacy_vc']); +if ($legacy === false) { + include_once $config['homedir'].'/operation/visual_console/public_view.php'; } else { - echo '
    '; + include_once $config['homedir'].'/operation/visual_console/legacy_public_view.php'; } - -// Floating menu - Start -echo '
    '; - -echo ''; - -echo '
    '; -// Floating menu - End -// QR code dialog -echo ''; - -ui_require_jquery_file('countdown'); -ui_require_javascript_file('wz_jsgraphics'); -ui_require_javascript_file('pandora_visual_console'); -$ignored_params['refr'] = ''; -?> - - diff --git a/pandora_console/operation/visual_console/public_view.php b/pandora_console/operation/visual_console/public_view.php new file mode 100644 index 0000000000..90fe29a545 --- /dev/null +++ b/pandora_console/operation/visual_console/public_view.php @@ -0,0 +1,226 @@ +'."\n"; +echo ''."\n"; +echo ''; + +global $vc_public_view; +$vc_public_view = true; +$config['public_view'] = true; + +// This starts the page head. In the call back function, +// things from $page['head'] array will be processed into the head. +ob_start('ui_process_page_head'); +// Enterprise main. +enterprise_include('index.php'); + +require_once 'include/functions_visual_map.php'; + +$hash = (string) get_parameter('hash'); +$visualConsoleId = (int) get_parameter('id_layout'); +$config['id_user'] = (string) get_parameter('id_user'); +$refr = (int) get_parameter('refr', $config['refr']); + +if (!isset($config['pure'])) { + $config['pure'] = 0; +} + +$myhash = md5($config['dbpass'].$visualConsoleId.$config['id_user']); + +// Check input hash. +if ($myhash != $hash) { + exit; +} + +// Load Visual Console. +use Models\VisualConsole\Container as VisualConsole; +$visualConsole = null; +try { + $visualConsole = VisualConsole::fromDB(['id' => $visualConsoleId]); +} catch (Throwable $e) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without Id' + ); + include $config['homedir'].'/general/noaccess.php'; + exit; +} + +$visualConsoleData = $visualConsole->toArray(); +$visualConsoleName = $visualConsoleData['name']; + +echo '
    '; + +// Floating menu - Start. +echo '
    '; + +echo ''; + +echo '
    '; + +// QR code dialog. +echo ''; + +// Check groups can access user. +$aclUserGroups = []; +if (!users_can_manage_group_all('AR')) { + $aclUserGroups = array_keys(users_get_groups(false, 'AR')); +} + +$ignored_params['refr'] = ''; +ui_require_javascript_file('pandora_visual_console'); +include_javascript_d3(); +visual_map_load_client_resources(); + +// Load Visual Console Items. +$visualConsoleItems = VisualConsole::getItemsFromDB( + $visualConsoleId, + $aclUserGroups +); + +?> + + + + diff --git a/pandora_console/operation/visual_console/render_view.php b/pandora_console/operation/visual_console/render_view.php index 4799932e2c..00cdc2d113 100755 --- a/pandora_console/operation/visual_console/render_view.php +++ b/pandora_console/operation/visual_console/render_view.php @@ -13,368 +13,9 @@ // GNU General Public License for more details. global $config; -// Login check -require_once $config['homedir'].'/include/functions_visual_map.php'; - -check_login(); - -if (!defined('METACONSOLE')) { - $id_layout = (int) get_parameter('id'); +$legacy = (bool) get_parameter('legacy', $config['legacy_vc']); +if ($legacy === false) { + include_once $config['homedir'].'/operation/visual_console/view.php'; } else { - $id_layout = (int) get_parameter('id_visualmap'); + include_once $config['homedir'].'/operation/visual_console/legacy_view.php'; } - -if ($id_layout) { - $default_action = 'edit'; -} else { - $default_action = 'new'; -} - -if (!defined('METACONSOLE')) { - $action = get_parameterBetweenListValues( - 'action', - [ - 'new', - 'save', - 'edit', - 'update', - 'delete', - ], - $default_action - ); -} else { - $action = get_parameterBetweenListValues( - 'action2', - [ - 'new', - 'save', - 'edit', - 'update', - 'delete', - ], - $default_action - ); -} - -$refr = (int) get_parameter('refr', $config['vc_refr']); -$graph_javascript = (bool) get_parameter('graph_javascript', true); -$vc_refr = false; - -if (isset($config['vc_refr']) and $config['vc_refr'] != 0) { - $view_refresh = $config['vc_refr']; -} else { - $view_refresh = '300'; -} - -// Get input parameter for layout id -if (! $id_layout) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access visual console without id layout' - ); - include 'general/noaccess.php'; - exit; -} - -$layout = db_get_row('tlayout', 'id', $id_layout); - -if (! $layout) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access visual console without id layout' - ); - include 'general/noaccess.php'; - exit; -} - -$id_group = $layout['id_group']; -$layout_name = $layout['name']; -$background = $layout['background']; -$bwidth = $layout['width']; -$bheight = $layout['height']; - -$pure_url = '&pure='.$config['pure']; - -// ACL -$vconsole_read = check_acl($config['id_user'], $id_group, 'VR'); -$vconsole_write = check_acl($config['id_user'], $id_group, 'VW'); -$vconsole_manage = check_acl($config['id_user'], $id_group, 'VM'); - -if (! $vconsole_read && !$vconsole_write && !$vconsole_manage) { - db_pandora_audit( - 'ACL Violation', - 'Trying to access visual console without group access' - ); - include 'general/noaccess.php'; - exit; -} - -// Render map -$options = []; - -$options['consoles_list']['text'] = ''.html_print_image( - 'images/visual_console.png', - true, - ['title' => __('Visual consoles list')] -).''; - -if ($vconsole_write || $vconsole_manage) { - $url_base = 'index.php?sec=network&sec2=godmode/reporting/visual_console_builder&action='; - - $hash = md5($config['dbpass'].$id_layout.$config['id_user']); - - $options['public_link']['text'] = ''.html_print_image( - 'images/camera_mc.png', - true, - ['title' => __('Show link to public Visual Console')] - ).''; - $options['public_link']['active'] = false; - - $options['data']['text'] = ''.html_print_image( - 'images/op_reporting.png', - true, - ['title' => __('Main data')] - ).''; - $options['list_elements']['text'] = ''.html_print_image( - 'images/list.png', - true, - ['title' => __('List elements')] - ).''; - - if (enterprise_installed()) { - $options['wizard_services']['text'] = ''.html_print_image( - 'images/wand_services.png', - true, - ['title' => __('Services wizard')] - ).''; - } - - $options['wizard']['text'] = ''.html_print_image( - 'images/wand.png', - true, - ['title' => __('Wizard')] - ).''; - $options['editor']['text'] = ''.html_print_image( - 'images/builder.png', - true, - ['title' => __('Builder')] - ).''; -} - -$options['view']['text'] = ''.html_print_image('images/operation.png', true, ['title' => __('View')]).''; -$options['view']['active'] = true; - -if (!is_metaconsole()) { - if (!$config['pure']) { - $options['pure']['text'] = ''.html_print_image('images/full_screen.png', true, ['title' => __('Full screen mode')]).''; - ui_print_page_header($layout_name, 'images/visual_console.png', false, '', false, $options); - } - - // Set the hidden value for the javascript - html_print_input_hidden('metaconsole', 0); -} else { - // Set the hidden value for the javascript - html_print_input_hidden('metaconsole', 1); -} - -if ($config['pure']) { - // Container of the visual map (ajax loaded) - echo '
    '.visual_map_print_visual_map( - $id_layout, - true, - true, - null, - null, - '', - false, - true - ).'
    '; - - // Floating menu - Start - echo '
    '; - - echo ''; - - echo '
    '; - // Floating menu - End - ui_require_jquery_file('countdown'); - - ?> - - - - \ No newline at end of file diff --git a/pandora_console/operation/visual_console/view.php b/pandora_console/operation/visual_console/view.php new file mode 100644 index 0000000000..9c79ba7742 --- /dev/null +++ b/pandora_console/operation/visual_console/view.php @@ -0,0 +1,334 @@ + $visualConsoleId]); +} catch (Throwable $e) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without Id' + ); + include 'general/noaccess.php'; + exit; +} + +$visualConsoleData = $visualConsole->toArray(); +$groupId = $visualConsoleData['groupId']; +$visualConsoleName = $visualConsoleData['name']; + +// ACL. +$aclRead = check_acl($config['id_user'], $groupId, 'VR'); +$aclWrite = check_acl($config['id_user'], $groupId, 'VW'); +$aclManage = check_acl($config['id_user'], $groupId, 'VM'); + +if (!$aclRead && !$aclWrite && !$aclManage) { + db_pandora_audit( + 'ACL Violation', + 'Trying to access visual console without group access' + ); + include 'general/noaccess.php'; + exit; +} + +// Render map. +$options = []; + +$options['consoles_list']['text'] = ''.html_print_image( + 'images/visual_console.png', + true, + ['title' => __('Visual consoles list')] +).''; + +if ($aclWrite || $aclManage) { + $action = get_parameterBetweenListValues( + is_metaconsole() ? 'action2' : 'action', + [ + 'new', + 'save', + 'edit', + 'update', + 'delete', + ], + 'edit' + ); + + $baseUrl = 'index.php?sec=network&sec2=godmode/reporting/visual_console_builder&action='.$action; + + $hash = md5($config['dbpass'].$visualConsoleId.$config['id_user']); + + $options['public_link']['text'] = ''.html_print_image( + 'images/camera_mc.png', + true, + ['title' => __('Show link to public Visual Console')] + ).''; + $options['public_link']['active'] = false; + + $options['data']['text'] = ''.html_print_image( + 'images/op_reporting.png', + true, + ['title' => __('Main data')] + ).''; + $options['list_elements']['text'] = ''.html_print_image( + 'images/list.png', + true, + ['title' => __('List elements')] + ).''; + + if (enterprise_installed()) { + $options['wizard_services']['text'] = ''.html_print_image( + 'images/wand_services.png', + true, + ['title' => __('Services wizard')] + ).''; + } + + $options['wizard']['text'] = ''.html_print_image( + 'images/wand.png', + true, + ['title' => __('Wizard')] + ).''; + $options['editor']['text'] = ''.html_print_image( + 'images/builder.png', + true, + ['title' => __('Builder')] + ).''; +} + +$options['view']['text'] = ''.html_print_image( + 'images/operation.png', + true, + ['title' => __('View')] +).''; +$options['view']['active'] = true; + +if (!is_metaconsole()) { + if (!$config['pure']) { + $options['pure']['text'] = ''.html_print_image( + 'images/full_screen.png', + true, + ['title' => __('Full screen mode')] + ).''; + ui_print_page_header( + $visualConsoleName, + 'images/visual_console.png', + false, + '', + false, + $options + ); + } + + // Set the hidden value for the javascript. + html_print_input_hidden('metaconsole', 0); +} else { + // Set the hidden value for the javascript. + html_print_input_hidden('metaconsole', 1); +} + +echo '
    '; + +if ($pure === true) { + // Floating menu - Start. + echo '
    '; + + echo ''; + + echo '
    '; + // Floating menu - End. + ?> + + + + diff --git a/pandora_console/pandora_console.redhat.spec b/pandora_console/pandora_console.redhat.spec index ddc7f947c0..e584ab1642 100644 --- a/pandora_console/pandora_console.redhat.spec +++ b/pandora_console/pandora_console.redhat.spec @@ -2,8 +2,8 @@ # Pandora FMS Console # %define name pandorafms_console -%define version 7.0NG.732 -%define release 190319 +%define version 7.0NG.735 +%define release 190610 # User and Group under which Apache is running %define httpd_name httpd diff --git a/pandora_console/pandora_console.rhel7.spec b/pandora_console/pandora_console.rhel7.spec new file mode 100644 index 0000000000..ad64ec0769 --- /dev/null +++ b/pandora_console/pandora_console.rhel7.spec @@ -0,0 +1,81 @@ +# +# Pandora FMS Console +# +%define name pandorafms_console +%define version 7.0NG.735 +%define release 190610 + +# User and Group under which Apache is running +%define httpd_name httpd +%define httpd_user apache +%define httpd_group apache + +Summary: Pandora FMS Console +Name: %{name} +Version: %{version} +Release: %{release} +License: GPL +Vendor: Artica ST +#Source0: %{name}-%{version}-%{revision}.tar.gz +Source0: %{name}-%{version}.tar.gz +URL: http://www.pandorafms.com +Group: Productivity/Networking/Web/Utilities +Packager: Sancho Lerena +Prefix: /opt/rh/httpd24/root/var/www/html +BuildRoot: %{_tmppath}/%{name} +BuildArch: noarch +AutoReq: 0 +Requires: httpd24-httpd +Requires: httpd24-mod_php >= 7.2 +Requires: rh-php72-php-gd, rh-php72-php-ldap, rh-php72-php-snmp, rh-php72-php-session, rh-php72-php-gettext +Requires: rh-php72-php-mysqlnd, rh-php72-php-mbstring, rh-php72-php-zip, rh-php72-php-zlib, rh-php72-php-curl +Requires: xorg-x11-fonts-75dpi, xorg-x11-fonts-misc, php-pecl-zip +Requires: graphviz +Provides: %{name}-%{version} + + +%description +The Web Console is a web application that allows to see graphical reports, state of every agent, also to access to the information sent by the agent, to see every monitored parameter and to see its evolution throughout the time, to form the different nodes, groups and users of the system. It is the part that interacts with the final user, and that will allows you to administer the system. + +%prep +rm -rf $RPM_BUILD_ROOT + +%setup -q -n pandora_console + +%build + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT%{prefix}/pandora_console +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/ +cp -aRf * $RPM_BUILD_ROOT%{prefix}/pandora_console +rm $RPM_BUILD_ROOT%{prefix}/pandora_console/*.spec +rm $RPM_BUILD_ROOT%{prefix}/pandora_console/pandora_console_install +install -m 0644 pandora_console_logrotate_centos $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/pandora_console + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +# Has an install already been done, if so we only want to update the files +# push install.php aside so that the console works immediately using existing +# configuration. +# +if [ -f %{prefix}/pandora_console/include/config.php ] ; then + mv %{prefix}/pandora_console/install.php %{prefix}/pandora_console/install.done +else + echo "Please, now, point your browser to http://your_IP_address/pandora_console/install.php and follow all the steps described on it." +fi + +%preun + +# Upgrading +if [ "$1" = "1" ]; then + exit 0 +fi + +%files +%defattr(0644,%{httpd_user},%{httpd_group},0755) +%docdir %{prefix}/pandora_console/docs +%{prefix}/pandora_console +%config(noreplace) %{_sysconfdir}/logrotate.d/pandora_console diff --git a/pandora_console/pandora_console.spec b/pandora_console/pandora_console.spec index 757ed34392..6b80400da6 100644 --- a/pandora_console/pandora_console.spec +++ b/pandora_console/pandora_console.spec @@ -2,8 +2,8 @@ # Pandora FMS Console # %define name pandorafms_console -%define version 7.0NG.732 -%define release 190319 +%define version 7.0NG.735 +%define release 190610 %define httpd_name httpd # User and Group under which Apache is running %define httpd_name apache2 diff --git a/pandora_console/pandora_console_install b/pandora_console/pandora_console_install index 1e11c575bf..1e813790ee 100644 --- a/pandora_console/pandora_console_install +++ b/pandora_console/pandora_console_install @@ -9,7 +9,7 @@ # This code is licensed under GPL 2.0 license. # ********************************************************************** -PI_VERSION="7.0NG.732" +PI_VERSION="7.0NG.735" FORCE=0 DESTDIR="" LOG_TIMESTAMP=`date +"%Y/%m/%d %H:%M:%S"` diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql index ac1f16c6f7..b30295bba6 100644 --- a/pandora_console/pandoradb.sql +++ b/pandora_console/pandoradb.sql @@ -14,7 +14,7 @@ -- along with this program; if not, write to the Free Software -- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- PLEASE NO NOT USE MULTILINE COMMENTS +-- PLEASE NO NOT USE MULTILINE COMMENTS -- Because Pandora Installer don't understand them -- and fails creating database !!! @@ -164,6 +164,9 @@ CREATE TABLE IF NOT EXISTS `tagente_estado` ( `last_known_status` tinyint(4) default 0, `last_error` int(4) NOT NULL default '0', `ff_start_utimestamp` bigint(20) default 0, + `ff_normal` int(4) unsigned default '0', + `ff_warning` int(4) unsigned default '0', + `ff_critical` int(4) unsigned default '0', `last_dynamic_update` bigint(20) NOT NULL default '0', `last_unknown_update` bigint(20) NOT NULL default '0', PRIMARY KEY (`id_agente_estado`), @@ -249,6 +252,7 @@ CREATE TABLE IF NOT EXISTS `tagente_modulo` ( `min_ff_event_normal` int(4) unsigned default '0', `min_ff_event_warning` int(4) unsigned default '0', `min_ff_event_critical` int(4) unsigned default '0', + `ff_type` tinyint(1) unsigned default '0', `each_ff` tinyint(1) unsigned default '0', `ff_timeout` int(4) unsigned default '0', `dynamic_interval` int(4) unsigned default '0', @@ -374,6 +378,7 @@ CREATE TABLE IF NOT EXISTS `talert_commands` ( `internal` tinyint(1) default 0, `fields_descriptions` TEXT, `fields_values` TEXT, + `fields_hidden` TEXT, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -444,7 +449,7 @@ CREATE TABLE IF NOT EXISTS `talert_templates` ( `field13` text NOT NULL, `field14` text NOT NULL, `field15` text NOT NULL, - `type` ENUM ('regex', 'max_min', 'max', 'min', 'equal', 'not_equal', 'warning', 'critical', 'onchange', 'unknown', 'always'), + `type` ENUM ('regex', 'max_min', 'max', 'min', 'equal', 'not_equal', 'warning', 'critical', 'onchange', 'unknown', 'always', 'not_normal'), `value` varchar(255) default '', `matches_value` tinyint(1) default 0, `max_value` double(18,2) default NULL, @@ -785,6 +790,8 @@ CREATE TABLE IF NOT EXISTS `trecon_task` ( `wmi_enabled` tinyint(1) unsigned DEFAULT '0', `auth_strings` text, `autoconfiguration_enabled` tinyint(1) unsigned default '0', + `summary` text, + `type` int NOT NULL default 0, PRIMARY KEY (`id_rt`), KEY `recon_task_daemon` (`id_recon_server`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -862,6 +869,7 @@ CREATE TABLE IF NOT EXISTS `tnetwork_component` ( `min_ff_event_normal` int(4) unsigned default '0', `min_ff_event_warning` int(4) unsigned default '0', `min_ff_event_critical` int(4) unsigned default '0', + `ff_type` tinyint(1) unsigned default '0', `each_ff` tinyint(1) unsigned default '0', `dynamic_interval` int(4) unsigned default '0', `dynamic_max` int(4) default '0', @@ -961,6 +969,7 @@ CREATE TABLE IF NOT EXISTS `trecon_script` ( `description` TEXT, `script` varchar(250) default '', `macros` TEXT, + `type` int NOT NULL default 0, PRIMARY KEY (`id_recon_script`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -1137,6 +1146,9 @@ CREATE TABLE IF NOT EXISTS `tusuario` ( `autorefresh_white_list` text not null default '', `time_autorefresh` int(5) unsigned NOT NULL default '30', `default_custom_view` int(10) unsigned NULL default '0', + `ehorus_user_level_user` VARCHAR(60), + `ehorus_user_level_pass` VARCHAR(45), + `ehorus_user_level_enabled` TINYINT(1), CONSTRAINT `fk_filter_id` FOREIGN KEY (`id_filter`) REFERENCES tevent_filter (`id_filter`) ON DELETE SET NULL, UNIQUE KEY `id_user` (`id_user`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -1412,6 +1424,20 @@ CREATE TABLE IF NOT EXISTS `treport_content` ( `visual_format` tinyint(1) UNSIGNED NOT NULL default '0', `hide_no_data` tinyint(1) default 0, `recursion` tinyint(1) default NULL, + `show_extended_events` tinyint(1) default '0', + `total_time` TINYINT(1) DEFAULT '1', + `time_failed` TINYINT(1) DEFAULT '1', + `time_in_ok_status` TINYINT(1) DEFAULT '1', + `time_in_unknown_status` TINYINT(1) DEFAULT '1', + `time_of_not_initialized_module` TINYINT(1) DEFAULT '1', + `time_of_downtime` TINYINT(1) DEFAULT '1', + `total_checks` TINYINT(1) DEFAULT '1', + `checks_failed` TINYINT(1) DEFAULT '1', + `checks_in_ok_status` TINYINT(1) DEFAULT '1', + `unknown_checks` TINYINT(1) DEFAULT '1', + `agent_max_value` TINYINT(1) DEFAULT '1', + `agent_min_value` TINYINT(1) DEFAULT '1', + `current_month` TINYINT(1) DEFAULT '1', PRIMARY KEY(`id_rc`), FOREIGN KEY (`id_report`) REFERENCES treport(`id_report`) ON UPDATE CASCADE ON DELETE CASCADE @@ -1511,7 +1537,7 @@ CREATE TABLE IF NOT EXISTS `tlayout_data` ( `time_format` varchar(60) NOT NULL default "time", `timezone` varchar(60) NOT NULL default "Europe/Madrid", `show_last_value` tinyint(1) UNSIGNED NULL default '0', - + `cache_expiration` INTEGER UNSIGNED NOT NULL default 0, PRIMARY KEY(`id`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8; @@ -1961,7 +1987,6 @@ CREATE TABLE IF NOT EXISTS `tnetflow_filter` ( `advanced_filter` TEXT NOT NULL, `filter_args` TEXT NOT NULL, `aggregate` varchar(60), - `output` varchar(60), PRIMARY KEY (`id_sg`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -2220,6 +2245,7 @@ CREATE TABLE IF NOT EXISTS `tlocal_component` ( `min_ff_event_normal` int(4) unsigned default '0', `min_ff_event_warning` int(4) unsigned default '0', `min_ff_event_critical` int(4) unsigned default '0', + `ff_type` tinyint(1) unsigned default '0', `each_ff` tinyint(1) unsigned default '0', `ff_timeout` int(4) unsigned default '0', `dynamic_interval` int(4) unsigned default '0', @@ -2298,6 +2324,7 @@ CREATE TABLE IF NOT EXISTS `tpolicy_modules` ( `min_ff_event_normal` int(4) unsigned default '0', `min_ff_event_warning` int(4) unsigned default '0', `min_ff_event_critical` int(4) unsigned default '0', + `ff_type` tinyint(1) unsigned default '0', `each_ff` tinyint(1) unsigned default '0', `ff_timeout` int(4) unsigned default '0', `dynamic_interval` int(4) unsigned default '0', @@ -2938,6 +2965,19 @@ CREATE TABLE IF NOT EXISTS `treport_content_template` ( `lapse` int(11) UNSIGNED NOT NULL default '300', `visual_format` tinyint(1) UNSIGNED NOT NULL default '0', `hide_no_data` tinyint(1) default 0, + `total_time` TINYINT(1) DEFAULT '1', + `time_failed` TINYINT(1) DEFAULT '1', + `time_in_ok_status` TINYINT(1) DEFAULT '1', + `time_in_unknown_status` TINYINT(1) DEFAULT '1', + `time_of_not_initialized_module` TINYINT(1) DEFAULT '1', + `time_of_downtime` TINYINT(1) DEFAULT '1', + `total_checks` TINYINT(1) DEFAULT '1', + `checks_failed` TINYINT(1) DEFAULT '1', + `checks_in_ok_status` TINYINT(1) DEFAULT '1', + `unknown_checks` TINYINT(1) DEFAULT '1', + `agent_max_value` TINYINT(1) DEFAULT '1', + `agent_min_value` TINYINT(1) DEFAULT '1', + `current_month` TINYINT(1) DEFAULT '1', PRIMARY KEY(`id_rc`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8; @@ -3487,4 +3527,66 @@ CREATE TABLE IF NOT EXISTS `tagent_custom_fields_filter` ( `recursion` int(1) unsigned default '0', `group_search` int(10) unsigned default '0', PRIMARY KEY(`id`) -) ENGINE = InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file +) ENGINE = InnoDB DEFAULT CHARSET=utf8; + +-- ----------------------------------------------------- +-- Table `tnetwork_matrix` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tnetwork_matrix` ( + `id` int(10) unsigned NOT NULL auto_increment, + `source` varchar(60) default '', + `destination` varchar(60) default '', + `utimestamp` bigint(20) default 0, + `bytes` int(18) unsigned default 0, + `pkts` int(18) unsigned default 0, + PRIMARY KEY (`id`), + UNIQUE (`source`, `destination`, `utimestamp`) +) ENGINE = InnoDB DEFAULT CHARSET=utf8 ; + +-- --------------------------------------------------------------------- +-- Table `user_task` +-- --------------------------------------------------------------------- +CREATE TABLE `tuser_task` ( + `id` int(20) unsigned NOT NULL auto_increment, + `function_name` varchar(80) NOT NULL default '', + `parameters` text NOT NULL default '', + `name` varchar(60) NOT NULL default '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- --------------------------------------------------------------------- +-- Table `user_task_scheduled` +-- --------------------------------------------------------------------- +CREATE TABLE `tuser_task_scheduled` ( + `id` int(20) unsigned NOT NULL auto_increment, + `id_usuario` varchar(60) NOT NULL default '0', + `id_user_task` int(20) unsigned NOT NULL default '0', + `args` TEXT NOT NULL, + `scheduled` enum('no','hourly','daily','weekly','monthly','yearly','custom') default 'no', + `last_run` int(20) unsigned default '0', + `custom_data` int(10) NULL default '0', + `flag_delete` tinyint(1) UNSIGNED NOT NULL default 0, + `id_grupo` int(10) unsigned NOT NULL default 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- --------------------------------------------------------------------- +-- Table `tvisual_console_items_cache` +-- --------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tvisual_console_elements_cache` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `vc_id` INTEGER UNSIGNED NOT NULL, + `vc_item_id` INTEGER UNSIGNED NOT NULL, + `user_id` VARCHAR(60) DEFAULT NULL, + `data` TEXT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `expiration` INTEGER UNSIGNED NOT NULL COMMENT 'Seconds to expire', + PRIMARY KEY(`id`), + FOREIGN KEY(`vc_id`) REFERENCES `tlayout`(`id`) + ON DELETE CASCADE, + FOREIGN KEY(`vc_item_id`) REFERENCES `tlayout_data`(`id`) + ON DELETE CASCADE, + FOREIGN KEY (`user_id`) REFERENCES `tusuario`(`id_user`) + ON DELETE CASCADE + ON UPDATE CASCADE +) engine=InnoDB DEFAULT CHARSET=utf8; diff --git a/pandora_console/pandoradb_data.sql b/pandora_console/pandoradb_data.sql index 9b5ec600a2..eb52c1dad8 100644 --- a/pandora_console/pandoradb_data.sql +++ b/pandora_console/pandoradb_data.sql @@ -31,6 +31,9 @@ INSERT INTO `talert_commands` (`id`, `name`, `command`, `description`, `internal LOCK TABLES `tconfig` WRITE; INSERT INTO `tconfig` (`token`, `value`) VALUES +('cron_extension_installed','1'), +('cron_extension_sql_version','1'), +('cron_last_run','0'), ('language','en_GB'), ('block_size','20'), ('days_purge','45'), @@ -111,10 +114,10 @@ INSERT INTO `tconfig` (`token`, `value`) VALUES ('custom_report_front_logo', 'images/pandora_logo_white.jpg'), ('custom_report_front_header', ''), ('custom_report_front_footer', ''), -('MR', 26), +('MR', 28), ('identification_reminder', 1), ('identification_reminder_timestamp', 0), -('current_package_enterprise', '733'), +('current_package_enterprise', '735'), ('post_process_custom_values', '{"0.00000038580247":"Seconds to months","0.00000165343915":"Seconds to weeks","0.00001157407407":"Seconds to days","0.01666666666667":"Seconds to minutes","0.00000000093132":"Bytes to Gigabytes","0.00000095367432":"Bytes to Megabytes","0.0009765625":"Bytes to Kilobytes","0.00000001653439":"Timeticks to weeks","0.00000011574074":"Timeticks to days"}'), ('custom_docs_logo', 'default_docs.png'), ('custom_support_logo', 'default_support.png'), @@ -267,7 +270,6 @@ INSERT INTO `ttipo_modulo` VALUES (21,'async_proc', 7, 'Asyncronous proc data', 'mod_async_proc.png'), (22,'async_data', 6, 'Asyncronous numeric data', 'mod_async_data.png'), (23,'async_string', 8, 'Asyncronous string data', 'mod_async_string.png'), -(24,'log4x',0,'Log4x','mod_log4x.png'), (25,'web_analysis', 8, 'Web analysis data', 'module-wux.png'), (30,'web_data',9,'Remote HTTP module to check latency','mod_web_data.png'), (31,'web_proc',9,'Remote HTTP module to check server response','mod_web_proc.png'), @@ -342,8 +344,7 @@ INSERT INTO `tperfil` VALUES (1,'Operator (Read)',0,1,0,1,0,0,0,0,0 -- Dumping data for table `tnews` -- -INSERT INTO `tnews` (`id_news`, `author`, `subject`, `text`, `timestamp`) VALUES (1,'admin','Welcome to Pandora FMS!','This is the Pandora FMS Console. A lot of new features have been added since last version. Please read the documentation about it, and feel free to test any option.\r\n\r\nThe Pandora FMS Team.',NOW()); -INSERT INTO `tnews` (`id_news`, `author`, `subject`, `text`, `timestamp`) VALUES (2,'admin','New Pandora FMS Agent Features','Feel free to test our new features for both Windows and Linux agents: Proxy and Broker modes.',NOW()); +INSERT INTO `tnews` (`id_news`, `author`, `subject`, `text`, `timestamp`) VALUES (1,'admin','Welcome to Pandora FMS Console', '&lt;p style="text-align: center; font-size: 13px;"&gt;Hello, congratulations, if you've arrived here you already have an operational monitoring console. Remember that our forums and online documentation are available 24x7 to get you out of any trouble. You can replace this message with a personalized one at Admin tools -&amp;gt; Site news.&lt;/p&gt; ',NOW()); INSERT INTO tmodule VALUES (1,'Agent module'); INSERT INTO tmodule VALUES (2,'Network module'); @@ -1132,10 +1133,10 @@ INSERT INTO `treport_custom_sql` (`id`, `name`, `sql`) VALUES (3, 'Monitoring&#x INSERT INTO `treport_custom_sql` (`id`, `name`, `sql`) VALUES (4, 'Group view', 'select t1.nombre, (select count(t3.id_agente) from tagente as t3 where t1.id_grupo = t3.id_grupo) as agents, (SELECT COUNT(t4.id_agente) FROM tagente as t4 WHERE t4.id_grupo = t1.id_grupo AND t4.disabled = 0 AND t4.ultimo_contacto < NOW() - (intervalo / (1/2))) as agent_unknown, (SELECT COUNT(tagente_estado.id_agente_estado) FROM tagente_estado, tagente, tagente_modulo WHERE tagente.id_grupo = t1.id_grupo AND tagente.disabled = 0 AND tagente.id_agente = tagente_estado.id_agente AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND tagente_modulo.disabled = 0 AND utimestamp > 0 AND tagente_modulo.id_tipo_modulo NOT IN(21,22,23,24,100) AND (UNIX_TIMESTAMP(NOW()) - tagente_estado.utimestamp) >= (tagente_estado.current_interval / (1/2))) as monitor_unknow, (SELECT COUNT(tagente_estado.id_agente_estado) FROM tagente_estado, tagente, tagente_modulo WHERE tagente.id_grupo = t1.id_grupo AND tagente.disabled = 0 AND tagente.id_agente = tagente_estado.id_agente AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND tagente_modulo.disabled = 0 AND tagente_modulo.id_tipo_modulo NOT IN (21,22,23,24) AND utimestamp = 0) as monitor_no_init, (SELECT COUNT(tagente_estado.id_agente_estado) FROM tagente_estado, tagente, tagente_modulo WHERE tagente.id_grupo = t1.id_grupo AND tagente.disabled = 0 AND tagente_estado.id_agente = tagente.id_agente AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND tagente_modulo.disabled = 0 AND estado = 0 AND ((UNIX_TIMESTAMP(NOW()) - tagente_estado.utimestamp) < (tagente_estado.current_interval / (1/2)) OR (tagente_modulo.id_tipo_modulo IN(21,22,23,24,100))) AND (utimestamp > 0 OR (tagente_modulo.id_tipo_modulo IN(21,22,23,24)))) as monitor_ok, (SELECT COUNT(tagente_estado.id_agente_estado) FROM tagente_estado, tagente, tagente_modulo WHERE tagente.id_grupo = t1.id_grupo AND tagente.disabled = 0 AND tagente_estado.id_agente = tagente.id_agente AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND tagente_modulo.disabled = 0 AND estado = 1 AND ((UNIX_TIMESTAMP(NOW()) - tagente_estado.utimestamp) < (tagente_estado.current_interval / (1/2)) OR (tagente_modulo.id_tipo_modulo IN(21,22,23,24,100))) AND utimestamp > 0) as monitor_critical, (SELECT COUNT(talert_template_modules.id) FROM talert_template_modules, tagente_modulo, tagente_estado, tagente WHERE tagente.id_grupo = t1.id_grupo AND tagente_modulo.id_agente = tagente.id_agente AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND tagente_modulo.disabled = 0 AND tagente.disabled = 0 AND talert_template_modules.id_agent_module = tagente_modulo.id_agente_modulo AND times_fired > 0) as monitor_alert_fired from tgrupo as t1 where 0 < (select count(t2.id_agente) from tagente as t2 where t1.id_grupo = t2.id_grupo)'); -- trecon scripts -INSERT INTO `trecon_script` VALUES (2,'IPMI Recon','Specific Pandora FMS Intel DCM Discovery (c) Artica ST 2011 <info@artica.es> Usage: ./ipmi-recon.pl <task_id> <group_id> <create_incident_flag> <custom_field1> <custom_field2> <custom_field3> <custom_field4> * custom_field1 = Network i.e.: 192.168.100.0/24 * custom_field2 = Username * custom_field3 = Password * custom_field4 = Additional parameters i.e.: -D LAN_2_0','/usr/share/pandora_server/util/recon_scripts/ipmi-recon.pl','{\"1\":{\"macro\":\"_field1_\",\"desc\":\"Network\",\"help\":\"i.e.: 192.168.100.0/24\",\"value\":\"\",\"hide\":\"\"},\"2\":{\"macro\":\"_field2_\",\"desc\":\"Username\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"},\"3\":{\"macro\":\"_field3_\",\"desc\":\"Password\",\"help\":\"\",\"value\":\"\",\"hide\":\"1\"},\"4\":{\"macro\":\"_field4_\",\"desc\":\"Additional parameters\",\"help\":\"Optional additional parameters such as -D LAN_2_0 to use IPMI ver 2.0 instead of 1.5. These options will also be passed to the IPMI plugin when the current values are read.\",\"value\":\"\",\"hide\":\"\"}}'); -INSERT INTO `trecon_script` VALUES (5,'WMI Recon Script','This script is used to automatically gather host information via WMI. Available parameters: * Network = network to scan (e.g. 192.168.100.0/24). * WMI auth = comma separated list of WMI authentication tokens in the format username%password (e.g. Administrador%pass). See the documentation for more information.','/usr/share/pandora_server/util/recon_scripts/wmi-recon.pl','{\"1\":{\"macro\":\"_field1_\",\"desc\":\"Network\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"},\"2\":{\"macro\":\"_field2_\",\"desc\":\"WMI auth\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"}}'); -INSERT INTO `trecon_script` (`name`,`description`,`script`,`macros`) VALUES ('Discovery.Application.VMware', 'Discovery Application script to monitor VMware technologies (ESXi, VCenter, VSphere)', '/usr/share/pandora_server/util/recon_scripts/vmware-plugin.pl', '{"1":{"macro":"_field1_","desc":"Configuration file","help":"","value":"","hide":""}}'); -INSERT INTO `trecon_script` (`name`,`description`,`script`,`macros`) VALUES ('Discovery.Cloud', 'Discovery Cloud script to monitor Cloud technologies (AWS.EC2, AWS.S3, AWS.RDS, RDS,ȊWS.EKS)', '/usr/share/pandora_server/util/recon_scripts/pcm_client.pl', '{"1":{"macro":"_field1_","desc":"Configuration file","help":"","value":"","hide":""}}'); +INSERT INTO `trecon_script` (`type`,`name`,`description`,`script`,`macros`) VALUES (1, 'Discovery.Application.VMware', 'Discovery Application script to monitor VMware technologies (ESXi, VCenter, VSphere)', '/usr/share/pandora_server/util/recon_scripts/vmware-plugin.pl', '{"1":{"macro":"_field1_","desc":"Configuration file","help":"","value":"","hide":""}}'); +INSERT INTO `trecon_script` (`type`,`name`,`description`,`script`,`macros`) VALUES (2, 'Discovery.Cloud', 'Discovery Cloud script to monitor Cloud technologies (AWS.EC2, AWS.S3, AWS.RDS, RDS,ȊWS.EKS)', '/usr/share/pandora_server/util/recon_scripts/pcm_client.pl', '{"1":{"macro":"_field1_","desc":"Configuration file","help":"","value":"","hide":""}}'); +-- IPAM is 3. +INSERT INTO `trecon_script` (`type`,`name`,`description`,`script`,`macros`) VALUES (4, 'IPMI Recon','Specific Pandora FMS Intel DCM Discovery (c) Artica ST 2011 <info@artica.es> Usage: ./ipmi-recon.pl <task_id> <group_id> <create_incident_flag> <custom_field1> <custom_field2> <custom_field3> <custom_field4> * custom_field1 = Network i.e.: 192.168.100.0/24 * custom_field2 = Username * custom_field3 = Password * custom_field4 = Additional parameters i.e.: -D LAN_2_0','/usr/share/pandora_server/util/recon_scripts/ipmi-recon.pl','{\"1\":{\"macro\":\"_field1_\",\"desc\":\"Network\",\"help\":\"i.e.: 192.168.100.0/24\",\"value\":\"\",\"hide\":\"\"},\"2\":{\"macro\":\"_field2_\",\"desc\":\"Username\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"},\"3\":{\"macro\":\"_field3_\",\"desc\":\"Password\",\"help\":\"\",\"value\":\"\",\"hide\":\"1\"},\"4\":{\"macro\":\"_field4_\",\"desc\":\"Additional parameters\",\"help\":\"Optional additional parameters such as -D LAN_2_0 to use IPMI ver 2.0 instead of 1.5. These options will also be passed to the IPMI plugin when the current values are read.\",\"value\":\"\",\"hide\":\"\"}}'); INSERT INTO `tplugin` (`id`, `name`, `description`, `max_timeout`, `execute`, `plugin_type`, `macros`, `parameters`) VALUES (1,'IPMI Plugin','Plugin to get IPMI monitors from a IPMI Device.',0,'/usr/share/pandora_server/util/plugin/ipmi-plugin.pl',0,'{\"1\":{\"macro\":\"_field1_\",\"desc\":\"Target IP\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"},\"2\":{\"macro\":\"_field2_\",\"desc\":\"Username\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"},\"3\":{\"macro\":\"_field3_\",\"desc\":\"Password\",\"help\":\"\",\"value\":\"\",\"hide\":\"true\"},\"4\":{\"macro\":\"_field4_\",\"desc\":\"Sensor\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"},\"5\":{\"macro\":\"_field5_\",\"desc\":\"Additional Options\",\"help\":\"\",\"value\":\"\",\"hide\":\"\"}}','-h _field1_ -u _field2_ -p _field3_ -s _field4_ -- _field5_'); @@ -1155,7 +1156,7 @@ INSERT INTO `tagent_custom_fields` VALUES (1,'Serial Number',0,0,''),(2,'De INSERT INTO `ttag` VALUES (1,'network','Network equipment','http://artica.es','',''),(2,'critical','Critical modules','','',''),(3,'dmz','DMZ Network Zone','','',''),(4,'performance','Performance anda capacity modules','','',''),(5,'configuration','','','',''); -INSERT INTO `tevent_response` VALUES (1,'Ping to host','Ping to the agent host','ping -c 5 _agent_address_','command',0,620,500,0,'',0),(3,'Create incident from event','Create a incident from the event with the standard incidents system of Pandora FMS','index.php?sec=workspace&sec2=operation/incidents/incident_detail&insert_form&from_event=_event_id_','url',0,0,0,1,'',0),(4,'Create Integria IMS incident from event','Create a incident from the event with integria incidents system of Pandora FMS. Is necessary to enable and configure the Integria incidents in Pandora FMS setup.','index.php?sec=workspace&sec2=operation/integria_incidents/incident&tab=editor&from_event=_event_id_','url',0,0,0,1,'',0),(5,'Restart agent','Restart the agent with using UDP protocol. To use this response is necessary to have installed Pandora FMS server and console in the same machine.','/usr/share/pandora_server/util/udp_client.pl _agent_address_ 41122 "REFRESH AGENT"','command',0,620,500,0,'',0),(6,'Ping to module agent host','Ping to the module agent host','ping -c 5 _module_address_','command',0,620,500,0,'',0); +INSERT INTO `tevent_response` VALUES (1,'Ping to host','Ping to the agent host','ping -c 5 _agent_address_','command',0,620,500,0,'',0),(3,'Create incident from event','Create a incident from the event with the standard incidents system of Pandora FMS','index.php?sec=workspace&sec2=operation/incidents/incident_detail&insert_form&from_event=_event_id_','url',0,0,0,1,'',0),(5,'Restart agent','Restart the agent with using UDP protocol. To use this response is necessary to have installed Pandora FMS server and console in the same machine.','/usr/share/pandora_server/util/udp_client.pl _agent_address_ 41122 "REFRESH AGENT"','command',0,620,500,0,'',0),(6,'Ping to module agent host','Ping to the module agent host','ping -c 5 _module_address_','command',0,620,500,0,'',0); INSERT INTO `tupdate_settings` VALUES ('current_update', '412'), ('customer_key', 'PANDORA-FREE'), ('updating_binary_path', 'Path where the updated binary files will be stored'), ('updating_code_path', 'Path where the updated code is stored'), ('dbname', ''), ('dbhost', ''), ('dbpass', ''), ('dbuser', ''), ('dbport', ''), ('proxy', ''), ('proxy_port', ''), ('proxy_user', ''), ('proxy_pass', ''); @@ -1301,7 +1302,7 @@ INSERT INTO `tnotification_source`(`description`, `icon`, `max_postpone_time`, ` ("Message", "icono_info_mr.png", 86400, 1, 1, 0), ("Pending task", "icono_info_mr.png", 86400, 1, 1, 0), ("Advertisement", "icono_info_mr.png", 86400, 1, 1, 0), - ("Official communication", "icono_info_mr.png", 86400, 1, 1, 0), + ("Official communication", "icono_logo_pandora.png", 86400, 1, 1, 0), ("Sugerence", "icono_info_mr.png", 86400, 1, 1, 0); -- @@ -1312,4 +1313,8 @@ INSERT INTO `tnotification_source_user`(`id_source`,`id_user`,`enabled`,`also_ma INSERT INTO `tnotification_source_group` SELECT `id`,0 FROM `tnotification_source` WHERE `description`="Message"; - \ No newline at end of file +INSERT INTO `tnotification_source_user` (`id_source`, `id_user`, `enabled`, `also_mail`) VALUES + ((SELECT `id` FROM `tnotification_source` WHERE `description`="Official communication"), "admin", 1, 0); + +UPDATE `tnotification_source` SET `enabled`=1 WHERE `description` = 'System status' OR `description` = 'Official communication'; + diff --git a/pandora_console/tests/Functional/Models/VisualConsole/ContainerTest.php b/pandora_console/tests/Functional/Models/VisualConsole/ContainerTest.php new file mode 100644 index 0000000000..b7a1a7c6de --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/ContainerTest.php @@ -0,0 +1,268 @@ +assertInstanceOf( + VisualConsole::class, + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'foo', + 'groupId' => 1, + 'backgroundURL' => 'aaa', + 'backgroundColor' => 'bbb', + 'width' => 800, + 'height' => 800, + ] + ) + ); + + $this->assertInstanceOf( + VisualConsole::class, + VisualConsole::fromArray( + [ + 'id' => 69, + 'name' => 'New visual console', + 'groupId' => 0, + 'background' => 'globalmap.jpg', + 'background_color' => 'white', + 'is_favourite' => 1, + 'width' => 100, + 'height' => 200, + ] + ) + ); + + $this->assertInstanceOf( + VisualConsole::class, + VisualConsole::fromArray( + [ + 'id' => 1030, + 'name' => 'console2', + 'groupId' => 12, + 'width' => 1024, + 'height' => 768, + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + VisualConsole::fromArray( + [ + 'id' => 'bar', + 'name' => 'foo', + 'groupId' => 0, + 'is_favourite' => 1, + 'width' => 1024, + 'height' => 768, + ] + ); + // Missing id. + VisualConsole::fromArray( + [ + 'name' => 'foo', + 'groupId' => 0, + 'width' => 1024, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid name. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidName(): void + { + $this->expectException(InvalidArgumentException::class); + // Empty name. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => '', + 'groupId' => 0, + 'width' => 1024, + 'height' => 768, + ] + ); + // Missing name. + VisualConsole::fromArray( + [ + 'id' => 1, + 'groupId' => 8, + 'width' => 1024, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid group id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidGroupId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid group id. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 'Hi', + 'width' => 1024, + 'height' => 768, + ] + ); + + // Missing group id. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'width' => 1024, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid width. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidWidth(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid width. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'width' => 0, + 'height' => 768, + ] + ); + + // Missing width. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'height' => 768, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid height. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidHeigth(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid height. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'width' => 1024, + 'height' => -1, + ] + ); + + // Missing height. + VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'test', + 'groupId' => 10, + 'width' => 1024, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"backgroundColor":null,"backgroundURL":null,"groupId":0,"height":768,"id":1,"isFavorite":false,"name":"foo","width":1024}', + (string) VisualConsole::fromArray( + [ + 'id' => 1, + 'name' => 'foo', + 'groupId' => 0, + 'width' => 1024, + 'height' => 768, + ] + ) + ); + } + + + /** + * Test if the item's instance is returned properly. + * + * @return void + */ + public function testItemClassIsReturned(): void + { + $this->assertEquals( + VisualConsole::getItemClass(STATIC_GRAPH), + Models\VisualConsole\Items\StaticGraph::class + ); + + $this->assertEquals( + VisualConsole::getItemClass(COLOR_CLOUD), + Models\VisualConsole\Items\ColorCloud::class + ); + + $this->assertEquals( + VisualConsole::getItemClass(LABEL), + Models\VisualConsole\Items\Label::class + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/ItemTest.php b/pandora_console/tests/Functional/Models/VisualConsole/ItemTest.php new file mode 100644 index 0000000000..b4b5dea59b --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/ItemTest.php @@ -0,0 +1,298 @@ +assertInstanceOf( + ItemConsole::class, + ItemConsole::fromArray( + [ + 'id' => 1, + 'type' => 5, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ) + ); + + $this->assertInstanceOf( + ItemConsole::class, + ItemConsole::fromArray( + [ + 'id' => 1, + 'type' => 5, + 'width' => 0, + 'height' => 0, + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 'foo', + 'type' => 5, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'type' => 5, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid type. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidType(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 'clock', + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'id' => 6, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid width. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidWidth(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => -1, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid height. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidHeight(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => -1, + 'x' => 0, + 'y' => 0, + ] + ); + // Missing id. + ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 600, + 'x' => 0, + 'y' => 0, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testItemIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":12,"height":600,"id":15,"isLinkEnabled":false,"isOnTop":true,"label":"test","labelPosition":"down","parentId":0,"type":3,"width":800,"x":0,"y":0}', + (string) ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => 'test', + 'labelPosition' => 'down', + 'isLinkEnabled' => false, + 'isOnTop' => true, + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":12,"height":600,"id":15,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","parentId":0,"type":3,"width":800,"x":0,"y":0}', + (string) ItemConsole::fromArray( + [ + 'id' => 15, + 'type' => 3, + 'label' => '', + 'labelPosition' => 'test', + 'parentId' => 0, + 'aclGroupId' => 12, + 'width' => 800, + 'height' => 600, + 'x' => 0, + 'y' => 0, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":69,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ItemConsole::fromArray( + [ + 'id' => 69, + 'type' => 20, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/BarsGraphTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/BarsGraphTest.php new file mode 100644 index 0000000000..16866a4fc6 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/BarsGraphTest.php @@ -0,0 +1,111 @@ +assertInstanceOf( + BarsGraph::class, + BarsGraph::fromArray( + [ + 'id' => 7, + 'type' => BARS_GRAPH, + 'width' => '600', + 'height' => '500', + 'typeGraph' => 'horizontal', + 'backgroundColor' => 'white', + 'gridColor' => '#33CCFF', + 'encodedHtml' => '

    Foo

    ', + ] + ) + ); + + $this->assertInstanceOf( + BarsGraph::class, + BarsGraph::fromArray( + [ + 'id' => 23, + 'type' => BARS_GRAPH, + 'width' => '800', + 'height' => '600', + 'type_graph' => 'vertical', + 'image' => 'transparent', + 'border_color' => '#33CCFF', + 'html' => '

    Foo

    ', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"backgroundColor":"transparent","encodedHtml":"PGgxPkZvbzwvaDE+","gridColor":"#33CCFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","moduleId":null,"moduleName":null,"parentId":null,"type":18,"typeGraph":"vertical","width":0,"x":-666,"y":76}', + (string) BarsGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'type_graph' => 'vertical', + 'image' => 'transparent', + 'border_color' => '#33CCFF', + 'html' => '

    Foo

    ', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"backgroundColor":"white","encodedHtml":"PGgxPkZvbzwvaDE+","gridColor":"#33CCFF","height":300,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":"test","labelPosition":"left","moduleId":null,"moduleName":null,"parentId":null,"type":18,"typeGraph":"horizontal","width":300,"x":-666,"y":76}', + (string) BarsGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => 'test', + 'labelPosition' => 'left', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '300', + 'height' => '300', + 'x' => -666, + 'y' => 76, + 'typeGraph' => 'horizontal', + 'backgroundColor' => 'white', + 'gridColor' => '#33CCFF', + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/BoxTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/BoxTest.php new file mode 100644 index 0000000000..9d2e0b2f4d --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/BoxTest.php @@ -0,0 +1,84 @@ +assertInstanceOf( + Box::class, + Box::fromArray( + [ + 'id' => 69, + 'type' => 12, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + + $this->assertInstanceOf( + Box::class, + Box::fromArray( + [ + 'id' => 1000, + 'type' => 8, + 'name' => 'test', + 'width' => 100, + 'height' => 900, + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"borderColor":null,"borderWidth":0,"fillColor":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","parentId":null,"type":12,"width":0,"x":-666,"y":76}', + (string) Box::fromArray( + [ + 'id' => 7, + 'type' => 10, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/ClockTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/ClockTest.php new file mode 100644 index 0000000000..269bb7bef5 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/ClockTest.php @@ -0,0 +1,173 @@ +assertInstanceOf( + Clock::class, + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Madrid', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ) + ); + + $this->assertInstanceOf( + Clock::class, + Clock::fromArray( + [ + 'id' => 1000, + 'type' => CLOCK, + 'width' => 100, + 'height' => 900, + 'clockType' => 'analogic', + 'clockFormat' => 'datetime', + 'clockTimezone' => 'Asia/Tokyo', + 'showClockTimezone' => true, + 'color' => 'red', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid clockTimezone. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidClockTimezone(): void + { + $this->expectException(Exception::class); + // Invalid clockTimezone. + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Tokyo', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ); + + // Invalid clockTimezone. + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Tokyo', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ); + + // Missing clockTimezone. + Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"clockFormat":"time","clockTimezone":"Europe\/Madrid","clockTimezoneOffset":7200,"clockType":"digital","color":"white","height":0,"id":69,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"showClockTimezone":false,"type":19,"width":0,"x":-666,"y":76}', + (string) Clock::fromArray( + [ + 'id' => 69, + 'type' => CLOCK, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'clockType' => 'digital', + 'clockFormat' => 'time', + 'clockTimezone' => 'Europe/Madrid', + 'showClockTimezone' => false, + 'color' => 'white', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/ColorCloudTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/ColorCloudTest.php new file mode 100644 index 0000000000..3dbb8abe9a --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/ColorCloudTest.php @@ -0,0 +1,253 @@ +assertInstanceOf( + ColorCloud::class, + ColorCloud::fromArray( + [ + 'id' => 345, + 'type' => COLOR_CLOUD, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'colorRanges' => [], + ] + ) + ); + + $this->assertInstanceOf( + ColorCloud::class, + ColorCloud::fromArray( + [ + 'id' => 1000, + 'type' => COLOR_CLOUD, + 'width' => 100, + 'height' => 900, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'colorRanges' => [ + [ + 'color' => '#000', + 'fromValue' => 10.05, + 'toValue' => 100.0, + ], + ], + 'color' => '#000', + ] + ) + ); + + $this->assertInstanceOf( + ColorCloud::class, + ColorCloud::fromArray( + [ + 'id' => 1000, + 'type' => COLOR_CLOUD, + 'width' => 100, + 'height' => 900, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'colorRanges' => [ + [ + 'color' => '#000', + 'fromValue' => 10.05, + 'toValue' => 100.0, + ], + ], + 'color' => '#000', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"colorRanges":[],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'colorRanges' => [], + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'color' => '#000', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'defaultColor' => '#FFF', + 'color' => '#000', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}', + (string) ColorCloud::fromArray( + [ + 'id' => 7, + 'type' => COLOR_CLOUD, + 'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'colorRanges' => [ + [ + 'color' => '#000', + 'fromValue' => 10.05, + 'toValue' => 100.0, + ], + ], + 'color' => '#000', + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid dynamic data. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidDynamicData(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid dynamic data. + ColorCloud::fromArray( + [ + 'id' => 3, + 'type' => COLOR_CLOUD, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + ] + ); + // Missing dynamic data. + ColorCloud::fromArray( + [ + 'id' => 3, + 'type' => COLOR_CLOUD, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + ] + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/DonutGraphTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/DonutGraphTest.php new file mode 100644 index 0000000000..e9c0367151 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/DonutGraphTest.php @@ -0,0 +1,103 @@ +assertInstanceOf( + DonutGraph::class, + DonutGraph::fromArray( + [ + 'id' => 3, + 'type' => DONUT_GRAPH, + 'width' => '600', + 'height' => '500', + 'legendBackgroundColor' => '#33CCFF', + 'html' => '

    Foo

    ', + ] + ) + ); + + $this->assertInstanceOf( + DonutGraph::class, + DonutGraph::fromArray( + [ + 'id' => 14, + 'type' => DONUT_GRAPH, + 'width' => '600', + 'height' => '500', + 'border_color' => '#000000', + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","legendBackgroundColor":"#33CCFF","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":17,"width":0,"x":-666,"y":76}', + (string) DonutGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'border_color' => '#33CCFF', + 'html' => '

    Foo

    ', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"left","legendBackgroundColor":"#000000","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":17,"width":0,"x":-666,"y":76}', + (string) DonutGraph::fromArray( + [ + 'id' => 7, + 'type' => DONUT_GRAPH, + 'label' => null, + 'labelPosition' => 'left', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'legendBackgroundColor' => '#000000', + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/EventsHistoryTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/EventsHistoryTest.php new file mode 100644 index 0000000000..3bd8e65949 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/EventsHistoryTest.php @@ -0,0 +1,176 @@ +assertInstanceOf( + EventsHistory::class, + EventsHistory::fromArray( + [ + 'id' => 3, + 'type' => AUTO_SLA_GRAPH, + 'width' => '600', + 'height' => '500', + 'maxTime' => null, + 'html' => '

    Foo

    ', + ] + ) + ); + + $this->assertInstanceOf( + EventsHistory::class, + EventsHistory::fromArray( + [ + 'id' => 14, + 'type' => AUTO_SLA_GRAPH, + 'width' => '600', + 'height' => '500', + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxTime":null,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => null, + 'html' => '

    Foo

    ', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxTime":12800,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","maxTime":null,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => null, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","maxTime":12800,"metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":21,"agentName":null,"encodedHtml":"PGgxPkZvbzwvaDE+","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":15,"linkedLayoutId":3,"linkedLayoutStatusType":"default","maxTime":12800,"metaconsoleId":2,"moduleId":385,"moduleName":"module_test","parentId":null,"type":14,"width":0,"x":-666,"y":76}', + (string) EventsHistory::fromArray( + [ + 'id' => 7, + 'type' => AUTO_SLA_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'encodedHtml' => 'PGgxPkZvbzwvaDE+', + 'id_metaconsole' => 2, + 'linked_layout_node_id' => 15, + 'linkedLayoutId' => 3, + 'agentId' => 21, + 'moduleId' => 385, + 'moduleName' => 'module_test', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/GroupTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/GroupTest.php new file mode 100644 index 0000000000..00e3a90620 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/GroupTest.php @@ -0,0 +1,279 @@ +assertInstanceOf( + Group::class, + Group::fromArray( + [ + 'id' => 13, + 'type' => GROUP_ITEM, + 'width' => '600', + 'height' => '500', + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + + $this->assertInstanceOf( + Group::class, + Group::fromArray( + [ + 'id' => 1004, + 'type' => GROUP_ITEM, + 'width' => '600', + 'height' => '500', + 'image' => 'test_image.png', + 'id_group' => 0, + 'statusImageSrc' => 'test_image.bad.jpg', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid image src. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidImageSrc(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid imageSrc. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => '', + 'groupId' => 0, + ] + ); + // Missing imageSrc. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'id_group' => 11, + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid group Id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidGroupId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid groupId. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'test.jpg', + 'groupId' => 'bar', + 'statusImageSrc' => 'image.bad.jpg', + ] + ); + // Missing groupId. + Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'test.jpg', + 'statusImageSrc' => 'image.bad.jpg', + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + + // With a linked layout. + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"weight","linkedLayoutStatusTypeWeight":80,"metaconsoleId":5,"parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'linkedLayoutStatusType' => 'weight', + 'linkedLayoutStatusTypeWeight' => 80, + ] + ) + ); + $this->assertEquals( + '{"aclGroupId":null,"groupId":12,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"parentId":null,"statusImageSrc":"image.bad.jpg","type":11,"width":0,"x":-666,"y":76}', + (string) Group::fromArray( + [ + 'id' => 7, + 'type' => GROUP_ITEM, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'groupId' => 12, + 'statusImageSrc' => 'image.bad.jpg', + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/IconTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/IconTest.php new file mode 100644 index 0000000000..44fa077959 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/IconTest.php @@ -0,0 +1,207 @@ +assertInstanceOf( + Icon::class, + Icon::fromArray( + [ + 'id' => 69, + 'type' => ICON, + 'width' => '0', + 'height' => '0', + 'imageSrc' => 'image.jpg', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid image src. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidImageSrc(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid imageSrc. + Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => '', + ] + ); + // Missing imageSrc. + Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + ] + ) + ); + + // With a linked layout. + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"weight","linkedLayoutStatusTypeWeight":80,"metaconsoleId":5,"parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'linkedLayoutStatusType' => 'weight', + 'linkedLayoutStatusTypeWeight' => 80, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"metaconsoleId":5,"parentId":null,"type":5,"width":0,"x":-666,"y":76}', + (string) Icon::fromArray( + [ + 'id' => 7, + 'type' => ICON, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + ] + ) + ); + + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/LabelTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/LabelTest.php new file mode 100644 index 0000000000..2c1fd963cd --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/LabelTest.php @@ -0,0 +1,129 @@ +assertInstanceOf( + Label::class, + Label::fromArray( + [ + 'id' => 69, + 'type' => LABEL, + 'width' => '0', + 'height' => '0', + 'label' => 'Label', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid label. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidLabel(): void + { + $this->expectException(InvalidArgumentException::class); + // Missing label. + Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ); + // Empty label. + Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'label' => '', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":"Label","labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","parentId":null,"type":4,"width":0,"x":-666,"y":76}', + (string) Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'label' => 'Label', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + ] + ) + ); + + // With a linked layout. + $this->assertEquals( + '{"aclGroupId":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":"Label","labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","parentId":null,"type":4,"width":0,"x":-666,"y":76}', + (string) Label::fromArray( + [ + 'id' => 7, + 'type' => LABEL, + 'label' => 'Label', + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'id_layout_linked' => 1, + ] + ) + ); + + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/LineTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/LineTest.php new file mode 100644 index 0000000000..b70b1699a6 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/LineTest.php @@ -0,0 +1,156 @@ +assertInstanceOf( + Line::class, + Line::fromArray( + [ + 'id' => 10, + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ) + ); + + $this->assertInstanceOf( + Line::class, + Line::fromArray( + [ + 'id' => 10, + 'type' => LINE_ITEM, + 'startX' => 50, + 'endY' => 10, + 'borderColor' => 'black', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid Id. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidId(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid id. + Line::fromArray( + [ + 'id' => 'foo', + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + // Missing id. + Line::fromArray( + [ + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + } + + + /** + * Test if the instance is not created when using a invalid type. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidtype(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid type. + Line::fromArray( + [ + 'id' => 13, + 'type' => 'test', + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + // Missing type. + Line::fromArray( + [ + 'id' => 13, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => true, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"borderColor":"white","borderWidth":0,"endX":0,"endY":10,"id":1,"isOnTop":false,"startX":50,"startY":100,"type":13}', + (string) Line::fromArray( + [ + 'id' => 1, + 'type' => LINE_ITEM, + 'startX' => 50, + 'startY' => 100, + 'endX' => 0, + 'endY' => 10, + 'isOnTop' => false, + 'borderWidth' => 0, + 'borderColor' => 'white', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/PercentileTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/PercentileTest.php new file mode 100644 index 0000000000..ebe80f6b8c --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/PercentileTest.php @@ -0,0 +1,149 @@ +assertInstanceOf( + Percentile::class, + Percentile::fromArray( + [ + 'id' => 3, + 'type' => PERCENTILE_BAR, + 'width' => '600', + 'height' => '500', + 'maxTime' => null, + 'valueType' => 'value', + 'value' => '123ms', + ] + ) + ); + + $this->assertInstanceOf( + Percentile::class, + Percentile::fromArray( + [ + 'id' => 14, + 'type' => PERCENTILE_BUBBLE, + 'width' => '600', + 'height' => '500', + 'maxTime' => 12800, + 'valueType' => 'image', + 'value' => 'data:image;asdasoih==', + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"progress-bar","type":3,"unit":null,"value":null,"valueType":"percent","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => PERCENTILE_BAR, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => null, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"bubble","type":3,"unit":null,"value":null,"valueType":"percent","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => PERCENTILE_BUBBLE, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"circular-progress-bar","type":3,"unit":null,"value":1,"valueType":"value","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => CIRCULAR_PROGRESS_BAR, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'valueType' => 'value', + 'value' => '1', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelColor":"#000","labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","maxValue":0,"minValue":null,"moduleId":null,"moduleName":null,"parentId":null,"percentileType":"circular-progress-bar","type":3,"unit":null,"value":80,"valueType":"percent","width":0,"x":-666,"y":76}', + (string) Percentile::fromArray( + [ + 'id' => 7, + 'type' => CIRCULAR_PROGRESS_BAR, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'maxTime' => 12800, + 'valueType' => 'percent', + 'value' => '80', + 'color' => '#FFF', + 'labelColor' => '#000', + ] + ) + ); + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/SimpleValueTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/SimpleValueTest.php new file mode 100644 index 0000000000..a417699fe0 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/SimpleValueTest.php @@ -0,0 +1,148 @@ +assertInstanceOf( + SimpleValue::class, + SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'processValue' => 'avg', + 'period' => 12800, + ] + ) + ); + + $this->assertInstanceOf( + SimpleValue::class, + SimpleValue::fromArray( + [ + 'id' => 14, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'image', + 'value' => 3598, + 'processValue' => 'max', + 'period' => 9000, + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"period":12800,"processValue":"avg","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'processValue' => 'avg', + 'period' => 12800, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":21,"agentName":null,"height":500,"id":3,"isLinkEnabled":false,"isOnTop":false,"label":null,"labelPosition":"down","linkedLayoutAgentId":15,"linkedLayoutId":3,"linkedLayoutStatusType":"default","metaconsoleId":2,"moduleId":385,"moduleName":"module_test","parentId":null,"processValue":"none","type":2,"value":57,"valueType":"string","width":600,"x":0,"y":0}', + (string) SimpleValue::fromArray( + [ + 'id' => 3, + 'type' => SIMPLE_VALUE, + 'width' => '600', + 'height' => '500', + 'valueType' => 'string', + 'value' => 57, + 'id_metaconsole' => 2, + 'linked_layout_node_id' => 15, + 'linkedLayoutId' => 3, + 'agentId' => 21, + 'moduleId' => 385, + 'moduleName' => 'module_test', + ] + ) + ); + + } + + +} diff --git a/pandora_console/tests/Functional/Models/VisualConsole/Items/StaticGraphTest.php b/pandora_console/tests/Functional/Models/VisualConsole/Items/StaticGraphTest.php new file mode 100644 index 0000000000..9b77fc7540 --- /dev/null +++ b/pandora_console/tests/Functional/Models/VisualConsole/Items/StaticGraphTest.php @@ -0,0 +1,224 @@ +assertInstanceOf( + StaticGraph::class, + StaticGraph::fromArray( + [ + 'id' => 345, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'aaaaa', + 'showLastValueTooltip' => 'enabled', + ] + ) + ); + + $this->assertInstanceOf( + StaticGraph::class, + StaticGraph::fromArray( + [ + 'id' => 1000, + 'type' => STATIC_GRAPH, + 'width' => 100, + 'height' => 900, + 'image' => 'test.jpg', + 'show_last_value' => 2, + ] + ) + ); + } + + + /** + * Test if the model has a valid JSON representation. + * + * @return void + */ + public function testContainerIsRepresentedAsJson(): void + { + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":null,"type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"disabled","statusImageSrc":null,"type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'image' => 'image.jpg', + 'showLastValueTooltip' => 'disabled', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":"image.bad.jpg","type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'imageSrc' => 'image.jpg', + 'id_metaconsole' => 5, + 'linked_layout_node_id' => 3, + 'linkedLayoutId' => 2, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":null,"type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'image' => 'image.jpg', + 'id_layout_linked' => 1, + ] + ) + ); + + $this->assertEquals( + '{"aclGroupId":null,"agentId":null,"agentName":null,"height":0,"id":7,"imageSrc":"image.jpg","isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"moduleId":null,"moduleName":null,"parentId":null,"showLastValueTooltip":"default","statusImageSrc":"image.bad.jpg","type":0,"width":0,"x":-666,"y":76}', + (string) StaticGraph::fromArray( + [ + 'id' => 7, + 'type' => STATIC_GRAPH, + 'label' => null, + 'labelPosition' => 'up', + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '0', + 'height' => '0', + 'x' => -666, + 'y' => 76, + 'image' => 'image.jpg', + 'linkedLayoutId' => 2, + 'linked_layout_status_type' => 'service', + 'linkedLayoutStatusTypeWarningThreshold' => 50, + 'linked_layout_status_as_service_critical' => 80, + 'statusImageSrc' => 'image.bad.jpg', + ] + ) + ); + } + + + /** + * Test if the instance is not created when using a invalid image src. + * + * @return void + */ + public function testCannotBeCreatedWithInvalidImageSrc(): void + { + $this->expectException(InvalidArgumentException::class); + // Invalid imageSrc. + StaticGraph::fromArray( + [ + 'id' => 3, + 'type' => STATIC_GRAPH, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + 'imageSrc' => 45, + 'showLastValueTooltip' => 'disabled', + ] + ); + + // Missing imageSrc. + StaticGraph::fromArray( + [ + 'id' => 3, + 'type' => STATIC_GRAPH, + 'label' => null, + 'isLinkEnabled' => true, + 'isOnTop' => false, + 'parentId' => null, + 'width' => '330', + 'height' => '0', + 'x' => 511, + 'y' => 76, + 'showLastValueTooltip' => 'enabled', + ] + ); + } + + +} diff --git a/pandora_console/tests/bootstrap.php b/pandora_console/tests/bootstrap.php new file mode 100644 index 0000000000..c2d6c31bf2 --- /dev/null +++ b/pandora_console/tests/bootstrap.php @@ -0,0 +1,4 @@ +apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** diff --git a/pandora_console/vendor/composer/autoload_psr4.php b/pandora_console/vendor/composer/autoload_psr4.php index cd3de7c147..57404185e1 100644 --- a/pandora_console/vendor/composer/autoload_psr4.php +++ b/pandora_console/vendor/composer/autoload_psr4.php @@ -6,8 +6,11 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Tests\\' => array($baseDir . '/tests'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Mpdf\\' => array($vendorDir . '/mpdf/mpdf/src'), + 'Models\\' => array($baseDir . '/include/rest-api/models'), + 'Enterprise\\Models\\' => array($baseDir . '/enterprise/include/rest-api/models'), 'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/EmailValidator'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), ); diff --git a/pandora_console/vendor/composer/autoload_static.php b/pandora_console/vendor/composer/autoload_static.php index 2e8ac5a72e..f6d8bc0456 100644 --- a/pandora_console/vendor/composer/autoload_static.php +++ b/pandora_console/vendor/composer/autoload_static.php @@ -12,6 +12,10 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa ); public static $prefixLengthsPsr4 = array ( + 'T' => + array ( + 'Tests\\' => 6, + ), 'P' => array ( 'Psr\\Log\\' => 8, @@ -19,9 +23,11 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa 'M' => array ( 'Mpdf\\' => 5, + 'Models\\' => 7, ), 'E' => array ( + 'Enterprise\\Models\\' => 18, 'Egulias\\EmailValidator\\' => 23, ), 'D' => @@ -31,6 +37,10 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa ); public static $prefixDirsPsr4 = array ( + 'Tests\\' => + array ( + 0 => __DIR__ . '/../..' . '/tests', + ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', @@ -39,6 +49,14 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa array ( 0 => __DIR__ . '/..' . '/mpdf/mpdf/src', ), + 'Models\\' => + array ( + 0 => __DIR__ . '/../..' . '/include/rest-api/models', + ), + 'Enterprise\\Models\\' => + array ( + 0 => __DIR__ . '/../..' . '/enterprise/include/rest-api/models', + ), 'Egulias\\EmailValidator\\' => array ( 0 => __DIR__ . '/..' . '/egulias/email-validator/EmailValidator', diff --git a/pandora_server/DEBIAN/control b/pandora_server/DEBIAN/control index b68d27b784..606133e117 100644 --- a/pandora_server/DEBIAN/control +++ b/pandora_server/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-server -Version: 7.0NG.732-190319 +Version: 7.0NG.735-190610 Architecture: all Priority: optional Section: admin diff --git a/pandora_server/DEBIAN/make_deb_package.sh b/pandora_server/DEBIAN/make_deb_package.sh index fbd2878a10..0e846535b5 100644 --- a/pandora_server/DEBIAN/make_deb_package.sh +++ b/pandora_server/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.732-190319" +pandora_version="7.0NG.735-190610" package_cpan=0 package_pandora=1 diff --git a/pandora_server/bin/PandoraFMS b/pandora_server/bin/PandoraFMS deleted file mode 120000 index 404b77b915..0000000000 --- a/pandora_server/bin/PandoraFMS +++ /dev/null @@ -1 +0,0 @@ -../lib/PandoraFMS \ No newline at end of file diff --git a/pandora_server/bin/PandoraFMS b/pandora_server/bin/PandoraFMS new file mode 100644 index 0000000000..404b77b915 --- /dev/null +++ b/pandora_server/bin/PandoraFMS @@ -0,0 +1 @@ +../lib/PandoraFMS \ No newline at end of file diff --git a/pandora_server/conf/pandora_server.conf.new b/pandora_server/conf/pandora_server.conf.new index 933613ef82..88868034ce 100644 --- a/pandora_server/conf/pandora_server.conf.new +++ b/pandora_server/conf/pandora_server.conf.new @@ -1,7 +1,7 @@ ############################################################################# # Pandora FMS Server Parameters # Pandora FMS, the Flexible Monitoring System. -# Version 7.0NG.732 +# Version 7.0NG.735 # Licensed under GPL license v2, # (c) 2003-2017 Artica Soluciones Tecnologicas # http://www.pandorafms.com diff --git a/pandora_server/lib/PandoraFMS/Config.pm b/pandora_server/lib/PandoraFMS/Config.pm index 2769703368..5a78f9b58e 100644 --- a/pandora_server/lib/PandoraFMS/Config.pm +++ b/pandora_server/lib/PandoraFMS/Config.pm @@ -26,7 +26,7 @@ use lib '/usr/lib/perl5'; use PandoraFMS::Tools; use PandoraFMS::DB; -use PandoraFMS::Core; + require Exporter; our @ISA = ("Exporter"); @@ -44,8 +44,8 @@ our @EXPORT = qw( ); # version: Defines actual version of Pandora Server for this module only -my $pandora_version = "7.0NG.732"; -my $pandora_build = "190319"; +my $pandora_version = "7.0NG.735"; +my $pandora_build = "190610"; our $VERSION = $pandora_version." ".$pandora_build; # Setup hash @@ -175,6 +175,7 @@ sub pandora_get_sharedconfig ($$) { $pa_config->{"provisioning_mode"} = pandora_get_tconfig_token ($dbh, 'provisioning_mode', ''); + $pa_config->{"event_storm_protection"} = pandora_get_tconfig_token ($dbh, 'event_storm_protection', 0); if ($pa_config->{'include_agents'} eq '') { $pa_config->{'include_agents'} = 0; @@ -226,7 +227,7 @@ sub pandora_load_config { $pa_config->{"dataserver"} = 1; # default $pa_config->{"networkserver"} = 1; # default $pa_config->{"snmpconsole"} = 1; # default - $pa_config->{"discoveryserver"} = 1; # default + $pa_config->{"discoveryserver"} = 0; # default $pa_config->{"wmiserver"} = 1; # default $pa_config->{"pluginserver"} = 1; # default $pa_config->{"predictionserver"} = 1; # default @@ -1132,6 +1133,18 @@ sub pandora_load_config { elsif ($parametro =~ m/^fsnmp\s(.*)/i) { $pa_config->{'fsnmp'}= clean_blank($1); } + + # Pandora HA extra + elsif ($parametro =~ m/^ha_file\s(.*)/i) { + $pa_config->{'ha_file'} = clean_blank($1); + } + elsif ($parametro =~ m/^ha_pid_file\s(.*)/i) { + $pa_config->{'ha_pid_file'} = clean_blank($1); + } + elsif ($parametro =~ m/^pandora_service_cmd\s(.*)/i) { + $pa_config->{'pandora_service_cmd'} = clean_blank($1); + } + } # end of loop for parameter # # Set to RDBMS' standard port @@ -1199,7 +1212,7 @@ sub pandora_get_tconfig_token ($$$) { my ($dbh, $token, $default_value) = @_; my $token_value = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = ?", $token); - if (defined ($token_value)) { + if (defined ($token_value) && $token_value ne '') { return safe_output ($token_value); } diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm index f45731ba13..548ddafd06 100644 --- a/pandora_server/lib/PandoraFMS/Core.pm +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -360,9 +360,12 @@ sub pandora_generate_alerts ($$$$$$$$;$$$) { my ($pa_config, $data, $status, $agent, $module, $utimestamp, $dbh, $timestamp, $extra_macros, $last_data_value, $alert_type) = @_; # No alerts when event storm protection is enabled - if ($EventStormProtection == 1) { + + if ($EventStormProtection == 1) { + return; } + # Warmup interval for alerts. if ($pa_config->{'warmup_alert_on'} == 1) { @@ -391,7 +394,11 @@ sub pandora_generate_alerts ($$$$$$$$;$$$) { } # Get enabled alerts associated with this module - my $alert_type_filter = defined ($alert_type) ? " AND type = '$alert_type'" : ''; + my $alert_type_filter = ''; + if (defined($alert_type)) { + # not_normal includes unknown! + $alert_type_filter = $alert_type eq 'unknown' ? " AND (type = 'unknown' OR type = 'not_normal')" : " AND type = '$alert_type'"; + } my @alerts = get_db_rows ($dbh, ' SELECT talert_template_modules.id as id_template_module, talert_template_modules.*, talert_templates.* @@ -566,6 +573,7 @@ sub pandora_evaluate_alert ($$$$$$$;$$$) { return $status if ($last_status != 1 && $alert->{'type'} eq 'critical'); return $status if ($last_status != 2 && $alert->{'type'} eq 'warning'); return $status if ($last_status != 3 && $alert->{'type'} eq 'unknown'); + return $status if ($last_status == 0 && $alert->{'type'} eq 'not_normal'); } # Event alert else { @@ -1605,53 +1613,116 @@ sub pandora_process_module ($$$$$$$$$;$) { $current_interval = $module->{'module_interval'}; } - #Update module status + # Update module status. my $min_ff_event = $module->{'min_ff_event'}; my $current_utimestamp = time (); my $ff_timeout = $module->{'ff_timeout'}; + # Counters. + my $ff_warning = $agent_status->{'ff_warning'}; + my $ff_critical = $agent_status->{'ff_critical'}; + my $ff_normal = $agent_status->{'ff_normal'}; + if ($module->{'each_ff'}) { $min_ff_event = $module->{'min_ff_event_normal'} if ($new_status == 0); $min_ff_event = $module->{'min_ff_event_critical'} if ($new_status == 1); $min_ff_event = $module->{'min_ff_event_warning'} if ($new_status == 2); } - + if ($last_known_status == $new_status) { # Avoid overflows - $status_changes = $min_ff_event if ($status_changes > $min_ff_event); + $status_changes = $min_ff_event if ($status_changes > $min_ff_event && $module->{'ff_type'} == 0); $status_changes++; if ($module_type =~ m/async/ && $min_ff_event != 0 && $ff_timeout != 0 && ($utimestamp - $ff_start_utimestamp) > $ff_timeout) { - $status_changes = 0; + # Only type ff with counters. + $status_changes = 0 if ($module->{'ff_type'} == 0); + $ff_start_utimestamp = $utimestamp; + + # Reset counters because expired timeout. + $ff_normal = 0; + $ff_critical = 0; + $ff_warning = 0; } } else { - $status_changes = 0; + # Only type ff with counters. + $status_changes = 0 if ($module->{'ff_type'} == 0); + $ff_start_utimestamp = $utimestamp if ($module_type =~ m/async/); } - # Active ff interval - if ($module->{'module_ff_interval'} != 0 && $status_changes < $min_ff_event) { - $current_interval = $module->{'module_ff_interval'}; - } - - # Change status - if ($status_changes >= $min_ff_event && $known_status != $new_status) { - generate_status_event ($pa_config, $processed_data, $agent, $module, $new_status, $status, $known_status, $dbh); - $status = $new_status; + if ($module->{'ff_type'} == 0) { + # Active ff interval. + if ($module->{'module_ff_interval'} != 0 && $status_changes < $min_ff_event) { + $current_interval = $module->{'module_ff_interval'}; + } + + # Change status. + if ($status_changes >= $min_ff_event && $known_status != $new_status) { + generate_status_event ($pa_config, $processed_data, $agent, $module, $new_status, $status, $known_status, $dbh); + $status = $new_status; - # Update module status count. - $mark_for_update = 1; + # Update module status count. + $mark_for_update = 1; - # Safe mode execution. - if ($agent->{'safe_mode_module'} == $module->{'id_agente_modulo'}) { - safe_mode($pa_config, $agent, $module, $new_status, $known_status, $dbh); + # Safe mode execution. + if ($agent->{'safe_mode_module'} == $module->{'id_agente_modulo'}) { + safe_mode($pa_config, $agent, $module, $new_status, $known_status, $dbh); + } + } + } else { + if ($status == $new_status) { + # If the status is equal to the previous status reset the counters. + $ff_normal = 0; + $ff_critical = 0; + $ff_warning = 0; + } else { + # Sequential critical and normal status are needed + # if don't, reset counters. + if ($last_known_status == 1 && $new_status != 1) { + $ff_critical = 0; + } elsif ($last_known_status == 0 && $new_status != 0) { + $ff_normal = 0; + } + + # Increase counters. + $ff_critical++ if ($new_status == 1); + $ff_warning++ if ($new_status == 2); + $ff_normal++ if ($new_status == 0); + } + + if ( ($new_status == 0 && $ff_normal >= $min_ff_event) + || ($new_status == 1 && $ff_critical >= $min_ff_event) + || ($new_status == 2 && $ff_warning >= $min_ff_event)) { + # Change status generate event. + generate_status_event ($pa_config, $processed_data, $agent, $module, $new_status, $status, $known_status, $dbh); + $status = $new_status; + + # Update module status count. + $mark_for_update = 1; + + # Safe mode execution. + if ($agent->{'safe_mode_module'} == $module->{'id_agente_modulo'}) { + safe_mode($pa_config, $agent, $module, $new_status, $known_status, $dbh); + } + + # Reset counters because change status. + $ff_normal = 0; + $ff_critical = 0; + $ff_warning = 0; + } else { + # Active ff interval + if ($module->{'module_ff_interval'} != 0) { + $current_interval = $module->{'module_ff_interval'}; + } } } + # Set not-init modules to normal status even if min_ff_event is not matched the first time they receive data. # if critical or warning status, just pass through here and wait the time min_ff_event will be matched. - elsif ($status == 4) { + if ($status == 4) { generate_status_event ($pa_config, $processed_data, $agent, $module, 0, $status, $known_status, $dbh); $status = 0; @@ -1663,6 +1734,11 @@ sub pandora_process_module ($$$$$$$$$;$) { generate_status_event ($pa_config, $processed_data, $agent, $module, $known_status, $status, $known_status, $dbh); $status = $known_status; + # reset counters because change status. + $ff_normal = 0; + $ff_critical = 0; + $ff_warning = 0; + # Update module status count. $mark_for_update = 1; } @@ -1692,10 +1768,11 @@ sub pandora_process_module ($$$$$$$$$;$) { status_changes = ?, utimestamp = ?, timestamp = ?, id_agente = ?, current_interval = ?, running_by = ?, last_execution_try = ?, last_try = ?, last_error = ?, - ff_start_utimestamp = ? + ff_start_utimestamp = ?, ff_normal = ?, ff_warning = ?, ff_critical = ? WHERE id_agente_modulo = ?', $processed_data, $status, $status, $new_status, $new_status, $status_changes, $current_utimestamp, $timestamp, $module->{'id_agente'}, $current_interval, $server_id, - $utimestamp, ($save == 1) ? $timestamp : $agent_status->{'last_try'}, $last_error, $ff_start_utimestamp, $module->{'id_agente_modulo'}); + $utimestamp, ($save == 1) ? $timestamp : $agent_status->{'last_try'}, $last_error, $ff_start_utimestamp, + $ff_normal, $ff_warning, $ff_critical, $module->{'id_agente_modulo'}); } # Save module data. Async and log4x modules are not compressed. @@ -3079,12 +3156,14 @@ Create a new entry in B optionaly with position information =cut ########################################################################## -sub pandora_create_agent ($$$$$$$$$$;$$$$$$$$$) { +sub pandora_create_agent ($$$$$$$$$$;$$$$$$$$$$) { + # If parameter event_id is not undef, then create an extended event + # related to it instead launch new event. my ($pa_config, $server_name, $agent_name, $address, $group_id, $parent_id, $os_id, $description, $interval, $dbh, $timezone_offset, $longitude, $latitude, $altitude, $position_description, - $custom_id, $url_address, $agent_mode, $alias) = @_; + $custom_id, $url_address, $agent_mode, $alias, $event_id) = @_; logger ($pa_config, "Server '$server_name' creating agent '$agent_name' address '$address'.", 10); @@ -3127,7 +3206,11 @@ sub pandora_create_agent ($$$$$$$$$$;$$$$$$$$$) { } logger ($pa_config, "Server '$server_name' CREATED agent '$agent_name' address '$address'.", 10); - pandora_event ($pa_config, "Agent [" . safe_output($alias) . "] created by $server_name", $group_id, $agent_id, 2, 0, 0, 'new_agent', 0, $dbh); + if (!defined($event_id)) { + pandora_event ($pa_config, "Agent [" . safe_output($alias) . "] created by $server_name", $group_id, $agent_id, 2, 0, 0, 'new_agent', 0, $dbh); + } else { + pandora_extended_event($pa_config, $dbh, $event_id, "Agent [" . safe_output($alias) . "][#".$agent_id."] created by $server_name"); + } return $agent_id; } @@ -3813,7 +3896,10 @@ sub subst_alert_macros ($$;$$$$) { ########################################################################## sub subst_column_macros ($$;$$$$) { my ($string, $macros, $pa_config, $dbh, $agent, $module) = @_; - + + # Avoid to manipulate null strings + return $string unless defined($string); + # Do not attempt to substitute macros unless the string # begins with an underscore. return $string unless substr($string, 0, 1) eq '_'; @@ -4160,7 +4246,7 @@ sub get_module_status ($$$) { $critical_str = (defined ($critical_str) && valid_regex ($critical_str) == 1) ? safe_output($critical_str) : ''; $warning_str = (defined ($warning_str) && valid_regex ($warning_str) == 1) ? safe_output($warning_str) : ''; - if ($module_type =~ m/_proc$/ && ($critical_min eq $critical_max)) { + if (($module_type =~ m/_proc$/ || $module_type =~ /web_analysis/) && ($critical_min eq $critical_max)) { ($critical_min, $critical_max) = (0, 1); } elsif ($module_type =~ m/keep_alive/ && ($critical_min eq $critical_max)) { @@ -4773,32 +4859,36 @@ sub pandora_process_policy_queue ($) { logger($pa_config, "Starting policy queue patrol process.", 1); while($THRRUN == 1) { + eval {{ + local $SIG{__DIE__}; - # If we are not the master server sleep and check again. - if (pandora_is_master($pa_config) == 0) { - sleep ($pa_config->{'server_threshold'}); - next; - } + # If we are not the master server sleep and check again. + if (pandora_is_master($pa_config) == 0) { + sleep ($pa_config->{'server_threshold'}); + next; + } + + my $operation = enterprise_hook('get_first_policy_queue', [$dbh]); + next unless (defined ($operation) && $operation ne ''); + + if($operation->{'operation'} eq 'apply' || $operation->{'operation'} eq 'apply_db') { + enterprise_hook('pandora_apply_policy', [$dbh, $pa_config, $operation->{'id_policy'}, $operation->{'id_agent'}, $operation->{'id'}, $operation->{'operation'}]); + } + elsif($operation->{'operation'} eq 'delete') { + if($operation->{'id_agent'} == 0) { + enterprise_hook('pandora_purge_policy_agents', [$dbh, $pa_config, $operation->{'id_policy'}]); + } + else { + enterprise_hook('pandora_delete_agent_from_policy', [$dbh, $pa_config, $operation->{'id_policy'}, $operation->{'id_agent'}]); + } + } + + enterprise_hook('pandora_finish_queue_operation', [$dbh, $operation->{'id'}]); + }}; # Check the queue each 5 seconds - sleep (5); + sleep(5); - my $operation = enterprise_hook('get_first_policy_queue', [$dbh]); - next unless (defined ($operation) && $operation ne ''); - - if($operation->{'operation'} eq 'apply' || $operation->{'operation'} eq 'apply_db') { - enterprise_hook('pandora_apply_policy', [$dbh, $pa_config, $operation->{'id_policy'}, $operation->{'id_agent'}, $operation->{'id'}, $operation->{'operation'}]); - } - elsif($operation->{'operation'} eq 'delete') { - if($operation->{'id_agent'} == 0) { - enterprise_hook('pandora_purge_policy_agents', [$dbh, $pa_config, $operation->{'id_policy'}]); - } - else { - enterprise_hook('pandora_delete_agent_from_policy', [$dbh, $pa_config, $operation->{'id_policy'}, $operation->{'id_agent'}]); - } - } - - enterprise_hook('pandora_finish_queue_operation', [$dbh, $operation->{'id'}]); } db_disconnect($dbh); diff --git a/pandora_server/lib/PandoraFMS/DataServer.pm b/pandora_server/lib/PandoraFMS/DataServer.pm index 43092c0f40..8e08a7d482 100644 --- a/pandora_server/lib/PandoraFMS/DataServer.pm +++ b/pandora_server/lib/PandoraFMS/DataServer.pm @@ -29,6 +29,8 @@ use XML::Parser::Expat; use XML::Simple; use POSIX qw(setsid strftime); use IO::Uncompress::Unzip; +use JSON qw(decode_json); +use MIME::Base64; # Required for file names with accents use Encode qw(decode); @@ -222,6 +224,10 @@ sub data_consumer ($$) { process_xml_server ($self->getConfig (), $file_name, $xml_data, $self->getDBH ()); } elsif (defined($xml_data->{'connection_source'})) { enterprise_hook('process_xml_connections', [$self->getConfig (), $file_name, $xml_data, $self->getDBH ()]); + } elsif (defined($xml_data->{'network_matrix'})){ + process_xml_matrix_network( + $self->getConfig(), $xml_data, $self->getDBH() + ); } else { process_xml_data ($self->getConfig (), $file_name, $xml_data, $self->getServerID (), $self->getDBH ()); } @@ -326,6 +332,7 @@ sub process_xml_data ($$$$$) { # Get agent id my $agent_id = get_agent_id ($dbh, $agent_name); + my $group_id = 0; if ($agent_id < 1) { if ($pa_config->{'autocreate'} == 0) { logger($pa_config, "ERROR: There is no agent defined with name $agent_name", 3); @@ -334,7 +341,7 @@ sub process_xml_data ($$$$$) { # Get OS, group and description my $os = pandora_get_os ($dbh, $data->{'os_name'}); - my $group_id = $pa_config->{'autocreate_group'}; + $group_id = $pa_config->{'autocreate_group'}; if (! defined (get_group_name ($dbh, $group_id))) { if (defined ($data->{'group_id'}) && $data->{'group_id'} ne '') { $group_id = $data->{'group_id'}; @@ -600,6 +607,9 @@ sub process_xml_data ($$$$$) { # Process snmptrapd modules enterprise_hook('process_snmptrap_data', [$pa_config, $data, $server_id, $dbh]); + # Process events + process_events_dataserver($pa_config, $data, $agent_id, $group_id, $dbh); + # Process disovery modules enterprise_hook('process_discovery_data', [$pa_config, $data, $server_id, $dbh]); } @@ -631,7 +641,7 @@ sub process_module_data ($$$$$$$$$$) { 'unknown_instructions' => '', 'tags' => '', 'critical_inverse' => 0, 'warning_inverse' => 0, 'quiet' => 0, 'module_ff_interval' => 0, 'alert_template' => '', 'crontab' => '', 'min_ff_event_normal' => 0, 'min_ff_event_warning' => 0, 'min_ff_event_critical' => 0, 'ff_timeout' => 0, 'each_ff' => 0, 'module_parent' => 0, - 'module_parent_unlink' => 0, 'cron_interval' => 0}; + 'module_parent_unlink' => 0, 'cron_interval' => 0, 'ff_type' => 0}; # Other tags will be saved here $module_conf->{'extended_info'} = ''; @@ -970,5 +980,84 @@ sub unlink_modules { db_do($dbh, "UPDATE tagente_modulo SET parent_module_id = 0 WHERE id_agente_modulo = ?", $child_id); } +########################################################################## +# Process events in the XML. +########################################################################## +sub process_events_dataserver { + my ($pa_config, $data, $agent_id, $group_id, $dbh) = @_; + + return unless defined($data->{'events'}->[0]->{'event'}); + + foreach my $event (@{$data->{'events'}->[0]->{'event'}}) { + next unless defined($event); + + # Try to decode the base64 inside + my $event_info; + eval { + $event_info = decode_json(decode_base64($event)); + }; + + if ($@) { + logger($pa_config, "Error processing base64 event data '$event'.", 5); + next; + } + next unless defined($event_info->{'data'}); + + pandora_event( + $pa_config, + $event_info->{'data'}, + $group_id, + $agent_id, + defined($event_info->{'severity'}) ? $event_info->{'severity'} : 0, + 0, + 0, + 'system', + 0, + $dbh + ); + } + + return; +} + + +########################################################################## +# Process events in the XML. +########################################################################## +sub process_xml_matrix_network { + my ($pa_config, $data, $dbh) = @_; + + my $utimestamp = $data->{'network_matrix'}->[0]->{'utimestamp'}; + my $content = $data->{'network_matrix'}->[0]->{'content'}; + return unless defined($utimestamp) && defined($content); + + # Try to decode the base64 inside + my $matrix_info; + eval { + $matrix_info = decode_json(decode_base64($content)); + }; + + if ($@) { + logger($pa_config, "Error processing base64 matrix data '$content'.", 5); + return; + } + foreach my $source (keys %$matrix_info) { + foreach my $destination (keys %{$matrix_info->{$source}}) { + my $matrix_single_data = $matrix_info->{$source}->{$destination}; + $matrix_single_data->{'source'} = $source; + $matrix_single_data->{'destination'} = $destination; + $matrix_single_data->{'utimestamp'} = $utimestamp; + eval { + db_process_insert($dbh, 'id', 'tnetwork_matrix', $matrix_single_data); + }; + if ($@) { + logger($pa_config, "Error inserted matrix data. Source: $source, destination: $destination, utimestamp: $utimestamp.", 5); + } + } + } + + return; +} + 1; __END__ diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index bc4e1caa73..e15511de44 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -53,787 +53,979 @@ my $Sem :shared; my $TaskSem :shared; # IDs from tconfig_os. -use constant OS_OTHER => 10; -use constant OS_ROUTER => 17; -use constant OS_SWITCH => 18; +use constant { + OS_OTHER => 10, + OS_ROUTER => 17, + OS_SWITCH => 18, + DISCOVERY_HOSTDEVICES => 0, + DISCOVERY_HOSTDEVICES_CUSTOM => 1, + DISCOVERY_CLOUD_AWS => 2, + DISCOVERY_APP_VMWARE => 3, + DISCOVERY_APP_MYSQL => 4, + DISCOVERY_APP_ORACLE => 5, + DISCOVERY_CLOUD_AWS_EC2 => 6, + DISCOVERY_CLOUD_AWS_RDS => 7 +}; ######################################################################################## # Discovery Server class constructor. ######################################################################################## sub new ($$$$$$) { - my ($class, $config, $dbh) = @_; - - return undef unless $config->{'reconserver'} == 1 || $config->{'discoveryserver'} == 1; - - if (! -e $config->{'nmap'}) { - logger ($config, ' [E] ' . $config->{'nmap'} . " needed by " . $config->{'rb_product_name'} . " Discovery Server not found.", 1); - print_message ($config, ' [E] ' . $config->{'nmap'} . " needed by " . $config->{'rb_product_name'} . " Discovery Server not found.", 1); - return undef; - } + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'reconserver'} == 1 || $config->{'discoveryserver'} == 1; + + if (! -e $config->{'nmap'}) { + logger ($config, ' [E] ' . $config->{'nmap'} . " needed by " . $config->{'rb_product_name'} . " Discovery Server not found.", 1); + print_message ($config, ' [E] ' . $config->{'nmap'} . " needed by " . $config->{'rb_product_name'} . " Discovery Server not found.", 1); + return undef; + } - # Initialize semaphores and queues - @TaskQueue = (); - %PendingTasks = (); - $Sem = Thread::Semaphore->new; - $TaskSem = Thread::Semaphore->new (0); - - # Restart automatic recon tasks. - db_do ($dbh, 'UPDATE trecon_task SET utimestamp = 0 WHERE id_recon_server = ? AND status <> -1 AND interval_sweep > 0', - get_server_id ($dbh, $config->{'servername'}, DISCOVERYSERVER)); + # Initialize semaphores and queues + @TaskQueue = (); + %PendingTasks = (); + $Sem = Thread::Semaphore->new; + $TaskSem = Thread::Semaphore->new (0); + + # Restart automatic recon tasks. + db_do ($dbh, 'UPDATE trecon_task SET utimestamp = 0 WHERE id_recon_server = ? AND status <> -1 AND interval_sweep > 0', + get_server_id ($dbh, $config->{'servername'}, DISCOVERYSERVER)); - # Reset (but do not restart) manual recon tasks. - db_do ($dbh, 'UPDATE trecon_task SET status = -1 WHERE id_recon_server = ? AND status <> -1 AND interval_sweep = 0', - get_server_id ($dbh, $config->{'servername'}, DISCOVERYSERVER)); + # Reset (but do not restart) manual recon tasks. + db_do ($dbh, 'UPDATE trecon_task SET status = -1 WHERE id_recon_server = ? AND status <> -1 AND interval_sweep = 0', + get_server_id ($dbh, $config->{'servername'}, DISCOVERYSERVER)); - # Call the constructor of the parent class - my $self = $class->SUPER::new($config, DISCOVERYSERVER, \&PandoraFMS::DiscoveryServer::data_producer, \&PandoraFMS::DiscoveryServer::data_consumer, $dbh); - - bless $self, $class; - return $self; + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, DISCOVERYSERVER, \&PandoraFMS::DiscoveryServer::data_producer, \&PandoraFMS::DiscoveryServer::data_consumer, $dbh); + + bless $self, $class; + return $self; } ############################################################################### # Run. ############################################################################### sub run ($) { - my $self = shift; - my $pa_config = $self->getConfig (); - - print_message ($pa_config, " [*] Starting " . $pa_config->{'rb_product_name'} . " Discovery Server.", 1); - my $threads = $pa_config->{'recon_threads'}; + my $self = shift; + my $pa_config = $self->getConfig (); + my $dbh = $self->getDBH(); + + print_message ($pa_config, " [*] Starting " . $pa_config->{'rb_product_name'} . " Discovery Server.", 1); + my $threads = $pa_config->{'recon_threads'}; - # Use hightest value - if ($pa_config->{'discovery_threads'} > $pa_config->{'recon_threads'}) { - $threads = $pa_config->{'discovery_threads'}; - } - $self->setNumThreads($threads); - $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); + # Prepare some environmental variables. + $ENV{'AWS_ACCESS_KEY_ID'} = pandora_get_config_value($dbh, 'aws_access_key_id'); + $ENV{'AWS_SECRET_ACCESS_KEY'} = pandora_get_config_value($dbh, 'aws_secret_access_key'); + + # Use hightest value + if ($pa_config->{'discovery_threads'} > $pa_config->{'recon_threads'}) { + $threads = $pa_config->{'discovery_threads'}; + } + $self->setNumThreads($threads); + $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); } ############################################################################### # Data producer. ############################################################################### sub data_producer ($) { - my $self = shift; - my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); - - my @tasks; - - my $server_id = get_server_id ($dbh, $pa_config->{'servername'}, $self->getServerType ()); - return @tasks unless defined ($server_id); - - # Manual tasks have interval_sweep = 0 - # Manual tasks are "forced" like the other, setting the utimestamp to 1 - # By default, after create a tasks it takes the utimestamp to 0 - # Status -1 means "done". - - my @rows = get_db_rows ($dbh, 'SELECT * FROM trecon_task - WHERE id_recon_server = ? - AND disabled = 0 - AND ((utimestamp = 0 AND interval_sweep != 0 OR status = 1) - OR (status = -1 AND interval_sweep > 0 AND (utimestamp + interval_sweep) < UNIX_TIMESTAMP()))', $server_id); - foreach my $row (@rows) { - - # Update task status - update_recon_task ($dbh, $row->{'id_rt'}, 1); - - push (@tasks, $row->{'id_rt'}); - } - - return @tasks; + my $self = shift; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my @tasks; + + my $server_id = get_server_id ($dbh, $pa_config->{'servername'}, $self->getServerType ()); + return @tasks unless defined ($server_id); + + # Manual tasks have interval_sweep = 0 + # Manual tasks are "forced" like the other, setting the utimestamp to 1 + # By default, after create a tasks it takes the utimestamp to 0 + # Status -1 means "done". + + my @rows = get_db_rows ($dbh, 'SELECT * FROM trecon_task + WHERE id_recon_server = ? + AND disabled = 0 + AND ((utimestamp = 0 AND interval_sweep != 0 OR status = 1) + OR (status = -1 AND interval_sweep > 0 AND (utimestamp + interval_sweep) < UNIX_TIMESTAMP()))', $server_id); + foreach my $row (@rows) { + + # Update task status + update_recon_task ($dbh, $row->{'id_rt'}, 1); + + push (@tasks, $row->{'id_rt'}); + } + + return @tasks; } ############################################################################### # Data consumer. ############################################################################### sub data_consumer ($$) { - my ($self, $task_id) = @_; - my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + my ($self, $task_id) = @_; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); - # Get recon task data - my $task = get_db_single_row ($dbh, 'SELECT * FROM trecon_task WHERE id_rt = ?', $task_id); - return -1 unless defined ($task); + # Get server id. + my $server_id = get_server_id($dbh, $pa_config->{'servername'}, $self->getServerType()); - # Is it a recon script? - if (defined ($task->{'id_recon_script'}) && ($task->{'id_recon_script'} != 0)) { - exec_recon_script ($pa_config, $dbh, $task); - return; - } else { - logger($pa_config, 'Starting recon task for net ' . $task->{'subnet'} . '.', 10); - } + # Get recon task data + my $task = get_db_single_row ($dbh, 'SELECT * FROM trecon_task WHERE id_rt = ?', $task_id); + return -1 unless defined ($task); - eval { - my @subnets = split(/,/, safe_output($task->{'subnet'})); - my @communities = split(/,/, safe_output($task->{'snmp_community'})); - my @auth_strings = (); - if(defined($task->{'auth_strings'})) { - @auth_strings = split(/,/, safe_output($task->{'auth_strings'})); + # Is it a recon script? + if (defined ($task->{'id_recon_script'}) && ($task->{'id_recon_script'} != 0)) { + exec_recon_script ($pa_config, $dbh, $task); + return; + } else { + logger($pa_config, 'Starting recon task for net ' . $task->{'subnet'} . '.', 10); + } + + eval { + my @subnets = split(/,/, safe_output($task->{'subnet'})); + my @communities = split(/,/, safe_output($task->{'snmp_community'})); + my @auth_strings = (); + if(defined($task->{'auth_strings'})) { + @auth_strings = split(/,/, safe_output($task->{'auth_strings'})); + } + + my $main_event = pandora_event($pa_config, "[Discovery] Execution summary",$task->{'id_group'}, 0, 0, 0, 0, 'system', 0, $dbh); + + my %cnf_extra; + if ($task->{'type'} == DISCOVERY_CLOUD_AWS_EC2 + || $task->{'type'} == DISCOVERY_CLOUD_AWS_RDS) { + $cnf_extra{'aws_access_key_id'} = pandora_get_config_value($dbh, 'aws_access_key_id'); + $cnf_extra{'aws_secret_access_key'} = pandora_get_config_value($dbh, 'aws_secret_access_key'); + $cnf_extra{'cloud_util_path'} = pandora_get_config_value($dbh, 'cloud_util_path'); + + if (!defined($ENV{'AWS_ACCESS_KEY_ID'}) || !defined($ENV{'AWS_SECRET_ACCESS_KEY'}) + || $cnf_extra{'aws_secret_access_key'} ne $ENV{'AWS_ACCESS_KEY_ID'} + || $cnf_extra{'cloud_util_path'} ne $ENV{'AWS_SECRET_ACCESS_KEY'}) { + # Environmental data is out of date. Create a tmp file to manage + # credentials. Perl limitation. We cannot update ENV here. + $cnf_extra{'creds_file'} = $pa_config->{'temporal'} . '/tmp_discovery.' . md5($task->{'id_rt'} . $task->{'name'} . time()); + eval { + open(my $__file_cfg, '> '. $cnf_extra{'creds_file'}) or die($!); + print $__file_cfg $cnf_extra{'aws_access_key_id'} . "\n"; + print $__file_cfg $cnf_extra{'aws_secret_access_key'} . "\n"; + close($__file_cfg); + set_file_permissions( + $pa_config, + $cnf_extra{'creds_file'}, + "0600" + ); + }; + if ($@) { + logger( + $pa_config, + 'Cannot instantiate configuration file for task: ' . safe_output($task->{'name'}), + 5 + ); + # A server restart will override ENV definition (see run) + logger( + $pa_config, + 'Cannot execute Discovery task: ' . safe_output($task->{'name'}) . '. Please restart the server.', + 1 + ); + # Skip this task. + return; + } + } + } + + my $recon = new PandoraFMS::Recon::Base( + communities => \@communities, + dbh => $dbh, + group_id => $task->{'id_group'}, + id_os => $task->{'id_os'}, + id_network_profile => $task->{'id_network_profile'}, + os_detection => $task->{'os_detect'}, + parent_detection => $task->{'parent_detection'}, + parent_recursion => $task->{'parent_recursion'}, + pa_config => $pa_config, + recon_ports => $task->{'recon_ports'}, + resolve_names => $task->{'resolve_names'}, + snmp_auth_user => $task->{'snmp_auth_user'}, + snmp_auth_pass => $task->{'snmp_auth_pass'}, + snmp_auth_method => $task->{'snmp_auth_method'}, + snmp_checks => $task->{'snmp_checks'}, + snmp_enabled => $task->{'snmp_enabled'}, + snmp_privacy_method => $task->{'snmp_privacy_method'}, + snmp_privacy_pass => $task->{'snmp_privacy_pass'}, + snmp_security_level => $task->{'snmp_security_level'}, + snmp_timeout => $task->{'snmp_timeout'}, + snmp_version => $task->{'snmp_version'}, + subnets => \@subnets, + task_id => $task->{'id_rt'}, + vlan_cache_enabled => $task->{'vlan_enabled'}, + wmi_enabled => $task->{'wmi_enabled'}, + auth_strings_array => \@auth_strings, + autoconfiguration_enabled => $task->{'autoconfiguration_enabled'}, + main_event_id => $main_event, + server_id => $server_id, + %{$pa_config}, + task_data => $task, + %cnf_extra + ); + + $recon->scan(); + + # Clean tmp file. + if (defined($cnf_extra{'creds_file'}) + && -f $cnf_extra{'creds_file'}) { + unlink($cnf_extra{'creds_file'}); } - - my $main_event = pandora_event($pa_config, "[Discovery] Execution summary",$task->{'id_group'}, 0, 0, 0, 0, 'system', 0, $dbh); - - my $recon = new PandoraFMS::Recon::Base( - communities => \@communities, - dbh => $dbh, - group_id => $task->{'id_group'}, - id_os => $task->{'id_os'}, - id_network_profile => $task->{'id_network_profile'}, - os_detection => $task->{'os_detect'}, - parent_detection => $task->{'parent_detection'}, - parent_recursion => $task->{'parent_recursion'}, - pa_config => $pa_config, - recon_ports => $task->{'recon_ports'}, - resolve_names => $task->{'resolve_names'}, - snmp_auth_user => $task->{'snmp_auth_user'}, - snmp_auth_pass => $task->{'snmp_auth_pass'}, - snmp_auth_method => $task->{'snmp_auth_method'}, - snmp_checks => $task->{'snmp_checks'}, - snmp_enabled => $task->{'snmp_enabled'}, - snmp_privacy_method => $task->{'snmp_privacy_method'}, - snmp_privacy_pass => $task->{'snmp_privacy_pass'}, - snmp_security_level => $task->{'snmp_security_level'}, - snmp_timeout => $task->{'snmp_timeout'}, - snmp_version => $task->{'snmp_version'}, - subnets => \@subnets, - task_id => $task->{'id_rt'}, - vlan_cache_enabled => $task->{'vlan_enabled'}, - wmi_enabled => $task->{'wmi_enabled'}, - auth_strings_array => \@auth_strings, - autoconfiguration_enabled => $task->{'autoconfiguration_enabled'}, - main_event_id => $main_event, - %{$pa_config} - ); - - $recon->scan(); - }; - if ($@) { - update_recon_task ($dbh, $task_id, -1); - return; - } + }; + if ($@) { + logger( + $pa_config, + 'Cannot execute Discovery task: ' . safe_output($task->{'name'}) . $@, + 10 + ); + update_recon_task ($dbh, $task_id, -1); + return; + } } ########################################################################## # Update recon task status. ########################################################################## sub update_recon_task ($$$) { - my ($dbh, $id_task, $status) = @_; - - db_do ($dbh, 'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?', time (), $status, $id_task); + my ($dbh, $id_task, $status) = @_; + + db_do ($dbh, 'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?', time (), $status, $id_task); } ########################################################################## # Executes recon scripts ########################################################################## sub exec_recon_script ($$$) { - my ($pa_config, $dbh, $task) = @_; - - # Get recon plugin data - my $script = get_db_single_row ($dbh, 'SELECT * FROM trecon_script WHERE id_recon_script = ?', $task->{'id_recon_script'}); - return -1 unless defined ($script); - - logger($pa_config, 'Executing recon script ' . safe_output($script->{'name'}), 10); - - my $command = safe_output($script->{'script'}); - - my $macros = safe_output($task->{'macros'}); + my ($pa_config, $dbh, $task) = @_; + + # Get recon plugin data + my $script = get_db_single_row ($dbh, 'SELECT * FROM trecon_script WHERE id_recon_script = ?', $task->{'id_recon_script'}); + return -1 unless defined ($script); + + logger($pa_config, 'Executing recon script ' . safe_output($script->{'name'}), 10); + + my $command = safe_output($script->{'script'}); + + my $macros = safe_output($task->{'macros'}); - # \r and \n should be escaped for decode_json(). - $macros =~ s/\n/\\n/g; - $macros =~ s/\r/\\r/g; - my $decoded_macros; - - if ($macros) { - eval { - $decoded_macros = decode_json(encode_utf8($macros)); - }; - } - - my $macros_parameters = ''; - - # Add module macros as parameter - if(ref($decoded_macros) eq "HASH") { - # Convert the hash to a sorted array - my @sorted_macros; - while (my ($i, $m) = each (%{$decoded_macros})) { - $sorted_macros[$i] = $m; - } + # \r and \n should be escaped for decode_json(). + $macros =~ s/\n/\\n/g; + $macros =~ s/\r/\\r/g; + my $decoded_macros; + + if ($macros) { + eval { + $decoded_macros = decode_json(encode_utf8($macros)); + }; + } + + my $macros_parameters = ''; + + # Add module macros as parameter + if(ref($decoded_macros) eq "HASH") { + # Convert the hash to a sorted array + my @sorted_macros; + while (my ($i, $m) = each (%{$decoded_macros})) { + $sorted_macros[$i] = $m; + } - # Remove the 0 position - shift @sorted_macros; + # Remove the 0 position + shift @sorted_macros; - foreach my $m (@sorted_macros) { - $macros_parameters = $macros_parameters . ' "' . $m->{"value"} . '"'; - } - } + foreach my $m (@sorted_macros) { + $macros_parameters = $macros_parameters . ' "' . $m->{"value"} . '"'; + } + } - my $ent_script = 0; - my $args = enterprise_hook('discovery_custom_recon_scripts',[$pa_config, $dbh, $task, $script]); - if (!$args) { - $args = "$task->{'id_rt'} $task->{'id_group'} $task->{'create_incident'} $macros_parameters"; - } else { - $ent_script = 1; - } - - if (-x $command) { - my $exec_output = `$command $args`; - logger($pa_config, "Execution output: \n". $exec_output, 10); - } else { - logger($pa_config, "Cannot execute recon task command $command.", 10); - } - - # Only update the timestamp in case something went wrong. The script should set the status. - db_do ($dbh, 'UPDATE trecon_task SET utimestamp = ? WHERE id_rt = ?', time (), $task->{'id_rt'}); + my $ent_script = 0; + my $args = enterprise_hook('discovery_custom_recon_scripts',[$pa_config, $dbh, $task, $script]); + if (!$args) { + $args = "$task->{'id_rt'} $task->{'id_group'} $task->{'create_incident'} $macros_parameters"; + } else { + $ent_script = 1; + } + + if (-x $command) { + my $exec_output = `$command $args`; + logger($pa_config, "Execution output: \n". $exec_output, 10); + } else { + logger($pa_config, "Cannot execute recon task command $command.", 10); + } + + # Only update the timestamp in case something went wrong. The script should set the status. + db_do ($dbh, 'UPDATE trecon_task SET utimestamp = ? WHERE id_rt = ?', time (), $task->{'id_rt'}); - if ($ent_script == 1) { - enterprise_hook('discovery_clean_custom_recon',[$pa_config, $dbh, $task, $script]); - } - - logger($pa_config, 'Done executing recon script ' . safe_output($script->{'name'}), 10); - return 0; + if ($ent_script == 1) { + enterprise_hook('discovery_clean_custom_recon',[$pa_config, $dbh, $task, $script]); + } + + logger($pa_config, 'Done executing recon script ' . safe_output($script->{'name'}), 10); + return 0; } ########################################################################## # Guess the OS using xprobe2 or nmap. ########################################################################## sub PandoraFMS::Recon::Base::guess_os($$) { - my ($self, $device) = @_; + my ($self, $device) = @_; - # OS detection disabled. Use the device type. - if ($self->{'os_detection'} == 0) { - my $device_type = $self->get_device_type($device); - return OS_OTHER unless defined($device_type); + # OS detection disabled. Use the device type. + if ($self->{'os_detection'} == 0) { + my $device_type = $self->get_device_type($device); + return OS_OTHER unless defined($device_type); - return OS_ROUTER if ($device_type eq 'router'); - return OS_SWITCH if ($device_type eq 'switch'); - return OS_OTHER; - } + return OS_ROUTER if ($device_type eq 'router'); + return OS_SWITCH if ($device_type eq 'switch'); + return OS_OTHER; + } - # Use xprobe2 if available - if (-e $self->{pa_config}->{xprobe2}) { - my $output = `"$self->{pa_config}->{xprobe2}" $device 2>$DEVNULL | grep 'Running OS' | head -1`; - return OS_OTHER if ($? != 0); - return pandora_get_os($self->{'dbh'}, $output); - } - - # Use nmap by default - if (-e $self->{pa_config}->{nmap}) { - my $output = `"$self->{pa_config}->{nmap}" -F -O $device 2>$DEVNULL | grep 'Aggressive OS guesses'`; - return OS_OTHER if ($? != 0); - return pandora_get_os($self->{'dbh'}, $output); - } + # Use xprobe2 if available + if (-e $self->{pa_config}->{xprobe2}) { + my $output = `"$self->{pa_config}->{xprobe2}" $device 2>$DEVNULL | grep 'Running OS' | head -1`; + return OS_OTHER if ($? != 0); + return pandora_get_os($self->{'dbh'}, $output); + } + + # Use nmap by default + if (-e $self->{pa_config}->{nmap}) { + my $output = `"$self->{pa_config}->{nmap}" -F -O $device 2>$DEVNULL | grep 'Aggressive OS guesses'`; + return OS_OTHER if ($? != 0); + return pandora_get_os($self->{'dbh'}, $output); + } - return OS_OTHER; + return OS_OTHER; } ############################################################################## # Returns the number of open ports from the given list. ############################################################################## sub PandoraFMS::Recon::Base::tcp_scan ($$) { - my ($self, $host) = @_; + my ($self, $host) = @_; - my $open_ports = `"$self->{pa_config}->{nmap}" -p$self->{recon_ports} $host | grep open | wc -l`; - return $open_ports; + my $open_ports = `"$self->{pa_config}->{nmap}" -p$self->{recon_ports} $host | grep open | wc -l`; + return $open_ports; } ########################################################################## # Create network profile modules for the given agent. ########################################################################## sub PandoraFMS::Recon::Base::create_network_profile_modules($$$) { - my ($self, $agent_id, $device) = @_; - - return unless ($self->{'id_network_profile'} > 0); - - # Get network components associated to the network profile. - my @np_components = get_db_rows($self->{'dbh'}, 'SELECT * FROM tnetwork_profile_component WHERE id_np = ?', $self->{'id_network_profile'}); - foreach my $np_component (@np_components) { + my ($self, $agent_id, $device) = @_; + + return unless ($self->{'id_network_profile'} > 0); + + # Get network components associated to the network profile. + my @np_components = get_db_rows($self->{'dbh'}, 'SELECT * FROM tnetwork_profile_component WHERE id_np = ?', $self->{'id_network_profile'}); + foreach my $np_component (@np_components) { - # Get network component data - my $component = get_db_single_row($self->{'dbh'}, 'SELECT * FROM tnetwork_component WHERE id_nc = ?', $np_component->{'id_nc'}); - if (!defined ($component)) { - $self->call('message', "Network component ID " . $np_component->{'id_nc'} . " not found.", 5); - next; - } + # Get network component data + my $component = get_db_single_row($self->{'dbh'}, 'SELECT * FROM tnetwork_component WHERE id_nc = ?', $np_component->{'id_nc'}); + if (!defined ($component)) { + $self->call('message', "Network component ID " . $np_component->{'id_nc'} . " not found.", 5); + next; + } - # Use snmp_community from network task instead the component snmp_community - $component->{'snmp_community'} = safe_output($self->get_community($device)); - $component->{'tcp_send'} = $self->{'snmp_version'}; - $component->{'custom_string_1'} = $self->{'snmp_privacy_method'}; - $component->{'custom_string_2'} = $self->{'snmp_privacy_pass'}; - $component->{'custom_string_3'} = $self->{'snmp_security_level'}; - $component->{'plugin_parameter'} = $self->{'snmp_auth_method'}; - $component->{'plugin_user'} = $self->{'snmp_auth_user'}; - $component->{'plugin_pass'} = $self->{'snmp_auth_pass'}; + # Use snmp_community from network task instead the component snmp_community + $component->{'snmp_community'} = safe_output($self->get_community($device)); + $component->{'tcp_send'} = $self->{'snmp_version'}; + $component->{'custom_string_1'} = $self->{'snmp_privacy_method'}; + $component->{'custom_string_2'} = $self->{'snmp_privacy_pass'}; + $component->{'custom_string_3'} = $self->{'snmp_security_level'}; + $component->{'plugin_parameter'} = $self->{'snmp_auth_method'}; + $component->{'plugin_user'} = $self->{'snmp_auth_user'}; + $component->{'plugin_pass'} = $self->{'snmp_auth_pass'}; - pandora_create_module_from_network_component($self->{'pa_config'}, $component, $agent_id, $self->{'dbh'}); - } + pandora_create_module_from_network_component($self->{'pa_config'}, $component, $agent_id, $self->{'dbh'}); + } } ########################################################################## # Connect the given devices in the Pandora FMS database. ########################################################################## sub PandoraFMS::Recon::Base::connect_agents($$$$$) { - my ($self, $dev_1, $if_1, $dev_2, $if_2) = @_; + my ($self, $dev_1, $if_1, $dev_2, $if_2) = @_; - # Get the agent for the first device. - my $agent_1 = get_agent_from_addr($self->{'dbh'}, $dev_1); - if (!defined($agent_1)) { - $agent_1 = get_agent_from_name($self->{'dbh'}, $dev_1); - } - return unless defined($agent_1); + # Get the agent for the first device. + my $agent_1 = get_agent_from_addr($self->{'dbh'}, $dev_1); + if (!defined($agent_1)) { + $agent_1 = get_agent_from_name($self->{'dbh'}, $dev_1); + } + return unless defined($agent_1); - # Get the agent for the second device. - my $agent_2 = get_agent_from_addr($self->{'dbh'}, $dev_2); - if (!defined($agent_2)) { - $agent_2 = get_agent_from_name($self->{'dbh'}, $dev_2); - } - return unless defined($agent_2); + # Get the agent for the second device. + my $agent_2 = get_agent_from_addr($self->{'dbh'}, $dev_2); + if (!defined($agent_2)) { + $agent_2 = get_agent_from_name($self->{'dbh'}, $dev_2); + } + return unless defined($agent_2); - # Use ping modules by default. - $if_1 = 'ping' if ($if_1 eq ''); - $if_2 = 'ping' if ($if_2 eq ''); + # Use ping modules by default. + $if_1 = 'ping' if ($if_1 eq ''); + $if_2 = 'ping' if ($if_2 eq ''); - # Check whether the modules exists. - my $module_name_1 = $if_1 eq 'ping' ? 'ping' : "${if_1}_ifOperStatus"; - my $module_name_2 = $if_2 eq 'ping' ? 'ping' : "${if_2}_ifOperStatus"; - my $module_id_1 = get_agent_module_id($self->{'dbh'}, $module_name_1, $agent_1->{'id_agente'}); - if ($module_id_1 <= 0) { - $self->call('message', "ERROR: Module " . safe_output($module_name_1) . " does not exist for agent $dev_1.", 5); - return; - } - my $module_id_2 = get_agent_module_id($self->{'dbh'}, $module_name_2, $agent_2->{'id_agente'}); - if ($module_id_2 <= 0) { - $self->call('message', "ERROR: Module " . safe_output($module_name_2) . " does not exist for agent $dev_2.", 5); - return; - } + # Check whether the modules exists. + my $module_name_1 = $if_1 eq 'ping' ? 'ping' : "${if_1}_ifOperStatus"; + my $module_name_2 = $if_2 eq 'ping' ? 'ping' : "${if_2}_ifOperStatus"; + my $module_id_1 = get_agent_module_id($self->{'dbh'}, $module_name_1, $agent_1->{'id_agente'}); + if ($module_id_1 <= 0) { + $self->call('message', "ERROR: Module " . safe_output($module_name_1) . " does not exist for agent $dev_1.", 5); + return; + } + my $module_id_2 = get_agent_module_id($self->{'dbh'}, $module_name_2, $agent_2->{'id_agente'}); + if ($module_id_2 <= 0) { + $self->call('message', "ERROR: Module " . safe_output($module_name_2) . " does not exist for agent $dev_2.", 5); + return; + } - # Connect the modules if they are not already connected. - my $connection_id = get_db_value($self->{'dbh'}, 'SELECT id FROM tmodule_relationship WHERE (module_a = ? AND module_b = ?) OR (module_b = ? AND module_a = ?)', $module_id_1, $module_id_2, $module_id_1, $module_id_2); - if (! defined($connection_id)) { - db_do($self->{'dbh'}, 'INSERT INTO tmodule_relationship (`module_a`, `module_b`, `id_rt`) VALUES(?, ?, ?)', $module_id_1, $module_id_2, $self->{'task_id'}); - } + # Connect the modules if they are not already connected. + my $connection_id = get_db_value($self->{'dbh'}, 'SELECT id FROM tmodule_relationship WHERE (module_a = ? AND module_b = ?) OR (module_b = ? AND module_a = ?)', $module_id_1, $module_id_2, $module_id_1, $module_id_2); + if (! defined($connection_id)) { + db_do($self->{'dbh'}, 'INSERT INTO tmodule_relationship (`module_a`, `module_b`, `id_rt`) VALUES(?, ?, ?)', $module_id_1, $module_id_2, $self->{'task_id'}); + } } + +########################################################################## +# Create agents from db_scan. Uses DataServer methods. +# data = [ +# 'agent_data' => {}, +# 'module_data' => [] +# ] +########################################################################## +sub PandoraFMS::Recon::Base::create_agents($$) { + my ($self, $data) = @_; + + my $pa_config = $self->{'pa_config'}; + my $dbh = $self->{'dbh'}; + my $server_id = $self->{'server_id'}; + + return undef if (ref($data) ne "ARRAY"); + + foreach my $information (@{$data}) { + my $agent = $information->{'agent_data'}; + my $modules = $information->{'module_data'}; + my $force_processing = 0; + + # Search agent + my $current_agent = PandoraFMS::Core::locate_agent( + $pa_config, $dbh, $agent->{'agent_name'} + ); + + my $parent_id; + if (defined($agent->{'parent_agent_name'})) { + $parent_id = PandoraFMS::Core::locate_agent( + $pa_config, $dbh, $agent->{'parent_agent_name'} + ); + if ($parent_id) { + $parent_id = $parent_id->{'id_agente'}; + } + } + + my $agent_id; + my $os_id = get_os_id($dbh, $agent->{'os'}); + + if ($os_id < 0) { + $os_id = get_os_id($dbh, 'Other'); + } + + if (!$current_agent) { + # Create agent. + $agent_id = pandora_create_agent( + $pa_config, $pa_config->{'servername'}, $agent->{'agent_name'}, + $agent->{'address'}, $agent->{'id_group'}, $parent_id, + $os_id, $agent->{'description'}, + $agent->{'interval'}, $dbh, $agent->{'timezone_offset'} + ); + + $current_agent = $parent_id = PandoraFMS::Core::locate_agent( + $pa_config, $dbh, $agent->{'agent_name'} + ); + + $force_processing = 1; + + } else { + $agent_id = $current_agent->{'id_agente'}; + } + + if (!defined($agent_id)) { + return undef; + } + + if ($agent->{'address'} ne '') { + pandora_add_agent_address( + $pa_config, $agent_id, $agent->{'agent_name'}, + $agent->{'address'}, $dbh + ); + } + + # Update agent information + pandora_update_agent( + $pa_config, strftime("%Y-%m-%d %H:%M:%S", localtime()), $agent_id, + $agent->{'os_version'}, $agent->{'agent_version'}, + $agent->{'interval'}, $dbh, undef, $parent_id + ); + + # Add modules. + if (ref($modules) eq "ARRAY") { + foreach my $module (@{$modules}) { + # Translate data structure to simulate XML parser return. + my %data_translated = map { $_ => [ $module->{$_} ] } keys %{$module}; + + # Process modules. + PandoraFMS::DataServer::process_module_data ( + $pa_config, \%data_translated, + $server_id, $current_agent, + $module->{'name'}, $module->{'type'}, + $agent->{'interval'}, + strftime ("%Y/%m/%d %H:%M:%S", localtime()), + $dbh, $force_processing + ); + } + } + } + +} + + ########################################################################## # Create an agent for the given device. Returns the ID of the new (or # existing) agent, undef on error. ########################################################################## sub PandoraFMS::Recon::Base::create_agent($$) { - my ($self, $device) = @_; + my ($self, $device) = @_; - my @agents = get_db_rows($self->{'dbh'}, - 'SELECT * FROM taddress, taddress_agent, tagente - WHERE tagente.id_agente = taddress_agent.id_agent - AND taddress_agent.id_a = taddress.id_a - AND ip = ?', $device - ); + # Clean name. + $device = clean_blank($device); - # Does the host already exist? - my $agent; - foreach my $candidate (@agents) { - $agent = {map {$_} %$candidate}; # copy contents, do not use shallow copy - # exclude $device itself, because it handle corner case when target includes NAT - my @registered = map {$_->{ip}} get_db_rows($self->{'dbh'}, - 'SELECT ip FROM taddress, taddress_agent, tagente - WHERE tagente.id_agente = taddress_agent.id_agent - AND taddress_agent.id_a = taddress.id_a - AND tagente.id_agente = ? - AND taddress.ip != ?', $agent->{id_agente}, $device - ); - foreach my $ip_addr (@registered) { - my @matched = grep { $_ =~ /^$ip_addr$/ } $self->get_addresses($device); - if (scalar(@matched) == 0) { - $agent = undef; - last; - } - } - last if(defined($agent)); # exit loop if match all ip_addr - } + my @agents = get_db_rows($self->{'dbh'}, + 'SELECT * FROM taddress, taddress_agent, tagente + WHERE tagente.id_agente = taddress_agent.id_agent + AND taddress_agent.id_a = taddress.id_a + AND ip = ?', $device + ); - if (!defined($agent)) { - $agent = get_agent_from_name($self->{'dbh'}, $device); - } + # Does the host already exist? + my $agent; + foreach my $candidate (@agents) { + $agent = {map {$_} %$candidate}; # copy contents, do not use shallow copy + # exclude $device itself, because it handle corner case when target includes NAT + my @registered = map {$_->{ip}} get_db_rows($self->{'dbh'}, + 'SELECT ip FROM taddress, taddress_agent, tagente + WHERE tagente.id_agente = taddress_agent.id_agent + AND taddress_agent.id_a = taddress.id_a + AND tagente.id_agente = ? + AND taddress.ip != ?', $agent->{id_agente}, $device + ); + foreach my $ip_addr (@registered) { + my @matched = grep { $_ =~ /^$ip_addr$/ } $self->get_addresses($device); + if (scalar(@matched) == 0) { + $agent = undef; + last; + } + } + last if(defined($agent)); # exit loop if match all ip_addr + } - my ($agent_id, $agent_learning); - if (!defined($agent)) { + if (!defined($agent)) { + $agent = get_agent_from_name($self->{'dbh'}, $device); + } - # Resolve hostnames. - my $host_name = $self->{'resolve_names'} == 1 ? gethostbyaddr (inet_aton($device), AF_INET) : $device; - $host_name = $device unless defined ($host_name); + my ($agent_id, $agent_learning); + if (!defined($agent)) { - # Guess the OS. - my $id_os = $self->guess_os($device); + # Resolve hostnames. + my $host_name = $self->{'resolve_names'} == 1 ? gethostbyaddr (inet_aton($device), AF_INET) : $device; + $host_name = $device unless defined ($host_name); - # Are we filtering hosts by OS? - return if ($self->{'id_os'} > 0 && $id_os != $self->{'id_os'}); + # Guess the OS. + my $id_os = $self->guess_os($device); - # Are we filtering hosts by TCP port? - return if ($self->{'recon_ports'} ne '' && $self->tcp_scan($device) == 0); - my $location = get_geoip_info($self->{'pa_config'}, $device); - $agent_id = pandora_create_agent($self->{'pa_config'}, $self->{'pa_config'}->{'servername'}, $host_name, $device, - $self->{'group_id'}, 0, $id_os, - '', 300, $self->{'dbh'}, undef, - $location->{'longitude'}, $location->{'latitude'} - ); - return undef unless defined ($agent_id) and ($agent_id > 0); + # Are we filtering hosts by OS? + return if ($self->{'id_os'} > 0 && $id_os != $self->{'id_os'}); - # Autoconfigure agent - if (defined($self->{'autoconfiguration_enabled'}) && $self->{'autoconfiguration_enabled'} == 1) { - my $agent_data = PandoraFMS::DB::get_db_single_row($self->{'dbh'}, 'SELECT * FROM tagente WHERE id_agente = ?', $agent_id); - # Update agent configuration once, after create agent. - enterprise_hook('autoconfigure_agent', [$self->{'pa_config'}, $host_name, $agent_id, $agent_data, $self->{'dbh'}, 1]); - } - - if (defined($self->{'main_event_id'})) { - my $addresses_str = join(',', safe_output($self->get_addresses($device))); - pandora_extended_event( - $self->{'pa_config'}, $self->{'dbh'}, $self->{'main_event_id'}, - "[Discovery] New " . safe_output($self->get_device_type($device)) . " found " . $host_name . " (" . $addresses_str . ") Agent $agent_id." - ); + # Are we filtering hosts by TCP port? + return if ($self->{'recon_ports'} ne '' && $self->tcp_scan($device) == 0); + my $location = get_geoip_info($self->{'pa_config'}, $device); + $agent_id = pandora_create_agent( + $self->{'pa_config'}, $self->{'pa_config'}->{'servername'}, + $host_name, $device, $self->{'group_id'}, 0, $id_os, + '', 300, $self->{'dbh'}, undef, $location->{'longitude'}, + $location->{'latitude'} + ); + return undef unless defined ($agent_id) and ($agent_id > 0); - } - - $agent_learning = 1; + # Autoconfigure agent + if (defined($self->{'autoconfiguration_enabled'}) && $self->{'autoconfiguration_enabled'} == 1) { + my $agent_data = PandoraFMS::DB::get_db_single_row($self->{'dbh'}, 'SELECT * FROM tagente WHERE id_agente = ?', $agent_id); + # Update agent configuration once, after create agent. + enterprise_hook('autoconfigure_agent', [$self->{'pa_config'}, $host_name, $agent_id, $agent_data, $self->{'dbh'}, 1]); + } + + if (defined($self->{'main_event_id'})) { + my $addresses_str = join(',', safe_output($self->get_addresses($device))); + pandora_extended_event( + $self->{'pa_config'}, $self->{'dbh'}, $self->{'main_event_id'}, + "[Discovery] New " . safe_output($self->get_device_type($device)) . " found " . $host_name . " (" . $addresses_str . ") Agent $agent_id." + ); - # Create network profile modules for the agent - $self->create_network_profile_modules($agent_id, $device); - } - else { - $agent_id = $agent->{'id_agente'}; - $agent_learning = $agent->{'modo'}; - } + } + + $agent_learning = 1; - # Do not create any modules if the agent is not in learning mode. - return unless ($agent_learning == 1); + # Create network profile modules for the agent + $self->create_network_profile_modules($agent_id, $device); + } + else { + $agent_id = $agent->{'id_agente'}; + $agent_learning = $agent->{'modo'}; + } - # Add found IP addresses to the agent. - foreach my $ip_addr ($self->get_addresses($device)) { - my $addr_id = get_addr_id($self->{'dbh'}, $ip_addr); - $addr_id = add_address($self->{'dbh'}, $ip_addr) unless ($addr_id > 0); - next unless ($addr_id > 0); + # Do not create any modules if the agent is not in learning mode. + return unless ($agent_learning == 1); - # Assign the new address to the agent - my $agent_addr_id = get_agent_addr_id($self->{'dbh'}, $addr_id, $agent_id); - if ($agent_addr_id <= 0) { - db_do($self->{'dbh'}, 'INSERT INTO taddress_agent (`id_a`, `id_agent`) - VALUES (?, ?)', $addr_id, $agent_id); - } - } + # Add found IP addresses to the agent. + foreach my $ip_addr ($self->get_addresses($device)) { + my $addr_id = get_addr_id($self->{'dbh'}, $ip_addr); + $addr_id = add_address($self->{'dbh'}, $ip_addr) unless ($addr_id > 0); + next unless ($addr_id > 0); - # Create a ping module. - my $module_id = get_agent_module_id($self->{'dbh'}, "ping", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 6, - 'id_modulo' => 2, - 'nombre' => "ping", - 'descripcion' => '', - 'id_agente' => $agent_id, - 'ip_target' => $device); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } + # Assign the new address to the agent + my $agent_addr_id = get_agent_addr_id($self->{'dbh'}, $addr_id, $agent_id); + if ($agent_addr_id <= 0) { + db_do($self->{'dbh'}, 'INSERT INTO taddress_agent (`id_a`, `id_agent`) + VALUES (?, ?)', $addr_id, $agent_id); + } + } - # Add interfaces to the agent if it responds to SNMP. - return $agent_id unless ($self->is_snmp_discovered($device)); - my $community = $self->get_community($device); + # Create a ping module. + my $module_id = get_agent_module_id($self->{'dbh'}, "ping", $agent_id); + if ($module_id <= 0) { + my %module = ('id_tipo_modulo' => 6, + 'id_modulo' => 2, + 'nombre' => "ping", + 'descripcion' => '', + 'id_agente' => $agent_id, + 'ip_target' => $device); + pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); + } - my @output = $self->snmp_get_value_array($device, $PandoraFMS::Recon::Base::IFINDEX); - foreach my $if_index (@output) { - next unless ($if_index =~ /^[0-9]+$/); + # Add interfaces to the agent if it responds to SNMP. + return $agent_id unless ($self->is_snmp_discovered($device)); + my $community = $self->get_community($device); - # Check the status of the interface. - if ($self->{'all_ifaces'} == 0) { - my $if_status = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index"); - next unless $if_status == 1; - } + my @output = $self->snmp_get_value_array($device, $PandoraFMS::Recon::Base::IFINDEX); + foreach my $if_index (@output) { + next unless ($if_index =~ /^[0-9]+$/); - # Fill the module description with the IP and MAC addresses. - my $mac = $self->get_if_mac($device, $if_index); - my $ip = $self->get_if_ip($device, $if_index); - my $if_desc = ($mac ne '' ? "MAC $mac " : '') . ($ip ne '' ? "IP $ip" : ''); + # Check the status of the interface. + if ($self->{'all_ifaces'} == 0) { + my $if_status = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index"); + next unless $if_status == 1; + } - # Get the name of the network interface. - my $if_name = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFNAME.$if_index"); - $if_name = "if$if_index" unless defined ($if_name); - $if_name =~ s/"//g; + # Fill the module description with the IP and MAC addresses. + my $mac = $self->get_if_mac($device, $if_index); + my $ip = $self->get_if_ip($device, $if_index); + my $if_desc = ($mac ne '' ? "MAC $mac " : '') . ($ip ne '' ? "IP $ip" : ''); - # Check whether the module already exists. - my $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifOperStatus", $agent_id); - next if ($module_id > 0 && !$agent_learning); - - # Encode problematic characters. - $if_name = safe_input($if_name); - $if_desc = safe_input($if_desc); + # Get the name of the network interface. + my $if_name = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFNAME.$if_index"); + $if_name = "if$if_index" unless defined ($if_name); + $if_name =~ s/"//g; + $if_name = clean_blank($if_name); - # Interface status module. - $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifOperStatus", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 18, - 'id_modulo' => 2, - 'nombre' => "${if_name}_ifOperStatus", - 'descripcion' => $if_desc, - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index" - ); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'descripcion' => $if_desc, - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'tcp_send' => $self->{'snmp_version'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } + # Check whether the module already exists. + my $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifOperStatus', $agent_id); - # Incoming traffic module. - my $if_hc_in_octets = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index"); - if (defined($if_hc_in_octets)) { - $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifHCInOctets", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => "${if_name}_ifHCInOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifInOctets.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } - # ifInOctets - elsif (defined($self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFINOCTECTS.$if_index"))) { - $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifInOctets", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => "${if_name}_ifInOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFINOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } + next if ($module_id > 0 && !$agent_learning); + + # Encode problematic characters. + $if_desc = safe_input($if_desc); - # Outgoing traffic module. - my $if_hc_out_octets = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index"); - if (defined($if_hc_out_octets)) { - $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifHCOutOctets", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => "${if_name}_ifHCOutOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifOutOctets.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } - # ifOutOctets - elsif (defined($self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFOUTOCTECTS.$if_index"))) { - $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifOutOctets", $agent_id); - if ($module_id <= 0) { - my %module = ('id_tipo_modulo' => 16, - 'id_modulo' => 2, - 'nombre' => "${if_name}_ifOutOctets", - 'descripcion' => 'The total number of octets received on the interface, including framing characters.', - 'id_agente' => $agent_id, - 'ip_target' => $device, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - 'snmp_community' => $community, - 'snmp_oid' => "$PandoraFMS::Recon::Base::IFOUTOCTECTS.$if_index"); - pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); - } else { - my %module = ( - 'ip_target' => $device, - 'snmp_community' => $community, - 'tcp_send' => $self->{'snmp_version'}, - 'tcp_send' => $self->{'snmp_version'}, - 'custom_string_1' => $self->{'snmp_privacy_method'}, - 'custom_string_2' => $self->{'snmp_privacy_pass'}, - 'custom_string_3' => $self->{'snmp_security_level'}, - 'plugin_parameter' => $self->{'snmp_auth_method'}, - 'plugin_user' => $self->{'snmp_auth_user'}, - 'plugin_pass' => $self->{'snmp_auth_pass'}, - ); - pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); - } - } - } + # Interface status module. + $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifOperStatus', $agent_id); + if ($module_id <= 0) { + my %module = ('id_tipo_modulo' => 18, + 'id_modulo' => 2, + 'nombre' => safe_input($if_name)."_ifOperStatus", + 'descripcion' => $if_desc, + 'id_agente' => $agent_id, + 'ip_target' => $device, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + 'snmp_community' => $community, + 'snmp_oid' => "$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index" + ); + pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); + } else { + my %module = ( + 'descripcion' => $if_desc, + 'ip_target' => $device, + 'snmp_community' => $community, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + 'tcp_send' => $self->{'snmp_version'}, + ); + pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); + } - return $agent_id; + # Incoming traffic module. + my $if_hc_in_octets = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index"); + if (defined($if_hc_in_octets)) { + $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifHCInOctets', $agent_id); + if ($module_id <= 0) { + my %module = ('id_tipo_modulo' => 16, + 'id_modulo' => 2, + 'nombre' => safe_input($if_name)."_ifHCInOctets", + 'descripcion' => 'The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifInOctets.', + 'id_agente' => $agent_id, + 'ip_target' => $device, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + 'snmp_community' => $community, + 'snmp_oid' => "$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index"); + pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); + } else { + my %module = ( + 'ip_target' => $device, + 'snmp_community' => $community, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + ); + pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); + } + } + # ifInOctets + elsif (defined($self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFINOCTECTS.$if_index"))) { + $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifInOctets', $agent_id); + if ($module_id <= 0) { + my %module = ('id_tipo_modulo' => 16, + 'id_modulo' => 2, + 'nombre' => safe_input($if_name)."_ifInOctets", + 'descripcion' => 'The total number of octets received on the interface, including framing characters.', + 'id_agente' => $agent_id, + 'ip_target' => $device, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + 'snmp_community' => $community, + 'snmp_oid' => "$PandoraFMS::Recon::Base::IFINOCTECTS.$if_index"); + pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); + } else { + my %module = ( + 'ip_target' => $device, + 'snmp_community' => $community, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + ); + pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); + } + } + + # Outgoing traffic module. + my $if_hc_out_octets = $self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index"); + if (defined($if_hc_out_octets)) { + $module_id = get_agent_module_id($self->{'dbh'}, $if_name.'_ifHCOutOctets', $agent_id); + if ($module_id <= 0) { + my %module = ('id_tipo_modulo' => 16, + 'id_modulo' => 2, + 'nombre' => safe_input($if_name)."_ifHCOutOctets", + 'descripcion' => 'The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifOutOctets.', + 'id_agente' => $agent_id, + 'ip_target' => $device, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + 'snmp_community' => $community, + 'snmp_oid' => "$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index"); + pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); + } else { + my %module = ( + 'ip_target' => $device, + 'snmp_community' => $community, + 'tcp_send' => $self->{'snmp_version'}, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + ); + pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); + } + } + # ifOutOctets + elsif (defined($self->snmp_get_value($device, "$PandoraFMS::Recon::Base::IFOUTOCTECTS.$if_index"))) { + $module_id = get_agent_module_id($self->{'dbh'}, "${if_name}_ifOutOctets", $agent_id); + if ($module_id <= 0) { + my %module = ('id_tipo_modulo' => 16, + 'id_modulo' => 2, + 'nombre' => safe_input($if_name)."_ifOutOctets", + 'descripcion' => 'The total number of octets received on the interface, including framing characters.', + 'id_agente' => $agent_id, + 'ip_target' => $device, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + 'snmp_community' => $community, + 'snmp_oid' => "$PandoraFMS::Recon::Base::IFOUTOCTECTS.$if_index"); + pandora_create_module_from_hash ($self->{'pa_config'}, \%module, $self->{'dbh'}); + } else { + my %module = ( + 'ip_target' => $device, + 'snmp_community' => $community, + 'tcp_send' => $self->{'snmp_version'}, + 'tcp_send' => $self->{'snmp_version'}, + 'custom_string_1' => $self->{'snmp_privacy_method'}, + 'custom_string_2' => $self->{'snmp_privacy_pass'}, + 'custom_string_3' => $self->{'snmp_security_level'}, + 'plugin_parameter' => $self->{'snmp_auth_method'}, + 'plugin_user' => $self->{'snmp_auth_user'}, + 'plugin_pass' => $self->{'snmp_auth_pass'}, + ); + pandora_update_module_from_hash ($self->{'pa_config'}, \%module, 'id_agente_modulo', $module_id, $self->{'dbh'}); + } + } + } + + return $agent_id; } ########################################################################## # Delete already existing connections. ########################################################################## sub PandoraFMS::Recon::Base::delete_connections($) { - my ($self) = @_; + my ($self) = @_; - $self->call('message', "Deleting connections...", 10); - db_do($self->{'dbh'}, 'DELETE FROM tmodule_relationship WHERE id_rt=?', $self->{'task_id'}); + $self->call('message', "Deleting connections...", 10); + db_do($self->{'dbh'}, 'DELETE FROM tmodule_relationship WHERE id_rt=?', $self->{'task_id'}); } ####################################################################### # Print log messages. ####################################################################### sub PandoraFMS::Recon::Base::message($$$) { - my ($self, $message, $verbosity) = @_; - - logger($self->{'pa_config'}, "[Recon task " . $self->{'task_id'} . "] $message", $verbosity); + my ($self, $message, $verbosity) = @_; + + logger($self->{'pa_config'}, "[Recon task " . $self->{'task_id'} . "] $message", $verbosity); } ########################################################################## # Connect the given hosts to its parent. ########################################################################## sub PandoraFMS::Recon::Base::set_parent($$$) { - my ($self, $host, $parent) = @_; + my ($self, $host, $parent) = @_; - return unless ($self->{'parent_detection'} == 1); + return unless ($self->{'parent_detection'} == 1); - # Get the agent for the host. - my $agent = get_agent_from_addr($self->{'dbh'}, $host); - if (!defined($agent)) { - $agent = get_agent_from_name($self->{'dbh'}, $host); - } - return unless defined($agent); + # Get the agent for the host. + my $agent = get_agent_from_addr($self->{'dbh'}, $host); + if (!defined($agent)) { + $agent = get_agent_from_name($self->{'dbh'}, $host); + } + return unless defined($agent); - # Check if the parent agent exists. - my $agent_parent = get_agent_from_addr($self->{'dbh'}, $parent); - if (!defined($agent_parent)) { - $agent_parent = get_agent_from_name($self->{'dbh'}, $parent); - } - return unless (defined ($agent_parent)); + # Check if the parent agent exists. + my $agent_parent = get_agent_from_addr($self->{'dbh'}, $parent); + if (!defined($agent_parent)) { + $agent_parent = get_agent_from_name($self->{'dbh'}, $parent); + } + return unless (defined ($agent_parent)); - # Is the agent in learning mode? - return unless ($agent_parent->{'modo'} == 1); + # Is the agent in learning mode? + return unless ($agent_parent->{'modo'} == 1); - # Connect the host to its parent. - db_do($self->{'dbh'}, 'UPDATE tagente SET id_parent=? WHERE id_agente=?', $agent_parent->{'id_agente'}, $agent->{'id_agente'}); + # Connect the host to its parent. + db_do($self->{'dbh'}, 'UPDATE tagente SET id_parent=? WHERE id_agente=?', $agent_parent->{'id_agente'}, $agent->{'id_agente'}); } ########################################################################## # Create a WMI module for the given agent. ########################################################################## sub PandoraFMS::Recon::Base::wmi_module { - my ($self, $agent_id, $target, $wmi_query, $wmi_auth, $column, + my ($self, $agent_id, $target, $wmi_query, $wmi_auth, $column, $module_name, $module_description, $module_type, $unit) = @_; - # Check whether the module already exists. - my $module_id = get_agent_module_id($self->{'dbh'}, $module_name, $agent_id); - return if ($module_id > 0); + # Check whether the module already exists. + my $module_id = get_agent_module_id($self->{'dbh'}, $module_name, $agent_id); + return if ($module_id > 0); - my ($user, $pass) = ($wmi_auth ne '') ? split('%', $wmi_auth) : (undef, undef); - my %module = ( - 'descripcion' => safe_input($module_description), - 'id_agente' => $agent_id, - 'id_modulo' => 6, - 'id_tipo_modulo' => get_module_id($self->{'dbh'}, $module_type), - 'ip_target' => $target, - 'nombre' => safe_input($module_name), - 'plugin_pass' => defined($pass) ? $pass : '', - 'plugin_user' => defined($user) ? $user : '', - 'snmp_oid' => $wmi_query, - 'tcp_port' => $column, - 'unit' => defined($unit) ? $unit : '' - ); - - pandora_create_module_from_hash($self->{'pa_config'}, \%module, $self->{'dbh'}); + my ($user, $pass) = ($wmi_auth ne '') ? split('%', $wmi_auth) : (undef, undef); + my %module = ( + 'descripcion' => safe_input($module_description), + 'id_agente' => $agent_id, + 'id_modulo' => 6, + 'id_tipo_modulo' => get_module_id($self->{'dbh'}, $module_type), + 'ip_target' => $target, + 'nombre' => safe_input($module_name), + 'plugin_pass' => defined($pass) ? $pass : '', + 'plugin_user' => defined($user) ? $user : '', + 'snmp_oid' => $wmi_query, + 'tcp_port' => $column, + 'unit' => defined($unit) ? $unit : '' + ); + + pandora_create_module_from_hash($self->{'pa_config'}, \%module, $self->{'dbh'}); } ########################################################################## # Update recon task status. ########################################################################## sub PandoraFMS::Recon::Base::update_progress ($$) { - my ($self, $progress) = @_; + my ($self, $progress) = @_; - db_do ($self->{'dbh'}, 'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?', time (), $progress, $self->{'task_id'}); + my $stats = {}; + if (defined($self->{'summary'}) && $self->{'summary'} ne '') { + $stats->{'summary'} = $self->{'summary'}; + } + $stats->{'step'} = $self->{'step'}; + $stats->{'c_network_name'} = $self->{'c_network_name'}; + $stats->{'c_network_percent'} = $self->{'c_network_percent'}; + + # Store progress, last contact and overall status. + db_do ($self->{'dbh'}, 'UPDATE trecon_task SET utimestamp = ?, status = ?, summary = ? WHERE id_rt = ?', + time (), $progress, encode_json($stats), $self->{'task_id'}); } 1; diff --git a/pandora_server/lib/PandoraFMS/PluginTools.pm b/pandora_server/lib/PandoraFMS/PluginTools.pm index e19a542b17..6941e5e5a1 100644 --- a/pandora_server/lib/PandoraFMS/PluginTools.pm +++ b/pandora_server/lib/PandoraFMS/PluginTools.pm @@ -31,8 +31,8 @@ use base 'Exporter'; our @ISA = qw(Exporter); # version: Defines actual version of Pandora Server for this module only -my $pandora_version = "7.0NG.732"; -my $pandora_build = "190319"; +my $pandora_version = "7.0NG.735"; +my $pandora_build = "190610"; our $VERSION = $pandora_version." ".$pandora_build; our %EXPORT_TAGS = ( 'all' => [ qw() ] ); @@ -721,6 +721,9 @@ sub print_module { if (! (empty ($data->{min_ff_event_critical}))) { $xml_module .= "\t{min_ff_event_critical} . "]]>\n"; } + if (! (empty ($data->{ff_type}))) { + $xml_module .= "\t{ff_type} . "]]>\n"; + } if (! (empty ($data->{ff_timeout}))) { $xml_module .= "\t{ff_timeout} . "]]>\n"; } diff --git a/pandora_server/lib/PandoraFMS/Recon/Base.pm b/pandora_server/lib/PandoraFMS/Recon/Base.pm index 45f5d87a27..23532d843b 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Base.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Base.pm @@ -15,6 +15,25 @@ use PandoraFMS::Recon::NmapParser; use PandoraFMS::Recon::Util; use Socket qw/inet_aton/; +# Constants. +use constant { + STEP_SCANNING => 1, + STEP_AFT => 2, + STEP_TRACEROUTE => 3, + STEP_GATEWAY => 4, + STEP_STATISTICS => 1, + STEP_DATABASE_SCAN => 2, + STEP_CUSTOM_QUERIES => 3, + DISCOVERY_HOSTDEVICES => 0, + DISCOVERY_HOSTDEVICES_CUSTOM => 1, + DISCOVERY_CLOUD_AWS => 2, + DISCOVERY_APP_VMWARE => 3, + DISCOVERY_APP_MYSQL => 4, + DISCOVERY_APP_ORACLE => 5, + DISCOVERY_CLOUD_AWS_EC2 => 6, + DISCOVERY_CLOUD_AWS_RDS => 7 +}; + # /dev/null my $DEVNULL = ($^O eq 'MSWin32') ? '/Nul' : '/dev/null'; @@ -47,33 +66,33 @@ our $SYSUPTIME = ".1.3.6.1.2.1.1.3"; our $VTPVLANIFINDEX = ".1.3.6.1.4.1.9.9.46.1.3.1.1.18.1"; our @ISA = ("Exporter"); -our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); +our %EXPORT_TAGS = ( 'all' => [qw( )] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( - $DOT1DBASEBRIDGEADDRESS - $DOT1DBASEPORTIFINDEX - $DOT1DTPFDBADDRESS - $DOT1DTPFDBPORT - $IFDESC - $IFHCINOCTECTS - $IFHCOUTOCTECTS - $IFINDEX - $IFINOCTECTS - $IFOPERSTATUS - $IFOUTOCTECTS - $IPADENTIFINDEX - $IPENTADDR - $IFNAME - $IPNETTOMEDIAPHYSADDRESS - $IFPHYSADDRESS - $IPADENTIFINDEX - $IPROUTEIFINDEX - $IPROUTENEXTHOP - $IPROUTETYPE - $PRTMARKERINDEX - $SYSDESCR - $SYSSERVICES - $SYSUPTIME + $DOT1DBASEBRIDGEADDRESS + $DOT1DBASEPORTIFINDEX + $DOT1DTPFDBADDRESS + $DOT1DTPFDBPORT + $IFDESC + $IFHCINOCTECTS + $IFHCOUTOCTECTS + $IFINDEX + $IFINOCTECTS + $IFOPERSTATUS + $IFOUTOCTECTS + $IPADENTIFINDEX + $IPENTADDR + $IFNAME + $IPNETTOMEDIAPHYSADDRESS + $IFPHYSADDRESS + $IPADENTIFINDEX + $IPROUTEIFINDEX + $IPROUTENEXTHOP + $IPROUTETYPE + $PRTMARKERINDEX + $SYSDESCR + $SYSSERVICES + $SYSUPTIME ); ####################################################################### @@ -167,6 +186,18 @@ sub new { snmp_version => 1, subnets => [], autoconfiguration_enabled => 0, + + # Store progress summary - Discovery progress view. + step => 0, + c_network_name => '', + c_network_percent => 0.0, + summary => { + SNMP => 0, + WMI => 0, + discovered => 0, + alive => 0, + not_alive => 0 + }, @_, }; @@ -174,27 +205,30 @@ sub new { # Perform some sanity checks. die("No subnet was specified.") unless defined($self->{'subnets'}); + $self = bless($self, $class); + # Check SNMP params id SNMP is enabled if ($self->{'snmp_enabled'}) { + # Check SNMP version - if ($self->{'snmp_version'} ne '1' && $self->{'snmp_version'} ne '2' - && $self->{'snmp_version'} ne '2c' && $self->{'snmp_version'} ne '3' - ) { + if ( $self->{'snmp_version'} ne '1' + && $self->{'snmp_version'} ne '2' + && $self->{'snmp_version'} ne '2c' + && $self->{'snmp_version'} ne '3') { $self->{'snmp_enabled'} = 0; $self->call('message', "SNMP version " . $self->{'snmp_version'} . " not supported (only 1, 2, 2c and 3).", 5); } # Check the version 3 parameters if ($self->{'snmp_version'} eq '3') { + # Fixed some vars $self->{'communities'} = []; # SNMP v3 checks - if ( - $self->{'snmp_security_level'} ne 'noAuthNoPriv' && - $self->{'snmp_security_level'} ne 'authNoPriv' && - $self->{'snmp_security_level'} ne 'authPriv' - ) { + if ( $self->{'snmp_security_level'} ne 'noAuthNoPriv' + &&$self->{'snmp_security_level'} ne 'authNoPriv' + &&$self->{'snmp_security_level'} ne 'authPriv') { $self->{'snmp_enabled'} = 0; $self->call('message', "Invalid SNMP security level " . $self->{'snmp_security_level'} . ".", 5); } @@ -207,6 +241,7 @@ sub new { $self->call('message', "Invalid SNMP authentication method " . $self->{'snmp_auth_method'} . ".", 5); } } else { + # Fixed some vars $self->{'snmp_auth_user'} = ''; $self->{'snmp_auth_pass'} = ''; @@ -218,7 +253,7 @@ sub new { # Disable SNMP scans if no community was given. if (ref($self->{'communities'}) ne "ARRAY" || scalar(@{$self->{'communities'}}) == 0) { $self->{'snmp_enabled'} = 0; - $self->call('message', "There is not any SNMP community configured.", 5); + $self->call('message', "There is no SNMP community configured.", 5); } } @@ -248,7 +283,7 @@ sub new { $self->{'snmp_security_level'} = ''; } - return bless($self, $class); + return $self; } ######################################################################################## @@ -296,6 +331,7 @@ sub aft_connectivity($$) { foreach my $mac ($self->snmp_get_value_array($switch, $DOT1DTPFDBADDRESS)) { push(@aft, parse_mac($mac)); } + # Search for matching entries. foreach my $aft_mac (@aft) { @@ -309,7 +345,7 @@ sub aft_connectivity($$) { # Get the interface associated to the port were we found the MAC address. my $switch_if_name = $self->get_if_from_aft($switch, $aft_mac); - next unless defined ($switch_if_name) and ($switch_if_name ne ''); + next unless defined($switch_if_name) and ($switch_if_name ne ''); # Do not connect a host to a switch twice using the same interface. # The switch is probably connected to another switch. @@ -342,8 +378,8 @@ sub are_connected($$$$$) { $if_1 = "ping" if $if_1 eq ''; $if_2 = "ping" if $if_2 eq ''; - if (defined($self->{'connections'}->{"${dev_1}\t${if_1}\t${dev_2}\t${if_2}"}) || - defined($self->{'connections'}->{"${dev_2}\t${if_2}\t${dev_1}\t${if_1}"})) { + if ( defined($self->{'connections'}->{"${dev_1}\t${if_1}\t${dev_2}\t${if_2}"}) + ||defined($self->{'connections'}->{"${dev_2}\t${if_2}\t${dev_1}\t${if_1}"})) { return 1; } @@ -367,19 +403,20 @@ sub snmp_discovery($$) { # Try to find the MAC with an ARP request. $self->get_mac_from_ip($device); - + # Check if the device responds to SNMP. if ($self->snmp_responds($device)) { - + $self->{'summary'}->{'SNMP'} += 1; + # Fill the VLAN cache. $self->find_vlans($device); - + # Guess the device type. $self->guess_device_type($device); - + # Find aliases for the device. $self->find_aliases($device); - + # Find interfaces for the device. $self->find_ifaces($device); @@ -655,12 +692,12 @@ sub get_if_from_ip($$$) { # Get the port associated to the IP address. my $if_index = $self->snmp_get_value($device, "$IPROUTEIFINDEX.$ip_addr"); - return '' unless defined ($if_index); + return '' unless defined($if_index); # Get the name of the interface associated to the port. my $if_name = $self->snmp_get_value($device, "$IFNAME.$if_index"); - return '' unless defined ($if_name); - + return '' unless defined($if_name); + $if_name =~ s/"//g; return $if_name; } @@ -686,12 +723,12 @@ sub get_if_from_mac($$$) { # Get the name of the interface associated to the port. my $if_name = $self->snmp_get_value($device, "$IFNAME.$if_index"); - return '' unless defined ($if_name); - + return '' unless defined($if_name); + $if_name =~ s/"//g; return $if_name; } - + return ''; } @@ -718,13 +755,13 @@ sub get_if_from_port($$$) { ######################################################################################## sub get_if_ip($$$) { my ($self, $device, $if_index) = @_; - + my @output = $self->snmp_get($device, $IPADENTIFINDEX); foreach my $line (@output) { - chomp ($line); + chomp($line); return $1 if ($line =~ m/^$IPADENTIFINDEX.(\S+)\s+=\s+\S+:\s+$if_index$/); } - + return ''; } @@ -748,7 +785,7 @@ sub get_if_mac($$$) { ######################################################################################## sub get_if_type($$$) { my ($self, $device, $if_index) = @_; - + my $type = $self->snmp_get_value($device, "$IFTYPE.$if_index"); return '' unless defined($type); @@ -769,7 +806,7 @@ sub get_ip_from_mac($$) { } ######################################################################################## -# Attemtps to find +# Attemtps to find ######################################################################################## sub get_mac_from_ip($$) { my ($self, $host) = @_; @@ -824,7 +861,7 @@ sub get_routes($) { } # Replace 0.0.0.0 with the default gateway's IP. - return unless defined ($self->{'default_gw'}); + return unless defined($self->{'default_gw'}); foreach my $route (@{$self->{'routes'}}) { $route->{gw} = $self->{'default_gw'} if ($route->{'gw'} eq '0.0.0.0'); } @@ -906,12 +943,15 @@ sub guess_device_type($$) { # L2? my $device_type; if ($service_bits[1] == 1) { + # L3? if ($service_bits[2] == 1) { + # Bridge MIB? if (defined($bridge_mib)) { $device_type = 'switch'; } else { + # L7? if ($service_bits[6] == 1) { $device_type = 'host'; @@ -919,8 +959,8 @@ sub guess_device_type($$) { $device_type = 'router'; } } - } - else { + }else { + # Bridge MIB? if (defined($bridge_mib)) { $device_type = 'switch'; @@ -928,14 +968,16 @@ sub guess_device_type($$) { $device_type = 'host'; } } - } - else { + }else { + # L3? if ($service_bits[2] == 1) { + # L4? if ($service_bits[3] == 1) { $device_type = 'switch'; } else { + # L7? if ($service_bits[6] == 1) { $device_type = 'host'; @@ -943,8 +985,8 @@ sub guess_device_type($$) { $device_type = 'router'; } } - } - else { + }else { + # Printer MIB? my $printer_mib = $self->snmp_get_value($device, $PRTMARKERINDEX); if (defined($printer_mib)) { @@ -1016,7 +1058,7 @@ sub is_switch_connected($$$) { # Check for aliases! $device = $self->{'aliases'}->{$device} if defined($self->{'aliases'}->{$device}); - return 1 if defined ($self->{'switch_to_switch'}->{"${device}\t${iface}"}); + return 1 if defined($self->{'switch_to_switch'}->{"${device}\t${iface}"}); return 0; } @@ -1069,8 +1111,8 @@ sub mark_connected($$;$$$) { } # Prevent parent-child loops. - if (!defined($self->{'parents'}->{$parent}) || - $self->{'parents'}->{$parent} ne $child) { + if (!defined($self->{'parents'}->{$parent}) + ||$self->{'parents'}->{$parent} ne $child) { # A parent-child relationship is always created to help complete the map with # layer 3 information. @@ -1097,8 +1139,10 @@ sub mark_switch_connected($$$) { sub mark_visited($$) { my ($self, $device) = @_; - $self->{'visited_devices'}->{$device} = { 'addr' => { $device => '' }, - 'type' => 'host' }; + $self->{'visited_devices'}->{$device} = { + 'addr' => { $device => '' }, + 'type' => 'host' + }; } ######################################################################################## @@ -1121,8 +1165,8 @@ sub snmp_responds($$) { return 1 if($self->is_snmp_discovered($device)); return ($self->{'snmp_version'} eq "3") - ? $self->snmp_responds_v3($device) - : $self->snmp_responds_v122c($device); + ? $self->snmp_responds_v3($device) + : $self->snmp_responds_v122c($device); } ######################################################################################## @@ -1221,11 +1265,7 @@ sub remote_arp($$) { ############################################################################## sub ping ($$$) { my ($self, $host) = @_; - my ($timeout, $retries, $packets) = ( - $self->{'icmp_timeout'}, - $self->{'icmp_checks'}, - 1, - ); + my ($timeout, $retries, $packets) = ($self->{'icmp_timeout'},$self->{'icmp_checks'},1,); # Windows if (($^O eq "MSWin32") || ($^O eq "MSWin32-x64") || ($^O eq "cygwin")){ @@ -1237,11 +1277,12 @@ sub ping ($$$) { return 0; } - + # Solaris if ($^O eq "solaris"){ my $ping_command = $host =~ /\d+:|:\d+/ ? "ping -A inet6" : "ping"; for (my $i = 0; $i < $retries; $i++) { + # Note: There is no timeout option. `$ping_command -s -n $host 56 $packets >/dev/null 2>&1`; return 1 if ($? == 0); @@ -1249,11 +1290,12 @@ sub ping ($$$) { return 0; } - + # FreeBSD if ($^O eq "freebsd"){ my $ping_command = $host =~ /\d+:|:\d+/ ? "ping6" : "ping -t $timeout"; for (my $i = 0; $i < $retries; $i++) { + # Note: There is no timeout option for ping6. `$ping_command -q -n -c $packets $host >/dev/null 2>&1`; return 1 if ($? == 0); @@ -1263,9 +1305,10 @@ sub ping ($$$) { } # NetBSD - if ($^O eq "netbsd"){ + if ($^O eq "netbsd"){ my $ping_command = $host =~ /\d+:|:\d+/ ? "ping6" : "ping -w $timeout"; for (my $i = 0; $i < $retries; $i++) { + # Note: There is no timeout option for ping6. `$ping_command -q -n -c $packets $host >/dev/null 2>&1`; if ($? == 0) { @@ -1275,11 +1318,11 @@ sub ping ($$$) { return 0; } - + # Assume Linux by default. my $ping_command = $host =~ /\d+:|:\d+/ ? "ping6" : "ping"; for (my $i = 0; $i < $retries; $i++) { - `$ping_command -q -W $timeout -n -c $packets $host >/dev/null 2>&1`; + `$ping_command -q -W $timeout -n -c $packets $host >/dev/null 2>&1`; return 1 if ($? == 0); } @@ -1295,11 +1338,13 @@ sub scan_subnet($) { my @subnets = @{$self->get_subnets()}; foreach my $subnet (@subnets) { + $self->{'c_network_percent'} = 0; + $self->{'c_network_name'} = $subnet; # Clean blanks. $subnet =~ s/\s+//g; - my $net_addr = new NetAddr::IP ($subnet); + my $net_addr = new NetAddr::IP($subnet); if (!defined($net_addr)) { $self->call('message', "Invalid network: $subnet", 3); next; @@ -1312,46 +1357,68 @@ sub scan_subnet($) { # fping scan. if (-x $self->{'fping'} && $net_addr->num() > 1) { $self->call('message', "Calling fping...", 5); - + my @hosts = `"$self->{'fping'}" -ga "$subnet" 2>DEVNULL`; next if (scalar(@hosts) == 0); - + + $self->{'summary'}->{'discovered'} += scalar(@hosts); + my $step = 50.0 / scalar(@subnets) / scalar(@hosts); # The first 50% of the recon task approx. + my $subnet_step = 100.0 / scalar(@hosts); foreach my $line (@hosts) { chomp($line); my @temp = split(/ /, $line); - next if (scalar(@temp) != 1); # Junk is shown for broadcast addresses. + if (scalar(@temp) != 1) { + + # Junk is shown for broadcast addresses. + # Increase summary.not_alive hosts. + $self->{'summary'}->{'not_alive'} += 1; + next; + } my $host = $temp[0]; # Skip network and broadcast addresses. next if ($host eq $network->addr() || $host eq $broadcast->addr()); - + + # Increase self summary.alive hosts. + $self->{'summary'}->{'alive'} += 1; $self->call('message', "Scanning host: $host", 5); $self->call('update_progress', ceil($progress)); $progress += $step; - + $self->{'c_network_percent'} += $subnet_step; + $self->snmp_discovery($host); # Add wmi scan if enabled. $self->wmi_scan($host) if ($self->{'wmi_enabled'} == 1); } } + # ping scan. else { my @hosts = map { (split('/', $_))[0] } $net_addr->hostenum; next if (scalar(@hosts) == 0); - + + $self->{'summary'}->{'discovered'} += scalar(@hosts); + my $step = 50.0 / scalar(@subnets) / scalar(@hosts); # The first 50% of the recon task approx. + my $subnet_step = 100.0 / scalar(@hosts); foreach my $host (@hosts) { - + $self->call('message', "Scanning host: $host", 5); $self->call('update_progress', ceil($progress)); $progress += $step; - + # Check if the host is up. - next if ($self->ping($host) == 0); - + if ($self->ping($host) == 0) { + $self->{'summary'}->{'not_alive'} += 1; + next; + } + + $self->{'summary'}->{'alive'} += 1; + $self->{'c_network_percent'} += $subnet_step; + $self->snmp_discovery($host); # Add wmi scan if enabled. @@ -1361,6 +1428,210 @@ sub scan_subnet($) { } } + +########################################################################## +# Perform a Cloud scan +########################################################################## +sub cloud_scan($) { + my $self = shift; + my ($progress, $step); + + my $type = ''; + + if ($self->{'task_data'}->{'type'} == DISCOVERY_CLOUD_AWS_EC2 + || $self->{'task_data'}->{'type'} == DISCOVERY_CLOUD_AWS_RDS) { + $type = 'Aws'; + } else { + # Unrecognized task type. + call('message', 'Unrecognized task type', 1); + $self->call('update_progress', -1); + return; + } + + # Initialize cloud object. + my $cloudObj = PandoraFMS::Recon::Util::enterprise_new( + 'PandoraFMS::Recon::Cloud::'.$type, + [ + task_data => $self->{'task_data'}, + aws_access_key_id => $self->{'aws_access_key_id'}, + aws_secret_access_key => $self->{'aws_secret_access_key'}, + cloud_util_path => $self->{'cloud_util_path'}, + creds_file => $self->{'creds_file'}, + parent => $self + ] + + ); + + if (!$cloudObj) { + # Failed to initialize, check Cloud credentials or anything. + call('message', 'Unable to initialize PandoraFMS::Recon::Cloud::'.$type, 3); + } else { + # Let Cloud object manage scan. + $cloudObj->scan(); + } + + # Update progress. + # Done! + $self->{'step'} = ''; + $self->call('update_progress', -1); +} + + +########################################################################## +# Perform an Application scan. +########################################################################## +sub app_scan($) { + my ($self) = @_; + my ($progress, $step); + + my $type = ''; + + if ($self->{'task_data'}->{'type'} == DISCOVERY_APP_MYSQL) { + $type = 'MySQL'; + } elsif ($self->{'task_data'}->{'type'} == DISCOVERY_APP_ORACLE) { + $type = 'Oracle'; + } else { + # Unrecognized task type. + call('message', 'Unrecognized task type', 1); + $self->call('update_progress', -1); + return; + } + + my @targets = split /,/, $self->{'task_data'}->{'subnet'}; + + my $global_step = 100 / (scalar @targets); + my $global_percent = 0; + my $i = 0; + foreach my $target (@targets) { + my @data; + my @modules; + + $self->{'step'} = STEP_DATABASE_SCAN; + $self->{'c_network_name'} = $target; + $self->{'c_network_percent'} = 0; + + # Send message + call('message', 'Checking target ' . $target, 10); + + # Force target acquirement. + $self->{'task_data'}->{'dbhost'} = $target; + $self->{'task_data'}->{'target_index'} = $i++; + + # Update progress + $self->{'c_network_percent'} = 10; + $self->call('update_progress', $global_percent + (10 / (scalar @targets))); + + # Connect to target. + my $dbObj = PandoraFMS::Recon::Util::enterprise_new( + 'PandoraFMS::Recon::Applications::'.$type, + $self->{'task_data'} + ); + + if (!$dbObj->is_connected()) { + call('message', 'Cannot connect to target ' . $target, 3); + $global_percent += $global_step; + $self->{'c_network_percent'} = 90; + # Update progress + $self->call('update_progress', $global_percent + (90 / (scalar @targets))); + $self->{'summary'}->{'not_alive'} += 1; + push @modules, { + name => $type . ' connection', + type => 'generic_proc', + data => 0, + description => $type . ' availability' + }; + + } else { + my $dbObjCfg = $dbObj->get_config(); + + $self->{'summary'}->{'discovered'} += 1; + $self->{'summary'}->{'alive'} += 1; + + push @modules, { + name => $type . ' connection', + type => 'generic_proc', + data => 1, + description => $type . ' availability' + }; + + # Analyze. + $self->{'step'} = STEP_STATISTICS; + $self->{'c_network_percent'} = 30; + $self->call('update_progress', $global_percent + (30 / (scalar @targets))); + $self->{'c_network_name'} = $dbObj->get_host(); + + # Retrieve connection statistics. + # Retrieve uptime statistics + # Retrieve query stats + # Retrieve connections + # Retrieve innodb + # Retrieve cache + $self->{'c_network_percent'} = 50; + $self->call('update_progress', $global_percent + (50 / (scalar @targets))); + push @modules, $dbObj->get_statistics(); + + # Custom queries. + $self->{'step'} = STEP_CUSTOM_QUERIES; + $self->{'c_network_percent'} = 80; + $self->call('update_progress', $global_percent + (80 / (scalar @targets))); + push @modules, $dbObj->execute_custom_queries(); + + if (defined($dbObjCfg->{'scan_databases'}) + && $dbObjCfg->{'scan_databases'} == 1) { + # Skip database scan in Oracle tasks + next if $self->{'type'} == DISCOVERY_APP_ORACLE; + + my $__data = $dbObj->scan_databases(); + + if (ref($__data) eq "ARRAY") { + if (defined($dbObjCfg->{'agent_per_database'}) + && $dbObjCfg->{'agent_per_database'} == 1) { + # Agent per database detected. + push @data, @{$__data}; + } else { + # Merge modules into engine agent. + my @_modules = map { + map { $_ } @{$_->{'module_data'}} + } @{$__data}; + + push @modules, @_modules; + } + } + } + } + + # Put engine agent at the beginning of the list. + my $version = $dbObj->get_version(); + unshift @data,{ + 'agent_data' => { + 'agent_name' => $dbObj->get_agent_name(), + 'os' => $type, + 'os_version' => (defined($version) ? $version : 'Discovery'), + 'interval' => $self->{'task_data'}->{'interval_sweep'}, + 'id_group' => $self->{'task_data'}->{'id_group'}, + 'address' => $dbObj->get_host(), + 'description' => '', + }, + 'module_data' => \@modules, + }; + + $self->call('create_agents', \@data); + + # Destroy item. + undef($dbObj); + + $global_percent += $global_step; + $self->{'c_network_percent'} = 100; + $self->call('update_progress', $global_percent); + } + + # Update progress. + # Done! + $self->{'step'} = ''; + $self->call('update_progress', -1); + +} + ########################################################################## # Perform a network scan. ########################################################################## @@ -1371,8 +1642,23 @@ sub scan($) { # 1% $self->call('update_progress', 1); + if (defined($self->{'task_data'})) { + if ($self->{'task_data'}->{'type'} == DISCOVERY_APP_MYSQL + || $self->{'task_data'}->{'type'} == DISCOVERY_APP_ORACLE) { + # Database scan. + return $self->app_scan(); + } + + if ($self->{'task_data'}->{'type'} == DISCOVERY_CLOUD_AWS_RDS) { + # Cloud scan. + return $self->cloud_scan(); + } + } + # Find devices. $self->call('message', "[1/5] Scanning the network...", 3); + $self->{'step'} = STEP_SCANNING; + $self->call('update_progress', $progress); $self->scan_subnet(); # Read the local ARP cache. @@ -1381,20 +1667,23 @@ sub scan($) { # Get a list of found hosts. my @hosts = @{$self->get_hosts()}; if (scalar(@hosts) > 0 && $self->{'parent_detection'} == 1) { - # Delete previous connections. + + # Delete previous connections. $self->call('delete_connections'); - + # Connectivity from address forwarding tables. $self->call('message', "[1/4] Finding address forwarding table connectivity...", 3); + $self->{'step'} = STEP_AFT; ($progress, $step) = (50, 20.0 / scalar(@hosts)); # From 50% to 70%. for (my $i = 0; defined($hosts[$i]); $i++) { $self->call('update_progress', $progress); $progress += $step; $self->aft_connectivity($hosts[$i]); } - + # Connect hosts that are still unconnected using traceroute. $self->call('message', "[3/4] Finding traceroute connectivity.", 3); + $self->{'step'} = STEP_TRACEROUTE; ($progress, $step) = (70, 20.0 / scalar(@hosts)); # From 70% to 90%. foreach my $host (@hosts) { $self->call('update_progress', $progress); @@ -1402,9 +1691,10 @@ sub scan($) { next if ($self->has_parent($host) || $self->has_children($host)); $self->traceroute_connectivity($host); } - + # Connect hosts that are still unconnected using known gateways. $self->call('message', "[4/4] Finding host to gateway connectivity.", 3); + $self->{'step'} = STEP_GATEWAY; ($progress, $step) = (90, 10.0 / scalar(@hosts)); # From 70% to 90%. $self->get_routes(); # Update the route cache. foreach my $host (@hosts) { @@ -1416,8 +1706,9 @@ sub scan($) { } # Done! + $self->{'step'} = ''; $self->call('update_progress', -1); - + # Print debug information on found devices. $self->call('message', "[Summary]", 3); foreach my $host (@hosts) { @@ -1425,7 +1716,7 @@ sub scan($) { next unless defined($device); # Print device information. - my $dev_info = "Device: " . $device->{'type'} . " ("; + my $dev_info = "Device: " . $device->{'type'} . " ("; foreach my $ip_address ($self->get_addresses($host)) { $dev_info .= "$ip_address,"; } @@ -1473,13 +1764,13 @@ sub snmp_get($$$) { if (scalar(@vlans) == 0) { my $command = $self->snmp_get_command($device, $oid, $community); @output = `$command`; - } - else { + }else { + # Handle duplicate lines. my %output_hash; foreach my $vlan (@vlans) { my $command = $self->snmp_get_command($device, $oid, $community, $vlan); - foreach my $line (`$vlan`) { + foreach my $line (`$command`) { $output_hash{$line} = 1; } } @@ -1528,7 +1819,7 @@ sub snmp_get_value($$$) { my @output = $self->snmp_get($device, $oid); foreach my $line (@output) { - chomp ($line); + chomp($line); return $1 if ($line =~ /^$oid\s+=\s+\S+:\s+(.*)$/); } @@ -1544,7 +1835,7 @@ sub snmp_get_value_array($$$) { my @output = $self->snmp_get($device, $oid); foreach my $line (@output) { - chomp ($line); + chomp($line); push(@values, $1) if ($line =~ /^$oid\S*\s+=\s+\S+:\s+(.*)$/); } @@ -1575,22 +1866,20 @@ sub traceroute_connectivity($$) { # Perform a traceroute. my $nmap_args = '-nsP -PE --traceroute --max-retries '.$self->{'icmp_checks'}.' --host-timeout '.$self->{'icmp_timeout'}.'s -T'.$self->{'recon_timing_template'}; my $np = PandoraFMS::Recon::NmapParser->new(); - eval { - $np->parsescan($self->{'nmap'}, $nmap_args, ($host)); - }; + eval {$np->parsescan($self->{'nmap'}, $nmap_args, ($host));}; return if ($@); - + # Get hops to the host. - my ($h) = $np->all_hosts (); - return unless defined ($h); - my @hops = $h->all_trace_hops (); + my ($h) = $np->all_hosts(); + return unless defined($h); + my @hops = $h->all_trace_hops(); # Skip the target host. pop(@hops); - + # Reverse the host order (closest hosts first). @hops = reverse(@hops); - + # Look for parents. my $device = $host; for (my $i = 0; $i < $self->{'parent_recursion'}; $i++) { @@ -1601,7 +1890,7 @@ sub traceroute_connectivity($$) { $self->call('create_agent', $parent); $self->call('message', "Host $device is one hop away from host $parent.", 5); - $self->mark_connected($parent, '', $device, ''); + $self->mark_connected($parent, '', $device, ''); # Move on to the next hop. $device = $parent; @@ -1643,7 +1932,10 @@ sub wmi_scan { my $auth = $self->responds_to_wmi($target); return unless defined($auth); + $self->{'summary'}->{'WMI'} += 1; + $self->call('message', "[".$target."] WMI available.", 10); + # Create the agent if it does not exist. my $agent_id = $self->call('create_agent', $target); next unless defined($agent_id); @@ -1651,56 +1943,19 @@ sub wmi_scan { # CPU. my @cpus = $self->wmi_get_value_array($target, $auth, 'SELECT DeviceId FROM Win32_Processor', 0); foreach my $cpu (@cpus) { - $self->call( - 'wmi_module', - ( - $agent_id, - $target, - "SELECT LoadPercentage FROM Win32_Processor WHERE DeviceId='$cpu'", - $auth, - 1, - "CPU Load $cpu", - "Load for $cpu (%)", - 'generic_data' - ) - ); + $self->call('wmi_module',($agent_id,$target,"SELECT LoadPercentage FROM Win32_Processor WHERE DeviceId='$cpu'",$auth,1,"CPU Load $cpu","Load for $cpu (%)",'generic_data')); } # Memory. my $mem = $self->wmi_get_value($target, $auth, 'SELECT FreePhysicalMemory FROM Win32_OperatingSystem', 0); if (defined($mem)) { - $self->call('wmi_module', - ( - $agent_id, - $target, - "SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem", - $auth, - 0, - 'FreeMemory', - 'Free memory', - 'generic_data', - 'KB' - ) - ); + $self->call('wmi_module',($agent_id,$target,"SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem",$auth,0,'FreeMemory','Free memory','generic_data','KB')); } # Disk. my @units = $self->wmi_get_value_array($target, $auth, 'SELECT DeviceID FROM Win32_LogicalDisk', 0); foreach my $unit (@units) { - $self->call( - 'wmi_module', - ( - $agent_id, - $target, - "SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='$unit'", - $auth, - 1, - "FreeDisk $unit", - 'Available disk space in kilobytes', - 'generic_data', - 'KB' - ) - ); + $self->call('wmi_module',($agent_id,$target,"SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='$unit'",$auth,1,"FreeDisk $unit",'Available disk space in kilobytes','generic_data','KB')); } } diff --git a/pandora_server/lib/PandoraFMS/Recon/Util.pm b/pandora_server/lib/PandoraFMS/Recon/Util.pm index 85d251ff1b..64cc7b28e6 100644 --- a/pandora_server/lib/PandoraFMS/Recon/Util.pm +++ b/pandora_server/lib/PandoraFMS/Recon/Util.pm @@ -12,16 +12,47 @@ use lib '/usr/lib/perl5'; use Socket qw/inet_aton/; our @ISA = ("Exporter"); -our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); +our %EXPORT_TAGS = ( 'all' => [qw( )] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( - ip_to_long - mac_matches - mac_to_dec - parse_mac - subnet_matches + enterprise_new + ip_to_long + mac_matches + mac_to_dec + parse_mac + subnet_matches ); +######################################################################################## +# Return an Enterprise Recon object. +######################################################################################## +sub enterprise_new($$) { + my ($class, $arguments) = @_; + + my @args; + if (ref($arguments) eq "HASH") { + @args = %{$arguments}; + } + if (ref($arguments) eq "ARRAY") { + @args = @{$arguments}; + } + + # Try to load the module + if ($^O eq 'MSWin32') { + # If the Windows service dies the service is stopped, even inside an eval ($RUN is set to 0)! + eval 'local $SIG{__DIE__}; require '.$class.';'; + }else { + eval 'require '.$class.';'; + } + if ($@) { + # Not loaded. + return undef; + } + + return new $class(@args); +} + + ######################################################################################## # Return the numeric representation of the given IP address. ######################################################################################## @@ -35,13 +66,13 @@ sub ip_to_long($) { # Returns 1 if the two given MAC addresses are the same. ######################################################################################## sub mac_matches($$) { - my ($mac_1, $mac_2) = @_; + my ($mac_1, $mac_2) = @_; - if (parse_mac($mac_1) eq parse_mac($mac_2)) { - return 1; - } + if (parse_mac($mac_1) eq parse_mac($mac_2)) { + return 1; + } - return 0; + return 0; } ######################################################################################## @@ -53,7 +84,7 @@ sub mac_to_dec($) { my $dec_mac = ''; my @elements = split(/:/, $mac); foreach my $element (@elements) { - $dec_mac .= unpack('s', pack 's', hex($element)) . '.' + $dec_mac .= unpack('s', pack 's', hex($element)) . '.'; } chop($dec_mac); @@ -64,23 +95,23 @@ sub mac_to_dec($) { # Make sure all MAC addresses are in the same format (00 11 22 33 44 55 66). ######################################################################################## sub parse_mac($) { - my ($mac) = @_; + my ($mac) = @_; - # Remove leading and trailing whitespaces. - $mac =~ s/(^\s+)|(\s+$)//g; + # Remove leading and trailing whitespaces. + $mac =~ s/(^\s+)|(\s+$)//g; - # Replace whitespaces and dots with colons. - $mac =~ s/\s+|\./:/g; + # Replace whitespaces and dots with colons. + $mac =~ s/\s+|\./:/g; - # Convert hex digits to uppercase. - $mac =~ s/([a-f])/\U$1/g; + # Convert hex digits to uppercase. + $mac =~ s/([a-f])/\U$1/g; - # Add a leading 0 to single digits. - $mac =~ s/^([0-9A-F]):/0$1:/g; - $mac =~ s/:([0-9A-F]):/:0$1:/g; - $mac =~ s/:([0-9A-F])$/:0$1/g; + # Add a leading 0 to single digits. + $mac =~ s/^([0-9A-F]):/0$1:/g; + $mac =~ s/:([0-9A-F]):/:0$1:/g; + $mac =~ s/:([0-9A-F])$/:0$1/g; - return $mac; + return $mac; } ######################################################################################## @@ -95,6 +126,7 @@ sub subnet_matches($$;$) { $netaddr = $subnet; $netmask = ip_to_long($mask); } + # CIDR notation. else { ($netaddr, $netmask) = split('/', $subnet); diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm index aababebd69..6271698c03 100755 --- a/pandora_server/lib/PandoraFMS/Tools.pm +++ b/pandora_server/lib/PandoraFMS/Tools.pm @@ -29,6 +29,7 @@ use Sys::Syslog; use Scalar::Util qw(looks_like_number); use LWP::UserAgent; use threads; +use threads::shared; # New in 3.2. Used to sendmail internally, without external scripts # use Module::Loaded; @@ -120,6 +121,7 @@ our @EXPORT = qw( month_have_days translate_obj valid_regex + read_file set_file_permissions uri_encode check_server_threads @@ -330,6 +332,28 @@ my @ServerThreads; # Keep threads running. our $THRRUN :shared = 1; +########################################################################## +## Reads a file and returns entire content or undef if error. +########################################################################## +sub read_file { + my $path = shift; + + my $_FILE; + if( !open($_FILE, "<", $path) ) { + # failed to open, return undef + return undef; + } + + # Slurp configuration file content. + my $content = do { local $/; <$_FILE> }; + + # Close file + close($_FILE); + + return $content; +} + + ############################################################################### # Sets user:group owner for the given file ############################################################################### @@ -1359,45 +1383,27 @@ sub cron_next_execution { } # Get day of the week and month from cron config - my ($mday, $wday) = (split (/\s/, $cron))[2, 4]; + my ($wday) = (split (/\s/, $cron))[4]; + # Check the wday values to avoid infinite loop + my ($wday_down, $wday_up) = cron_get_interval($wday); + if ($wday_down ne "*" && ($wday_down > 6 || (defined($wday_up) && $wday_up > 6))) { + $wday = "*"; + } # Get current time and day of the week my $cur_time = time(); my $cur_wday = (localtime ($cur_time))[6]; - # Any day of the week - if ($wday eq '*') { - my $nex_time = cron_next_execution_date ($cron, $cur_time, $interval); - return $nex_time - time(); - } - # A range? - else { - $wday = cron_get_closest_in_range ($cur_wday, $wday); + my $nex_time = cron_next_execution_date ($cron, $cur_time, $interval); + + # Check the day + while (!cron_check_interval($wday, (localtime ($nex_time))[6])) { + # If it does not acomplish the day of the week, go to the next day. + $nex_time += 86400; + $nex_time = cron_next_execution_date ($cron, $nex_time, 0); } - # A specific day of the week - my $count = 0; - my $nex_time = $cur_time; - do { - $nex_time = cron_next_execution_date ($cron, $nex_time, $interval); - my $nex_time_wd = $nex_time; - my ($nex_mon, $nex_wday) = (localtime ($nex_time_wd))[4, 6]; - my $nex_mon_wd; - do { - # Check the day of the week - if ($nex_wday == $wday) { - return $nex_time_wd - time(); - } - - # Move to the next day of the month - $nex_time_wd += 86400; - ($nex_mon_wd, $nex_wday) = (localtime ($nex_time_wd))[4, 6]; - } while ($mday eq '*' && $nex_mon_wd == $nex_mon); - $count++; - } while ($count < 60); - - # Something went wrong, default to 5 minutes - return $interval; + return $nex_time - time(); } ############################################################################### # Get the number of seconds left to the next execution of the given cron entry. @@ -1409,6 +1415,30 @@ sub cron_check_syntax ($) { return ($cron =~ m/^(\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+$/); } ############################################################################### +# Check if a value is inside an interval. +############################################################################### +sub cron_check_interval { + my ($elem_cron, $elem_curr_time) = @_; + + # Return 1 if wildcard. + return 1 if ($elem_cron eq "*"); + + my ($down, $up) = cron_get_interval($elem_cron); + # Check if it is not a range + if (!defined($up)) { + return ($down == $elem_curr_time) ? 1 : 0; + } + + # Check if it is on the range + if ($down < $up) { + return 0 if ($elem_curr_time < $down || $elem_curr_time > $up); + } else { + return 0 if ($elem_curr_time > $down || $elem_curr_time < $up); + } + + return 1; +} +############################################################################### # Get the next execution date for the given cron entry in seconds since epoch. ############################################################################### sub cron_next_execution_date { @@ -1445,8 +1475,7 @@ sub cron_next_execution_date { my @nex_time_array = @curr_time_array; # Update minutes - my ($min_down, undef) = cron_get_interval ($min); - $nex_time_array[0] = ($min_down eq '*') ? 0 : $min_down; + $nex_time_array[0] = cron_get_next_time_element($min); $nex_time = cron_valid_date(@nex_time_array, $cur_year); if ($nex_time >= $cur_time) { @@ -1479,8 +1508,7 @@ sub cron_next_execution_date { return $nex_time if cron_is_in_cron(\@cron_array, \@nex_time_array); #Update the hour if fails - my ($hour_down, undef) = cron_get_interval ($hour); - $nex_time_array[1] = ($hour_down eq '*') ? 0 : $hour_down; + $nex_time_array[1] = cron_get_next_time_element($hour); # When an overflow is passed check the hour update again $nex_time = cron_valid_date(@nex_time_array, $cur_year); @@ -1507,8 +1535,7 @@ sub cron_next_execution_date { return $nex_time if cron_is_in_cron(\@cron_array, \@nex_time_array); #Update the day if fails - my ($mday_down, undef) = cron_get_interval ($mday); - $nex_time_array[2] = ($mday_down eq '*') ? 1 : $mday_down; + $nex_time_array[2] = cron_get_next_time_element($mday, 1); # When an overflow is passed check the hour update in the next execution $nex_time = cron_valid_date(@nex_time_array, $cur_year); @@ -1530,8 +1557,7 @@ sub cron_next_execution_date { return $nex_time if cron_is_in_cron(\@cron_array, \@nex_time_array); #Update the month if fails - my ($mon_down, undef) = cron_get_interval ($mon); - $nex_time_array[3] = ($mon_down eq '*') ? 0 : $mon_down; + $nex_time_array[3] = cron_get_next_time_element($mon); # When an overflow is passed check the hour update in the next execution $nex_time = cron_valid_date(@nex_time_array, $cur_year); @@ -1560,22 +1586,30 @@ sub cron_is_in_cron { #If there is no elements means that is in cron return 1 unless (defined($elem_cron) || defined($elem_curr_time)); - # Go to last element if current is a wild card - if ($elem_cron ne '*') { - my ($down, $up) = cron_get_interval($elem_cron); - # Check if there is no a range - return 0 if (!defined($up) && ($down != $elem_curr_time)); - # Check if there is on the range - if (defined($up)) { - if ($down < $up) { - return 0 if ($elem_curr_time < $down || $elem_curr_time > $up); - } else { - return 0 if ($elem_curr_time > $down || $elem_curr_time < $up); - } - } - } + # Check the element interval + return 0 unless (cron_check_interval($elem_cron, $elem_curr_time)); + return cron_is_in_cron(\@deref_elems_cron, \@deref_elems_curr_time); } +################################################################################ +#Get the next tentative time for a cron value or interval in case of overflow. +#Floor data is the minimum localtime data for a position. Ex: +#Ex: +# * should returns floor data. +# 5 should returns 5. +# 10-55 should returns 10. +# 55-10 should retunrs floor data. +################################################################################ +sub cron_get_next_time_element { + # Default floor data is 0 + my ($curr_element, $floor_data) = @_; + $floor_data = 0 unless defined($floor_data); + + my ($elem_down, $elem_up) = cron_get_interval ($curr_element); + return ($elem_down eq '*' || (defined($elem_up) && $elem_down > $elem_up)) + ? $floor_data + : $elem_down; +} ############################################################################### # Returns the interval of a cron element. If there is not a range, # returns an array with the first element in the first place of array @@ -1774,7 +1808,7 @@ sub api_call_url { my $ua = LWP::UserAgent->new(); - $ua->timeout($options->{lwp_timeout}); + $ua->timeout($pa_config->{'tcp_timeout'}); # Enable environmental proxy settings $ua->env_proxy; # Enable in-memory cookie management @@ -1831,7 +1865,7 @@ sub stop_server_threads { $THRRUN = 0; foreach my $thr (@ServerThreads) { - $thr->detach(); + $thr->join(); } @ServerThreads = (); diff --git a/pandora_server/pandora_server.redhat.spec b/pandora_server/pandora_server.redhat.spec index 412f187003..0da7565ea1 100644 --- a/pandora_server/pandora_server.redhat.spec +++ b/pandora_server/pandora_server.redhat.spec @@ -2,8 +2,8 @@ # Pandora FMS Server # %define name pandorafms_server -%define version 7.0NG.732 -%define release 190319 +%define version 7.0NG.735 +%define release 190610 Summary: Pandora FMS Server Name: %{name} diff --git a/pandora_server/pandora_server.spec b/pandora_server/pandora_server.spec index 35b6134867..80738a60cc 100644 --- a/pandora_server/pandora_server.spec +++ b/pandora_server/pandora_server.spec @@ -2,8 +2,8 @@ # Pandora FMS Server # %define name pandorafms_server -%define version 7.0NG.732 -%define release 190319 +%define version 7.0NG.735 +%define release 190610 Summary: Pandora FMS Server Name: %{name} diff --git a/pandora_server/pandora_server_installer b/pandora_server/pandora_server_installer index 8841e33b5f..7c7d05d8d9 100755 --- a/pandora_server/pandora_server_installer +++ b/pandora_server/pandora_server_installer @@ -8,8 +8,8 @@ # This code is licensed under GPL 2.0 license. # ********************************************************************** -PI_VERSION="7.0NG.732" -PI_BUILD="190319" +PI_VERSION="7.0NG.735" +PI_BUILD="190610" MODE=$1 if [ $# -gt 1 ]; then diff --git a/pandora_server/util/PandoraFMS b/pandora_server/util/PandoraFMS deleted file mode 120000 index fde1b9ac41..0000000000 --- a/pandora_server/util/PandoraFMS +++ /dev/null @@ -1 +0,0 @@ -../bin/PandoraFMS/ \ No newline at end of file diff --git a/pandora_server/util/PandoraFMS b/pandora_server/util/PandoraFMS new file mode 100644 index 0000000000..fde1b9ac41 --- /dev/null +++ b/pandora_server/util/PandoraFMS @@ -0,0 +1 @@ +../bin/PandoraFMS/ \ No newline at end of file diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl index 85fc414caa..e46e867adf 100644 --- a/pandora_server/util/pandora_db.pl +++ b/pandora_server/util/pandora_db.pl @@ -34,7 +34,7 @@ use PandoraFMS::Config; use PandoraFMS::DB; # version: define current version -my $version = "7.0NG.732 PS190319"; +my $version = "7.0NG.735 PS190610"; # Pandora server configuration my %conf; @@ -340,7 +340,7 @@ sub pandora_purgedb ($$) { } else { my @blacklist_types = ("'SLA_services'", "'custom_graph'", "'sql_graph_vbar'", "'sql_graph_hbar'", "'sql_graph_pie'", "'database_serialized'", "'sql'", "'inventory'", "'inventory_changes'", - "'netflow_area'", "'netflow_pie'", "'netflow_data'", "'netflow_statistics'", "'netflow_summary'"); + "'netflow_area'", "'netflow_data'", "'netflow_summary'"); my $blacklist_types_str = join(',', @blacklist_types); # Deleted modules @@ -429,16 +429,27 @@ sub pandora_purgedb ($$) { WHERE date < CURDATE() - $conf->{'_num_past_special_days'} AND date > '0001-01-01'"); } } - + # Delete old tgraph_source data db_do ($dbh,"DELETE FROM tgraph_source WHERE id_graph NOT IN (SELECT id_graph FROM tgraph)"); + # Delete network traffic old data. + log_message ('PURGE', 'Deleting old network matrix data.'); + if ($conf->{'_delete_old_network_matrix'} > 0) { + my $matrix_limit = time() - 86400 * $conf->{'_delete_old_network_matrix'}; + db_do ($dbh, "DELETE FROM tnetwork_matrix WHERE utimestamp < ?", $matrix_limit); + } + # Delete old messages log_message ('PURGE', "Deleting old messages."); if ($conf->{'_delete_old_messages'} > 0) { my $message_limit = time() - 86400 * $conf->{'_delete_old_messages'}; db_do ($dbh, "DELETE FROM tmensajes WHERE timestamp < ?", $message_limit); } + + # Delete old cache data + log_message ('PURGE', "Deleting old cache data."); + db_do ($dbh, "DELETE FROM `tvisual_console_elements_cache` WHERE (UNIX_TIMESTAMP(`created_at`) + `expiration`) < UNIX_TIMESTAMP()"); } ############################################################################### @@ -659,6 +670,7 @@ sub pandora_load_config_pdb ($) { $conf->{'_days_delete_unknown'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'days_delete_unknown'"); $conf->{'_inventory_purge'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'inventory_purge'"); $conf->{'_delete_old_messages'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'delete_old_messages'"); + $conf->{'_delete_old_network_matrix'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'delete_old_network_matrix'"); $conf->{'_enterprise_installed'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'enterprise_installed'"); $conf->{'_metaconsole'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'metaconsole'"); $conf->{'_metaconsole_events_history'} = get_db_value ($dbh, "SELECT value FROM tconfig WHERE token = 'metaconsole_events_history'"); diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl index 7379821f9b..a47637761a 100644 --- a/pandora_server/util/pandora_manage.pl +++ b/pandora_server/util/pandora_manage.pl @@ -36,7 +36,7 @@ use Encode::Locale; Encode::Locale::decode_argv; # version: define current version -my $version = "7.0NG.732 PS190319"; +my $version = "7.0NG.735 PS190610"; # save program name for logging my $progname = basename($0); @@ -155,6 +155,7 @@ sub help_screen{ help_screen_line('--update_module', ' ', 'Update a module field'); help_screen_line('--get_agents_module_current_data', '', "Get the agent and current data \n\t of all the modules with a given name"); help_screen_line('--create_network_module_from_component', ' ', "Create a new network \n\t module from a network component"); + help_screen_line('--create_network_component', " \n\t [ \n\t \n\t \n\t \n\t \n\t \n\t ]", "Create a new network component"); help_screen_line('--create_synthetic', " ,,|, \n\t [ , | ,, ]", "Create a new Synthetic module"); print "\nALERTS:\n\n" unless $param ne ''; help_screen_line('--create_template_module', ' ', 'Add alert template to module'); @@ -1685,6 +1686,23 @@ sub cli_create_network_module_from_component() { pandora_create_module_from_network_component ($conf, $component, $agent_id, $dbh); } +############################################################################## +# Create a network component. +# Related option: --create_network_component +############################################################################## +sub cli_create_network_component() { + my ($c_name, $c_group, $c_type) = @ARGV[2..4]; + my @todo = @ARGV[5..20]; + my $other = join('|', @todo); + my @todo2 = @ARGV[22..26]; + my $other2 = join('|', @todo2); + + # Call the API. + my $result = api_call( $conf, 'set', 'new_network_component', $c_name, undef, "$c_type|$other|$c_group|$other2"); + + print "$result \n\n "; +} + ############################################################################## # Create netflow filter # Related option: --create_netflow_filter @@ -6204,6 +6222,10 @@ sub pandora_manage_main ($$$) { param_check($ltotal, 2); cli_create_network_module_from_component(); } + elsif ($param eq '--create_network_component') { + param_check($ltotal, 24, 21); + cli_create_network_component(); + } elsif ($param eq '--create_netflow_filter') { param_check($ltotal, 5); cli_create_netflow_filter(); diff --git a/pandora_server/util/recon_scripts/PandoraFMS b/pandora_server/util/recon_scripts/PandoraFMS deleted file mode 120000 index d77e8a956e..0000000000 --- a/pandora_server/util/recon_scripts/PandoraFMS +++ /dev/null @@ -1 +0,0 @@ -../../bin/PandoraFMS/ \ No newline at end of file diff --git a/pandora_server/util/recon_scripts/PandoraFMS b/pandora_server/util/recon_scripts/PandoraFMS new file mode 100644 index 0000000000..d77e8a956e --- /dev/null +++ b/pandora_server/util/recon_scripts/PandoraFMS @@ -0,0 +1 @@ +../../bin/PandoraFMS/ \ No newline at end of file diff --git a/visual_console_client/.eslintrc b/visual_console_client/.eslintrc new file mode 100644 index 0000000000..af67551834 --- /dev/null +++ b/visual_console_client/.eslintrc @@ -0,0 +1,19 @@ +{ + "env": { + "browser": true, + "node": true, + "jest": true + }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "prettier"], + "extends": [ + "eslint:recommended", + "plugin:prettier/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "no-console": "off", + "@typescript-eslint/indent": "off", + "@typescript-eslint/explicit-function-return-type": "off" + } +} diff --git a/visual_console_client/.gitignore b/visual_console_client/.gitignore new file mode 100644 index 0000000000..46091fbe36 --- /dev/null +++ b/visual_console_client/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build +/dist + +# editor +# .vscode + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/visual_console_client/.vscode/launch.json b/visual_console_client/.vscode/launch.json new file mode 100644 index 0000000000..115b2f51b6 --- /dev/null +++ b/visual_console_client/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense para saber los atributos posibles. + // Mantenga el puntero para ver las descripciones de los existentes atributos + // Para más información, visite: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Debug", + "url": "http://localhost:8080", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/visual_console_client/.vscode/settings.json b/visual_console_client/.vscode/settings.json new file mode 100644 index 0000000000..024b36878e --- /dev/null +++ b/visual_console_client/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "editor.formatOnSave": true, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ] +} diff --git a/visual_console_client/README.md b/visual_console_client/README.md new file mode 100644 index 0000000000..b480fac939 --- /dev/null +++ b/visual_console_client/README.md @@ -0,0 +1 @@ +# Visual Console Client diff --git a/visual_console_client/__mocks__/fileMock.js b/visual_console_client/__mocks__/fileMock.js new file mode 100644 index 0000000000..0a445d0600 --- /dev/null +++ b/visual_console_client/__mocks__/fileMock.js @@ -0,0 +1 @@ +module.exports = "test-file-stub"; diff --git a/visual_console_client/__mocks__/styleMock.js b/visual_console_client/__mocks__/styleMock.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/visual_console_client/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/visual_console_client/jest.config.js b/visual_console_client/jest.config.js new file mode 100644 index 0000000000..ec2b954ec2 --- /dev/null +++ b/visual_console_client/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + roots: ["/src"], + transform: { + "^.+\\.tsx?$": "ts-jest" + }, + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + // This configuration is used to mock the css and file imports used by Webpack. + // https://jestjs.io/docs/en/webpack.html + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": + "/__mocks__/fileMock.js", + "\\.(css|less)$": "/__mocks__/styleMock.js" + } +}; diff --git a/visual_console_client/package-lock.json b/visual_console_client/package-lock.json new file mode 100644 index 0000000000..c5a4da6f7a --- /dev/null +++ b/visual_console_client/package-lock.json @@ -0,0 +1,7957 @@ +{ + "name": "pandora-fms-visual-console", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz", + "integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.0", + "@babel/helpers": "^7.4.3", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "requires": { + "minimist": "^1.2.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "@babel/generator": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", + "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", + "requires": { + "@babel/types": "^7.4.0", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==" + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", + "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", + "requires": { + "@babel/types": "^7.4.0" + } + }, + "@babel/helpers": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.3.tgz", + "integrity": "sha512-BMh7X0oZqb36CfyhvtbSmcWc3GXocfxv3yNsAEuM0l+fAqSO22rQrUpijr3oE/10jCTrB6/0b9kzmG4VetCj8Q==", + "requires": { + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz", + "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==" + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", + "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.0", + "@babel/types": "^7.4.0" + } + }, + "@babel/traverse": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz", + "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.0", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/types": "^7.4.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "@babel/types": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", + "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@jest/console": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", + "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", + "requires": { + "@jest/source-map": "^24.3.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.7.1.tgz", + "integrity": "sha512-ivlZ8HX/FOASfHcb5DJpSPFps8ydfUYzLZfgFFqjkLijYysnIEOieg72YRhO4ZUB32xu40hsSMmaw+IGYeKONA==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.7.0", + "jest-config": "^24.7.1", + "jest-haste-map": "^24.7.1", + "jest-message-util": "^24.7.1", + "jest-regex-util": "^24.3.0", + "jest-resolve-dependencies": "^24.7.1", + "jest-runner": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-snapshot": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "jest-watcher": "^24.7.1", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "@jest/environment": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.7.1.tgz", + "integrity": "sha512-wmcTTYc4/KqA+U5h1zQd5FXXynfa7VGP2NfF+c6QeGJ7c+2nStgh65RQWNX62SC716dTtqheTRrZl0j+54oGHw==", + "requires": { + "@jest/fake-timers": "^24.7.1", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "jest-mock": "^24.7.0" + } + }, + "@jest/fake-timers": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.7.1.tgz", + "integrity": "sha512-4vSQJDKfR2jScOe12L9282uiwuwQv9Lk7mgrCSZHA9evB9efB/qx8i0KJxsAKtp8fgJYBJdYY7ZU6u3F4/pyjA==", + "requires": { + "@jest/types": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-mock": "^24.7.0" + } + }, + "@jest/reporters": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.7.1.tgz", + "integrity": "sha512-bO+WYNwHLNhrjB9EbPL4kX/mCCG4ZhhfWmO3m4FSpbgr7N83MFejayz30kKjgqr7smLyeaRFCBQMbXpUgnhAJw==", + "requires": { + "@jest/environment": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-api": "^2.1.1", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-source-maps": "^3.0.1", + "jest-haste-map": "^24.7.1", + "jest-resolve": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "node-notifier": "^5.2.1", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/source-map": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", + "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/test-result": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.7.1.tgz", + "integrity": "sha512-3U7wITxstdEc2HMfBX7Yx3JZgiNBubwDqQMh+BXmZXHa3G13YWF3p6cK+5g0hGkN3iufg/vGPl3hLxQXD74Npg==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.7.1.tgz", + "integrity": "sha512-84HQkCpVZI/G1zq53gHJvSmhUer4aMYp9tTaffW28Ih5OxfCg8hGr3nTSbL1OhVDRrFZwvF+/R9gY6JRkDUpUA==", + "requires": { + "@jest/test-result": "^24.7.1", + "jest-haste-map": "^24.7.1", + "jest-runner": "^24.7.1", + "jest-runtime": "^24.7.1" + } + }, + "@jest/transform": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.7.1.tgz", + "integrity": "sha512-EsOUqP9ULuJ66IkZQhI5LufCHlTbi7hrcllRMUEV/tOgqBVQi93+9qEvkX0n8mYpVXQ8VjwmICeRgg58mrtIEw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.7.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.7.1", + "jest-regex-util": "^24.3.0", + "jest-util": "^24.7.1", + "micromatch": "^3.1.10", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/types": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.7.0.tgz", + "integrity": "sha512-ipJUa2rFWiKoBqMKP63Myb6h9+iT3FHRTF2M8OR6irxWzItisa8i4dcSg14IbvmXUnBlHBlUQPYUHWyX3UPpYA==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/yargs": "^12.0.9" + } + }, + "@types/babel__core": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.1.tgz", + "integrity": "sha512-+hjBtgcFPYyCTo0A15+nxrCVJL7aC6Acg87TXd5OW3QhHswdrOLoles+ldL2Uk8q++7yIfl4tURtztccdeeyOw==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", + "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", + "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==" + }, + "@types/d3-shape": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.1.tgz", + "integrity": "sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", + "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==" + }, + "@types/jest": { + "version": "24.0.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.11.tgz", + "integrity": "sha512-2kLuPC5FDnWIDvaJBzsGTBQaBbnDweznicvK7UGYzlIJP4RJR2a4A/ByLUXEyEgag6jz8eHdlWExGDtH3EYUXQ==", + "requires": { + "@types/jest-diff": "*" + } + }, + "@types/jest-diff": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz", + "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==" + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "@types/yargs": { + "version": "12.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", + "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz", + "integrity": "sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==", + "requires": { + "@typescript-eslint/parser": "1.6.0", + "@typescript-eslint/typescript-estree": "1.6.0", + "requireindex": "^1.2.0", + "tsutils": "^3.7.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.6.0.tgz", + "integrity": "sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==", + "requires": { + "@typescript-eslint/typescript-estree": "1.6.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz", + "integrity": "sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==", + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + }, + "acorn-globals": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", + "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==" + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" + }, + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==" + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" + }, + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "requires": { + "lodash": "^4.17.11" + } + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "awesome-typescript-loader": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", + "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==", + "requires": { + "chalk": "^2.4.1", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.1.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.3", + "webpack-log": "^1.2.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "babel-jest": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", + "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", + "requires": { + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.6.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz", + "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==", + "requires": { + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.0.0", + "test-exclude": "^5.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", + "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", + "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.6.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==" + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", + "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", + "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==" + }, + "camelcase": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz", + "integrity": "sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ==" + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "chokidar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", + "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.0" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-webpack-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-2.0.1.tgz", + "integrity": "sha512-vway5pXGVd91bicwjaf8j188Al6VMf9R9Ekl6q0qeiaWStRsOOXuh4qtjX1UrUvmz5XevQVCdjBuzr4Tzsnpog==", + "requires": { + "del": "^4.0.0" + }, + "dependencies": { + "del": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-4.0.0.tgz", + "integrity": "sha512-/BnSJ+SuZyLu7xMn48kZY0nMXDi+5KNmR4g8n21Wivsl8+B9njV6/5kcTNE9juSprp0zRWBU28JuHUq0FqK1Nw==", + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.2" + } + }, + "is-path-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.0.0.tgz", + "integrity": "sha512-m5dHHzpOXEiv18JEORttBO64UgTEypx99vCxQLjbBvGhOJxnTNglYoFXxwo6AbsQb79sqqycQEHv2hWkHZAijA==" + }, + "is-path-in-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.0.0.tgz", + "integrity": "sha512-6Vz5Gc9s/sDA3JBVu0FzWufm8xaBsqy1zn8Q6gmvGP6nSDMw78aS4poBNeatWjaRpTpxxLn1WOndAiOlk+qY8A==", + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "p-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.0.0.tgz", + "integrity": "sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "compare-versions": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", + "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "compressible": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", + "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "requires": { + "mime-db": ">= 1.38.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", + "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0", + "require-from-string": "^2.0.1" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-loader": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz", + "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==", + "requires": { + "camelcase": "^5.2.0", + "icss-utils": "^4.1.0", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.14", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^2.0.6", + "postcss-modules-scope": "^2.1.0", + "postcss-modules-values": "^2.0.0", + "postcss-value-parser": "^3.3.0", + "schema-utils": "^1.0.0" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssom": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", + "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==" + }, + "cssstyle": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", + "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "requires": { + "cssom": "0.3.x" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "d3-path": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz", + "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==" + }, + "d3-shape": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", + "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==", + "requires": { + "d3-path": "1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "requires": { + "strip-bom": "^3.0.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.0.tgz", + "integrity": "sha512-C4kvKNlYrwXhKxz97BuohF8YoGgQ23Xm9lvoHmgT7JaPGprSEjk3+XFled74Yt/x0ZABUHg2D67covzAPUKx5Q==", + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "diff-sequences": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", + "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.47", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.47.tgz", + "integrity": "sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } + } + }, + "eslint-config-prettier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.1.0.tgz", + "integrity": "sha512-zILwX9/Ocz4SV2vX7ox85AsrAgXV3f2o2gpIicdMIOra48WYqgUnWNH/cR/iHtmD2Vb3dLSC3LiEJnS05Gkw7w==", + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-plugin-prettier": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz", + "integrity": "sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==", + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==" + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==" + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", + "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==" + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "expect": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz", + "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==", + "requires": { + "@jest/types": "^24.7.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.3.0", + "jest-matcher-utils": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-regex-util": "^24.3.0" + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "requires": { + "bser": "^2.0.0" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", + "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + } + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==" + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "requires": { + "debug": "^3.2.6" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==" + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==" + }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-parser-js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==" + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "icss-utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.0.tgz", + "integrity": "sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ==", + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", + "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + } + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + } + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-path-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.0.0.tgz", + "integrity": "sha512-m5dHHzpOXEiv18JEORttBO64UgTEypx99vCxQLjbBvGhOJxnTNglYoFXxwo6AbsQb79sqqycQEHv2hWkHZAijA==" + }, + "is-path-in-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.0.0.tgz", + "integrity": "sha512-6Vz5Gc9s/sDA3JBVu0FzWufm8xaBsqy1zn8Q6gmvGP6nSDMw78aS4poBNeatWjaRpTpxxLn1WOndAiOlk+qY8A==", + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul-api": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", + "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", + "requires": { + "async": "^2.6.1", + "compare-versions": "^3.2.1", + "fileset": "^2.0.3", + "istanbul-lib-coverage": "^2.0.3", + "istanbul-lib-hook": "^2.0.3", + "istanbul-lib-instrument": "^3.1.0", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.2", + "istanbul-reports": "^2.1.1", + "js-yaml": "^3.12.0", + "make-dir": "^1.3.0", + "minimatch": "^3.0.4", + "once": "^1.4.0" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==" + }, + "istanbul-lib-hook": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", + "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", + "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "requires": { + "@babel/generator": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "istanbul-lib-coverage": "^2.0.3", + "semver": "^5.5.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", + "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "requires": { + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "supports-color": "^6.0.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", + "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "rimraf": "^2.6.2", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "istanbul-reports": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", + "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", + "requires": { + "handlebars": "^4.1.0" + } + }, + "jest": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.7.1.tgz", + "integrity": "sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA==", + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.7.1" + }, + "dependencies": { + "jest-cli": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.7.1.tgz", + "integrity": "sha512-32OBoSCVPzcTslGFl6yVCMzB2SqX3IrWwZCY5mZYkb0D2WsogmU3eV2o8z7+gRQa4o4sZPX/k7GU+II7CxM6WQ==", + "requires": { + "@jest/core": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^12.0.2" + } + } + } + }, + "jest-changed-files": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.7.0.tgz", + "integrity": "sha512-33BgewurnwSfJrW7T5/ZAXGE44o7swLslwh8aUckzq2e17/2Os1V0QU506ZNik3hjs8MgnEMKNkcud442NCDTw==", + "requires": { + "@jest/types": "^24.7.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", + "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.7.1", + "@jest/types": "^24.7.0", + "babel-jest": "^24.7.1", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.7.1", + "jest-environment-node": "^24.7.1", + "jest-get-type": "^24.3.0", + "jest-jasmine2": "^24.7.1", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.7.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", + "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.3.0", + "jest-get-type": "^24.3.0", + "pretty-format": "^24.7.0" + } + }, + "jest-docblock": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", + "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", + "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", + "requires": { + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.3.0", + "jest-util": "^24.7.1", + "pretty-format": "^24.7.0" + } + }, + "jest-environment-jsdom": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", + "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", + "requires": { + "@jest/environment": "^24.7.1", + "@jest/fake-timers": "^24.7.1", + "@jest/types": "^24.7.0", + "jest-mock": "^24.7.0", + "jest-util": "^24.7.1", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", + "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", + "requires": { + "@jest/environment": "^24.7.1", + "@jest/fake-timers": "^24.7.1", + "@jest/types": "^24.7.0", + "jest-mock": "^24.7.0", + "jest-util": "^24.7.1" + } + }, + "jest-get-type": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", + "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==" + }, + "jest-haste-map": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", + "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", + "requires": { + "@jest/types": "^24.7.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.4.0", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", + "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.7.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.7.1", + "jest-matcher-utils": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-snapshot": "^24.7.1", + "jest-util": "^24.7.1", + "pretty-format": "^24.7.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz", + "integrity": "sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ==", + "requires": { + "pretty-format": "^24.7.0" + } + }, + "jest-matcher-utils": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", + "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.7.0", + "jest-get-type": "^24.3.0", + "pretty-format": "^24.7.0" + } + }, + "jest-message-util": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", + "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", + "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", + "requires": { + "@jest/types": "^24.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==" + }, + "jest-regex-util": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", + "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==" + }, + "jest-resolve": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", + "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", + "requires": { + "@jest/types": "^24.7.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.7.1.tgz", + "integrity": "sha512-2Eyh5LJB2liNzfk4eo7bD1ZyBbqEJIyyrFtZG555cSWW9xVHxII2NuOkSl1yUYTAYCAmM2f2aIT5A7HzNmubyg==", + "requires": { + "@jest/types": "^24.7.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.7.1" + } + }, + "jest-runner": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.7.1.tgz", + "integrity": "sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.7.1", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.7.1", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.7.1", + "jest-jasmine2": "^24.7.1", + "jest-leak-detector": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-resolve": "^24.7.1", + "jest-runtime": "^24.7.1", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", + "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.7.1", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/yargs": "^12.0.2", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.7.1", + "jest-haste-map": "^24.7.1", + "jest-message-util": "^24.7.1", + "jest-mock": "^24.7.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.7.1", + "jest-snapshot": "^24.7.1", + "jest-util": "^24.7.1", + "jest-validate": "^24.7.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^12.0.2" + } + }, + "jest-serializer": { + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", + "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==" + }, + "jest-snapshot": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz", + "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==", + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "expect": "^24.7.1", + "jest-diff": "^24.7.0", + "jest-matcher-utils": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-resolve": "^24.7.1", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.7.0", + "semver": "^5.5.0" + } + }, + "jest-util": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", + "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/fake-timers": "^24.7.1", + "@jest/source-map": "^24.3.0", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "jest-validate": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", + "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", + "requires": { + "@jest/types": "^24.7.0", + "camelcase": "^5.0.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.3.0", + "leven": "^2.1.0", + "pretty-format": "^24.7.0" + } + }, + "jest-watcher": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.7.1.tgz", + "integrity": "sha512-Wd6TepHLRHVKLNPacEsBwlp9raeBIO+01xrN24Dek4ggTS8HHnOzYSFnvp+6MtkkJ3KfMzy220KTi95e2rRkrw==", + "requires": { + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/yargs": "^12.0.9", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.7.1", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", + "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "requires": { + "merge-stream": "^1.0.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=" + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "requires": { + "chalk": "^2.0.1" + } + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=" + }, + "loglevelnext": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", + "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", + "requires": { + "es6-symbol": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==" + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "requires": { + "tmpl": "1.0.x" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "requires": { + "mime-db": "~1.38.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "mini-css-extract-plugin": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", + "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" + }, + "node-libs-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" + }, + "node-notifier": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", + "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwsapi": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz", + "integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==" + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=" + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "portfinder": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-load-config": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", + "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "requires": { + "cosmiconfig": "^4.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", + "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^3.3.1" + } + }, + "postcss-modules-scope": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", + "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", + "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^7.0.6" + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "prettier": { + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", + "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==" + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-format": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", + "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==", + "requires": { + "@jest/types": "^24.7.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "prompts": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.4.tgz", + "integrity": "sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==", + "requires": { + "kleur": "^3.0.2", + "sisteransi": "^1.0.0" + } + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "requires": { + "util.promisify": "^1.0.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + } + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", + "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==" + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serialize-javascript": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", + "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==" + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sisteransi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", + "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==" + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==" + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "readable-stream": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=" + }, + "table": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", + "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", + "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==" + }, + "terser": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", + "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "requires": { + "commander": "^2.19.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.10" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "terser-webpack-plugin": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", + "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.16.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "test-exclude": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz", + "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==", + "requires": { + "arrify": "^1.0.1", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^1.0.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==" + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "ts-jest": { + "version": "24.0.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz", + "integrity": "sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==", + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "make-error": "1.x", + "mkdirp": "0.x", + "resolve": "1.x", + "semver": "^5.5", + "yargs-parser": "10.x" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "requires": { + "minimist": "^1.2.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.3.tgz", + "integrity": "sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ==" + }, + "uglify-js": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.4.tgz", + "integrity": "sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==", + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==" + } + } + }, + "url-parse": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.5.tgz", + "integrity": "sha512-4XDvC5vZRjEpjP0L4znrWeoH8P8F0XGBlfLdABi/6oV4o8xUVbTpyrxWHxkK2bT0pSIpcjdIzSoWUhlUfawCAQ==", + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "v8-compile-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "requires": { + "indexof": "0.0.1" + } + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "requires": { + "makeerror": "1.0.x" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "webpack": { + "version": "4.29.6", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.6.tgz", + "integrity": "sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.0.5", + "acorn-dynamic-import": "^4.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^1.0.0", + "tapable": "^1.1.0", + "terser-webpack-plugin": "^1.1.0", + "watchpack": "^1.5.0", + "webpack-sources": "^1.3.0" + } + }, + "webpack-cli": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.0.tgz", + "integrity": "sha512-t1M7G4z5FhHKJ92WRKwZ1rtvi7rHc0NZoZRbSkol0YKl4HvcC8+DsmGDmK7MmZxHSAetHagiOsjOB6MmzC2TUw==", + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.0", + "findup-sync": "^2.0.0", + "global-modules": "^1.0.0", + "import-local": "^2.0.0", + "interpret": "^1.1.0", + "loader-utils": "^1.1.0", + "supports-color": "^5.5.0", + "v8-compile-cache": "^2.0.2", + "yargs": "^12.0.5" + } + }, + "webpack-dev-middleware": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz", + "integrity": "sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg==", + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.3.1", + "range-parser": "^1.0.3", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", + "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==" + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + } + } + }, + "webpack-dev-server": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.3.1.tgz", + "integrity": "sha512-jY09LikOyGZrxVTXK0mgIq9y2IhCoJ05848dKZqX1gAGLU1YDqgpOT71+W53JH/wI4v6ky4hm+KvSyW14JEs5A==", + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.5", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.0", + "express": "^4.16.4", + "html-entities": "^1.2.1", + "http-proxy-middleware": "^0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.2.0", + "ip": "^1.1.5", + "killable": "^1.0.1", + "loglevel": "^1.6.1", + "opn": "^5.5.0", + "portfinder": "^1.0.20", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.4", + "semver": "^6.0.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.6.2", + "webpack-log": "^2.0.0", + "yargs": "12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "chokidar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", + "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "semver": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", + "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==" + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + } + } + }, + "webpack-log": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", + "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "requires": { + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "loglevelnext": "^1.0.1", + "uuid": "^3.1.0" + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/visual_console_client/package.json b/visual_console_client/package.json new file mode 100644 index 0000000000..d049a39b10 --- /dev/null +++ b/visual_console_client/package.json @@ -0,0 +1,50 @@ +{ + "name": "pandora-fms-visual-console", + "version": "1.0.0", + "description": "Visual Console", + "scripts": { + "build": "BUILD_PATH=\"pandora_console/include/visual-console-client\" NODE_ENV=production webpack", + "build:dev": "BUILD_PATH=\"pandora_console/include/visual-console-client\" NODE_ENV=development webpack", + "build:watch": "npm run build -- --watch", + "format": "prettier", + "lint": "eslint \"src/**/*.ts\"", + "start": "webpack-dev-server --color --mode=development", + "test": "jest", + "test:watch": "jest --watch" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/pandorafms/pandorafms.git" + }, + "author": "Alejandro Gallardo Escobar ", + "license": "GPL2", + "private": true, + "bugs": { + "url": "https://github.com/pandorafms/pandorafms/issues" + }, + "homepage": "https://github.com/pandorafms/pandorafms#readme", + "dependencies": { + "@types/d3-shape": "^1.3.1", + "@types/jest": "^24.0.11", + "@typescript-eslint/eslint-plugin": "^1.6.0", + "@typescript-eslint/parser": "^1.6.0", + "awesome-typescript-loader": "^5.2.1", + "clean-webpack-plugin": "^2.0.1", + "css-loader": "^2.1.1", + "d3-shape": "^1.3.5", + "eslint": "^5.16.0", + "eslint-config-prettier": "^4.1.0", + "eslint-plugin-prettier": "^3.0.1", + "file-loader": "^3.0.1", + "jest": "^24.7.1", + "mini-css-extract-plugin": "^0.5.0", + "postcss-loader": "^3.0.0", + "prettier": "^1.16.1", + "ts-jest": "^24.0.2", + "typescript": "^3.4.3", + "url-loader": "^1.1.2", + "webpack": "^4.29.6", + "webpack-cli": "^3.3.0", + "webpack-dev-server": "^3.3.1" + } +} diff --git a/visual_console_client/playground/index.html b/visual_console_client/playground/index.html new file mode 100644 index 0000000000..a42ddd5907 --- /dev/null +++ b/visual_console_client/playground/index.html @@ -0,0 +1,365 @@ + + + + + + Visual Console Sandbox + + + + + +
    + + + + diff --git a/visual_console_client/src/Item.ts b/visual_console_client/src/Item.ts new file mode 100644 index 0000000000..7ad0f779d6 --- /dev/null +++ b/visual_console_client/src/Item.ts @@ -0,0 +1,553 @@ +import { Position, Size, UnknownObject, WithModuleProps } from "./types"; +import { + sizePropsDecoder, + positionPropsDecoder, + parseIntOr, + parseBoolean, + notEmptyStringOr, + replaceMacros, + humanDate, + humanTime +} from "./lib"; +import TypedEvent, { Listener, Disposable } from "./TypedEvent"; + +// Enum: https://www.typescriptlang.org/docs/handbook/enums.html. +export const enum ItemType { + STATIC_GRAPH = 0, + MODULE_GRAPH = 1, + SIMPLE_VALUE = 2, + PERCENTILE_BAR = 3, + LABEL = 4, + ICON = 5, + SIMPLE_VALUE_MAX = 6, + SIMPLE_VALUE_MIN = 7, + SIMPLE_VALUE_AVG = 8, + PERCENTILE_BUBBLE = 9, + SERVICE = 10, + GROUP_ITEM = 11, + BOX_ITEM = 12, + LINE_ITEM = 13, + AUTO_SLA_GRAPH = 14, + CIRCULAR_PROGRESS_BAR = 15, + CIRCULAR_INTERIOR_PROGRESS_BAR = 16, + DONUT_GRAPH = 17, + BARS_GRAPH = 18, + CLOCK = 19, + COLOR_CLOUD = 20 +} + +// Base item properties. This interface should be extended by the item implementations. +export interface ItemProps extends Position, Size { + readonly id: number; + readonly type: ItemType; + label: string | null; + labelPosition: "up" | "right" | "down" | "left"; + isLinkEnabled: boolean; + link: string | null; + isOnTop: boolean; + parentId: number | null; + aclGroupId: number | null; +} + +// FIXME: Fix type compatibility. +export interface ItemClickEvent { + // data: Props; + data: UnknownObject; + nativeEvent: Event; +} + +// FIXME: Fix type compatibility. +export interface ItemRemoveEvent { + // data: Props; + data: UnknownObject; +} + +/** + * Extract a valid enum value from a raw label positi9on value. + * @param labelPosition Raw value. + */ +const parseLabelPosition = ( + labelPosition: unknown +): ItemProps["labelPosition"] => { + switch (labelPosition) { + case "up": + case "right": + case "down": + case "left": + return labelPosition; + default: + return "down"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the item props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function itemBasePropsDecoder(data: UnknownObject): ItemProps | never { + if (data.id == null || isNaN(parseInt(data.id))) { + throw new TypeError("invalid id."); + } + if (data.type == null || isNaN(parseInt(data.type))) { + throw new TypeError("invalid type."); + } + + return { + id: parseInt(data.id), + type: parseInt(data.type), + label: notEmptyStringOr(data.label, null), + labelPosition: parseLabelPosition(data.labelPosition), + isLinkEnabled: parseBoolean(data.isLinkEnabled), + link: notEmptyStringOr(data.link, null), + isOnTop: parseBoolean(data.isOnTop), + parentId: parseIntOr(data.parentId, null), + aclGroupId: parseIntOr(data.aclGroupId, null), + ...sizePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...positionPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +/** + * Base class of the visual console items. Should be extended to use its capabilities. + */ +abstract class VisualConsoleItem { + // Properties of the item. + private itemProps: Props; + // Reference to the DOM element which will contain the item. + public elementRef: HTMLElement; + public readonly labelElementRef: HTMLElement; + // Reference to the DOM element which will contain the view of the item which extends this class. + protected readonly childElementRef: HTMLElement; + // Event manager for click events. + private readonly clickEventManager = new TypedEvent>(); + // Event manager for remove events. + private readonly removeEventManager = new TypedEvent< + ItemRemoveEvent + >(); + // List of references to clean the event listeners. + private readonly disposables: Disposable[] = []; + + /** + * To create a new element which will be inside the item box. + * @return Item. + */ + protected abstract createDomElement(): HTMLElement; + + public constructor(props: Props) { + this.itemProps = props; + + /* + * Get a HTMLElement which represents the container box + * of the Visual Console item. This element will manage + * all the common things like click events, show a border + * when hovered, etc. + */ + this.elementRef = this.createContainerDomElement(); + this.labelElementRef = this.createLabelDomElement(); + + /* + * Get a HTMLElement which represents the custom view + * of the Visual Console item. This element will be + * different depending on the item implementation. + */ + this.childElementRef = this.createDomElement(); + + // Insert the elements into the container. + this.elementRef.append(this.childElementRef, this.labelElementRef); + + // Resize element. + this.resizeElement(props.width, props.height); + // Set label position. + this.changeLabelPosition(props.labelPosition); + } + + /** + * To create a new box for the visual console item. + * @return Item box. + */ + private createContainerDomElement(): HTMLElement { + let box; + if (this.props.isLinkEnabled) { + box = document.createElement("a"); + box as HTMLAnchorElement; + if (this.props.link) box.href = this.props.link; + } else { + box = document.createElement("div"); + box as HTMLDivElement; + } + + box.className = "visual-console-item"; + box.style.zIndex = this.props.isOnTop ? "2" : "1"; + box.style.left = `${this.props.x}px`; + box.style.top = `${this.props.y}px`; + box.onclick = e => + this.clickEventManager.emit({ data: this.props, nativeEvent: e }); + + return box; + } + + /** + * To create a new label for the visual console item. + * @return Item label. + */ + protected createLabelDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "visual-console-item-label"; + // Add the label if it exists. + const label = this.getLabelWithMacrosReplaced(); + if (label.length > 0) { + // Ugly table we need to use to replicate the legacy style. + const table = document.createElement("table"); + const row = document.createElement("tr"); + const emptyRow1 = document.createElement("tr"); + const emptyRow2 = document.createElement("tr"); + const cell = document.createElement("td"); + + cell.innerHTML = label; + row.append(cell); + table.append(emptyRow1, row, emptyRow2); + table.style.textAlign = "center"; + + // Change the table size depending on its position. + switch (this.props.labelPosition) { + case "up": + case "down": + if (this.props.width > 0) { + table.style.width = `${this.props.width}px`; + table.style.height = null; + } + break; + case "left": + case "right": + if (this.props.height > 0) { + table.style.width = null; + table.style.height = `${this.props.height}px`; + } + break; + } + + // element.innerHTML = this.props.label; + element.append(table); + } + + return element; + } + + /** + * Return the label stored into the props with some macros replaced. + */ + protected getLabelWithMacrosReplaced(): string { + // We assert that the props may have some needed properties. + const props = this.props as Partial; + + return replaceMacros( + [ + { + macro: "_date_", + value: humanDate(new Date()) + }, + { + macro: "_time_", + value: humanTime(new Date()) + }, + { + macro: "_agent_", + value: props.agentAlias != null ? props.agentAlias : "" + }, + { + macro: "_agentdescription_", + value: props.agentDescription != null ? props.agentDescription : "" + }, + { + macro: "_address_", + value: props.agentAddress != null ? props.agentAddress : "" + }, + { + macro: "_module_", + value: props.moduleName != null ? props.moduleName : "" + }, + { + macro: "_moduledescription_", + value: props.moduleDescription != null ? props.moduleDescription : "" + } + ], + this.props.label || "" + ); + } + + /** + * To update the content element. + * @return Item. + */ + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.createDomElement().innerHTML; + } + + /** + * Public accessor of the `props` property. + * @return Properties. + */ + public get props(): Props { + return { ...this.itemProps }; // Return a copy. + } + + /** + * Public setter of the `props` property. + * If the new props are different enough than the + * stored props, a render would be fired. + * @param newProps + */ + public set props(newProps: Props) { + const prevProps = this.props; + // Update the internal props. + this.itemProps = newProps; + + // From this point, things which rely on this.props can access to the changes. + + // Check if we should re-render. + if (this.shouldBeUpdated(prevProps, newProps)) this.render(prevProps); + } + + /** + * To compare the previous and the new props and returns a boolean value + * in case the difference is meaningfull enough to perform DOM changes. + * + * Here, the only comparision is done by reference. + * + * Override this function to perform a different comparision depending on the item needs. + * + * @param prevProps + * @param newProps + * @return Whether the difference is meaningful enough to perform DOM changes or not. + */ + protected shouldBeUpdated(prevProps: Props, newProps: Props): boolean { + return prevProps !== newProps; + } + + /** + * To recreate or update the HTMLElement which represents the item into the DOM. + * @param prevProps If exists it will be used to only perform DOM updates instead of a full replace. + */ + public render(prevProps: Props | null = null): void { + this.updateDomElement(this.childElementRef); + + // Move box. + if (!prevProps || this.positionChanged(prevProps, this.props)) { + this.moveElement(this.props.x, this.props.y); + } + // Resize box. + if (!prevProps || this.sizeChanged(prevProps, this.props)) { + this.resizeElement(this.props.width, this.props.height); + } + // Change label. + const oldLabelHtml = this.labelElementRef.innerHTML; + const newLabelHtml = this.createLabelDomElement().innerHTML; + if (oldLabelHtml !== newLabelHtml) { + this.labelElementRef.innerHTML = newLabelHtml; + } + // Change label position. + if (!prevProps || prevProps.labelPosition !== this.props.labelPosition) { + this.changeLabelPosition(this.props.labelPosition); + } + // Change link. + if ( + prevProps && + (prevProps.isLinkEnabled !== this.props.isLinkEnabled || + (this.props.isLinkEnabled && prevProps.link !== this.props.link)) + ) { + const container = this.createContainerDomElement(); + // Add the children of the old element. + container.innerHTML = this.elementRef.innerHTML; + // Copy the attributes. + const attrs = this.elementRef.attributes; + for (let i = 0; i < attrs.length; i++) { + if (attrs[i].nodeName !== "id") { + container.setAttributeNode(attrs[i]); + } + } + // Replace the reference. + if (this.elementRef.parentNode !== null) { + this.elementRef.parentNode.replaceChild(container, this.elementRef); + } + + // Changed the reference to the main element. It's ugly, but needed. + this.elementRef = container; + } + } + + /** + * To remove the event listeners and the elements from the DOM. + */ + public remove(): void { + // Call the remove event. + this.removeEventManager.emit({ data: this.props }); + // Event listeners. + this.disposables.forEach(disposable => { + try { + disposable.dispose(); + } catch (ignored) {} // eslint-disable-line no-empty + }); + // VisualConsoleItem DOM element. + this.elementRef.remove(); + } + + /** + * Compare the previous and the new position and return + * a boolean value in case the position changed. + * @param prevPosition + * @param newPosition + * @return Whether the position changed or not. + */ + protected positionChanged( + prevPosition: Position, + newPosition: Position + ): boolean { + return prevPosition.x !== newPosition.x || prevPosition.y !== newPosition.y; + } + + /** + * Move the label around the item content. + * @param position Label position. + */ + protected changeLabelPosition(position: Props["labelPosition"]): void { + switch (position) { + case "up": + this.elementRef.style.flexDirection = "column-reverse"; + break; + case "left": + this.elementRef.style.flexDirection = "row-reverse"; + break; + case "right": + this.elementRef.style.flexDirection = "row"; + break; + case "down": + default: + this.elementRef.style.flexDirection = "column"; + break; + } + + // Ugly table to show the label as its legacy counterpart. + const tables = this.labelElementRef.getElementsByTagName("table"); + const table = tables.length > 0 ? tables.item(0) : null; + // Change the table size depending on its position. + if (table) { + switch (this.props.labelPosition) { + case "up": + case "down": + if (this.props.width > 0) { + table.style.width = `${this.props.width}px`; + table.style.height = null; + } + break; + case "left": + case "right": + if (this.props.height > 0) { + table.style.width = null; + table.style.height = `${this.props.height}px`; + } + break; + } + } + } + + /** + * Move the DOM container. + * @param x Horizontal axis position. + * @param y Vertical axis position. + */ + protected moveElement(x: number, y: number): void { + this.elementRef.style.left = `${x}px`; + this.elementRef.style.top = `${y}px`; + } + + /** + * Update the position into the properties and move the DOM container. + * @param x Horizontal axis position. + * @param y Vertical axis position. + */ + public move(x: number, y: number): void { + this.moveElement(x, y); + this.itemProps = { + ...this.props, // Object spread: http://es6-features.org/#SpreadOperator + x, + y + }; + } + + /** + * Compare the previous and the new size and return + * a boolean value in case the size changed. + * @param prevSize + * @param newSize + * @return Whether the size changed or not. + */ + protected sizeChanged(prevSize: Size, newSize: Size): boolean { + return ( + prevSize.width !== newSize.width || prevSize.height !== newSize.height + ); + } + + /** + * Resize the DOM content container. + * @param width + * @param height + */ + protected resizeElement(width: number, height: number): void { + // The most valuable size is the content size. + this.childElementRef.style.width = width > 0 ? `${width}px` : null; + this.childElementRef.style.height = height > 0 ? `${height}px` : null; + } + + /** + * Update the size into the properties and resize the DOM container. + * @param width + * @param height + */ + public resize(width: number, height: number): void { + this.resizeElement(width, height); + this.itemProps = { + ...this.props, // Object spread: http://es6-features.org/#SpreadOperator + width, + height + }; + } + + /** + * To add an event handler to the click of the linked visual console elements. + * @param listener Function which is going to be executed when a linked console is clicked. + */ + public onClick(listener: Listener>): Disposable { + /* + * The '.on' function returns a function which will clean the event + * listener when executed. We store all the 'dispose' functions to + * call them when the item should be cleared. + */ + const disposable = this.clickEventManager.on(listener); + this.disposables.push(disposable); + + return disposable; + } + + /** + * To add an event handler to the removal of the item. + * @param listener Function which is going to be executed when a item is removed. + */ + public onRemove(listener: Listener>): Disposable { + /* + * The '.on' function returns a function which will clean the event + * listener when executed. We store all the 'dispose' functions to + * call them when the item should be cleared. + */ + const disposable = this.removeEventManager.on(listener); + this.disposables.push(disposable); + + return disposable; + } +} + +export default VisualConsoleItem; diff --git a/visual_console_client/src/TypedEvent.ts b/visual_console_client/src/TypedEvent.ts new file mode 100644 index 0000000000..e199470f6c --- /dev/null +++ b/visual_console_client/src/TypedEvent.ts @@ -0,0 +1,40 @@ +export interface Listener { + (event: T): void; +} + +export interface Disposable { + dispose: () => void; +} + +/** passes through events as they happen. You will not get events from before you start listening */ +export default class TypedEvent { + private listeners: Listener[] = []; + private listenersOncer: Listener[] = []; + + public on = (listener: Listener): Disposable => { + this.listeners.push(listener); + return { + dispose: () => this.off(listener) + }; + }; + + public once = (listener: Listener): void => { + this.listenersOncer.push(listener); + }; + + public off = (listener: Listener): void => { + const callbackIndex = this.listeners.indexOf(listener); + if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1); + }; + + public emit = (event: T): void => { + /** Update any general listeners */ + this.listeners.forEach(listener => listener(event)); + + /** Clear the `once` queue */ + this.listenersOncer.forEach(listener => listener(event)); + this.listenersOncer = []; + }; + + public pipe = (te: TypedEvent): Disposable => this.on(e => te.emit(e)); +} diff --git a/visual_console_client/src/VisualConsole.ts b/visual_console_client/src/VisualConsole.ts new file mode 100644 index 0000000000..7b45ccc2f1 --- /dev/null +++ b/visual_console_client/src/VisualConsole.ts @@ -0,0 +1,560 @@ +import { UnknownObject, Size } from "./types"; +import { + parseBoolean, + sizePropsDecoder, + parseIntOr, + notEmptyStringOr +} from "./lib"; +import Item, { + ItemType, + ItemProps, + ItemClickEvent, + ItemRemoveEvent +} from "./Item"; +import StaticGraph, { staticGraphPropsDecoder } from "./items/StaticGraph"; +import Icon, { iconPropsDecoder } from "./items/Icon"; +import ColorCloud, { colorCloudPropsDecoder } from "./items/ColorCloud"; +import Group, { groupPropsDecoder } from "./items/Group"; +import Clock, { clockPropsDecoder } from "./items/Clock"; +import Box, { boxPropsDecoder } from "./items/Box"; +import Line, { linePropsDecoder } from "./items/Line"; +import Label, { labelPropsDecoder } from "./items/Label"; +import SimpleValue, { simpleValuePropsDecoder } from "./items/SimpleValue"; +import EventsHistory, { + eventsHistoryPropsDecoder +} from "./items/EventsHistory"; +import Percentile, { percentilePropsDecoder } from "./items/Percentile"; +import TypedEvent, { Disposable, Listener } from "./TypedEvent"; +import DonutGraph, { donutGraphPropsDecoder } from "./items/DonutGraph"; +import BarsGraph, { barsGraphPropsDecoder } from "./items/BarsGraph"; +import ModuleGraph, { moduleGraphPropsDecoder } from "./items/ModuleGraph"; +import Service, { servicePropsDecoder } from "./items/Service"; + +// TODO: Document. +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function itemInstanceFrom(data: UnknownObject) { + const type = parseIntOr(data.type, null); + if (type == null) throw new TypeError("missing item type."); + + switch (type as ItemType) { + case ItemType.STATIC_GRAPH: + return new StaticGraph(staticGraphPropsDecoder(data)); + case ItemType.MODULE_GRAPH: + return new ModuleGraph(moduleGraphPropsDecoder(data)); + case ItemType.SIMPLE_VALUE: + case ItemType.SIMPLE_VALUE_MAX: + case ItemType.SIMPLE_VALUE_MIN: + case ItemType.SIMPLE_VALUE_AVG: + return new SimpleValue(simpleValuePropsDecoder(data)); + case ItemType.PERCENTILE_BAR: + case ItemType.PERCENTILE_BUBBLE: + case ItemType.CIRCULAR_PROGRESS_BAR: + case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR: + return new Percentile(percentilePropsDecoder(data)); + case ItemType.LABEL: + return new Label(labelPropsDecoder(data)); + case ItemType.ICON: + return new Icon(iconPropsDecoder(data)); + case ItemType.SERVICE: + return new Service(servicePropsDecoder(data)); + case ItemType.GROUP_ITEM: + return new Group(groupPropsDecoder(data)); + case ItemType.BOX_ITEM: + return new Box(boxPropsDecoder(data)); + case ItemType.LINE_ITEM: + return new Line(linePropsDecoder(data)); + case ItemType.AUTO_SLA_GRAPH: + return new EventsHistory(eventsHistoryPropsDecoder(data)); + case ItemType.DONUT_GRAPH: + return new DonutGraph(donutGraphPropsDecoder(data)); + case ItemType.BARS_GRAPH: + return new BarsGraph(barsGraphPropsDecoder(data)); + case ItemType.CLOCK: + return new Clock(clockPropsDecoder(data)); + case ItemType.COLOR_CLOUD: + return new ColorCloud(colorCloudPropsDecoder(data)); + default: + throw new TypeError("item not found"); + } +} + +// TODO: Document. +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function decodeProps(data: UnknownObject) { + const type = parseIntOr(data.type, null); + if (type == null) throw new TypeError("missing item type."); + + switch (type as ItemType) { + case ItemType.STATIC_GRAPH: + return staticGraphPropsDecoder(data); + case ItemType.MODULE_GRAPH: + return moduleGraphPropsDecoder(data); + case ItemType.SIMPLE_VALUE: + case ItemType.SIMPLE_VALUE_MAX: + case ItemType.SIMPLE_VALUE_MIN: + case ItemType.SIMPLE_VALUE_AVG: + return simpleValuePropsDecoder(data); + case ItemType.PERCENTILE_BAR: + case ItemType.PERCENTILE_BUBBLE: + case ItemType.CIRCULAR_PROGRESS_BAR: + case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR: + return percentilePropsDecoder(data); + case ItemType.LABEL: + return labelPropsDecoder(data); + case ItemType.ICON: + return iconPropsDecoder(data); + case ItemType.SERVICE: + return servicePropsDecoder(data); + case ItemType.GROUP_ITEM: + return groupPropsDecoder(data); + case ItemType.BOX_ITEM: + return boxPropsDecoder(data); + case ItemType.LINE_ITEM: + return linePropsDecoder(data); + case ItemType.AUTO_SLA_GRAPH: + return eventsHistoryPropsDecoder(data); + case ItemType.DONUT_GRAPH: + return donutGraphPropsDecoder(data); + case ItemType.BARS_GRAPH: + return barsGraphPropsDecoder(data); + case ItemType.CLOCK: + return clockPropsDecoder(data); + case ItemType.COLOR_CLOUD: + return colorCloudPropsDecoder(data); + default: + throw new TypeError("decoder not found"); + } +} + +// Base properties. +export interface VisualConsoleProps extends Size { + readonly id: number; + name: string; + groupId: number; + backgroundURL: string | null; // URL? + backgroundColor: string | null; + isFavorite: boolean; + relationLineWidth: number; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the Visual Console props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function visualConsolePropsDecoder( + data: UnknownObject +): VisualConsoleProps | never { + // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation + const { + id, + name, + groupId, + backgroundURL, + backgroundColor, + isFavorite, + relationLineWidth + } = data; + + if (id == null || isNaN(parseInt(id))) { + throw new TypeError("invalid Id."); + } + if (typeof name !== "string" || name.length === 0) { + throw new TypeError("invalid name."); + } + if (groupId == null || isNaN(parseInt(groupId))) { + throw new TypeError("invalid group Id."); + } + + return { + id: parseInt(id), + name, + groupId: parseInt(groupId), + backgroundURL: notEmptyStringOr(backgroundURL, null), + backgroundColor: notEmptyStringOr(backgroundColor, null), + isFavorite: parseBoolean(isFavorite), + relationLineWidth: parseIntOr(relationLineWidth, 0), + ...sizePropsDecoder(data) + }; +} + +export default class VisualConsole { + // Reference to the DOM element which will contain the items. + private readonly containerRef: HTMLElement; + // Properties. + private _props: VisualConsoleProps; + // Visual Console Item instances by their Id. + private elementsById: { + [key: number]: Item; + } = {}; + // Visual Console Item Ids. + private elementIds: ItemProps["id"][] = []; + // Dictionary which store the created lines. + private relations: { + [key: string]: Line; + } = {}; + // Event manager for click events. + private readonly clickEventManager = new TypedEvent< + ItemClickEvent + >(); + // List of references to clean the event listeners. + private readonly disposables: Disposable[] = []; + + /** + * React to a click on an element. + * @param e Event object. + */ + private handleElementClick: (e: ItemClickEvent) => void = e => { + this.clickEventManager.emit(e); + // console.log(`Clicked element #${e.data.id}`, e); + }; + + /** + * Clear some element references. + * @param e Event object. + */ + private handleElementRemove: (e: ItemRemoveEvent) => void = e => { + // Remove the element from the list and its relations. + this.elementIds = this.elementIds.filter(id => id !== e.data.id); + delete this.elementsById[e.data.id]; + this.clearRelations(e.data.id); + }; + + public constructor( + container: HTMLElement, + props: UnknownObject, + items: UnknownObject[] + ) { + this.containerRef = container; + this._props = visualConsolePropsDecoder(props); + + // Force the first render. + this.render(); + + // Sort by isOnTop, id ASC + items = items.sort(function(a, b) { + if ( + a.isOnTop == null || + b.isOnTop == null || + a.id == null || + b.id == null + ) { + return 0; + } + + if (a.isOnTop && !b.isOnTop) return 1; + else if (!a.isOnTop && b.isOnTop) return -1; + else if (a.id > b.id) return 1; + else return -1; + }); + + // Initialize the items. + items.forEach(item => { + try { + const itemInstance = itemInstanceFrom(item); + // Add the item to the list. + this.elementsById[itemInstance.props.id] = itemInstance; + this.elementIds.push(itemInstance.props.id); + // Item event handlers. + itemInstance.onClick(this.handleElementClick); + itemInstance.onRemove(this.handleElementRemove); + // Add the item to the DOM. + this.containerRef.append(itemInstance.elementRef); + } catch (error) { + console.log("Error creating a new element:", error.message); + } + }); + + // Create lines. + this.buildRelations(); + } + + /** + * Public accessor of the `elements` property. + * @return Properties. + */ + public get elements(): Item[] { + // Ensure the type cause Typescript doesn't know the filter removes null items. + return this.elementIds + .map(id => this.elementsById[id]) + .filter(_ => _ != null) as Item[]; + } + + /** + * Public setter of the `elements` property. + * @param items. + */ + public updateElements(items: UnknownObject[]): void { + const itemIds = items.map(item => item.id || null).filter(id => id != null); + itemIds as number[]; // Tell the type system to rely on us. + // Get the elements we should delete. + const deletedIds: number[] = this.elementIds.filter( + id => itemIds.indexOf(id) < 0 + ); + // Delete the elements. + deletedIds.forEach(id => { + if (this.elementsById[id] != null) { + this.elementsById[id].remove(); + delete this.elementsById[id]; + } + }); + // Replace the element ids. + this.elementIds = itemIds; + + // Initialize the items. + items.forEach(item => { + if (item.id) { + if (this.elementsById[item.id] == null) { + // New item. + try { + const itemInstance = itemInstanceFrom(item); + // Add the item to the list. + this.elementsById[itemInstance.props.id] = itemInstance; + // Item event handlers. + itemInstance.onClick(this.handleElementClick); + itemInstance.onRemove(this.handleElementRemove); + // Add the item to the DOM. + this.containerRef.append(itemInstance.elementRef); + } catch (error) { + console.log("Error creating a new element:", error.message); + } + } else { + // Update item. + try { + this.elementsById[item.id].props = decodeProps(item); + } catch (error) { + console.log("Error updating an element:", error.message); + } + } + } + }); + + // Re-build relations. + this.buildRelations(); + } + + /** + * Public accessor of the `props` property. + * @return Properties. + */ + public get props(): VisualConsoleProps { + return { ...this._props }; // Return a copy. + } + + /** + * Public setter of the `props` property. + * If the new props are different enough than the + * stored props, a render would be fired. + * @param newProps + */ + public set props(newProps: VisualConsoleProps) { + const prevProps = this.props; + // Update the internal props. + this._props = newProps; + + // From this point, things which rely on this.props can access to the changes. + + // Re-render. + this.render(prevProps); + } + + /** + * Recreate or update the HTMLElement which represents the Visual Console into the DOM. + * @param prevProps If exists it will be used to only DOM updates instead of a full replace. + */ + public render(prevProps: VisualConsoleProps | null = null): void { + if (prevProps) { + if (prevProps.backgroundURL !== this.props.backgroundURL) { + this.containerRef.style.backgroundImage = + this.props.backgroundURL !== null + ? `url(${this.props.backgroundURL})` + : null; + } + if (prevProps.backgroundColor !== this.props.backgroundColor) { + this.containerRef.style.backgroundColor = this.props.backgroundColor; + } + if (this.sizeChanged(prevProps, this.props)) { + this.resizeElement(this.props.width, this.props.height); + } + } else { + this.containerRef.style.backgroundImage = + this.props.backgroundURL !== null + ? `url(${this.props.backgroundURL})` + : null; + + this.containerRef.style.backgroundColor = this.props.backgroundColor; + this.resizeElement(this.props.width, this.props.height); + } + } + + /** + * Compare the previous and the new size and return + * a boolean value in case the size changed. + * @param prevSize + * @param newSize + * @return Whether the size changed or not. + */ + public sizeChanged(prevSize: Size, newSize: Size): boolean { + return ( + prevSize.width !== newSize.width || prevSize.height !== newSize.height + ); + } + + /** + * Resize the DOM container. + * @param width + * @param height + */ + public resizeElement(width: number, height: number): void { + this.containerRef.style.width = `${width}px`; + this.containerRef.style.height = `${height}px`; + } + + /** + * Update the size into the properties and resize the DOM container. + * @param width + * @param height + */ + public resize(width: number, height: number): void { + this.props = { + ...this.props, // Object spread: http://es6-features.org/#SpreadOperator + width, + height + }; + } + + /** + * To remove the event listeners and the elements from the DOM. + */ + public remove(): void { + this.disposables.forEach(d => d.dispose()); // Arrow function. + this.elements.forEach(e => e.remove()); // Arrow function. + this.elementsById = {}; + this.elementIds = []; + // Clear relations. + this.clearRelations(); + // Clean container. + this.containerRef.innerHTML = ""; + } + + /** + * Create line elements which connect the elements with their parents. + */ + private buildRelations(): void { + // Clear relations. + this.clearRelations(); + // Add relations. + this.elements.forEach(item => { + if (item.props.parentId !== null) { + const parent = this.elementsById[item.props.parentId]; + const child = this.elementsById[item.props.id]; + if (parent && child) this.addRelationLine(parent, child); + } + }); + } + + /** + * @param itemId Optional identifier of a parent or child item. + * Remove the line elements which connect the elements with their parents. + */ + private clearRelations(itemId?: number): void { + if (itemId != null) { + for (let key in this.relations) { + const ids = key.split("|"); + const parentId = Number.parseInt(ids[0]); + const childId = Number.parseInt(ids[1]); + + if (itemId === parentId || itemId === childId) { + this.relations[key].remove(); + delete this.relations[key]; + } + } + } else { + for (let key in this.relations) { + this.relations[key].remove(); + delete this.relations[key]; + } + } + } + + /** + * Retrieve the line element which represent the relation between items. + * @param parentId Identifier of the parent item. + * @param childId Itentifier of the child item. + * @return The line element or nothing. + */ + private getRelationLine(parentId: number, childId: number): Line | null { + const identifier = `${parentId}|${childId}`; + return this.relations[identifier] || null; + } + + /** + * Add a new line item to represent a relation between the items. + * @param parent Parent item. + * @param child Child item. + * @return Whether the line was added or not. + */ + private addRelationLine( + parent: Item, + child: Item + ): Line { + const identifier = `${parent.props.id}|${child.props.id}`; + if (this.relations[identifier] != null) { + this.relations[identifier].remove(); + } + + // Get the items center. + const startX = parent.props.x + parent.elementRef.clientWidth / 2; + const startY = + parent.props.y + + (parent.elementRef.clientHeight - parent.labelElementRef.clientHeight) / + 2; + const endX = child.props.x + child.elementRef.clientWidth / 2; + const endY = + child.props.y + + (child.elementRef.clientHeight - child.labelElementRef.clientHeight) / 2; + + const line = new Line( + linePropsDecoder({ + id: 0, + type: ItemType.LINE_ITEM, + startX, + startY, + endX, + endY, + width: 0, + height: 0, + lineWidth: this.props.relationLineWidth, + color: "#CCCCCC" + }) + ); + // Save a reference to the line item. + this.relations[identifier] = line; + + // Add the line to the DOM. + line.elementRef.style.zIndex = "0"; + this.containerRef.append(line.elementRef); + + return line; + } + + /** + * Add an event handler to the click of the linked visual console elements. + * @param listener Function which is going to be executed when a linked console is clicked. + */ + public onClick(listener: Listener>): Disposable { + /* + * The '.on' function returns a function which will clean the event + * listener when executed. We store all the 'dispose' functions to + * call them when the item should be cleared. + */ + const disposable = this.clickEventManager.on(listener); + this.disposables.push(disposable); + + return disposable; + } +} diff --git a/visual_console_client/src/index.ts b/visual_console_client/src/index.ts new file mode 100644 index 0000000000..8a414471a0 --- /dev/null +++ b/visual_console_client/src/index.ts @@ -0,0 +1,18 @@ +/* + * Useful resources. + * http://es6-features.org/ + * http://exploringjs.com/es6 + * https://www.typescriptlang.org/ + */ + +import "./main.css"; // CSS import. +import VisualConsole from "./VisualConsole"; +import AsyncTaskManager from "./lib/AsyncTaskManager"; + +// Export the VisualConsole class to the global object. +// eslint-disable-next-line +(window as any).VisualConsole = VisualConsole; + +// Export the AsyncTaskManager class to the global object. +// eslint-disable-next-line +(window as any).AsyncTaskManager = AsyncTaskManager; diff --git a/visual_console_client/src/items/BarsGraph.ts b/visual_console_client/src/items/BarsGraph.ts new file mode 100644 index 0000000000..d1a6fe97a5 --- /dev/null +++ b/visual_console_client/src/items/BarsGraph.ts @@ -0,0 +1,67 @@ +import { UnknownObject, WithModuleProps } from "../types"; +import { modulePropsDecoder, decodeBase64, stringIsEmpty } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type BarsGraphProps = { + type: ItemType.BARS_GRAPH; + html: string; +} & ItemProps & + WithModuleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the bars graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function barsGraphPropsDecoder( + data: UnknownObject +): BarsGraphProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.BARS_GRAPH, + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class BarsGraph extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "bars-graph"; + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + setTimeout(() => { + if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim()); + }, 0); + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/Box.ts b/visual_console_client/src/items/Box.ts new file mode 100644 index 0000000000..ca3078c0a6 --- /dev/null +++ b/visual_console_client/src/items/Box.ts @@ -0,0 +1,68 @@ +import { UnknownObject } from "../types"; +import { parseIntOr, notEmptyStringOr } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +interface BoxProps extends ItemProps { + // Overrided properties. + readonly type: ItemType.BOX_ITEM; + label: null; + isLinkEnabled: false; + parentId: null; + aclGroupId: null; + // Custom properties. + borderWidth: number; + borderColor: string | null; + fillColor: string | null; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the item props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function boxPropsDecoder(data: UnknownObject): BoxProps | never { + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.BOX_ITEM, + label: null, + isLinkEnabled: false, + parentId: null, + aclGroupId: null, + // Custom properties. + borderWidth: parseIntOr(data.borderWidth, 0), + borderColor: notEmptyStringOr(data.borderColor, null), + fillColor: notEmptyStringOr(data.fillColor, null) + }; +} + +export default class Box extends Item { + protected createDomElement(): HTMLElement { + const box: HTMLDivElement = document.createElement("div"); + box.className = "box"; + // To prevent this item to expand beyond its parent. + box.style.boxSizing = "border-box"; + + if (this.props.fillColor) { + box.style.backgroundColor = this.props.fillColor; + } + + // Border. + if (this.props.borderWidth > 0) { + box.style.borderStyle = "solid"; + // Control the max width to prevent this item to expand beyond its parent. + const maxBorderWidth = Math.min(this.props.width, this.props.height) / 2; + const borderWidth = Math.min(this.props.borderWidth, maxBorderWidth); + box.style.borderWidth = `${borderWidth}px`; + + if (this.props.borderColor) { + box.style.borderColor = this.props.borderColor; + } + } + + return box; + } +} diff --git a/visual_console_client/src/items/Clock/alarm-clock.ttf b/visual_console_client/src/items/Clock/alarm-clock.ttf new file mode 100755 index 0000000000..9e9b593459 Binary files /dev/null and b/visual_console_client/src/items/Clock/alarm-clock.ttf differ diff --git a/visual_console_client/src/items/Clock/index.ts b/visual_console_client/src/items/Clock/index.ts new file mode 100644 index 0000000000..52e0e65775 --- /dev/null +++ b/visual_console_client/src/items/Clock/index.ts @@ -0,0 +1,612 @@ +import "./styles.css"; + +import { LinkedVisualConsoleProps, UnknownObject, Size } from "../../types"; +import { + linkedVCPropsDecoder, + parseIntOr, + parseBoolean, + prefixedCssRules, + notEmptyStringOr, + humanDate, + humanTime +} from "../../lib"; +import Item, { ItemProps, itemBasePropsDecoder, ItemType } from "../../Item"; + +export type ClockProps = { + type: ItemType.CLOCK; + clockType: "analogic" | "digital"; + clockFormat: "datetime" | "time"; + clockTimezone: string; + clockTimezoneOffset: number; // Offset of the timezone to UTC in seconds. + showClockTimezone: boolean; + color?: string | null; +} & ItemProps & + LinkedVisualConsoleProps; + +/** + * Extract a valid enum value from a raw unknown value. + * @param clockType Raw value. + */ +const parseClockType = (clockType: unknown): ClockProps["clockType"] => { + switch (clockType) { + case "analogic": + case "digital": + return clockType; + default: + return "analogic"; + } +}; + +/** + * Extract a valid enum value from a raw unknown value. + * @param clockFormat Raw value. + */ +const parseClockFormat = (clockFormat: unknown): ClockProps["clockFormat"] => { + switch (clockFormat) { + case "datetime": + case "time": + return clockFormat; + default: + return "datetime"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the clock props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function clockPropsDecoder(data: UnknownObject): ClockProps | never { + if ( + typeof data.clockTimezone !== "string" || + data.clockTimezone.length === 0 + ) { + throw new TypeError("invalid timezone."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.CLOCK, + clockType: parseClockType(data.clockType), + clockFormat: parseClockFormat(data.clockFormat), + clockTimezone: data.clockTimezone, + clockTimezoneOffset: parseIntOr(data.clockTimezoneOffset, 0), + showClockTimezone: parseBoolean(data.showClockTimezone), + color: notEmptyStringOr(data.color, null), + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Clock extends Item { + public static readonly TICK_INTERVAL = 1000; // In ms. + private intervalRef: number | null = null; + + public constructor(props: ClockProps) { + // Call the superclass constructor. + super(props); + + /* The item is already loaded and inserted into the DOM. + * The class properties are now initialized. + * Now you can modify the item, add event handlers, timers, etc. + */ + + /* The use of the arrow function is important here. startTick will + * use the function passed as an argument to call the global setInterval + * function. The interval, timeout or event functions, among other, are + * called into another execution loop and using a different context. + * The arrow functions, unlike the classic functions, doesn't create + * their own context (this), so their context at execution time will be + * use the current context at the declaration time. + * http://es6-features.org/#Lexicalthis + */ + this.startTick( + () => { + // Replace the old element with the updated date. + this.childElementRef.innerHTML = this.createClock().innerHTML; + }, + /* The analogic clock doesn't need to tick, + * but it will be refreshed every 20 seconds + * to avoid a desync caused by page freezes. + */ + this.props.clockType === "analogic" ? 20000 : Clock.TICK_INTERVAL + ); + } + + /** + * Wrap a window.clearInterval call. + */ + private stopTick(): void { + if (this.intervalRef !== null) { + window.clearInterval(this.intervalRef); + this.intervalRef = null; + } + } + + /** + * Wrap a window.setInterval call. + * @param handler Function to be called every time the interval + * timer is reached. + * @param interval Number in milliseconds for the interval timer. + */ + private startTick( + handler: TimerHandler, + interval: number = Clock.TICK_INTERVAL + ): void { + this.stopTick(); + this.intervalRef = window.setInterval(handler, interval); + } + + /** + * Create a element which contains the DOM representation of the item. + * @return DOM Element. + * @override + */ + protected createDomElement(): HTMLElement | never { + return this.createClock(); + } + + /** + * To remove the event listeners and the elements from the DOM. + * @override + */ + public remove(): void { + // Clear the interval. + this.stopTick(); + // Call to the parent clean function. + super.remove(); + } + + /** + * @override Item.resizeElement + * Resize the DOM content container. + * @param width + * @param height + */ + protected resizeElement(width: number, height: number): void { + const { width: newWidth, height: newHeight } = this.getElementSize( + width, + height + ); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation + super.resizeElement(newWidth, newHeight); + // Re-render the item to force it calculate a new font size. + if (this.props.clockType === "digital") { + // Replace the old element with the updated date. + this.childElementRef.innerHTML = this.createClock().innerHTML; + } + } + + /** + * Create a element which contains a representation of a clock. + * It choose between the clock types. + * @return DOM Element. + * @throws Error. + */ + private createClock(): HTMLElement | never { + switch (this.props.clockType) { + case "analogic": + return this.createAnalogicClock(); + case "digital": + return this.createDigitalClock(); + default: + throw new Error("invalid clock type."); + } + } + + /** + * Create a element which contains a representation of an analogic clock. + * @return DOM Element. + */ + private createAnalogicClock(): HTMLElement { + const svgNS = "http://www.w3.org/2000/svg"; + const colors = { + watchFace: "#FFFFF0", + watchFaceBorder: "#242124", + mark: "#242124", + handDark: "#242124", + handLight: "#525252", + secondHand: "#DC143C" + }; + + const { width, height } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation + + // Calculate font size to adapt the font to the item size. + const baseTimeFontSize = 20; // Per 100px of width. + const dateFontSizeMultiplier = 0.5; + const dateFontSize = + (baseTimeFontSize * dateFontSizeMultiplier * width) / 100; + + const div = document.createElement("div"); + div.className = "analogic-clock"; + div.style.width = `${width}px`; + div.style.height = `${height}px`; + + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 100"); + + // Clock face. + const clockFace = document.createElementNS(svgNS, "g"); + clockFace.setAttribute("class", "clockface"); + const clockFaceBackground = document.createElementNS(svgNS, "circle"); + clockFaceBackground.setAttribute("cx", "50"); + clockFaceBackground.setAttribute("cy", "50"); + clockFaceBackground.setAttribute("r", "48"); + clockFaceBackground.setAttribute("fill", colors.watchFace); + clockFaceBackground.setAttribute("stroke", colors.watchFaceBorder); + clockFaceBackground.setAttribute("stroke-width", "2"); + clockFaceBackground.setAttribute("stroke-linecap", "round"); + // Insert the clockface background into the clockface group. + clockFace.append(clockFaceBackground); + + // Timezone complication. + const city = this.getHumanTimezone(); + if (city.length > 0) { + const timezoneComplication = document.createElementNS(svgNS, "text"); + timezoneComplication.setAttribute("text-anchor", "middle"); + timezoneComplication.setAttribute("font-size", "8"); + timezoneComplication.setAttribute( + "transform", + "translate(30 50) rotate(90)" // Rotate to counter the clock rotation. + ); + timezoneComplication.setAttribute("fill", colors.mark); + timezoneComplication.textContent = city; + clockFace.append(timezoneComplication); + } + + // Marks group. + const marksGroup = document.createElementNS(svgNS, "g"); + marksGroup.setAttribute("class", "marks"); + // Build the 12 hours mark. + const mainMarkGroup = document.createElementNS(svgNS, "g"); + mainMarkGroup.setAttribute("class", "mark"); + mainMarkGroup.setAttribute("transform", "translate(50 50)"); + const mark1a = document.createElementNS(svgNS, "line"); + mark1a.setAttribute("x1", "36"); + mark1a.setAttribute("y1", "0"); + mark1a.setAttribute("x2", "46"); + mark1a.setAttribute("y2", "0"); + mark1a.setAttribute("stroke", colors.mark); + mark1a.setAttribute("stroke-width", "5"); + const mark1b = document.createElementNS(svgNS, "line"); + mark1b.setAttribute("x1", "36"); + mark1b.setAttribute("y1", "0"); + mark1b.setAttribute("x2", "46"); + mark1b.setAttribute("y2", "0"); + mark1b.setAttribute("stroke", colors.watchFace); + mark1b.setAttribute("stroke-width", "1"); + // Insert the 12 mark lines into their group. + mainMarkGroup.append(mark1a, mark1b); + // Insert the main mark into the marks group. + marksGroup.append(mainMarkGroup); + // Build the rest of the marks. + for (let i = 1; i < 60; i++) { + const mark = document.createElementNS(svgNS, "line"); + mark.setAttribute("y1", "0"); + mark.setAttribute("y2", "0"); + mark.setAttribute("stroke", colors.mark); + mark.setAttribute("transform", `translate(50 50) rotate(${i * 6})`); + + if (i % 5 === 0) { + mark.setAttribute("x1", "38"); + mark.setAttribute("x2", "46"); + mark.setAttribute("stroke-width", i % 15 === 0 ? "2" : "1"); + } else { + mark.setAttribute("x1", "42"); + mark.setAttribute("x2", "46"); + mark.setAttribute("stroke-width", "0.5"); + } + + // Insert the mark into the marks group. + marksGroup.append(mark); + } + + /* Clock hands */ + + // Hour hand. + const hourHand = document.createElementNS(svgNS, "g"); + hourHand.setAttribute("class", "hour-hand"); + hourHand.setAttribute("transform", "translate(50 50)"); + // This will go back and will act like a border. + const hourHandA = document.createElementNS(svgNS, "line"); + hourHandA.setAttribute("class", "hour-hand-a"); + hourHandA.setAttribute("x1", "0"); + hourHandA.setAttribute("y1", "0"); + hourHandA.setAttribute("x2", "30"); + hourHandA.setAttribute("y2", "0"); + hourHandA.setAttribute("stroke", colors.handLight); + hourHandA.setAttribute("stroke-width", "4"); + hourHandA.setAttribute("stroke-linecap", "round"); + // This will go in front of the previous line. + const hourHandB = document.createElementNS(svgNS, "line"); + hourHandB.setAttribute("class", "hour-hand-b"); + hourHandB.setAttribute("x1", "0"); + hourHandB.setAttribute("y1", "0"); + hourHandB.setAttribute("x2", "29.9"); + hourHandB.setAttribute("y2", "0"); + hourHandB.setAttribute("stroke", colors.handDark); + hourHandB.setAttribute("stroke-width", "3.1"); + hourHandB.setAttribute("stroke-linecap", "round"); + // Append the elements to finish the hour hand. + hourHand.append(hourHandA, hourHandB); + + // Minute hand. + const minuteHand = document.createElementNS(svgNS, "g"); + minuteHand.setAttribute("class", "minute-hand"); + minuteHand.setAttribute("transform", "translate(50 50)"); + // This will go back and will act like a border. + const minuteHandA = document.createElementNS(svgNS, "line"); + minuteHandA.setAttribute("class", "minute-hand-a"); + minuteHandA.setAttribute("x1", "0"); + minuteHandA.setAttribute("y1", "0"); + minuteHandA.setAttribute("x2", "40"); + minuteHandA.setAttribute("y2", "0"); + minuteHandA.setAttribute("stroke", colors.handLight); + minuteHandA.setAttribute("stroke-width", "2"); + minuteHandA.setAttribute("stroke-linecap", "round"); + // This will go in front of the previous line. + const minuteHandB = document.createElementNS(svgNS, "line"); + minuteHandB.setAttribute("class", "minute-hand-b"); + minuteHandB.setAttribute("x1", "0"); + minuteHandB.setAttribute("y1", "0"); + minuteHandB.setAttribute("x2", "39.9"); + minuteHandB.setAttribute("y2", "0"); + minuteHandB.setAttribute("stroke", colors.handDark); + minuteHandB.setAttribute("stroke-width", "1.5"); + minuteHandB.setAttribute("stroke-linecap", "round"); + const minuteHandPin = document.createElementNS(svgNS, "circle"); + minuteHandPin.setAttribute("r", "3"); + minuteHandPin.setAttribute("fill", colors.handDark); + // Append the elements to finish the minute hand. + minuteHand.append(minuteHandA, minuteHandB, minuteHandPin); + + // Second hand. + const secondHand = document.createElementNS(svgNS, "g"); + secondHand.setAttribute("class", "second-hand"); + secondHand.setAttribute("transform", "translate(50 50)"); + const secondHandBar = document.createElementNS(svgNS, "line"); + secondHandBar.setAttribute("x1", "0"); + secondHandBar.setAttribute("y1", "0"); + secondHandBar.setAttribute("x2", "46"); + secondHandBar.setAttribute("y2", "0"); + secondHandBar.setAttribute("stroke", colors.secondHand); + secondHandBar.setAttribute("stroke-width", "1"); + secondHandBar.setAttribute("stroke-linecap", "round"); + const secondHandPin = document.createElementNS(svgNS, "circle"); + secondHandPin.setAttribute("r", "2"); + secondHandPin.setAttribute("fill", colors.secondHand); + // Append the elements to finish the second hand. + secondHand.append(secondHandBar, secondHandPin); + + // Pin. + const pin = document.createElementNS(svgNS, "circle"); + pin.setAttribute("cx", "50"); + pin.setAttribute("cy", "50"); + pin.setAttribute("r", "0.3"); + pin.setAttribute("fill", colors.handDark); + + // Get the hand angles. + const date = this.getOriginDate(); + const seconds = date.getSeconds(); + const minutes = date.getMinutes(); + const hours = date.getHours(); + const secAngle = (360 / 60) * seconds; + const minuteAngle = (360 / 60) * minutes + (360 / 60) * (seconds / 60); + const hourAngle = (360 / 12) * hours + (360 / 12) * (minutes / 60); + // Set the clock time by moving the hands. + hourHand.setAttribute("transform", `translate(50 50) rotate(${hourAngle})`); + minuteHand.setAttribute( + "transform", + `translate(50 50) rotate(${minuteAngle})` + ); + secondHand.setAttribute( + "transform", + `translate(50 50) rotate(${secAngle})` + ); + + // Build the clock + svg.append(clockFace, marksGroup, hourHand, minuteHand, secondHand, pin); + // Rotate the clock to its normal position. + svg.setAttribute("transform", "rotate(-90)"); + + /* Add the animation declaration to the container. + * Since the animation keyframes need to know the + * start angle, this angle is dynamic (current time), + * and we can't edit keyframes through javascript + * safely and with backwards compatibility, we need + * to inject it. + */ + div.innerHTML = ` + + `; + // Add the clock to the container + div.append(svg); + + // Date. + if (this.props.clockFormat === "datetime") { + const dateElem: HTMLSpanElement = document.createElement("span"); + dateElem.className = "date"; + dateElem.textContent = humanDate(date, "default"); + dateElem.style.fontSize = `${dateFontSize}px`; + if (this.props.color) dateElem.style.color = this.props.color; + div.append(dateElem); + } + + return div; + } + + /** + * Create a element which contains a representation of a digital clock. + * @return DOM Element. + */ + private createDigitalClock(): HTMLElement { + const element: HTMLDivElement = document.createElement("div"); + element.className = "digital-clock"; + + const { width } = this.getElementSize(); // Destructuring assigment: http://es6-features.org/#ObjectMatchingShorthandNotation + + // Calculate font size to adapt the font to the item size. + const baseTimeFontSize = 20; // Per 100px of width. + const dateFontSizeMultiplier = 0.5; + const tzFontSizeMultiplier = 6 / this.props.clockTimezone.length; + const timeFontSize = (baseTimeFontSize * width) / 100; + const dateFontSize = + (baseTimeFontSize * dateFontSizeMultiplier * width) / 100; + const tzFontSize = Math.min( + (baseTimeFontSize * tzFontSizeMultiplier * width) / 100, + (width / 100) * 10 + ); + + // Date calculated using the original timezone. + const date = this.getOriginDate(); + + // Date. + if (this.props.clockFormat === "datetime") { + const dateElem: HTMLSpanElement = document.createElement("span"); + dateElem.className = "date"; + dateElem.textContent = humanDate(date, "default"); + dateElem.style.fontSize = `${dateFontSize}px`; + if (this.props.color) dateElem.style.color = this.props.color; + element.append(dateElem); + } + + // Time. + const timeElem: HTMLSpanElement = document.createElement("span"); + timeElem.className = "time"; + timeElem.textContent = humanTime(date); + timeElem.style.fontSize = `${timeFontSize}px`; + if (this.props.color) timeElem.style.color = this.props.color; + element.append(timeElem); + + // City name. + const city = this.getHumanTimezone(); + if (city.length > 0) { + const tzElem: HTMLSpanElement = document.createElement("span"); + tzElem.className = "timezone"; + tzElem.textContent = city; + tzElem.style.fontSize = `${tzFontSize}px`; + if (this.props.color) tzElem.style.color = this.props.color; + element.append(tzElem); + } + + return element; + } + + /** + * Generate the current date using the timezone offset stored into the properties. + * @return The current date. + */ + private getOriginDate(initialDate: Date | null = null): Date { + const d = initialDate ? initialDate : new Date(); + const targetTZOffset = this.props.clockTimezoneOffset * 1000; // In ms. + const localTZOffset = d.getTimezoneOffset() * 60 * 1000; // In ms. + const utimestamp = d.getTime() + targetTZOffset + localTZOffset; + + return new Date(utimestamp); + } + + /** + * Extract a human readable city name from the timezone text. + * @param timezone Timezone text. + */ + public getHumanTimezone(timezone: string = this.props.clockTimezone): string { + const [, city = ""] = timezone.split("/"); + return city.replace("_", " "); + } + + /** + * Generate a element size using the current size and the default values. + * @return The size. + */ + private getElementSize( + width: number = this.props.width, + height: number = this.props.height + ): Size { + switch (this.props.clockType) { + case "analogic": { + let diameter = 100; // Default value. + + if (width > 0 && height > 0) { + diameter = Math.min(width, height); + } else if (width > 0) { + diameter = width; + } else if (height > 0) { + diameter = height; + } + + return { + width: diameter, + height: diameter + }; + } + case "digital": { + if (width > 0 && height > 0) { + // The proportion of the clock should be (width = height / 2) aproximately. + height = width / 2 < height ? width / 2 : height; + } else if (width > 0) { + height = width / 2; + } else if (height > 0) { + // The proportion of the clock should be (height * 2 = width) aproximately. + width = height * 2; + } else { + width = 100; // Default value. + height = 50; // Default value. + } + + return { + width, + height + }; + } + default: + throw new Error("invalid clock type."); + } + } +} diff --git a/visual_console_client/src/items/Clock/spec.ts b/visual_console_client/src/items/Clock/spec.ts new file mode 100644 index 0000000000..7380f98468 --- /dev/null +++ b/visual_console_client/src/items/Clock/spec.ts @@ -0,0 +1,67 @@ +import Clock, { clockPropsDecoder } from "."; + +const genericRawProps = { + id: 1, + type: 19, // Clock item = 19 + label: null, + isLinkEnabled: false, + isOnTop: false, + parentId: null, + aclGroupId: null +}; + +const positionRawProps = { + x: 100, + y: 50 +}; + +const sizeRawProps = { + width: 100, + height: 100 +}; + +const digitalClockProps = { + clockType: "digital", + clockFormat: "datetime", + clockTimezone: "Madrid", + clockTimezoneOffset: 60, + showClockTimezone: true, + color: "white" +}; + +const linkedModuleProps = { + // Agent props. + agentId: null, + agentName: null, + // Module props. + moduleId: null, + moduleName: null +}; + +describe("Clock item", () => { + const clockInstance = new Clock( + clockPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...linkedModuleProps, + ...digitalClockProps + }) + ); + + it("should have the digital-clock class", () => { + expect( + clockInstance.elementRef.getElementsByClassName("digital-clock").length + ).toBeGreaterThan(0); + }); + + describe("getHumanTimezone function", () => { + it("should return a better timezone", () => { + expect(clockInstance.getHumanTimezone("America/New_York")).toBe( + "New York" + ); + expect(clockInstance.getHumanTimezone("Europe/Madrid")).toBe("Madrid"); + expect(clockInstance.getHumanTimezone("Asia/Tokyo")).toBe("Tokyo"); + }); + }); +}); diff --git a/visual_console_client/src/items/Clock/styles.css b/visual_console_client/src/items/Clock/styles.css new file mode 100644 index 0000000000..c1c0b7db2d --- /dev/null +++ b/visual_console_client/src/items/Clock/styles.css @@ -0,0 +1,52 @@ +@font-face { + font-family: Alarm Clock; + src: url(./alarm-clock.ttf); +} + +/* Digital clock */ + +.visual-console-item .digital-clock { + display: flex; + flex-direction: column; + justify-content: center; + justify-items: center; + align-content: center; + align-items: center; +} + +.visual-console-item .digital-clock > span { + font-family: "Alarm Clock", "Courier New", Courier, monospace; + font-size: 50px; + + /* To improve legibility */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px; +} + +.visual-console-item .digital-clock > span.date { + font-size: 25px; +} + +.visual-console-item .digital-clock > span.timezone { + font-size: 25px; +} + +/* Analog clock */ + +.visual-console-item .analogic-clock { + text-align: center; +} + +.visual-console-item .analogic-clock .hour-hand { + animation: rotate-hour 43200s infinite linear; +} + +.visual-console-item .analogic-clock .minute-hand { + animation: rotate-minute 3600s infinite linear; +} + +.visual-console-item .analogic-clock .second-hand { + animation: rotate-second 60s infinite linear; +} diff --git a/visual_console_client/src/items/ColorCloud.spec.ts b/visual_console_client/src/items/ColorCloud.spec.ts new file mode 100644 index 0000000000..103850b04b --- /dev/null +++ b/visual_console_client/src/items/ColorCloud.spec.ts @@ -0,0 +1,64 @@ +import ColorCloud, { colorCloudPropsDecoder } from "./ColorCloud"; + +const genericRawProps = { + id: 1, + type: 20, // COlor cloud item = 20 + label: null, + isLinkEnabled: false, + isOnTop: false, + parentId: null, + aclGroupId: null +}; + +const positionRawProps = { + x: 100, + y: 50 +}; + +const sizeRawProps = { + width: 100, + height: 100 +}; + +const colorCloudProps = { + color: "rgb(100, 50, 245)" +}; + +const linkedModuleProps = { + // Agent props. + agentId: null, + agentName: null, + // Module props. + moduleId: null, + moduleName: null +}; + +describe("Color cloud item", () => { + const colorCloudInstance = new ColorCloud( + colorCloudPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...linkedModuleProps, + ...colorCloudProps + }) + ); + + it("should throw when using an invalid color into the props decoder", () => { + expect(() => + colorCloudPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...linkedModuleProps, + color: null + }) + ).toThrowError(TypeError); + }); + + it("should have the color-cloud class", () => { + expect( + colorCloudInstance.elementRef.getElementsByClassName("color-cloud").length + ).toBeGreaterThan(0); + }); +}); diff --git a/visual_console_client/src/items/ColorCloud.ts b/visual_console_client/src/items/ColorCloud.ts new file mode 100644 index 0000000000..0b5dfe9948 --- /dev/null +++ b/visual_console_client/src/items/ColorCloud.ts @@ -0,0 +1,100 @@ +import { + WithModuleProps, + LinkedVisualConsoleProps, + UnknownObject +} from "../types"; +import { modulePropsDecoder, linkedVCPropsDecoder } from "../lib"; +import Item, { itemBasePropsDecoder, ItemType, ItemProps } from "../Item"; + +export type ColorCloudProps = { + type: ItemType.COLOR_CLOUD; + color: string; + // TODO: Add the rest of the color cloud values? +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the static graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function colorCloudPropsDecoder( + data: UnknownObject +): ColorCloudProps | never { + // TODO: Validate the color. + if (typeof data.color !== "string" || data.color.length === 0) { + throw new TypeError("invalid color."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.COLOR_CLOUD, + color: data.color, + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +const svgNS = "http://www.w3.org/2000/svg"; + +export default class ColorCloud extends Item { + protected createDomElement(): HTMLElement { + const container: HTMLDivElement = document.createElement("div"); + container.className = "color-cloud"; + + // Add the SVG. + container.append(this.createSvgElement()); + + return container; + } + + public createSvgElement(): SVGSVGElement { + const gradientId = `grad_${this.props.id}`; + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 100"); + + // Defs. + const defs = document.createElementNS(svgNS, "defs"); + // Radial gradient. + const radialGradient = document.createElementNS(svgNS, "radialGradient"); + radialGradient.setAttribute("id", gradientId); + radialGradient.setAttribute("cx", "50%"); + radialGradient.setAttribute("cy", "50%"); + radialGradient.setAttribute("r", "50%"); + radialGradient.setAttribute("fx", "50%"); + radialGradient.setAttribute("fy", "50%"); + // Stops. + const stop0 = document.createElementNS(svgNS, "stop"); + stop0.setAttribute("offset", "0%"); + stop0.setAttribute( + "style", + `stop-color:${this.props.color};stop-opacity:0.9` + ); + const stop100 = document.createElementNS(svgNS, "stop"); + stop100.setAttribute("offset", "100%"); + stop100.setAttribute( + "style", + `stop-color:${this.props.color};stop-opacity:0` + ); + // Circle. + const circle = document.createElementNS(svgNS, "circle"); + circle.setAttribute("fill", `url(#${gradientId})`); + circle.setAttribute("cx", "50%"); + circle.setAttribute("cy", "50%"); + circle.setAttribute("r", "50%"); + + // Append elements. + radialGradient.append(stop0, stop100); + defs.append(radialGradient); + svg.append(defs, circle); + + return svg; + } +} diff --git a/visual_console_client/src/items/DonutGraph.ts b/visual_console_client/src/items/DonutGraph.ts new file mode 100644 index 0000000000..d60f268567 --- /dev/null +++ b/visual_console_client/src/items/DonutGraph.ts @@ -0,0 +1,78 @@ +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + modulePropsDecoder, + decodeBase64, + stringIsEmpty +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type DonutGraphProps = { + type: ItemType.DONUT_GRAPH; + html: string; +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the donut graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function donutGraphPropsDecoder( + data: UnknownObject +): DonutGraphProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.DONUT_GRAPH, + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class DonutGraph extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "donut-graph"; + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + setTimeout(() => { + if (scripts[i].src.length === 0) eval(scripts[i].innerHTML.trim()); + }, 0); + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/EventsHistory.ts b/visual_console_client/src/items/EventsHistory.ts new file mode 100644 index 0000000000..7e9db0ae9b --- /dev/null +++ b/visual_console_client/src/items/EventsHistory.ts @@ -0,0 +1,78 @@ +import { UnknownObject, WithModuleProps } from "../types"; +import { + modulePropsDecoder, + parseIntOr, + decodeBase64, + stringIsEmpty +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type EventsHistoryProps = { + type: ItemType.AUTO_SLA_GRAPH; + maxTime: number | null; + html: string; +} & ItemProps & + WithModuleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the events history props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function eventsHistoryPropsDecoder( + data: UnknownObject +): EventsHistoryProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.AUTO_SLA_GRAPH, + maxTime: parseIntOr(data.maxTime, null), + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class EventsHistory extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "events-history"; + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + setTimeout(() => { + try { + eval(scripts[i].innerHTML.trim()); + } catch (ignored) {} // eslint-disable-line no-empty + }, 0); + } + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/Group.spec.ts b/visual_console_client/src/items/Group.spec.ts new file mode 100644 index 0000000000..e00a5cf327 --- /dev/null +++ b/visual_console_client/src/items/Group.spec.ts @@ -0,0 +1,44 @@ +import Group, { groupPropsDecoder } from "./Group"; + +const genericRawProps = { + id: 1, + type: 11, // Group item = 11 + label: null, + isLinkEnabled: false, + isOnTop: false, + parentId: null, + aclGroupId: null +}; + +const positionRawProps = { + x: 100, + y: 50 +}; + +const sizeRawProps = { + width: 100, + height: 100 +}; + +const groupRawProps = { + imageSrc: + "https://brutus.artica.lan:8081/uploads/-/system/project/avatar/1/1.png", + groupId: 1 +}; + +describe("Group item", () => { + const groupInstance = new Group( + groupPropsDecoder({ + ...genericRawProps, + ...positionRawProps, + ...sizeRawProps, + ...groupRawProps + }) + ); + + it("should have the group class", () => { + expect( + groupInstance.elementRef.getElementsByClassName("group").length + ).toBeGreaterThan(0); + }); +}); diff --git a/visual_console_client/src/items/Group.ts b/visual_console_client/src/items/Group.ts new file mode 100644 index 0000000000..fa97f69a62 --- /dev/null +++ b/visual_console_client/src/items/Group.ts @@ -0,0 +1,80 @@ +import { LinkedVisualConsoleProps, UnknownObject } from "../types"; +import { + linkedVCPropsDecoder, + parseIntOr, + notEmptyStringOr, + stringIsEmpty, + decodeBase64, + parseBoolean +} from "../lib"; +import Item, { ItemProps, itemBasePropsDecoder, ItemType } from "../Item"; + +export type GroupProps = { + type: ItemType.GROUP_ITEM; + groupId: number; + imageSrc: string | null; // URL? + statusImageSrc: string | null; + showStatistics: boolean; + html?: string | null; +} & ItemProps & + LinkedVisualConsoleProps; + +function extractHtml(data: UnknownObject): string | null { + if (!stringIsEmpty(data.html)) return data.html; + if (!stringIsEmpty(data.encodedHtml)) return decodeBase64(data.encodedHtml); + return null; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the group props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function groupPropsDecoder(data: UnknownObject): GroupProps | never { + if ( + (typeof data.imageSrc !== "string" || data.imageSrc.length === 0) && + data.encodedHtml === null + ) { + throw new TypeError("invalid image src."); + } + if (parseIntOr(data.groupId, null) === null) { + throw new TypeError("invalid group Id."); + } + + const showStatistics = parseBoolean(data.showStatistics); + const html = showStatistics ? extractHtml(data) : null; + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.GROUP_ITEM, + groupId: parseInt(data.groupId), + imageSrc: notEmptyStringOr(data.imageSrc, null), + statusImageSrc: notEmptyStringOr(data.statusImageSrc, null), + showStatistics, + html, + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Group extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "group"; + + if (!this.props.showStatistics && this.props.statusImageSrc !== null) { + // Icon with status. + element.style.background = `url(${this.props.statusImageSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + } else if (this.props.showStatistics && this.props.html != null) { + // Stats table. + element.innerHTML = this.props.html; + } + + return element; + } +} diff --git a/visual_console_client/src/items/Icon.ts b/visual_console_client/src/items/Icon.ts new file mode 100644 index 0000000000..12c1b035c7 --- /dev/null +++ b/visual_console_client/src/items/Icon.ts @@ -0,0 +1,43 @@ +import { LinkedVisualConsoleProps, UnknownObject } from "../types"; +import { linkedVCPropsDecoder } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type IconProps = { + type: ItemType.ICON; + imageSrc: string; // URL? +} & ItemProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the icon props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function iconPropsDecoder(data: UnknownObject): IconProps | never { + if (typeof data.imageSrc !== "string" || data.imageSrc.length === 0) { + throw new TypeError("invalid image src."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.ICON, + imageSrc: data.imageSrc, + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Icon extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "icon"; + element.style.background = `url(${this.props.imageSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + + return element; + } +} diff --git a/visual_console_client/src/items/Label.ts b/visual_console_client/src/items/Label.ts new file mode 100644 index 0000000000..c8de572c15 --- /dev/null +++ b/visual_console_client/src/items/Label.ts @@ -0,0 +1,47 @@ +import { LinkedVisualConsoleProps, UnknownObject } from "../types"; +import { linkedVCPropsDecoder } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type LabelProps = { + type: ItemType.LABEL; +} & ItemProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the label props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function labelPropsDecoder(data: UnknownObject): LabelProps | never { + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.LABEL, + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class Label extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "label"; + element.innerHTML = this.getLabelWithMacrosReplaced(); + + return element; + } + + /** + * @override Item.createLabelDomElement + * Create a new label for the visual console item. + * @return Item label. + */ + public createLabelDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "visual-console-item-label"; + // Always return an empty label. + return element; + } +} diff --git a/visual_console_client/src/items/Line.ts b/visual_console_client/src/items/Line.ts new file mode 100644 index 0000000000..20532e44ea --- /dev/null +++ b/visual_console_client/src/items/Line.ts @@ -0,0 +1,147 @@ +import { UnknownObject, Position, Size } from "../types"; +import { parseIntOr, notEmptyStringOr } from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +interface LineProps extends ItemProps { + // Overrided properties. + readonly type: ItemType.LINE_ITEM; + label: null; + isLinkEnabled: false; + parentId: null; + aclGroupId: null; + // Custom properties. + startPosition: Position; + endPosition: Position; + lineWidth: number; + color: string | null; +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the item props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function linePropsDecoder(data: UnknownObject): LineProps | never { + const props: LineProps = { + ...itemBasePropsDecoder({ ...data, width: 1, height: 1 }), // Object spread. It will merge the properties of the two objects. + type: ItemType.LINE_ITEM, + label: null, + isLinkEnabled: false, + parentId: null, + aclGroupId: null, + // Initialize Position & Size. + x: 0, + y: 0, + width: 0, + height: 0, + // Custom properties. + startPosition: { + x: parseIntOr(data.startX, 0), + y: parseIntOr(data.startY, 0) + }, + endPosition: { + x: parseIntOr(data.endX, 0), + y: parseIntOr(data.endY, 0) + }, + lineWidth: parseIntOr(data.lineWidth || data.borderWidth, 1), + color: notEmptyStringOr(data.borderColor || data.color, null) + }; + + /* + * We need to enhance the props with the extracted size and position + * of the box cause there are missing at the props update. A better + * solution would be overriding the props setter to do it there, but + * the language doesn't allow it while targetting ES5. + * TODO: We need to figure out a more consistent solution. + */ + + return { + ...props, + // Enhance the props extracting the box size and position. + // eslint-disable-next-line @typescript-eslint/no-use-before-define + ...Line.extractBoxSizeAndPosition(props) + }; +} + +export default class Line extends Item { + /** + * @override + */ + public constructor(props: LineProps) { + /* + * We need to override the constructor cause we need to obtain + * the + * box size and position from the start and finish points + * of the line. + */ + super({ + ...props, + ...Line.extractBoxSizeAndPosition(props) + }); + } + + /** + * @override + * To create the item's DOM representation. + * @return Item. + */ + protected createDomElement(): HTMLElement { + const element: HTMLDivElement = document.createElement("div"); + element.className = "line"; + + const svgNS = "http://www.w3.org/2000/svg"; + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + // Set SVG size. + svg.setAttribute( + "width", + (this.props.width + this.props.lineWidth).toString() + ); + svg.setAttribute( + "height", + (this.props.height + this.props.lineWidth).toString() + ); + const line = document.createElementNS(svgNS, "line"); + line.setAttribute( + "x1", + `${this.props.startPosition.x - this.props.x + this.props.lineWidth / 2}` + ); + line.setAttribute( + "y1", + `${this.props.startPosition.y - this.props.y + this.props.lineWidth / 2}` + ); + line.setAttribute( + "x2", + `${this.props.endPosition.x - this.props.x + this.props.lineWidth / 2}` + ); + line.setAttribute( + "y2", + `${this.props.endPosition.y - this.props.y + this.props.lineWidth / 2}` + ); + line.setAttribute("stroke", this.props.color || "black"); + line.setAttribute("stroke-width", this.props.lineWidth.toString()); + + svg.append(line); + element.append(svg); + + return element; + } + + /** + * Extract the size and position of the box from + * the start and the finish of the line. + * @param props Item properties. + */ + public static extractBoxSizeAndPosition(props: LineProps): Size & Position { + return { + width: Math.abs(props.startPosition.x - props.endPosition.x), + height: Math.abs(props.startPosition.y - props.endPosition.y), + x: Math.min(props.startPosition.x, props.endPosition.x), + y: Math.min(props.startPosition.y, props.endPosition.y) + }; + } +} diff --git a/visual_console_client/src/items/ModuleGraph.ts b/visual_console_client/src/items/ModuleGraph.ts new file mode 100644 index 0000000000..3440496d19 --- /dev/null +++ b/visual_console_client/src/items/ModuleGraph.ts @@ -0,0 +1,118 @@ +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + modulePropsDecoder, + decodeBase64, + stringIsEmpty +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type ModuleGraphProps = { + type: ItemType.MODULE_GRAPH; + html: string; +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the module graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function moduleGraphPropsDecoder( + data: UnknownObject +): ModuleGraphProps | never { + if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) { + throw new TypeError("missing html content."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.MODULE_GRAPH, + html: !stringIsEmpty(data.html) + ? data.html + : decodeBase64(data.encodedHtml), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class ModuleGraph extends Item { + /** + * @override Item.resizeElement. + * Resize the DOM content container. + * We need to override the resize function cause this item's height + * is larger than the configured and the graph is over the label. + * @param width + * @param height + */ + protected resizeElement(width: number): void { + super.resizeElement(width, 0); + } + + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "module-graph"; + element.innerHTML = this.props.html; + + // Remove the overview graph. + const legendP = element.getElementsByTagName("p"); + for (let i = 0; i < legendP.length; i++) { + legendP[i].style.margin = "0px"; + } + + // Remove the overview graph. + const overviewGraphs = element.getElementsByClassName("overview_graph"); + for (let i = 0; i < overviewGraphs.length; i++) { + overviewGraphs[i].remove(); + } + + // Hack to execute the JS after the HTML is added to the DOM. + const scripts = element.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + setTimeout(() => { + try { + eval(scripts[i].innerHTML.trim()); + } catch (ignored) {} // eslint-disable-line no-empty + }, 0); + } + } + + return element; + } + + protected updateDomElement(element: HTMLElement): void { + element.innerHTML = this.props.html; + + // Remove the overview graph. + const legendP = element.getElementsByTagName("p"); + for (let i = 0; i < legendP.length; i++) { + legendP[i].style.margin = "0px"; + } + + // Remove the overview graph. + const overviewGraphs = element.getElementsByClassName("overview_graph"); + for (let i = 0; i < overviewGraphs.length; i++) { + overviewGraphs[i].remove(); + } + + // Hack to execute the JS after the HTML is added to the DOM. + const aux = document.createElement("div"); + aux.innerHTML = this.props.html; + const scripts = aux.getElementsByTagName("script"); + for (let i = 0; i < scripts.length; i++) { + if (scripts[i].src.length === 0) { + eval(scripts[i].innerHTML.trim()); + } + } + } +} diff --git a/visual_console_client/src/items/Percentile.ts b/visual_console_client/src/items/Percentile.ts new file mode 100644 index 0000000000..4c93b86b1f --- /dev/null +++ b/visual_console_client/src/items/Percentile.ts @@ -0,0 +1,274 @@ +import { arc as arcFactory } from "d3-shape"; + +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + modulePropsDecoder, + notEmptyStringOr, + parseIntOr, + parseFloatOr +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type PercentileProps = { + type: ItemType.PERCENTILE_BAR; + percentileType: + | "progress-bar" + | "bubble" + | "circular-progress-bar" + | "circular-progress-bar-alt"; + valueType: "percent" | "value"; + minValue: number | null; + maxValue: number | null; + color: string | null; + labelColor: string | null; + value: number | null; + unit: string | null; +} & ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Extract a valid enum value from a raw type value. + * @param type Raw value. + */ +function extractPercentileType( + type: unknown +): PercentileProps["percentileType"] { + switch (type) { + case "progress-bar": + case "bubble": + case "circular-progress-bar": + case "circular-progress-bar-alt": + return type; + default: + case ItemType.PERCENTILE_BAR: + return "progress-bar"; + case ItemType.PERCENTILE_BUBBLE: + return "bubble"; + case ItemType.CIRCULAR_PROGRESS_BAR: + return "circular-progress-bar"; + case ItemType.CIRCULAR_INTERIOR_PROGRESS_BAR: + return "circular-progress-bar-alt"; + } +} + +/** + * Extract a valid enum value from a raw value type value. + * @param type Raw value. + */ +function extractValueType(valueType: unknown): PercentileProps["valueType"] { + switch (valueType) { + case "percent": + case "value": + return valueType; + default: + return "percent"; + } +} + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the percentile props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function percentilePropsDecoder( + data: UnknownObject +): PercentileProps | never { + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.PERCENTILE_BAR, + percentileType: extractPercentileType(data.percentileType || data.type), + valueType: extractValueType(data.valueType), + minValue: parseIntOr(data.minValue, null), + maxValue: parseIntOr(data.maxValue, null), + color: notEmptyStringOr(data.color, null), + labelColor: notEmptyStringOr(data.labelColor, null), + value: parseFloatOr(data.value, null), + unit: notEmptyStringOr(data.unit, null), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +const svgNS = "http://www.w3.org/2000/svg"; + +export default class Percentile extends Item { + protected createDomElement(): HTMLElement { + const colors = { + background: "#000000", + progress: this.props.color || "#F0F0F0", + text: this.props.labelColor || "#444444" + }; + // Progress. + const progress = this.getProgress(); + // Main element. + const element = document.createElement("div"); + // SVG container. + const svg = document.createElementNS(svgNS, "svg"); + + var formatValue; + if (this.props.value != null) { + if (Intl) { + formatValue = Intl.NumberFormat("en-EN").format(this.props.value); + } else { + formatValue = this.props.value; + } + } + + switch (this.props.percentileType) { + case "progress-bar": + { + const backgroundRect = document.createElementNS(svgNS, "rect"); + backgroundRect.setAttribute("fill", colors.background); + backgroundRect.setAttribute("fill-opacity", "0.5"); + backgroundRect.setAttribute("width", "100"); + backgroundRect.setAttribute("height", "20"); + backgroundRect.setAttribute("rx", "5"); + backgroundRect.setAttribute("ry", "5"); + const progressRect = document.createElementNS(svgNS, "rect"); + progressRect.setAttribute("fill", colors.progress); + progressRect.setAttribute("fill-opacity", "1"); + progressRect.setAttribute("width", `${progress}`); + progressRect.setAttribute("height", "20"); + progressRect.setAttribute("rx", "5"); + progressRect.setAttribute("ry", "5"); + const text = document.createElementNS(svgNS, "text"); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("alignment-baseline", "middle"); + text.setAttribute("font-size", "12"); + text.setAttribute("font-family", "arial"); + text.setAttribute("font-weight", "bold"); + text.setAttribute("transform", "translate(50 11)"); + text.setAttribute("fill", colors.text); + + if (this.props.valueType === "value") { + text.style.fontSize = "6pt"; + + text.textContent = this.props.unit + ? `${formatValue} ${this.props.unit}` + : `${formatValue}`; + } else { + text.textContent = `${progress}%`; + } + + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 20"); + svg.append(backgroundRect, progressRect, text); + } + break; + case "bubble": + case "circular-progress-bar": + case "circular-progress-bar-alt": + { + // Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/ + svg.setAttribute("viewBox", "0 0 100 100"); + + if (this.props.percentileType === "bubble") { + // Create and append the circles. + const backgroundCircle = document.createElementNS(svgNS, "circle"); + backgroundCircle.setAttribute("transform", "translate(50 50)"); + backgroundCircle.setAttribute("fill", colors.background); + backgroundCircle.setAttribute("fill-opacity", "0.5"); + backgroundCircle.setAttribute("r", "50"); + const progressCircle = document.createElementNS(svgNS, "circle"); + progressCircle.setAttribute("transform", "translate(50 50)"); + progressCircle.setAttribute("fill", colors.progress); + progressCircle.setAttribute("fill-opacity", "1"); + progressCircle.setAttribute("r", `${progress / 2}`); + + svg.append(backgroundCircle, progressCircle); + } else { + // Create and append the circles. + const arcProps = { + innerRadius: + this.props.percentileType === "circular-progress-bar" ? 30 : 0, + outerRadius: 50, + startAngle: 0, + endAngle: Math.PI * 2 + }; + const arc = arcFactory(); + + const backgroundCircle = document.createElementNS(svgNS, "path"); + backgroundCircle.setAttribute("transform", "translate(50 50)"); + backgroundCircle.setAttribute("fill", colors.background); + backgroundCircle.setAttribute("fill-opacity", "0.5"); + backgroundCircle.setAttribute("d", `${arc(arcProps)}`); + const progressCircle = document.createElementNS(svgNS, "path"); + progressCircle.setAttribute("transform", "translate(50 50)"); + progressCircle.setAttribute("fill", colors.progress); + progressCircle.setAttribute("fill-opacity", "1"); + progressCircle.setAttribute( + "d", + `${arc({ + ...arcProps, + endAngle: arcProps.endAngle * (progress / 100) + })}` + ); + + svg.append(backgroundCircle, progressCircle); + } + + // Create and append the text. + const text = document.createElementNS(svgNS, "text"); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("alignment-baseline", "middle"); + text.setAttribute("font-size", "16"); + text.setAttribute("font-family", "arial"); + text.setAttribute("font-weight", "bold"); + text.setAttribute("fill", colors.text); + + if (this.props.valueType === "value" && this.props.value != null) { + // Show value and unit in 1 (no unit) or 2 lines. + if (this.props.unit && this.props.unit.length > 0) { + const value = document.createElementNS(svgNS, "tspan"); + value.setAttribute("x", "0"); + value.setAttribute("dy", "1em"); + value.textContent = `${formatValue}`; + value.style.fontSize = "8pt"; + const unit = document.createElementNS(svgNS, "tspan"); + unit.setAttribute("x", "0"); + unit.setAttribute("dy", "1em"); + unit.textContent = `${this.props.unit}`; + unit.style.fontSize = "8pt"; + text.append(value, unit); + text.setAttribute("transform", "translate(50 33)"); + } else { + text.textContent = `${formatValue}`; + text.style.fontSize = "8pt"; + text.setAttribute("transform", "translate(50 50)"); + } + } else { + // Percentage. + text.textContent = `${progress}%`; + text.setAttribute("transform", "translate(50 50)"); + } + + svg.append(text); + } + break; + } + + element.append(svg); + + return element; + } + + private getProgress(): number { + const minValue = this.props.minValue || 0; + const maxValue = this.props.maxValue || 100; + const value = this.props.value == null ? 0 : this.props.value; + + if (value <= minValue) return 0; + else if (value >= maxValue) return 100; + else return Math.trunc(((value - minValue) / (maxValue - minValue)) * 100); + } +} diff --git a/visual_console_client/src/items/Service.ts b/visual_console_client/src/items/Service.ts new file mode 100644 index 0000000000..9440503e80 --- /dev/null +++ b/visual_console_client/src/items/Service.ts @@ -0,0 +1,70 @@ +import { UnknownObject } from "../types"; +import { + stringIsEmpty, + notEmptyStringOr, + decodeBase64, + parseIntOr +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type ServiceProps = { + type: ItemType.SERVICE; + serviceId: number; + imageSrc: string | null; + statusImageSrc: string | null; + encodedTitle: string | null; +} & ItemProps; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the service props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function servicePropsDecoder(data: UnknownObject): ServiceProps | never { + if (data.imageSrc !== null) { + if ( + typeof data.statusImageSrc !== "string" || + data.imageSrc.statusImageSrc === 0 + ) { + throw new TypeError("invalid status image src."); + } + } else { + if (stringIsEmpty(data.encodedTitle)) { + throw new TypeError("missing encode tittle content."); + } + } + + if (parseIntOr(data.serviceId, null) === null) { + throw new TypeError("invalid service id."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.SERVICE, + serviceId: data.serviceId, + imageSrc: notEmptyStringOr(data.imageSrc, null), + statusImageSrc: notEmptyStringOr(data.statusImageSrc, null), + encodedTitle: notEmptyStringOr(data.encodedTitle, null) + }; +} + +export default class Service extends Item { + public createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "service"; + + if (this.props.statusImageSrc !== null) { + element.style.background = `url(${this.props.statusImageSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + } else if (this.props.encodedTitle !== null) { + element.innerHTML = decodeBase64(this.props.encodedTitle); + } + + return element; + } +} diff --git a/visual_console_client/src/items/SimpleValue.ts b/visual_console_client/src/items/SimpleValue.ts new file mode 100644 index 0000000000..7cc5e139af --- /dev/null +++ b/visual_console_client/src/items/SimpleValue.ts @@ -0,0 +1,127 @@ +import { + LinkedVisualConsoleProps, + UnknownObject, + WithModuleProps +} from "../types"; +import { + linkedVCPropsDecoder, + parseIntOr, + modulePropsDecoder, + replaceMacros +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type SimpleValueProps = { + type: ItemType.SIMPLE_VALUE; + valueType: "string" | "image"; + value: string; +} & ( + | { + processValue: "none"; + } + | { + processValue: "avg" | "max" | "min"; + period: number; + }) & + ItemProps & + WithModuleProps & + LinkedVisualConsoleProps; + +/** + * Extract a valid enum value from a raw value type. + * @param valueType Raw value. + */ +const parseValueType = (valueType: unknown): SimpleValueProps["valueType"] => { + switch (valueType) { + case "string": + case "image": + return valueType; + default: + return "string"; + } +}; + +/** + * Extract a valid enum value from a raw process value. + * @param processValue Raw value. + */ +const parseProcessValue = ( + processValue: unknown +): SimpleValueProps["processValue"] => { + switch (processValue) { + case "none": + case "avg": + case "max": + case "min": + return processValue; + default: + return "none"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the simple value props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function simpleValuePropsDecoder( + data: UnknownObject +): SimpleValueProps | never { + if (typeof data.value !== "string" || data.value.length === 0) { + throw new TypeError("invalid value"); + } + + const processValue = parseProcessValue(data.processValue); + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.SIMPLE_VALUE, + valueType: parseValueType(data.valueType), + value: data.value, + ...(processValue === "none" + ? { processValue } + : { processValue, period: parseIntOr(data.period, 0) }), // Object spread. It will merge the properties of the two objects. + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class SimpleValue extends Item { + protected createDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "simple-value"; + + if (this.props.valueType === "image") { + const img = document.createElement("img"); + img.src = this.props.value; + element.append(img); + } else { + // Add the value to the label and show it. + let text = this.props.value; + let label = this.getLabelWithMacrosReplaced(); + if (label.length > 0) { + text = replaceMacros([{ macro: /\(?_VALUE_\)?/i, value: text }], label); + } + + element.innerHTML = text; + } + + return element; + } + + /** + * @override Item.createLabelDomElement + * Create a new label for the visual console item. + * @return Item label. + */ + protected createLabelDomElement(): HTMLElement { + const element = document.createElement("div"); + element.className = "visual-console-item-label"; + // Always return an empty label. + return element; + } +} diff --git a/visual_console_client/src/items/StaticGraph.ts b/visual_console_client/src/items/StaticGraph.ts new file mode 100644 index 0000000000..899d54ec70 --- /dev/null +++ b/visual_console_client/src/items/StaticGraph.ts @@ -0,0 +1,89 @@ +import { + WithModuleProps, + LinkedVisualConsoleProps, + UnknownObject +} from "../types"; + +import { + modulePropsDecoder, + linkedVCPropsDecoder, + notEmptyStringOr +} from "../lib"; +import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item"; + +export type StaticGraphProps = { + type: ItemType.STATIC_GRAPH; + imageSrc: string; // URL? + showLastValueTooltip: "default" | "enabled" | "disabled"; + statusImageSrc: string | null; // URL? + lastValue: string | null; +} & ItemProps & + (WithModuleProps | LinkedVisualConsoleProps); + +/** + * Extract a valid enum value from a raw unknown value. + * @param showLastValueTooltip Raw value. + */ +const parseShowLastValueTooltip = ( + showLastValueTooltip: unknown +): StaticGraphProps["showLastValueTooltip"] => { + switch (showLastValueTooltip) { + case "default": + case "enabled": + case "disabled": + return showLastValueTooltip; + default: + return "default"; + } +}; + +/** + * Build a valid typed object from a raw object. + * This will allow us to ensure the type safety. + * + * @param data Raw object. + * @return An object representing the static graph props. + * @throws Will throw a TypeError if some property + * is missing from the raw object or have an invalid type. + */ +export function staticGraphPropsDecoder( + data: UnknownObject +): StaticGraphProps | never { + if (typeof data.imageSrc !== "string" || data.imageSrc.length === 0) { + throw new TypeError("invalid image src."); + } + + return { + ...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + type: ItemType.STATIC_GRAPH, + imageSrc: data.imageSrc, + showLastValueTooltip: parseShowLastValueTooltip(data.showLastValueTooltip), + statusImageSrc: notEmptyStringOr(data.statusImageSrc, null), + lastValue: notEmptyStringOr(data.lastValue, null), + ...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects. + ...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects. + }; +} + +export default class StaticGraph extends Item { + protected createDomElement(): HTMLElement { + const imgSrc = this.props.statusImageSrc || this.props.imageSrc; + const element = document.createElement("div"); + element.className = "static-graph"; + element.style.background = `url(${imgSrc}) no-repeat`; + element.style.backgroundSize = "contain"; + element.style.backgroundPosition = "center"; + + // Show last value in a tooltip. + if ( + this.props.lastValue !== null && + this.props.showLastValueTooltip !== "disabled" + ) { + element.className = "static-graph image forced_title"; + element.setAttribute("data-use_title_for_force_title", "1"); + element.setAttribute("data-title", this.props.lastValue); + } + + return element; + } +} diff --git a/visual_console_client/src/lib/AsyncTaskManager.ts b/visual_console_client/src/lib/AsyncTaskManager.ts new file mode 100644 index 0000000000..5ad261194b --- /dev/null +++ b/visual_console_client/src/lib/AsyncTaskManager.ts @@ -0,0 +1,172 @@ +import TypedEvent, { Disposable, Listener } from "../TypedEvent"; + +interface Cancellable { + cancel(): void; +} + +type AsyncTaskStatus = "waiting" | "started" | "cancelled" | "finished"; +type AsyncTaskInitiator = (done: () => void) => Cancellable; + +/** + * Defines an async task which can be started and cancelled. + * It's possible to observe the status changes of the task. + */ +class AsyncTask { + private readonly taskInitiator: AsyncTaskInitiator; + private cancellable: Cancellable = { cancel: () => {} }; + private _status: AsyncTaskStatus = "waiting"; + + // Event manager for status change events. + private readonly statusChangeEventManager = new TypedEvent(); + // List of references to clean the event listeners. + private readonly disposables: Disposable[] = []; + + public constructor(taskInitiator: AsyncTaskInitiator) { + this.taskInitiator = taskInitiator; + } + + /** + * Public setter of the `status` property. + * @param status. + */ + public set status(status: AsyncTaskStatus) { + this._status = status; + this.statusChangeEventManager.emit(status); + } + + /** + * Public accessor of the `status` property. + * @return status. + */ + public get status() { + return this._status; + } + + /** + * Start the async task. + */ + public init(): void { + this.cancellable = this.taskInitiator(() => { + this.status = "finished"; + }); + this.status = "started"; + } + + /** + * Cancel the async task. + */ + public cancel(): void { + this.cancellable.cancel(); + this.status = "cancelled"; + } + + /** + * Add an event handler to the status change. + * @param listener Function which is going to be executed when the status changes. + */ + public onStatusChange(listener: Listener): Disposable { + /* + * The '.on' function returns a function which will clean the event + * listener when executed. We store all the 'dispose' functions to + * call them when the item should be cleared. + */ + const disposable = this.statusChangeEventManager.on(listener); + this.disposables.push(disposable); + + return disposable; + } +} + +/** + * Wrap an async task into another which will execute that task indefinitely + * every time the tash finnish and the chosen period ends. + * Will last until cancellation. + * + * @param task Async task to execute. + * @param period Time in milliseconds to wait until the next async esecution. + * + * @return A new async task. + */ +function asyncPeriodic(task: AsyncTask, period: number): AsyncTask { + return new AsyncTask(() => { + let ref: number | null = null; + + task.onStatusChange(status => { + if (status === "finished") { + ref = window.setTimeout(() => { + task.init(); + }, period); + } + }); + + task.init(); + + return { + cancel: () => { + if (ref) clearTimeout(ref); + task.cancel(); + } + }; + }); +} + +/** + * Manages a list of async tasks. + */ +export default class AsyncTaskManager { + private tasks: { [identifier: string]: AsyncTask } = {}; + + /** + * Adds an async task to the manager. + * + * @param identifier Unique identifier. + * @param taskInitiator Function to initialize the async task. + * Should return a structure to cancel the task. + * @param period Optional period to repeat the task indefinitely. + */ + public add( + identifier: string, + taskInitiator: AsyncTaskInitiator, + period: number = 0 + ): AsyncTask { + if (this.tasks[identifier] && this.tasks[identifier].status === "started") { + this.tasks[identifier].cancel(); + } + + const asyncTask = + period > 0 + ? asyncPeriodic(new AsyncTask(taskInitiator), period) + : new AsyncTask(taskInitiator); + + this.tasks[identifier] = asyncTask; + + return this.tasks[identifier]; + } + + /** + * Starts an async task. + * + * @param identifier Unique identifier. + */ + public init(identifier: string) { + if ( + this.tasks[identifier] && + (this.tasks[identifier].status === "waiting" || + this.tasks[identifier].status === "cancelled" || + this.tasks[identifier].status === "finished") + ) { + this.tasks[identifier].init(); + } + } + + /** + * Cancel a running async task. + * + * @param identifier Unique identifier. + */ + public cancel(identifier: string) { + if (this.tasks[identifier] && this.tasks[identifier].status === "started") { + this.tasks[identifier].cancel(); + } + } +} diff --git a/visual_console_client/src/lib/index.ts b/visual_console_client/src/lib/index.ts new file mode 100644 index 0000000000..1a04ca74af --- /dev/null +++ b/visual_console_client/src/lib/index.ts @@ -0,0 +1,334 @@ +import { + UnknownObject, + Position, + Size, + WithAgentProps, + WithModuleProps, + LinkedVisualConsoleProps, + LinkedVisualConsolePropsStatus +} from "../types"; + +/** + * Return a number or a default value from a raw value. + * @param value Raw value from which we will try to extract a valid number. + * @param defaultValue Default value to use if we cannot extract a valid number. + * @return A valid number or the default value. + */ +export function parseIntOr(value: unknown, defaultValue: T): number | T { + if (typeof value === "number") return value; + if (typeof value === "string" && value.length > 0 && !isNaN(parseInt(value))) + return parseInt(value); + else return defaultValue; +} + +/** + * Return a number or a default value from a raw value. + * @param value Raw value from which we will try to extract a valid number. + * @param defaultValue Default value to use if we cannot extract a valid number. + * @return A valid number or the default value. + */ +export function parseFloatOr(value: unknown, defaultValue: T): number | T { + if (typeof value === "number") return value; + if ( + typeof value === "string" && + value.length > 0 && + !isNaN(parseFloat(value)) + ) + return parseFloat(value); + else return defaultValue; +} + +/** + * Check if a string exists and it's not empty. + * @param value Value to check. + * @return The check result. + */ +export function stringIsEmpty(value?: string | null): boolean { + return value == null || value.length === 0; +} + +/** + * Return a not empty string or a default value from a raw value. + * @param value Raw value from which we will try to extract a non empty string. + * @param defaultValue Default value to use if we cannot extract a non empty string. + * @return A non empty string or the default value. + */ +export function notEmptyStringOr( + value: unknown, + defaultValue: T +): string | T { + return typeof value === "string" && value.length > 0 ? value : defaultValue; +} + +/** + * Return a boolean from a raw value. + * @param value Raw value from which we will try to extract the boolean. + * @return A valid boolean value. false by default. + */ +export function parseBoolean(value: unknown): boolean { + if (typeof value === "boolean") return value; + else if (typeof value === "number") return value > 0; + else if (typeof value === "string") return value === "1" || value === "true"; + else return false; +} + +/** + * Pad the current string with another string (multiple times, if needed) + * until the resulting string reaches the given length. + * The padding is applied from the start (left) of the current string. + * @param value Text that needs to be padded. + * @param length Length of the returned text. + * @param pad Text to add. + * @return Padded text. + */ +export function leftPad( + value: string | number, + length: number, + pad: string | number = " " +): string { + if (typeof value === "number") value = `${value}`; + if (typeof pad === "number") pad = `${pad}`; + + const diffLength = length - value.length; + if (diffLength === 0) return value; + if (diffLength < 0) return value.substr(Math.abs(diffLength)); + + if (diffLength === pad.length) return `${pad}${value}`; + if (diffLength < pad.length) return `${pad.substring(0, diffLength)}${value}`; + + const repeatTimes = Math.floor(diffLength / pad.length); + const restLength = diffLength - pad.length * repeatTimes; + + let newPad = ""; + for (let i = 0; i < repeatTimes; i++) newPad += pad; + + if (restLength === 0) return `${newPad}${value}`; + return `${newPad}${pad.substring(0, restLength)}${value}`; +} + +/* Decoders */ + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the position. + */ +export function positionPropsDecoder(data: UnknownObject): Position { + return { + x: parseIntOr(data.x, 0), + y: parseIntOr(data.y, 0) + }; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the size. + * @throws Will throw a TypeError if the width and height are not valid numbers. + */ +export function sizePropsDecoder(data: UnknownObject): Size | never { + if ( + data.width == null || + isNaN(parseInt(data.width)) || + data.height == null || + isNaN(parseInt(data.height)) + ) { + throw new TypeError("invalid size."); + } + + return { + width: parseInt(data.width), + height: parseInt(data.height) + }; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the agent properties. + */ +export function agentPropsDecoder(data: UnknownObject): WithAgentProps { + const agentProps: WithAgentProps = { + agentId: parseIntOr(data.agent, null), + agentName: notEmptyStringOr(data.agentName, null), + agentAlias: notEmptyStringOr(data.agentAlias, null), + agentDescription: notEmptyStringOr(data.agentDescription, null), + agentAddress: notEmptyStringOr(data.agentAddress, null) + }; + + return data.metaconsoleId != null + ? { + metaconsoleId: data.metaconsoleId, + ...agentProps // Object spread: http://es6-features.org/#SpreadOperator + } + : agentProps; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the module and agent properties. + */ +export function modulePropsDecoder(data: UnknownObject): WithModuleProps { + return { + moduleId: parseIntOr(data.moduleId, null), + moduleName: notEmptyStringOr(data.moduleName, null), + moduleDescription: notEmptyStringOr(data.moduleDescription, null), + ...agentPropsDecoder(data) // Object spread: http://es6-features.org/#SpreadOperator + }; +} + +/** + * Build a valid typed object from a raw object. + * @param data Raw object. + * @return An object representing the linked visual console properties. + * @throws Will throw a TypeError if the status calculation properties are invalid. + */ +export function linkedVCPropsDecoder( + data: UnknownObject +): LinkedVisualConsoleProps | never { + // Object destructuring: http://es6-features.org/#ObjectMatchingShorthandNotation + const { + metaconsoleId, + linkedLayoutId: id, + linkedLayoutAgentId: agentId + } = data; + + let linkedLayoutStatusProps: LinkedVisualConsolePropsStatus = { + linkedLayoutStatusType: "default" + }; + switch (data.linkedLayoutStatusType) { + case "weight": { + const weight = parseIntOr(data.linkedLayoutStatusTypeWeight, null); + if (weight == null) + throw new TypeError("invalid status calculation properties."); + + if (data.linkedLayoutStatusTypeWeight) + linkedLayoutStatusProps = { + linkedLayoutStatusType: "weight", + linkedLayoutStatusTypeWeight: weight + }; + break; + } + case "service": { + const warningThreshold = parseIntOr( + data.linkedLayoutStatusTypeWarningThreshold, + null + ); + const criticalThreshold = parseIntOr( + data.linkedLayoutStatusTypeCriticalThreshold, + null + ); + if (warningThreshold == null || criticalThreshold == null) { + throw new TypeError("invalid status calculation properties."); + } + + linkedLayoutStatusProps = { + linkedLayoutStatusType: "service", + linkedLayoutStatusTypeWarningThreshold: warningThreshold, + linkedLayoutStatusTypeCriticalThreshold: criticalThreshold + }; + break; + } + } + + const linkedLayoutBaseProps = { + linkedLayoutId: parseIntOr(id, null), + linkedLayoutAgentId: parseIntOr(agentId, null), + ...linkedLayoutStatusProps // Object spread: http://es6-features.org/#SpreadOperator + }; + + return metaconsoleId != null + ? { + metaconsoleId, + ...linkedLayoutBaseProps // Object spread: http://es6-features.org/#SpreadOperator + } + : linkedLayoutBaseProps; +} + +/** + * To get a CSS rule with the most used prefixes. + * @param ruleName Name of the CSS rule. + * @param ruleValue Value of the CSS rule. + * @return An array of rules with the prefixes applied. + */ +export function prefixedCssRules( + ruleName: string, + ruleValue: string +): string[] { + const rule = `${ruleName}: ${ruleValue};`; + return [ + `-webkit-${rule}`, + `-moz-${rule}`, + `-ms-${rule}`, + `-o-${rule}`, + `${rule}` + ]; +} + +/** + * Decode a base64 string. + * @param input Data encoded using base64. + * @return Decoded data. + */ +export function decodeBase64(input: string): string { + return decodeURIComponent(escape(window.atob(input))); +} + +/** + * Generate a date representation with the format 'd/m/Y'. + * @param initialDate Date to be used instead of a generated one. + * @param locale Locale to use if localization is required and available. + * @example 24/02/2020. + * @return Date representation. + */ +export function humanDate(date: Date, locale: string | null = null): string { + if (locale && Intl && Intl.DateTimeFormat) { + // Format using the user locale. + const options: Intl.DateTimeFormatOptions = { + day: "2-digit", + month: "2-digit", + year: "numeric" + }; + return Intl.DateTimeFormat(locale, options).format(date); + } else { + // Use getDate, getDay returns the week day. + const day = leftPad(date.getDate(), 2, 0); + // The getMonth function returns the month starting by 0. + const month = leftPad(date.getMonth() + 1, 2, 0); + const year = leftPad(date.getFullYear(), 4, 0); + + // Format: 'd/m/Y'. + return `${day}/${month}/${year}`; + } +} + +/** + * Generate a time representation with the format 'hh:mm:ss'. + * @param initialDate Date to be used instead of a generated one. + * @example 01:34:09. + * @return Time representation. + */ +export function humanTime(date: Date): string { + const hours = leftPad(date.getHours(), 2, 0); + const minutes = leftPad(date.getMinutes(), 2, 0); + const seconds = leftPad(date.getSeconds(), 2, 0); + + return `${hours}:${minutes}:${seconds}`; +} + +interface Macro { + macro: string | RegExp; + value: string; +} +/** + * Replace the macros of a text. + * @param macros List of macros and their replacements. + * @param text Text in which we need to replace the macros. + */ +export function replaceMacros(macros: Macro[], text: string): string { + return macros.reduce( + (acc, { macro, value }) => acc.replace(macro, value), + text + ); +} diff --git a/visual_console_client/src/lib/spec.ts b/visual_console_client/src/lib/spec.ts new file mode 100644 index 0000000000..ca86e2f499 --- /dev/null +++ b/visual_console_client/src/lib/spec.ts @@ -0,0 +1,120 @@ +import { + parseIntOr, + stringIsEmpty, + notEmptyStringOr, + leftPad, + prefixedCssRules, + decodeBase64, + humanDate, + humanTime, + replaceMacros +} from "."; + +describe("function parseIntOr", () => { + it("should retrieve valid int or a default value", () => { + expect(parseIntOr("Foo", null)).toBe(null); + expect(parseIntOr("1a", null)).toBe(1); + expect(parseIntOr("a1", null)).toBe(null); + expect(parseIntOr("1", null)).toBe(1); + expect(parseIntOr(false, null)).toBe(null); + expect(parseIntOr(true, null)).toBe(null); + expect(parseIntOr(1, null)).toBe(1); + }); +}); + +describe("function stringIsEmpty", () => { + it("should check properly if a string is empry or not", () => { + expect(stringIsEmpty()).toBe(true); + expect(stringIsEmpty("")).toBe(true); + expect(stringIsEmpty("foo")).toBe(false); + expect(stringIsEmpty("bar")).toBe(false); + }); +}); + +describe("function notEmptyStringOr", () => { + it("should retrieve not empty string or a default value", () => { + expect(notEmptyStringOr("", null)).toBe(null); + expect(notEmptyStringOr("Foo", null)).toBe("Foo"); + expect(notEmptyStringOr(1, 1)).toBe(1); + expect(notEmptyStringOr(1, 0)).toBe(0); + expect(notEmptyStringOr("", 0)).toBe(0); + expect(notEmptyStringOr("Foo", "Bar")).toBe("Foo"); + expect(notEmptyStringOr(0, "Bar")).toBe("Bar"); + }); +}); + +describe("function leftPad", () => { + it("should pad properly", () => { + expect(leftPad(1, 2, 0)).toBe("01"); + expect(leftPad(1, 4, 0)).toBe("0001"); + expect(leftPad(1, 4, "0")).toBe("0001"); + expect(leftPad("1", 4, "0")).toBe("0001"); + expect(leftPad(10, 4, 0)).toBe("0010"); + expect(leftPad("bar", 6, "foo")).toBe("foobar"); + expect(leftPad("bar", 11, "foo")).toBe("foofoofobar"); + expect(leftPad("bar", 4, "foo")).toBe("fbar"); + expect(leftPad("bar", 2, "foo")).toBe("ar"); + expect(leftPad("bar", 3, "foo")).toBe("bar"); + }); +}); + +describe("function prefixedCssRules", () => { + it("should add the prefixes to the rules", () => { + const rules = prefixedCssRules("transform", "rotate(0)"); + expect(rules).toContainEqual("transform: rotate(0);"); + expect(rules).toContainEqual("-webkit-transform: rotate(0);"); + expect(rules).toContainEqual("-moz-transform: rotate(0);"); + expect(rules).toContainEqual("-ms-transform: rotate(0);"); + expect(rules).toContainEqual("-o-transform: rotate(0);"); + expect(rules).toHaveLength(5); + }); +}); + +describe("function decodeBase64", () => { + it("should decode the base64 without errors", () => { + expect(decodeBase64("SGkgSSdtIGRlY29kZWQ=")).toEqual("Hi I'm decoded"); + expect(decodeBase64("Rk9PQkFSQkFa")).toEqual("FOOBARBAZ"); + expect(decodeBase64("eyJpZCI6MSwibmFtZSI6ImZvbyJ9")).toEqual( + '{"id":1,"name":"foo"}' + ); + expect( + decodeBase64("PGRpdj5Cb3ggPHA+UGFyYWdyYXBoPC9wPjxociAvPjwvZGl2Pg==") + ).toEqual("
    Box

    Paragraph


    "); + }); +}); + +describe("humanDate function", () => { + it("should return the date with padded 0's", () => { + const expected = "01/02/0123"; + const date = new Date(`02/01/0123 12:00:00`); + const digitalDate = humanDate(date); + expect(digitalDate).toBe(expected); + }); +}); + +describe("humanTime function", () => { + it("should return the time with padded 0's when hours/minutes/seconds are less than 10", () => { + const expected = "01:02:03"; + const date = new Date(`01/01/1970 ${expected}`); + const digitalTime = humanTime(date); + expect(digitalTime).toBe(expected); + }); +}); + +describe("replaceMacros function", () => { + const macros = [ + { macro: "_foo_", value: "foo" }, + { macro: "_bar_", value: "bar" }, + { macro: "_baz_", value: "baz" } + ]; + + it("should not replace anything if it doesn't find any macro", () => { + const text = "Lorem Ipsum"; + expect(replaceMacros(macros, text)).toBe(text); + }); + + it("should replace the macros", () => { + const text = "Lorem _foo_ Ipsum _baz_"; + expect(replaceMacros(macros, text)).toBe("Lorem foo Ipsum baz"); + }); +}); diff --git a/visual_console_client/src/main.css b/visual_console_client/src/main.css new file mode 100644 index 0000000000..427c8895af --- /dev/null +++ b/visual_console_client/src/main.css @@ -0,0 +1,16 @@ +#visual-console-container { + margin: 0px auto; + position: relative; + background-repeat: no-repeat; + background-size: 100% 100%; + background-position: center; +} + +.visual-console-item { + position: absolute; + display: flex; + flex-direction: initial; + justify-items: center; + align-items: center; + user-select: text; +} diff --git a/visual_console_client/src/types.ts b/visual_console_client/src/types.ts new file mode 100644 index 0000000000..79dee56e74 --- /dev/null +++ b/visual_console_client/src/types.ts @@ -0,0 +1,47 @@ +export interface UnknownObject { + [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any +} + +export interface Position { + x: number; + y: number; +} + +export interface Size { + width: number; + height: number; +} + +export interface WithAgentProps { + metaconsoleId?: number | null; + agentId: number | null; + agentName: string | null; + agentAlias: string | null; + agentDescription: string | null; + agentAddress: string | null; +} + +export interface WithModuleProps extends WithAgentProps { + moduleId: number | null; + moduleName: string | null; + moduleDescription: string | null; +} + +export type LinkedVisualConsolePropsStatus = + | { + linkedLayoutStatusType: "default"; + } + | { + linkedLayoutStatusType: "weight"; + linkedLayoutStatusTypeWeight: number; + } + | { + linkedLayoutStatusType: "service"; + linkedLayoutStatusTypeWarningThreshold: number; + linkedLayoutStatusTypeCriticalThreshold: number; + }; +export type LinkedVisualConsoleProps = { + metaconsoleId?: number | null; + linkedLayoutId: number | null; + linkedLayoutAgentId: number | null; +} & LinkedVisualConsolePropsStatus; diff --git a/visual_console_client/tsconfig.json b/visual_console_client/tsconfig.json new file mode 100644 index 0000000000..5f047a33d6 --- /dev/null +++ b/visual_console_client/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "es6", + "strict": true, + "alwaysStrict": true, + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "sourceMap": true, + // "isolatedModules": true, + "noEmit": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "pretty": true + }, + "include": ["**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/visual_console_client/webpack.config.js b/visual_console_client/webpack.config.js new file mode 100644 index 0000000000..ffce749d99 --- /dev/null +++ b/visual_console_client/webpack.config.js @@ -0,0 +1,104 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require("path"); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const CleanWebpackPlugin = require("clean-webpack-plugin"); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +const dev = process.env.NODE_ENV !== "production"; +const entry = path.join(__dirname, "src", "index.ts"); +const buildPath = path.join( + __dirname, + "..", + process.env.BUILD_PATH && process.env.BUILD_PATH.length > 0 + ? process.env.BUILD_PATH + : "build" +); + +module.exports = { + mode: dev ? "development" : "production", + entry, // Start from this file. + output: { + path: buildPath, // The files will be created here. + // filename: dev ? "vc.[name].min.js" : "vc.[name].[chunkhash:8].min.js" + filename: "vc.[name].min.js" + }, + devtool: "source-map", + resolve: { + extensions: [".ts", ".js", ".json"] + }, + module: { + rules: [ + // Loader for the Typescript compiler. + { + test: /\.ts$/, + loader: "awesome-typescript-loader" + }, + // This loader builds a main CSS file from all the CSS imports across the files. + { + test: /\.css$/, + loader: [ + // https://github.com/webpack-contrib/mini-css-extract-plugin + { + loader: MiniCssExtractPlugin.loader, + options: { + hot: true, // if you want HMR - we try to automatically inject hot reloading but if it's not working, add it to the config + reloadAll: true // when desperation kicks in - this is a brute force HMR flag + } + }, + // https://webpack.js.org/loaders/css-loader + { + loader: "css-loader", + options: { + sourceMap: true + } + }, + // To post process CSS and add some things like prefixes to the rules. e.g.: -webkit-... + // https://github.com/postcss/postcss-loader + { + loader: "postcss-loader", + options: { + plugins: () => [ + // To improve the support for old browsers. + require("autoprefixer")({ + browsers: ["> 1%", "last 2 versions"] + }) + ] + } + } + ] + }, + // To allow the use of file imports. The imported files are transformed into + // data uris if they are small enough or it returns a path to the file. + // https://webpack.js.org/loaders/url-loader + { + test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/, + loader: "url-loader", + options: { + limit: 10000, + // name: "[name].[hash:8].[ext]" + name: "[name].[ext]" + } + } + ] + }, + plugins: [ + // This plugin will remove all files inside Webpack's output.path directory, + // as well as all unused webpack assets after every successful rebuild. + new CleanWebpackPlugin(), + // Options for the plugin which extract the CSS files to build a main file. + new MiniCssExtractPlugin({ + // Options similar to the same options in webpackOptions.output + // both options are optional + // filename: dev ? "vc.[name].css" : "vc.[name].[contenthash:8].css", + filename: "vc.[name].css", + // Disable to remove warnings about conflicting order between imports. + orderWarning: true + }) + ], + // Static server which runs the playground on npm start. + devServer: { + open: true, + contentBase: "playground" + } +};