diff --git a/extras/deploy-scripts/pandora_deploy_community.sh b/extras/deploy-scripts/pandora_deploy_community.sh index fe4af3d924..534649be59 100644 --- a/extras/deploy-scripts/pandora_deploy_community.sh +++ b/extras/deploy-scripts/pandora_deploy_community.sh @@ -258,6 +258,7 @@ console_dependencies=" \ mod_ssl \ libzstd \ openldap-clients \ + chromium \ http://firefly.artica.es/centos8/phantomjs-2.1.1-1.el7.x86_64.rpm" execute_cmd "yum install -y $console_dependencies" "Installing Pandora FMS Console dependencies" diff --git a/extras/deploy-scripts/pandora_deploy_community_el8.sh b/extras/deploy-scripts/pandora_deploy_community_el8.sh index fd4855f67b..465ee302ea 100644 --- a/extras/deploy-scripts/pandora_deploy_community_el8.sh +++ b/extras/deploy-scripts/pandora_deploy_community_el8.sh @@ -312,6 +312,7 @@ console_dependencies=" \ mod_ssl \ libzstd \ openldap-clients \ + chromium \ http://firefly.artica.es/centos8/perl-Net-Telnet-3.04-1.el8.noarch.rpm \ http://firefly.artica.es/centos7/wmic-1.4-1.el7.x86_64.rpm \ http://firefly.artica.es/centos8/phantomjs-2.1.1-1.el7.x86_64.rpm" diff --git a/extras/deploy-scripts/pandora_deploy_community_ubuntu_2204.sh b/extras/deploy-scripts/pandora_deploy_community_ubuntu_2204.sh index feced2cbe7..1a357ed99f 100644 --- a/extras/deploy-scripts/pandora_deploy_community_ubuntu_2204.sh +++ b/extras/deploy-scripts/pandora_deploy_community_ubuntu_2204.sh @@ -271,6 +271,8 @@ echo -en "${cyan}Installing phantomjs...${reset}" /usr/bin/phantomjs --version &>> "$LOGFILE" check_cmd_status "Error Installing phanromjs" +# Chromium +execute_cmd "apt install -y chromium-browser" "Instaling chromium browser" # SDK VMware perl dependencies vmware_dependencies=" \ diff --git a/extras/docker/centos8/base/Dockerfile b/extras/docker/centos8/base/Dockerfile index c27b2db723..8106abf8ce 100644 --- a/extras/docker/centos8/base/Dockerfile +++ b/extras/docker/centos8/base/Dockerfile @@ -262,13 +262,11 @@ RUN dnf install -y --setopt=tsflags=nodocs \ http://firefly.artica.es/centos7/wmic-1.4-1.el7.x86_64.rpm # Install utils -RUN dnf install -y supervisor crontabs http://firefly.artica.es/centos8/phantomjs-2.1.1-1.el7.x86_64.rpm --setopt=tsflags=nodocs +RUN dnf install -y supervisor chromium crontabs http://firefly.artica.es/centos8/phantomjs-2.1.1-1.el7.x86_64.rpm --setopt=tsflags=nodocs # SDK VMware perl dependencies RUN dnf install -y http://firefly.artica.es/centos8/perl-Crypt-OpenSSL-AES-0.02-1.el8.x86_64.rpm http://firefly.artica.es/centos8/perl-Crypt-SSLeay-0.73_07-1.gf.el8.x86_64.rpm perl-Net-HTTP perl-libwww-perl openssl-devel perl-Crypt-CBC perl-Bytes-Random-Secure perl-Crypt-Random-Seed perl-Math-Random-ISAAC perl-JSON http://firefly.artica.es/centos8/VMware-vSphere-Perl-SDK-6.5.0-4566394.x86_64.rpm # Instant client Oracle RUN dnf install -y https://download.oracle.com/otn_software/linux/instantclient/19800/oracle-instantclient19.8-basic-19.8.0.0.0-1.x86_64.rpm https://download.oracle.com/otn_software/linux/instantclient/19800/oracle-instantclient19.8-sqlplus-19.8.0.0.0-1.x86_64.rpm -# Install Phantom -RUN dnf install -y supervisor crontabs http://firefly.artica.es/centos8/phantomjs-2.1.1-1.el7.x86_64.rpm --setopt=tsflags=nodocs EXPOSE 80 443 41121 162/udp diff --git a/pandora_agents/unix/DEBIAN/control b/pandora_agents/unix/DEBIAN/control index 5deb440991..908a62d45d 100644 --- a/pandora_agents/unix/DEBIAN/control +++ b/pandora_agents/unix/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-agent-unix -Version: 7.0NG.767-221216 +Version: 7.0NG.767-230102 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 e0878747c0..9be9fdc128 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.767-221216" +pandora_version="7.0NG.767-230102" 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/pandora_agent b/pandora_agents/unix/pandora_agent index 24d37049f3..57b8283447 100755 --- a/pandora_agents/unix/pandora_agent +++ b/pandora_agents/unix/pandora_agent @@ -1015,7 +1015,7 @@ my $Sem = undef; my $ThreadSem = undef; use constant AGENT_VERSION => '7.0NG.767'; -use constant AGENT_BUILD => '221216'; +use constant AGENT_BUILD => '230102'; # Agent log default file size maximum and instances use constant DEFAULT_MAX_LOG_SIZE => 600000; diff --git a/pandora_agents/unix/pandora_agent.redhat.spec b/pandora_agents/unix/pandora_agent.redhat.spec index 7f06901243..31f47bff03 100644 --- a/pandora_agents/unix/pandora_agent.redhat.spec +++ b/pandora_agents/unix/pandora_agent.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.767 -%define release 221216 +%define release 230102 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 fb8258680b..0ce1f393ec 100644 --- a/pandora_agents/unix/pandora_agent.spec +++ b/pandora_agents/unix/pandora_agent.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.767 -%define release 221216 +%define release 230102 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 3f1c287ce0..88925f7d6e 100755 --- a/pandora_agents/unix/pandora_agent_installer +++ b/pandora_agents/unix/pandora_agent_installer @@ -10,7 +10,7 @@ # ********************************************************************** PI_VERSION="7.0NG.767" -PI_BUILD="221216" +PI_BUILD="230102" OS_NAME=`uname -s` FORCE=0 diff --git a/pandora_agents/win32/installer/pandora.mpi b/pandora_agents/win32/installer/pandora.mpi index b7fa8b178f..8f32595808 100644 --- a/pandora_agents/win32/installer/pandora.mpi +++ b/pandora_agents/win32/installer/pandora.mpi @@ -186,7 +186,7 @@ UpgradeApplicationID {} Version -{221216} +{230102} ViewReadme {Yes} diff --git a/pandora_agents/win32/pandora.cc b/pandora_agents/win32/pandora.cc index dadf0a01aa..aeb74b5806 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.767 Build 221216") +#define PANDORA_VERSION ("7.0NG.767 Build 230102") string pandora_path; string pandora_dir; diff --git a/pandora_agents/win32/versioninfo.rc b/pandora_agents/win32/versioninfo.rc index d8e730a7d5..ffda05cedc 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.767(Build 221216))" + VALUE "ProductVersion", "(7.0NG.767(Build 230102))" VALUE "FileVersion", "1.0.0.0" END END diff --git a/pandora_console/DEBIAN/control b/pandora_console/DEBIAN/control index 73fc08a7c3..0c14067fab 100644 --- a/pandora_console/DEBIAN/control +++ b/pandora_console/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-console -Version: 7.0NG.767-221216 +Version: 7.0NG.767-230102 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 f90b2d5b3c..102f9172fb 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.767-221216" +pandora_version="7.0NG.767-230102" package_pear=0 package_pandora=1 diff --git a/pandora_console/composer.json b/pandora_console/composer.json index bd4ab0d5fa..0a9632d2dd 100644 --- a/pandora_console/composer.json +++ b/pandora_console/composer.json @@ -9,13 +9,24 @@ ], "config": { "platform": { - "php": "7.2.0" + "php": "8.0.0" } }, "require": { "mpdf/mpdf": "^8.0.15", "swiftmailer/swiftmailer": "^6.0", - "amphp/parallel-functions": "^1.0" + "amphp/parallel-functions": "^1.0", + "chrome-php/chrome": "^1.7.1", + "artica/phpchartjs": "^1.0" + }, + "repositories": { + "phpchartjs": { + "type": "path", + "url": "../../../phpchartjs", + "options": { + "symlink": false + } + } }, "autoload": { "psr-4": { diff --git a/pandora_console/composer.lock b/pandora_console/composer.lock index 05b12fe9b3..ec6825167c 100644 --- a/pandora_console/composer.lock +++ b/pandora_console/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "643ac0dc8a8e1f129104399054f8dd0c", + "content-hash": "be2ad3d9d4df55d7ccb87981e82c4932", "packages": [ { "name": "amphp/amp", @@ -549,6 +549,205 @@ ], "time": "2021-10-25T18:29:10+00:00" }, + { + "name": "artica/phpchartjs", + "version": "v1.0.2", + "dist": { + "type": "path", + "url": "../../../phpchartjs", + "reference": "4957e7cd699e50cee8e0ba7304e1423aafb2cad2" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "halfpastfouram/collection": "1.0.0", + "laminas/laminas-json": ">3.1.2", + "php": ">=7.2", + "symfony/var-dumper": "^3.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "*", + "phpunit/phpunit": "^9.5", + "sensiolabs/security-checker": "^5.0", + "squizlabs/php_codesniffer": "3.5.3" + }, + "type": "package", + "autoload": { + "psr-4": { + "Artica\\PHPChartJS\\": "src/", + "Test\\": "test/" + } + }, + "scripts": { + "test": [ + "./vendor/bin/phpunit" + ], + "cs-check": [ + "./vendor/bin/phpcs" + ], + "cs-fix": [ + "./vendor/bin/phpcbf" + ] + }, + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Bob Kruithof" + }, + { + "name": "Daniel Barbero" + } + ], + "description": "PHP library for ChartJS", + "homepage": "https://artica.es/", + "keywords": [ + "chartjs", + "graph", + "php" + ], + "transport-options": { + "symlink": false, + "relative": true + } + }, + { + "name": "chrome-php/chrome", + "version": "v1.7.1", + "source": { + "type": "git", + "url": "https://github.com/chrome-php/chrome.git", + "reference": "5783c749b2ee385d1c481b0906f1b8acef0296e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chrome-php/chrome/zipball/5783c749b2ee385d1c481b0906f1b8acef0296e4", + "reference": "5783c749b2ee385d1c481b0906f1b8acef0296e4", + "shasum": "" + }, + "require": { + "chrome-php/wrench": "^1.3", + "evenement/evenement": "^3.0.1", + "monolog/monolog": "^1.27.1 || ^2.8 || ^3.2", + "php": "^7.3 || ^8.0", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/filesystem": "^4.4 || ^5.0 || ^6.0", + "symfony/polyfill-mbstring": "^1.26", + "symfony/process": "^4.4 || ^5.0 || ^6.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^9.5.23", + "symfony/var-dumper": "^4.4 || ^5.0 || ^6.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "HeadlessChromium\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Enrico Dias", + "email": "enrico@enricodias.com", + "homepage": "https://github.com/enricodias" + } + ], + "description": "Instrument headless chrome/chromium instances from PHP", + "keywords": [ + "browser", + "chrome", + "chromium", + "crawl", + "headless", + "pdf", + "puppeteer", + "screenshot" + ], + "support": { + "issues": "https://github.com/chrome-php/chrome/issues", + "source": "https://github.com/chrome-php/chrome/tree/v1.7.1" + }, + "time": "2022-09-04T21:11:00+00:00" + }, + { + "name": "chrome-php/wrench", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/chrome-php/wrench.git", + "reference": "68b8282d5d0d54a519c3212ee3e4c35bef40b7d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/chrome-php/wrench/zipball/68b8282d5d0d54a519c3212ee3e4c35bef40b7d9", + "reference": "68b8282d5d0d54a519c3212ee3e4c35bef40b7d9", + "shasum": "" + }, + "require": { + "ext-sockets": "*", + "php": "^7.3 || ^8.0", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/polyfill-php80": "^1.26" + }, + "conflict": { + "wrench/wrench": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^9.5.23" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "Wrench\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "A simple PHP WebSocket implementation", + "keywords": [ + "WebSockets", + "hybi", + "websocket" + ], + "support": { + "issues": "https://github.com/chrome-php/wrench/issues", + "source": "https://github.com/chrome-php/wrench/tree/v1.3.0" + }, + "time": "2022-08-28T11:42:16+00:00" + }, { "name": "doctrine/lexer", "version": "1.2.2", @@ -693,6 +892,265 @@ ], "time": "2021-10-11T09:18:27+00:00" }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/master" + }, + "time": "2017-07-23T21:35:13+00:00" + }, + { + "name": "halfpastfouram/collection", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/halfpastfouram/collection.git", + "reference": "0862d0b431fef9dc2245518dc06b86ff00dcd102" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/halfpastfouram/collection/zipball/0862d0b431fef9dc2245518dc06b86ff00dcd102", + "reference": "0862d0b431fef9dc2245518dc06b86ff00dcd102", + "shasum": "" + }, + "require": { + "php": ">=5.6.0 || ^7.0" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "phpunit/phpunit": "5.2.*" + }, + "type": "package", + "autoload": { + "psr-4": { + "Test\\": "test/", + "Halfpastfour\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL 3.0" + ], + "authors": [ + { + "name": "Bob Kruithof" + } + ], + "description": "A flexible PHP Collection complete with custom Iterator.", + "homepage": "http://github.com/halfpastfouram/collection", + "keywords": [ + "collection", + "php" + ], + "support": { + "issues": "https://github.com/halfpastfouram/collection/issues", + "source": "https://github.com/halfpastfouram/collection/tree/master" + }, + "time": "2016-12-18T13:04:48+00:00" + }, + { + "name": "laminas/laminas-json", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-json.git", + "reference": "7a8a1d7bf2d05dd6c1fbd7c0868d3848cf2b57ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/7a8a1d7bf2d05dd6c1fbd7c0868d3848cf2b57ec", + "reference": "7a8a1d7bf2d05dd6c1fbd7c0868d3848cf2b57ec", + "shasum": "" + }, + "require": { + "php": "~8.0.0 || ~8.1.0 || ~8.2.0" + }, + "conflict": { + "zendframework/zend-json": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.4.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "phpunit/phpunit": "^9.5.25" + }, + "suggest": { + "laminas/laminas-json-server": "For implementing JSON-RPC servers", + "laminas/laminas-xml2json": "For converting XML documents to JSON" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Json\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://laminas.dev", + "keywords": [ + "json", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-json/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-json/issues", + "rss": "https://github.com/laminas/laminas-json/releases.atom", + "source": "https://github.com/laminas/laminas-json" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2022-10-17T04:06:45+00:00" + }, + { + "name": "monolog/monolog", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/720488632c590286b88b80e62aa3d3d551ad4a50", + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^0.12.91", + "phpunit/phpunit": "^8.5.14", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2022-07-24T11:55:47+00:00" + }, { "name": "mpdf/mpdf", "version": "v8.0.15", @@ -940,30 +1398,30 @@ }, { "name": "psr/log", - "version": "1.1.4", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -984,9 +1442,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/2.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:41:46+00:00" }, { "name": "setasign/fpdi", @@ -1136,6 +1594,152 @@ "abandoned": "symfony/mailer", "time": "2021-10-18T15:26:12+00:00" }, + { + "name": "symfony/filesystem", + "version": "v5.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-09-21T19:53:16+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, { "name": "symfony/polyfill-iconv", "version": "v1.24.0", @@ -1392,16 +1996,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.24.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -1416,7 +2020,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1424,12 +2028,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1455,7 +2059,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -1471,7 +2075,7 @@ "type": "tidelift" } ], - "time": "2021-11-30T18:21:41+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php72", @@ -1548,6 +2152,232 @@ } ], "time": "2021-05-27T09:17:38+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1", + "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T16:58:25+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "0719f6cf4633a38b2c1585140998579ce23b4b7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0719f6cf4633a38b2c1585140998579ce23b4b7d", + "reference": "0719f6cf4633a38b2c1585140998579ce23b4b7d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + }, + "require-dev": { + "ext-iconv": "*", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "ext-symfony_debug": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" } ], "packages-dev": [], @@ -1559,7 +2389,7 @@ "platform": [], "platform-dev": [], "platform-overrides": { - "php": "7.2.0" + "php": "8.0.0" }, "plugin-api-version": "2.3.0" } diff --git a/pandora_console/extras/delete_files/delete_files.txt b/pandora_console/extras/delete_files/delete_files.txt index 90107df226..d63f210b83 100644 --- a/pandora_console/extras/delete_files/delete_files.txt +++ b/pandora_console/extras/delete_files/delete_files.txt @@ -1666,6 +1666,14 @@ godmode/um_client/vendor/sebastian/object-enumerator godmode/um_client/vendor/sebastian godmode/um_client/vendor update_manager_client/resources/styles/pandora.css +enterprise/views/cluster/edit.php +enterprise/views/cluster/list.php +enterprise/views/cluster/view.php +enterprise/include/lib/Cluster.php +enterprise/include/lib/ClusterModule.php +enterprise/include/lib/ClusterViewer/ClusterManager.php +enterprise/include/lib/ClusterViewer/ClusterWizard.php +enterprise/operation/cluster/cluster.php enterprise/meta/general/upload_head_image.php general/first_task/transactional_list.php enterprise/include/ajax/transactional.ajax.php @@ -1677,3 +1685,5 @@ enterprise/operation/agentes/manage_transmap.php enterprise/operation/agentes/manage_transmap_creation.php enterprise/operation/agentes/manage_transmap_creation_phases_data.php enterprise/operation/agentes/transactional_map.php +include/test.js +include/web2image.js diff --git a/pandora_console/extras/mr/60.sql b/pandora_console/extras/mr/60.sql index c263f37864..7be8a0b4ef 100644 --- a/pandora_console/extras/mr/60.sql +++ b/pandora_console/extras/mr/60.sql @@ -11,4 +11,39 @@ ALTER TABLE `tagent_custom_fields` ADD `is_link_enabled` TINYINT(1) NOT NULL DEF ALTER TABLE `tevent_filter` ADD COLUMN `owner_user` TEXT; ALTER TABLE `tevent_filter` ADD COLUMN `not_search` INT NOT NULL DEFAULT 0; +ALTER TABLE `tusuario` MODIFY COLUMN `password` VARCHAR(60) DEFAULT NULL; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9jcHUucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAiOyI7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gIiBNaHo7IjsKCQl9CgoJCSMgRGVzY3JpcHRpb24KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0gLiAiOyI7CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE5hbWUsIE1heENsb2NrU3BlZWQsIERlc2NyaXB0aW9uIEZST00gV2luMzJfUHJvY2Vzc29yIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIk1heENsb2NrU3BlZWQiLCAiRGVzY3JpcHRpb24iLCBcQHJlc3VsdCk7CmV4aXQgMDsK' where `code`='IyEvdXNyL2Jpbi9wZXJsDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBwYW5kb3JhX2NwdS5wbA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgQ29weXJpZ2h0IChjKSAyMDA4IFJhbW9uIE5vdm9hLCBybm92b2FAYXJ0aWNhLmVzDQojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MDQojDQojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3INCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4NCiMNCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsDQojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mDQojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUNCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4NCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUNCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KdXNlIHN0cmljdDsNCnVzZSB3YXJuaW5nczsNCg0KdXNlIEZpbGU6OkJhc2VuYW1lOw0KdXNlIEhUTUw6OkVudGl0aWVzICgpOw0KDQojIENoZWNrIGZvciB3bWljDQpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsNCmlmIChzeXN0ZW0oIiR3bWlfY2xpZW50ID4gL2Rldi9udWxsIDI+JjEiKSAhPSAyNTYpIHsNCglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOw0KCWV4aXQgMTsNCn0NCg0KaWYgKCQjQVJHViAhPSAyKSB7DQoJcHJpbnQgIlVzYWdlOiAkMCA8dGFyZ2V0IGlwPiA8dXNlcm5hbWU+IDxwYXNzd29yZD5cbiI7DQoJZXhpdCAxOw0KfQ0KDQpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07DQpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsNCm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOw0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQ0KIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0Kc3ViIHJ1bl9xdWVyeSB7DQoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOw0KCW15IEByZXN1bHQgPSB7fTsNCg0KCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsNCg0KCSMgUnVuIHRoZSBEQ09NL1dNSSBjbGllbnQNCglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7DQoNCglteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsNCgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMNCglpZiAoJCNsaW5lcyA8IDIpIHsNCgkJZXhpdCAxOw0KCX0NCg0KCSMgRHJvcCB0aGUgaGVhZGVyDQoJc2hpZnQgKEBsaW5lcyk7DQoJDQoJIyBHZXQgY29sdW1uIG5hbWVzDQoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsNCg0KCSMgR2V0IHJvdyBkYXRhDQoJbXkgJGlkeCA9IDA7DQoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsNCg0KCQkjIENoZWNrIGZvciBlcnJvcnMNCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7DQoJCQlleGl0IDE7DQoJCX0NCg0KCQkjIEJsYWNrIGxpc3QNCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7DQoJCQluZXh0Ow0KCQl9DQoNCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOw0KCQlmb3IgKG15ICRqID0gMDsgJGogPD0gJCNjb2x1bW5fbmFtZXM7ICRqKyspIHsNCgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7DQoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOw0KCQkJfQ0KCQkJZWxzZSB7DQoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOw0KCQkJfQ0KCQl9DQoJCQ0KCQkkaWR4Kys7DQoJfQ0KCQ0KCXJldHVybiBAcmVzdWx0Ow0KfQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIA0KIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkNCiMgUHJpbnRzIGEgbW9kdWxlZGF0YSBYTUwgdGFnLiAkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSBhbmQNCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24NCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5Lg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCnN1YiBwcmludF9tb2R1bGVkYXRhIHsNCglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsNCglteSAkbW9kdWxlX2RhdGEgPSAkX1sxXTsNCglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07DQoJbXkgQGRhdGEgPSBAeyRfWzNdfTsNCglteSAkcmVzdWx0Ow0KDQoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsNCgkJDQoJCSRyZXN1bHQgPSAnJzsNCg0KCQkjIEl0ZW0NCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgew0KCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAiOyI7DQoJCX0NCg0KCQkjIERhdGENCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9KSkgew0KCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAiIE1oejsiOw0KCQl9DQoNCgkJIyBEZXNjcmlwdGlvbg0KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgew0KCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259IC4gIjsiOw0KCQl9DQoNCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQ0KCX0NCn0NCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgTWFpbg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE5hbWUsIE1heENsb2NrU3BlZWQsIERlc2NyaXB0aW9uIEZST00gV2luMzJfUHJvY2Vzc29yIik7DQpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJNYXhDbG9ja1NwZWVkIiwgIkRlc2NyaXB0aW9uIiwgXEByZXN1bHQpOw0KZXhpdCAwOw0K' and `name` = 'CPU' and `id_module_inventory`=2; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9yYW0ucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gc3ByaW50ZigiJS4xZiIsICRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSAvIDEwNDg1NzYpIC4gIiBNQnl0ZXM7IjsKCQl9CgoJCSMgRGVzY3JpcHRpb24KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn07CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIFRhZywgQ2FwYWNpdHksIE5hbWUgRlJPTSBXaW4zMl9QaHlzaWNhbE1lbW9yeSIpOwpwcmludF9tb2R1bGVkYXRhICgiVGFnIiwgIkNhcGFjaXR5IiwgIk5hbWUiLCBcQHJlc3VsdCk7CmV4aXQgMDsK' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9yYW0ucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDggQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnVzZSBzdHJpY3Q7CnVzZSB3YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgpOwoKIyBDaGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgiJHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gc3ByaW50ZigiJS4xZiIsICRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSAvIDEwNDg1NzYpIC4gIiBNQnl0ZXM7IjsKCQl9CgoJCSMgRGVzY3JpcHRpb24KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn07CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIERldmljZUxvY2F0b3IsIENhcGFjaXR5LCBTcGVlZCBGUk9NIFdpbjMyX1BoeXNpY2FsTWVtb3J5Iik7CnByaW50X21vZHVsZWRhdGEgKCJEZXZpY2VMb2NhdG9yIiwgIkNhcGFjaXR5IiwgIlNwZWVkIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' and `name` = 'RAM' and `id_module_inventory`=4; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV92aWRlby5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAnICcgLiBzcHJpbnRmKCIlLjFmIiwgJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC8gMTA0ODU3NikgLiAiIE1CeXRlczsiOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgQWRhcHRlclJBTSwgVmlkZW9Qcm9jZXNzb3IgRlJPTSBXaW4zMl9WaWRlb0NvbnRyb2xsZXIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIkNhcHRpb24iLCAiQWRhcHRlclJBTSIsICJWaWRlb1Byb2Nlc3NvciIsIFxAcmVzdWx0KTsKZXhpdCAwOwo=' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV92aWRlby5wbAojIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFy dGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2lj YXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3Ry aWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdl bmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUg Rm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGlu IHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJB TlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJ VFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdl bmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUg cmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25n IHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMg Rm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9u LCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNl IHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7Cgoj IENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21p X2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21p X2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXBy aW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhp dCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFd OwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9x dWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMg dGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBy dW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3 cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAk b3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdl dF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xu LywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVz IDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7 CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNo aWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRp ID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAo JGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0 CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29s dW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQj Y29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJ CQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7 CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJ CX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEs IAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1M IHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlv biBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwg dGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRh IHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07Cglt eSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkg JHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAn JzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJ CQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0 YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAu PSAnICcgLiBzcHJpbnRmKCIlLjFmIiwgJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC8gMTA0ODU3 NikgLiAiIE1CeXRlczsiOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVt ZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gJGVsZW1l bnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkK CX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVz dWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgQWRhcHRlclJBTSwgVmlkZW9Qcm9jZXNz b3IgRlJPTSBXaW4zMl9WaWRlb0NvbnRyb2xsZXIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIkNhcHRp b24iLCAiQWRhcHRlclJBTSIsICJWaWRlb1Byb2Nlc3NvciIsIFxAcmVzdWx0KTsKZXhpdCAwOwo=' and `name` = 'Video' and `id_module_inventory`=6; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9uaWMucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgTUFDQWRkcmVzcywgSVBBZGRyZXNzIEZST00gV2luMzJfTmV0d29ya0FkYXB0ZXJDb25maWd1cmF0aW9uIik7CnByaW50X21vZHVsZWRhdGEgKCJDYXB0aW9uIiwgIk1BQ0FkZHJlc3MiLCAiSVBBZGRyZXNzIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' where code='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9uaWMucGwKIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRp Y2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDggQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2Fz IFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmli dXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5l cmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZv dW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0 aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5U WTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZ IG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5l cmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJl Y2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3 aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZv dW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3Rvbiwg TUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnVzZSBzdHJpY3Q7CnVzZSB3 YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgpOwoKIyBD aGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgiJHdtaV9j bGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9j bGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7Cglwcmlu dCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQg MTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsK bXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVl cnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRo ZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVu X3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3Fs X3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91 dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRf aXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8s ICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8 IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJ CgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlm dCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9 IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRs aW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJ CWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVt biA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2Nv bHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJ JHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJ CQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9 CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAK IyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0 YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24g YXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRh Z3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7 CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkg JG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRy ZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7 CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJ JHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEK CQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0g JGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlm IChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQg Lj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQg LiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj CgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgTUFDQWRkcmVzcywgSVBB ZGRyZXNzIEZST00gV2luMzJfTmV0d29ya0FkYXB0ZXJDb25maWd1cmF0aW9uIik7CnByaW50X21v ZHVsZWRhdGEgKCJDYXB0aW9uIiwgIk1BQ0FkZHJlc3MiLCAiSVBBZGRyZXNzIiwgXEByZXN1bHQp OwpleGl0IDA7Cg==' and `name` = 'NIC' and `id_module_inventory`=8; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9oZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAnICcgLiBzcHJpbnRmKCIlLjFmIiwgJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC8gMTA3Mzc0MTgyNCkgLiAiIEdCeXRlczsiOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJyAoJyAuICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0gLiAiKSI7CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE1vZGVsLCBTaXplLCBTeXN0ZW1OYW1lIEZST00gV2luMzJfRGlza0RyaXZlIik7CnByaW50X21vZHVsZWRhdGEgKCJNb2RlbCIsICJTaXplIiwgIlN5c3RlbU5hbWUiLCBcQHJlc3VsdCk7CQpleGl0IDA7Cg==' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9oZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CiAgICAgICAgcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKICAgICAgICBleGl0IDE7Cn0KCmlmICgkI0FSR1YgIT0gMikgewogICAgICAgIHByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwogICAgICAgIGV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKICAgICAgICBteSAkd3FsX3F1ZXJ5ID0gJF9bMF07CiAgICAgICAgbXkgQHJlc3VsdCA9IHt9OwogICAgICAgICR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCiAgICAgICAgIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAogICAgICAgIG15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsKCiAgICAgICAgbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CiAgICAgICAgIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCiAgICAgICAgaWYgKCQjbGluZXMgPCAyKSB7CiAgICAgICAgICAgICAgICBleGl0IDE7CiAgICAgICAgfQoKICAgICAgICAjIERyb3AgdGhlIGhlYWRlcgogICAgICAgIHNoaWZ0IChAbGluZXMpOwoKICAgICAgICAjIEdldCBjb2x1bW4gbmFtZXMKICAgICAgICBteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOwoKICAgICAgICAjIEdldCByb3cgZGF0YQogICAgICAgIG15ICRpZHggPSAwOwogICAgICAgIGZvciAobXkgJGkgPSAwOyAkaSA8PSAkI2xpbmVzOyAkaSsrKSB7CgogICAgICAgICAgICAgICAgIyBDaGVjayBmb3IgZXJyb3JzCiAgICAgICAgICAgICAgICBpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKICAgICAgICAgICAgICAgICAgICAgICAgZXhpdCAxOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICMgQmxhY2sgbGlzdAogICAgICAgICAgICAgICAgaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIG5leHQ7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwogICAgICAgICAgICAgICAgZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRyZXN1bHRbJGlkeF0tPnskY29sdW1uX25hbWVzWyRqXX0gPSAkY29sdW1uWyRqXTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICRpZHgrKzsKICAgICAgICB9CgogICAgICAgIHJldHVybiBAcmVzdWx0Owp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBwcmludF9tb2R1bGVkYXRhICgkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSwKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CiAgICAgICAgbXkgJG1vZHVsZV9pdGVtID0gJF9bMF07CiAgICAgICAgbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CiAgICAgICAgbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwogICAgICAgIG15ICRtb2R1bGVfZXh0cmFfZGVzY3JpcHRpb24gPSAkX1szXTsKICAgICAgICBteSBAZGF0YSA9IEB7JF9bNF19OwogICAgICAgIG15ICRyZXN1bHQ7CgogICAgICAgIGZvcmVhY2ggbXkgJGVsZW1lbnQgKEBkYXRhKSB7CiAgICAgICAgICAgICAgICAkcmVzdWx0ID0gJyc7CgogICAgICAgICAgICAgICAgIyBJdGVtCiAgICAgICAgICAgICAgICBpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19IC4gJzsnOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICMgRGF0YQogICAgICAgICAgICAgICAgaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9KSkgewogICAgICAgICAgICAgICAgICAgICAgICAkcmVzdWx0IC49ICcgJyAuIHNwcmludGYoIiUuMWYiLCAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLyAxMDczNzQxODI0KSAuICIgR0J5dGVzOyI7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgIyBEZXNjcmlwdGlvbgogICAgICAgICAgICAgICAgaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAgJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGVsc2lmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9leHRyYV9kZXNjcmlwdGlvbn0pKXsKICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAgJGVsZW1lbnQtPnskbW9kdWxlX2V4dHJhX2Rlc2NyaXB0aW9ufTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBwcmludCAkcmVzdWx0IC4gIlxuIjsKICAgICAgICB9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUICogRlJPTSBXaW4zMl9EaXNrRHJpdmUiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIkNhcHRpb24iLCAiU2l6ZSIsICJTZXJpYWxOdW1iZXIiLCAiU2lnbmF0dXJlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' and `name` = 'HD' and `id_module_inventory`=10; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9jZHJvbS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBEZXNjcmlwdGlvbiwgRHJpdmUgRlJPTSBXaW4zMl9DRFJPTURyaXZlIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIkRlc2NyaXB0aW9uIiwgIkRyaXZlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9jZHJvbS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKI3VzZSBzdHJpY3Q7CnVzZSB3YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgpOwoKIyBDaGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgiJHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewogICAgICAgIHByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7CiAgICAgICAgZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKICAgICAgICBwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKICAgICAgICBleGl0IDE7Cn0KCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsKbXkgJHVzZXJuYW1lID0gJEFSR1ZbMV07Cm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHJ1bl9xdWVyeSB7CiAgICAgICAgbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwogICAgICAgIG15IEByZXN1bHQgPSB7fTsKCiAgICAgICAgJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOwoKICAgICAgICAjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CiAgICAgICAgbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKICAgICAgICBteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsKICAgICAgICAjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKICAgICAgICBpZiAoJCNsaW5lcyA8IDIpIHsKICAgICAgICAgICAgICAgIGV4aXQgMTsKICAgICAgICB9CgogICAgICAgICMgRHJvcCB0aGUgaGVhZGVyCiAgICAgICAgc2hpZnQgKEBsaW5lcyk7CgogICAgICAgICMgR2V0IGNvbHVtbiBuYW1lcwogICAgICAgIG15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgogICAgICAgICMgR2V0IHJvdyBkYXRhCiAgICAgICAgbXkgJGlkeCA9IDA7CiAgICAgICAgZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCiAgICAgICAgICAgICAgICAjIENoZWNrIGZvciBlcnJvcnMKICAgICAgICAgICAgICAgIGlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewogICAgICAgICAgICAgICAgICAgICAgICBleGl0IDE7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgIyBCbGFjayBsaXN0CiAgICAgICAgICAgICAgICBpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKICAgICAgICAgICAgICAgICAgICAgICAgbmV4dDsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CiAgICAgICAgICAgICAgICBmb3IgKG15ICRqID0gMDsgJGogPD0gJCNjb2x1bW5fbmFtZXM7ICRqKyspIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCEgZGVmaW5lZCgkY29sdW1uWyRqXSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgJGlkeCsrOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKICAgICAgICBteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKICAgICAgICBteSBAZGF0YSA9IEB7JF9bMV19OwogICAgICAgIG15ICRyZXN1bHQ7CgogICAgICAgICRyZXN1bHQgPSAnJzsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiwgQGRhdGEpCiMgUHJpbnRzIGEgbW9kdWxlZGF0YSBYTUwgdGFnLiAkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSBhbmQKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uIGFyZSB1c2VkIHRvIGluZGV4IHRoZSBpdGVtLCBkYXRhIGFuZCBkZXNjcmlwdGlvbgojIFhNTCB0YWdzIHJlc3BlY3RpdmVseS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHByaW50X21vZHVsZWRhdGEgewogICAgICAgIG15IEBtb2R1bGVfaGVhZGVycyA9IEB7JF9bMF19OwogICAgICAgIG15IEBkYXRhID0gQHskX1sxXX07CiAgICAgICAgbXkgJHJlc3VsdDsKCiAgICAgICAgZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKICAgICAgICAgICAgICAgICRyZXN1bHQgPSAnJzsKICAgICAgICAgICAgICAgIGZvcmVhY2ggbXkgJG1oZWFkZXIgKEBtb2R1bGVfaGVhZGVycykgewogICAgICAgICAgICAgICAgICAgICAgICBpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbWhlYWRlcn0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtaGVhZGVyfSAuICc7JzsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkcmVzdWx0IC49ICc7JzsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY2hvcCgkcmVzdWx0KTsKICAgICAgICAgICAgICAgIHByaW50ICRyZXN1bHQgLiAiXG4iOwogICAgICAgIH0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ICA9IHJ1bl9xdWVyeSgiU0VMRUNUICogRlJPTSBXaW4zMl9DRFJPTURyaXZlIik7Cm15IEBoZWFkZXJzID0gKCJOYW1lIiwgIkRlc2NyaXB0aW9uIiwgIkRyaXZlIiwgIkRldmljZUlkIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycywgXEByZXN1bHQpOwpleGl0IDA7Cgo=' and `name` = 'CDROM' and `id_module_inventory`=12; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zb2Z0d2FyZS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX07CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE5hbWUsIFZlcnNpb24gRlJPTSBXaW4zMl9Qcm9kdWN0Iik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIlZlcnNpb24iLCAiIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zb2Z0d2FyZS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKICAgICAgICBteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKICAgICAgICBteSAkbW9kdWxlX2RhdGEgPSAkX1sxXTsKICAgICAgICBteSBAZGF0YSA9IEB7JF9bMl19OwogICAgICAgIG15ICRyZXN1bHQ7CgogICAgICAgIGZvcmVhY2ggbXkgJGVsZW1lbnQgKEBkYXRhKSB7CiAgICAgICAgICAgICAgICAkcmVzdWx0ID0gJyc7CiAgICAgICAgICAgICAgICAjIEl0ZW0KICAgICAgICAgICAgICAgIGlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgIyBEYXRhCiAgICAgICAgICAgICAgICBpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9OwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHByaW50ICRyZXN1bHQgLiAiXG4iOwogICAgICAgIH0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgVmVyc2lvbiBGUk9NIFdpbjMyX1Byb2R1Y3QiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIk5hbWUiLCAiVmVyc2lvbiIsIFxAcmVzdWx0KTsKZXhpdCAwOwoK' and `name` = 'Software' and `id_module_inventory`=14; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wYXRjaC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBIb3RGaXhJRCwgRGVzY3JpcHRpb24sIEZpeENvbW1lbnRzIEZST00gV2luMzJfUXVpY2tGaXhFbmdpbmVlcmluZyIpOwpwcmludF9tb2R1bGVkYXRhICgiSG90Rml4SUQiLCAiRGVzY3JpcHRpb24iLCAiRml4Q29tbWVudHMiLCBcQHJlc3VsdCk7CmV4aXQgMDsK' where `code`='IyEvdXNyL2Jpbi9wZXJsDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBwYW5kb3JhX3BhdGNoLnBsDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMNCiMgICAgICAgICAgIChjKSAyMDA4IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwNCiMNCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcg0KIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLg0KIw0KIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwNCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YNCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQ0KIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLg0KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQ0KIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQp1c2Ugc3RyaWN0Ow0KdXNlIHdhcm5pbmdzOw0KDQp1c2UgRmlsZTo6QmFzZW5hbWU7DQp1c2UgSFRNTDo6RW50aXRpZXMgKCk7DQoNCiMgQ2hlY2sgZm9yIHdtaWMNCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOw0KaWYgKHN5c3RlbSgiJHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1Nikgew0KCXByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7DQoJZXhpdCAxOw0KfQ0KDQppZiAoJCNBUkdWICE9IDIpIHsNCglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsNCglleGl0IDE7DQp9DQoNCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsNCm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOw0KbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07DQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpDQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpzdWIgcnVuX3F1ZXJ5IHsNCglteSAkd3FsX3F1ZXJ5ID0gJF9bMF07DQoJbXkgQHJlc3VsdCA9IHt9Ow0KDQoJJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOw0KDQoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudA0KCW15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsNCg0KCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOw0KCSMgSGVhZGVyLCBkZXNjcmlwdGlvbiwgcmVzdWx0cw0KCWlmICgkI2xpbmVzIDwgMikgew0KCQlleGl0IDE7DQoJfQ0KDQoJIyBEcm9wIHRoZSBoZWFkZXINCglzaGlmdCAoQGxpbmVzKTsNCgkNCgkjIEdldCBjb2x1bW4gbmFtZXMNCglteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOw0KDQoJIyBHZXQgcm93IGRhdGENCglteSAkaWR4ID0gMDsNCglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgew0KDQoJCSMgQ2hlY2sgZm9yIGVycm9ycw0KCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsNCgkJCWV4aXQgMTsNCgkJfQ0KDQoJCSMgQmxhY2sgbGlzdA0KCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsNCgkJCW5leHQ7DQoJCX0NCg0KCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7DQoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgew0KCQkJaWYgKCEgZGVmaW5lZCgkY29sdW1uWyRqXSkpIHsNCgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7DQoJCQl9DQoJCQllbHNlIHsNCgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07DQoJCQl9DQoJCX0NCgkJDQoJCSRpZHgrKzsNCgl9DQoJDQoJcmV0dXJuIEByZXN1bHQ7DQp9DQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIFNVQiBwcmludF9tb2R1bGVkYXRhICgkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSwgDQojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQ0KIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCANCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhbmQgJG1vZHVsZV9zZXJ2aWNlcGFjayBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSwgZGVzY3JpcHRpb24gYW5kIHNlcnZpY2VwYWNrDQojIFhNTCB0YWdzIHJlc3BlY3RpdmVseS4NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7DQoJbXkgJG1vZHVsZV9pdGVtID0gJF9bMF07DQoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07DQoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOw0KCW15ICRtb2R1bGVfc2VydmljZXBhY2sgPSAkX1szXTsNCglteSBAZGF0YSA9IEB7JF9bNF19Ow0KCW15ICRyZXN1bHQ7DQoNCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgew0KCQkNCgkJJHJlc3VsdCA9ICcnOw0KDQoJCSMgSXRlbQ0KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0pKSB7DQoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsNCgkJfQ0KDQoJCSMgRGF0YQ0KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7DQoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSAuICc7JzsNCgkJfQ0KDQoJCSMgRGVzY3JpcHRpb24NCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsNCgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSAuICc7JzsNCgkJfQ0KDQoJCSMgU2VydmljZSBwYWNrDQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9zZXJ2aWNlcGFja30pKSB7DQoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9zZXJ2aWNlcGFja307DQoJCX0NCg0KCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJDQoJfQ0KfQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBNYWluDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgSG90Rml4SUQsIERlc2NyaXB0aW9uLCBGaXhDb21tZW50cywgU2VydmljZVBhY2tJbkVmZmVjdCBGUk9NIFdpbjMyX1F1aWNrRml4RW5naW5lZXJpbmciKTsNCnByaW50X21vZHVsZWRhdGEgKCJIb3RGaXhJRCIsICJEZXNjcmlwdGlvbiIsICJGaXhDb21tZW50cyIsICJTZXJ2aWNlUGFja0luRWZmZWN0IiwgXEByZXN1bHQpOw0KZXhpdCAwOw0K' and `name` = 'Patches' and `id_module_inventory`=15; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zZXJ2aWNlcy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBQYXRoTmFtZSwgU3RhdGUgRlJPTSBXaW4zMl9TZXJ2aWNlIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIlBhdGhOYW1lIiwgIlN0YXRlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zZXJ2aWNlcy5wbAoj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9h QGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xv Z2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRp c3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05V IEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdh cmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVk IGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdB UlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFC SUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05V IEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhh dmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFs b25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJl CiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9z dG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsK dXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7 CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIk d21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAk d21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsK CXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJ ZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdW WzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1 bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVy bnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1 YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoK CSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50Cglt eSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRh cmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQo L1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xp bmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5l cyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8s IHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15 ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlp ZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBs aXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBA Y29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9 ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7 CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxz ZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07 CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2Rh dGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEg WE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlw dGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBY TUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVk YXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07 CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJ bXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQg PSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkg ewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMg RGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3Vs dCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9u CgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJl c3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJl c3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBQYXRoTmFtZSwgU3Rh dGUgRlJPTSBXaW4zMl9TZXJ2aWNlIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIlBhdGhO YW1lIiwgIlN0YXRlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' and `name` = 'Init services' and `id_module_inventory`=17; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9jZXNzZXMucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgUGF0aE5hbWUsIFN0YXRlIEZST00gV2luMzJfU2VydmljZSIpOwpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJQYXRoTmFtZSIsICJTdGF0ZSIsIFxAcmVzdWx0KTsJCmV4aXQgMDsK' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9jZXNzZXMucGwK IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3Zv YUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDggQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9s b2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVk aXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdO VSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3 YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRl ZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBX QVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRB QklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdO VSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBo YXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBh bG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2Fy ZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJv c3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnVzZSBzdHJpY3Q7 CnVzZSB3YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgp OwoKIyBDaGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgi JHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewoJcHJpbnQgIltlcnJvcl0g JHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7 CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsK CWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJH VlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBy dW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1 cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpz dWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsK Cgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJ bXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0 YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0 KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNs aW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGlu ZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwv LCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yICht eSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJ aWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sg bGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkg QGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8 PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkg ewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVs c2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpd OwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9k YXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRh IFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3Jp cHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMg WE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxl ZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFd OwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsK CW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0 ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkp IHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkj IERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1 bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlv bgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRy ZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRy ZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgUGF0aE5hbWUsIFN0 YXRlIEZST00gV2luMzJfU2VydmljZSIpOwpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJQYXRo TmFtZSIsICJTdGF0ZSIsIFxAcmVzdWx0KTsJCmV4aXQgMDsK' and `name` = 'Process' and `id_module_inventory`=21; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9jZXNzZXMucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgUGF0aE5hbWUsIFN0YXRlIEZST00gV2luMzJfU2VydmljZSIpOwpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJQYXRoTmFtZSIsICJTdGF0ZSIsIFxAcmVzdWx0KTsJCmV4aXQgMDsK' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV91c2Vycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMgICAgICAgICAgIChjKSAyMDE1IEJvcmphIFNhbmNoZXogPGZib3JqYS5zYW5jaGV6QGFydGljYS5lcz4KIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBGdWxsTmFtZSwgU3RhdHVzIEZST00gV2luMzJfVXNlckFjY291bnQiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIk5hbWUiLCAiRnVsbE5hbWUiLCAiU3RhdHVzIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==' and `name` = 'Users' and `id_module_inventory`=23; + + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb3RoZXJib2FyZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCAqIEZST00gV2luMzJfY29tcHV0ZXJzeXN0ZW0iKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJNYW51ZmFjdHVyZXIiLCJNb2RlbCIsIk9FTVN0cmluZ0FycmF5Iik7Cm15IEB1bml0cyAgID0gKCIiLCIiLCIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoXEBoZWFkZXJzLFxAdW5pdHMsIFxAcmVzdWx0KTsKZXhpdCAwOwo=' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb3RoZXJib2FyZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCAqIEZST00gV2luMzJfY29tcHV0ZXJzeXN0ZW0iKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJNYW51ZmFjdHVyZXIiLCJNb2RlbCIsIk9FTVN0cmluZ0FycmF5Iik7Cm15IEB1bml0cyAgID0gKCIiLCIiLCIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoXEBoZWFkZXJzLFxAdW5pdHMsIFxAcmVzdWx0KTsKZXhpdCAwOwo=' and name = 'Motherboard' and id_module_inventory=32; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb25pdG9ycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLFBOUERldmljZUlEIEZST00gV2luMzJfRGVza3RvcE1vbml0b3IiKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJOYW1lIiwiUE5QRGV2aWNlSUQiKTsKbXkgQHVuaXRzICAgPSAoIiIsIiIpOwpwcmludF9tb2R1bGVkYXRhIChcQGhlYWRlcnMsXEB1bml0cywgXEByZXN1bHQpOwpleGl0IDA7' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb25pdG9ycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLFBOUERldmljZUlEIEZST00gV2luMzJfRGVza3RvcE1vbml0b3IiKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJOYW1lIiwiUE5QRGV2aWNlSUQiKTsKbXkgQHVuaXRzICAgPSAoIiIsIiIpOwpwcmludF9tb2R1bGVkYXRhIChcQGhlYWRlcnMsXEB1bml0cywgXEByZXN1bHQpOwpleGl0IDA7Cgo=' and `name` = 'Monitors' and `id_module_inventory`=33; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcmludGVycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDA4IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBDYXB0aW9uLERyaXZlck5hbWUsUG9ydE5hbWUgRlJPTSBXaW4zMl9wcmludGVyIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiQ2FwdGlvbiIsIkRyaXZlck5hbWUiLCJQb3J0TmFtZSIpOwpteSBAdW5pdHMgICA9ICgiIiwiIiwiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcmludGVycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDA4IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBDYXB0aW9uLERyaXZlck5hbWUsUG9ydE5hbWUgRlJPTSBXaW4zMl9wcmludGVyIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiQ2FwdGlvbiIsIkRyaXZlck5hbWUiLCJQb3J0TmFtZSIpOwpteSBAdW5pdHMgICA9ICgiIiwiIiwiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK' and `name` = 'Printers' and `id_module_inventory`=34; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0a2V5LnBsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ29weXJpZ2h0IChjKSAyMDE1IEJvcmphIFNhbmNoZXosIDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNlbGVjdCBPQTN4T3JpZ2luYWxQcm9kdWN0S2V5IGZyb20gU29mdHdhcmVMaWNlbnNpbmdTZXJ2aWNlIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiT0EzeE9yaWdpbmFsUHJvZHVjdEtleSIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0a2V5LnBsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ29weXJpZ2h0IChjKSAyMDE1IEJvcmphIFNhbmNoZXosIDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNlbGVjdCBPQTN4T3JpZ2luYWxQcm9kdWN0S2V5IGZyb20gU29mdHdhcmVMaWNlbnNpbmdTZXJ2aWNlIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiT0EzeE9yaWdpbmFsUHJvZHVjdEtleSIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK' and `name` = 'product_key' and `id_module_inventory`=35; + +update `tmodule_inventory` set `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0SUQucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMTUgQm9yamEgU2FuY2hleiwgPGZib3JqYS5zYW5jaGV6QGFydGljYS5lcz4KIyAgICAgICAgICAgKGMpIDIwMTUgQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiN1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwp1bmxlc3MoLWUgJHdtaV9jbGllbnQpIHsKCXByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7CglleGl0IDE7Cn0KCmlmICgkI0FSR1YgIT0gMikgewoJcHJpbnQgIlVzYWdlOiAkMCA8dGFyZ2V0IGlwPiA8dXNlcm5hbWU+IDxwYXNzd29yZD5cbiI7CglleGl0IDE7Cn0KCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsKbXkgJHVzZXJuYW1lID0gJEFSR1ZbMV07Cm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHJ1bl9xdWVyeSB7CglteSAkd3FsX3F1ZXJ5ID0gJF9bMF07CglteSBAcmVzdWx0ID0ge307CgoJJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOwoKCSMgUnVuIHRoZSBEQ09NL1dNSSBjbGllbnQKCW15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsKCglteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsKCSMgSGVhZGVyLCBkZXNjcmlwdGlvbiwgcmVzdWx0cwoJaWYgKCQjbGluZXMgPCAyKSB7CgkJZXhpdCAxOwoJfQoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCQoJIyBHZXQgY29sdW1uIG5hbWVzCglteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOwoKCSMgR2V0IHJvdyBkYXRhCglteSAkaWR4ID0gMDsKCWZvciAobXkgJGkgPSAwOyAkaSA8PSAkI2xpbmVzOyAkaSsrKSB7CgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15IEBtb2R1bGVfaGVhZGVycyA9IEB7JF9bMF19OwoJbXkgQG1vZHVsZV91bml0cyAgID0gQHskX1sxXX07CglteSBAZGF0YSAgICAgICAgICAgPSBAeyRfWzJdfTsKCW15ICRyZXN1bHQ7Cglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCSRyZXN1bHQgPSAnJzsKCQlmb3IgKCRpPTA7ICRpPEBtb2R1bGVfaGVhZGVyczskaSsrKSB7CgkJCWlmIChkZWZpbmVkICgkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19KSkgewoJCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19IC4gJG1vZHVsZV91bml0c1skaV0gLiAnOyc7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0IC49ICc7JzsKCQkJfQoJCX0KCQljaG9wKCRyZXN1bHQpOwoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ICA9IHJ1bl9xdWVyeSgiU2VsZWN0IFNlcmlhbE51bWJlciBmcm9tIFdpbjMyX09wZXJhdGluZ1N5c3RlbSIpOwojIFJlbWVtYmVyIHRvIG1hdGNoIGxlbmdodCBvZiBib3RoIGFycmF5cyBoZWFkZXJzICYgdW5pdHMKbXkgQGhlYWRlcnMgPSAoIlNlcmlhbE51bWJlciIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsKCg==' where `code`='IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0SUQucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMTUgQm9yamEgU2FuY2hleiwgPGZib3JqYS5zYW5jaGV6QGFydGljYS5lcz4KIyAgICAgICAgICAgKGMpIDIwMTUgQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiN1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAid21pYyI7CmlmIChzeXN0ZW0oIiR3bWlfY2xpZW50ID4gL2Rldi9udWxsIDI+JjEiKSAhPSAyNTYpIHsKCXByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7CglleGl0IDE7Cn0KCmlmICgkI0FSR1YgIT0gMikgewoJcHJpbnQgIlVzYWdlOiAkMCA8dGFyZ2V0IGlwPiA8dXNlcm5hbWU+IDxwYXNzd29yZD5cbiI7CglleGl0IDE7Cn0KCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsKbXkgJHVzZXJuYW1lID0gJEFSR1ZbMV07Cm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHJ1bl9xdWVyeSB7CglteSAkd3FsX3F1ZXJ5ID0gJF9bMF07CglteSBAcmVzdWx0ID0ge307CgoJJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOwoKCSMgUnVuIHRoZSBEQ09NL1dNSSBjbGllbnQKCW15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsKCglteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsKCSMgSGVhZGVyLCBkZXNjcmlwdGlvbiwgcmVzdWx0cwoJaWYgKCQjbGluZXMgPCAyKSB7CgkJZXhpdCAxOwoJfQoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCQoJIyBHZXQgY29sdW1uIG5hbWVzCglteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOwoKCSMgR2V0IHJvdyBkYXRhCglteSAkaWR4ID0gMDsKCWZvciAobXkgJGkgPSAwOyAkaSA8PSAkI2xpbmVzOyAkaSsrKSB7CgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15IEBtb2R1bGVfaGVhZGVycyA9IEB7JF9bMF19OwoJbXkgQG1vZHVsZV91bml0cyAgID0gQHskX1sxXX07CglteSBAZGF0YSAgICAgICAgICAgPSBAeyRfWzJdfTsKCW15ICRyZXN1bHQ7Cglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCSRyZXN1bHQgPSAnJzsKCQlmb3IgKCRpPTA7ICRpPEBtb2R1bGVfaGVhZGVyczskaSsrKSB7CgkJCWlmIChkZWZpbmVkICgkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19KSkgewoJCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19IC4gJG1vZHVsZV91bml0c1skaV0gLiAnOyc7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0IC49ICc7JzsKCQkJfQoJCX0KCQljaG9wKCRyZXN1bHQpOwoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ICA9IHJ1bl9xdWVyeSgiU2VsZWN0IFNlcmlhbE51bWJlciBmcm9tIFdpbjMyX09wZXJhdGluZ1N5c3RlbSIpOwojIFJlbWVtYmVyIHRvIG1hdGNoIGxlbmdodCBvZiBib3RoIGFycmF5cyBoZWFkZXJzICYgdW5pdHMKbXkgQGhlYWRlcnMgPSAoIlNlcmlhbE51bWJlciIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsKCg==' and `name` = 'product_ID' and `id_module_inventory`=36; + COMMIT; diff --git a/pandora_console/general/first_task/cluster_builder.php b/pandora_console/general/first_task/cluster_builder.php index 3e6fe3dad4..fe14491322 100644 --- a/pandora_console/general/first_task/cluster_builder.php +++ b/pandora_console/general/first_task/cluster_builder.php @@ -68,7 +68,7 @@ ui_print_info_message(['no_close' => true, 'message' => __('There are no cluster if (check_acl($config['id_user'], 0, 'AW')) { ?> -
+
@@ -76,4 +76,4 @@ ui_print_info_message(['no_close' => true, 'message' => __('There are no cluster } ?> - \ No newline at end of file + diff --git a/pandora_console/godmode/agentes/agent_conf_gis.php b/pandora_console/godmode/agentes/agent_conf_gis.php index 1b083e160f..6b80ed752a 100644 --- a/pandora_console/godmode/agentes/agent_conf_gis.php +++ b/pandora_console/godmode/agentes/agent_conf_gis.php @@ -59,6 +59,7 @@ ui_print_warning_message( ] ); +$table = new StdClass(); $table->width = '100%'; $table->class = 'databox filters'; $table->data = []; diff --git a/pandora_console/godmode/agentes/configurar_agente.php b/pandora_console/godmode/agentes/configurar_agente.php index 2fd9fa8948..4432fef1cd 100644 --- a/pandora_console/godmode/agentes/configurar_agente.php +++ b/pandora_console/godmode/agentes/configurar_agente.php @@ -2118,6 +2118,9 @@ if ($delete_module) { exit; } + // Before delete the main module, check and delete the childrens from the original module. + module_check_childrens_and_delete($id_borrar_modulo); + // Also call base function to delete modules. modules_delete_agent_module($id_borrar_modulo); @@ -2473,6 +2476,11 @@ switch ($tab) { } }); }); + + // Change description when edit port + $( "#text-tcp_port" ).change(function() { + $( "#textarea_description" ).text(`Checks port ${$( "#text-tcp_port" ).val()} is opened`); + }); // Set the position and width of the subtab /* diff --git a/pandora_console/godmode/agentes/modificar_agente.php b/pandora_console/godmode/agentes/modificar_agente.php index 3b714c277b..318d7859b3 100644 --- a/pandora_console/godmode/agentes/modificar_agente.php +++ b/pandora_console/godmode/agentes/modificar_agente.php @@ -732,17 +732,15 @@ if ($agents !== false) { } if ($agent['id_os'] == CLUSTER_OS_ID) { - if (enterprise_installed()) { - $cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId( - $agent['id_agente'] - ); - $url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR; - $url .= '/operation/cluster/cluster'; - $url = ui_get_full_url( - $url.'&op=update&id='.$cluster->id() - ); - echo ''.ui_print_truncate_text($agent['alias'], 'agent_medium').''; - } + $cluster = PandoraFMS\Cluster::loadFromAgentId( + $agent['id_agente'] + ); + $url = 'index.php?sec=reporting&sec2='; + $url .= 'operation/cluster/cluster'; + $url = ui_get_full_url( + $url.'&op=update&id='.$cluster->id() + ); + echo ''.ui_print_truncate_text($agent['alias'], 'agent_medium').''; } else { echo '
'; if ($check_aw) { if ($agent['id_os'] == CLUSTER_OS_ID) { - if (enterprise_installed()) { - $cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId( - $agent['id_agente'] - ); - $url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR; - $url .= '/operation/cluster/cluster'; - $url = ui_get_full_url( - $url.'&op=update&id='.$cluster->id() - ); - echo ''.__('Edit').''; - echo ' | '; - } + $cluster = PandoraFMS\Cluster::loadFromAgentId( + $agent['id_agente'] + ); + $url = 'index.php?sec=reporting&sec2='; + $url .= 'operation/cluster/cluster'; + $url = ui_get_full_url( + $url.'&op=update&id='.$cluster->id() + ); + echo ''.__('Edit').''; + echo ' | '; } else { echo ''.__('View').''; - } + $cluster = PandoraFMS\Cluster::loadFromAgentId( + $agent['id_agente'] + ); + $url = 'index.php?sec=reporting&sec2='; + $url .= 'operation/cluster/cluster'; + $url = ui_get_full_url( + $url.'&op=view&id='.$cluster->id() + ); + echo ''.__('View').''; } else { echo ''; echo ''; -echo "'; echo "'; -echo ""; echo ''; // Check if there is at least one server of each type available to assign that // kind of modules. If not, do not show server type in combo. @@ -179,7 +178,7 @@ if (($policy_page) || (isset($agent))) { // Create module/type combo. echo ''; if (!$policy_page) { - echo ''; } - echo ''; - echo ''; echo ''; @@ -247,6 +246,8 @@ if ($module_action === 'delete') { $print_result_msg = true; $count_correct_delete_modules = 0; foreach ($id_agent_modules_delete as $id_agent_module_del) { + // Before delete the main module, check and delete the childrens from the original module. + module_check_childrens_and_delete($id_agent_module_del); $id_grupo = (int) agents_get_agent_group($id_agente); $all_groups = agents_get_all_groups_agent($id_agente, $id_grupo); diff --git a/pandora_console/godmode/agentes/module_manager_editor_network.php b/pandora_console/godmode/agentes/module_manager_editor_network.php index 4a39db6d75..323c5c1566 100644 --- a/pandora_console/godmode/agentes/module_manager_editor_network.php +++ b/pandora_console/godmode/agentes/module_manager_editor_network.php @@ -69,7 +69,7 @@ if (strstr($page, 'policy_modules') === false) { define('ID_NETWORK_COMPONENT_TYPE', 2); -if (empty($update_module_id)) { +if (empty($edit_module)) { // Function in module_manager_editor_common.php. add_component_selection(ID_NETWORK_COMPONENT_TYPE); } diff --git a/pandora_console/godmode/agentes/module_manager_editor_plugin.php b/pandora_console/godmode/agentes/module_manager_editor_plugin.php index 62bac54340..c00515de77 100644 --- a/pandora_console/godmode/agentes/module_manager_editor_plugin.php +++ b/pandora_console/godmode/agentes/module_manager_editor_plugin.php @@ -36,7 +36,7 @@ if (strstr($page, 'policy_modules') === false) { define('ID_NETWORK_COMPONENT_TYPE', 4); -if (empty($update_module_id)) { +if (empty($edit_module)) { // Function in module_manager_editor_common.php add_component_selection(ID_NETWORK_COMPONENT_TYPE); } else { diff --git a/pandora_console/godmode/agentes/module_manager_editor_web.php b/pandora_console/godmode/agentes/module_manager_editor_web.php index 7812bf808d..683204c88d 100644 --- a/pandora_console/godmode/agentes/module_manager_editor_web.php +++ b/pandora_console/godmode/agentes/module_manager_editor_web.php @@ -82,7 +82,7 @@ if ($plugin_pass == '' && !$id_agent_module) { $plugin_pass = 1; } -if (empty($update_module_id)) { +if (empty($edit_module)) { // Function in module_manager_editor_common.php add_component_selection(ID_NETWORK_COMPONENT_TYPE); } else { diff --git a/pandora_console/godmode/agentes/module_manager_editor_wmi.php b/pandora_console/godmode/agentes/module_manager_editor_wmi.php index 9d570fb696..a9de26ac17 100644 --- a/pandora_console/godmode/agentes/module_manager_editor_wmi.php +++ b/pandora_console/godmode/agentes/module_manager_editor_wmi.php @@ -34,7 +34,7 @@ $extra_title = __('WMI server module'); define('ID_NETWORK_COMPONENT_TYPE', 6); -if (empty($update_module_id)) { +if (empty($edit_module)) { // Function in module_manager_editor_common.php add_component_selection(ID_NETWORK_COMPONENT_TYPE); } else { diff --git a/pandora_console/godmode/agentes/planned_downtime.editor.php b/pandora_console/godmode/agentes/planned_downtime.editor.php index ed41aee0ef..08688c623b 100644 --- a/pandora_console/godmode/agentes/planned_downtime.editor.php +++ b/pandora_console/godmode/agentes/planned_downtime.editor.php @@ -1842,6 +1842,10 @@ function insert_downtime_agent($id_downtime, $user_groups_ad) $(document).ready (function () { populate_agents_selector(); + // Add data-pendingdelete attribute to exclude delete_pending modules + document.querySelector("#id_agents").dataset.pendingdelete = true + document.querySelector("#modules_selection_mode").dataset.pendingdelete = true + $("#id_agents").change(agent_changed_by_multiple_agents); $("#modules_selection_mode").change(agent_changed_by_multiple_agents); diff --git a/pandora_console/godmode/alerts/alert_list.list.php b/pandora_console/godmode/alerts/alert_list.list.php index d2a7e89469..8956ee0528 100644 --- a/pandora_console/godmode/alerts/alert_list.list.php +++ b/pandora_console/godmode/alerts/alert_list.list.php @@ -231,7 +231,7 @@ if (strlen(trim($agentName)) > 0) { } if ($actionID != -1 && $actionID != '') { - $where .= ' AND talert_template_modules.id IN (SELECT id_alert_template_module FROM talert_template_module_actions WHERE id_alert_action = '.$actionID.') OR talert_template_modules.id IN (SELECT id FROM talert_template_modules ttm WHERE ttm.id_alert_template IN (SELECT tat.id FROM talert_templates tat WHERE tat.id_alert_action = '.$actionID.'))'; + $where .= ' AND (talert_template_modules.id IN (SELECT id_alert_template_module FROM talert_template_module_actions WHERE id_alert_action = '.$actionID.') OR talert_template_modules.id IN (SELECT id FROM talert_template_modules ttm WHERE ttm.id_alert_template IN (SELECT tat.id FROM talert_templates tat WHERE tat.id_alert_action = '.$actionID.')))'; } if ($status_alert === 'disabled') { diff --git a/pandora_console/godmode/groups/group_list.php b/pandora_console/godmode/groups/group_list.php index 7856f68665..196d6024d6 100644 --- a/pandora_console/godmode/groups/group_list.php +++ b/pandora_console/godmode/groups/group_list.php @@ -92,6 +92,7 @@ if (is_ajax() === true) { if ($get_group_agents === true) { ob_clean(); $id_group = (int) get_parameter('id_group'); + $id_os = (int) get_parameter('id_os', 0); $disabled = (int) get_parameter('disabled', 0); $search = (string) get_parameter('search', ''); $recursion = (int) get_parameter('recursion', 0); @@ -151,6 +152,10 @@ if (is_ajax() === true) { $filter['status'] = $status_agents; } + if ($id_os !== 0) { + $filter['id_os'] = $id_os; + } + $_sql_post = ' 1=1 '; if ($show_void_agents == 0) { $_sql_post .= ' AND id_agente IN (SELECT a.id_agente FROM tagente a, tagente_modulo b WHERE a.id_agente=b.id_agente AND b.delete_pending=0) AND \'1\''; @@ -782,18 +787,18 @@ if ($tab == 'tree') { $form = ""; $form .= "
"; -echo __('Search').' '.html_print_input_text( +echo "
"; +echo __('Search').''.html_print_input_text( 'search_string', $search_string, '', - 15, - 255, + 5, + 5, true ); html_print_input_hidden('search', 1); @@ -73,7 +73,6 @@ echo '"; html_print_submit_button(__('Filter'), 'filter', false, 'class="sub search"'); echo '
'; + echo ''; echo __('Show in hierachy mode'); if ($checked == 'true') { $checked = true; @@ -198,8 +197,8 @@ if (($policy_page) || (isset($agent))) { echo ''; - echo __('

Type

'); + echo '
'; + echo __('Type').''; html_print_select( $modules, 'moduletype', @@ -216,7 +215,7 @@ if (($policy_page) || (isset($agent))) { ); html_print_input_hidden('edit_module', 1); echo ''; + echo ''; echo ''; echo '
"; - $form .= ''; + $form .= ''; $form .= '
'.__('Search').' '; + $form .= '
'.__('Search').'   '; $form .= html_print_input_text( 'search', $search, '', - 100, - 100, + 30, + 30, true ); - $form .= ''; + $form .= ''; $form .= ""; - $form .= '
'; $form .= ''; diff --git a/pandora_console/godmode/menu.php b/pandora_console/godmode/menu.php index d63595d553..7d26d69d2a 100644 --- a/pandora_console/godmode/menu.php +++ b/pandora_console/godmode/menu.php @@ -200,14 +200,14 @@ if ($access_console_node === true) { $sub['gmassive']['type'] = 'direct'; $sub['gmassive']['subtype'] = 'nolink'; $sub2 = []; - $sub2['godmode/massive/massive_operations&tab=massive_agents']['text'] = __('Agents operations'); - $sub2['godmode/massive/massive_operations&tab=massive_modules']['text'] = __('Modules operations'); - $sub2['godmode/massive/massive_operations&tab=massive_plugins']['text'] = __('Plugins operations'); + $sub2['godmode/massive/massive_operations&tab=massive_agents']['text'] = __('Agents operations'); + $sub2['godmode/massive/massive_operations&tab=massive_modules']['text'] = __('Modules operations'); + $sub2['godmode/massive/massive_operations&tab=massive_plugins']['text'] = __('Plugins operations'); if ((bool) check_acl($config['id_user'], 0, 'UM') === true) { - $sub2['godmode/massive/massive_operations&tab=massive_users']['text'] = __('Users operations'); + $sub2['godmode/massive/massive_operations&tab=massive_users']['text'] = __('Users operations'); } - $sub2['godmode/massive/massive_operations&tab=massive_alerts']['text'] = __('Alerts operations'); + $sub2['godmode/massive/massive_operations&tab=massive_alerts']['text'] = __('Alerts operations'); enterprise_hook('massivepolicies_submenu'); enterprise_hook('massivesnmp_submenu'); enterprise_hook('massivesatellite_submenu'); diff --git a/pandora_console/godmode/reporting/reporting_builder.php b/pandora_console/godmode/reporting/reporting_builder.php index 054d612d56..5e3d76bfc4 100755 --- a/pandora_console/godmode/reporting/reporting_builder.php +++ b/pandora_console/godmode/reporting/reporting_builder.php @@ -2182,6 +2182,8 @@ switch ($action) { 'historical_db_check' ); $values['top_n_value'] = get_parameter('max_items'); + + $values['server_name'] = get_parameter('combo_server'); } else if ($values['type'] == 'url') { $values['external_source'] = get_parameter('url'); } else if ($values['type'] == 'event_report_group') { diff --git a/pandora_console/godmode/setup/performance.php b/pandora_console/godmode/setup/performance.php index ba965f026f..969ff868c1 100644 --- a/pandora_console/godmode/setup/performance.php +++ b/pandora_console/godmode/setup/performance.php @@ -684,23 +684,6 @@ $table_other->data[$i++][1] = html_print_input_text( true ); -if (enterprise_installed() === true) { - $table_other->data[$i][0] = __('PhantomJS cache cleanup ').$tip; - $table_other->data[$i++][1] = html_print_input( - [ - 'type' => 'select', - 'name' => 'phantomjs_cache_interval', - 'return' => true, - 'fields' => [ - PHANTOM_CACHE_CLEANUP_ONCE => __('No scheduled'), - PHANTOM_CACHE_CLEANUP_WEEKLY => __('Each week'), - PHANTOM_CACHE_CLEANUP_DAILY => __('Each day'), - ], - 'selected' => ($config['phantomjs_cache_interval'] ?? PHANTOM_CACHE_CLEANUP_ONCE), - ] - ); -} - // Agent Wizard defaults. $defaultAgentWizardOptions = json_decode(io_safe_output($config['agent_wizard_defaults'])); $tableSnmpWizard = new stdClass(); diff --git a/pandora_console/godmode/setup/setup_auth.php b/pandora_console/godmode/setup/setup_auth.php index 0a93a36da2..8bb5619796 100644 --- a/pandora_console/godmode/setup/setup_auth.php +++ b/pandora_console/godmode/setup/setup_auth.php @@ -77,6 +77,7 @@ if (is_ajax()) { true ).'  '; $table->data['autocreate_remote_users'] = $row; + $table->data['csrf_token'] = html_print_csrf_hidden(); add_enterprise_auth_autocreate_profiles($table, $type_auth); } @@ -475,6 +476,8 @@ if (!is_metaconsole()) { html_print_input_hidden('hash_save_config', md5('save'.$config['dbpass'])); } +html_print_csrf_hidden(); + html_print_table($table); echo '
'; echo '
'; diff --git a/pandora_console/godmode/setup/setup_general.php b/pandora_console/godmode/setup/setup_general.php index 93a3d64a27..f7b10f74e7 100644 --- a/pandora_console/godmode/setup/setup_general.php +++ b/pandora_console/godmode/setup/setup_general.php @@ -95,11 +95,11 @@ $table->data[$i++][1] = html_print_input_text( true ); -$table->data[$i][0] = __('Phantomjs bin directory'); +$table->data[$i][0] = __('Chromium path'); $table->data[$i++][1] = html_print_input_text( - 'phantomjs_bin', + 'chromium_path', io_safe_output( - $config['phantomjs_bin'] + $config['chromium_path'] ), '', 30, diff --git a/pandora_console/godmode/users/configure_profile.php b/pandora_console/godmode/users/configure_profile.php index b1db4e26e9..70342f1fea 100644 --- a/pandora_console/godmode/users/configure_profile.php +++ b/pandora_console/godmode/users/configure_profile.php @@ -404,6 +404,7 @@ if ($id_profile || $new_profile) { html_print_input_hidden('create_profile', 1); } else { html_print_input_hidden('id', $id_profile); + html_print_input_hidden('old_name_profile', $name); html_print_input_hidden('update_profile', 1); html_print_submit_button(__('Update'), 'upd', false, 'class="sub upd"'); } @@ -415,15 +416,53 @@ enterprise_hook('close_meta_frame'); ?> - diff --git a/pandora_console/images/widgets/DataMatrix.png b/pandora_console/images/widgets/DataMatrix.png new file mode 100644 index 0000000000..c19ed708c4 Binary files /dev/null and b/pandora_console/images/widgets/DataMatrix.png differ diff --git a/pandora_console/include/ajax/alert_list.ajax.php b/pandora_console/include/ajax/alert_list.ajax.php index ae7ff1501b..a92ba9f9d9 100644 --- a/pandora_console/include/ajax/alert_list.ajax.php +++ b/pandora_console/include/ajax/alert_list.ajax.php @@ -654,7 +654,7 @@ if ($get_agent_alerts_datatable === true) { } $idGroup = $filter_alert['ag_group']; - $tag_filter = $filter_alert['tag_filter']; + $tag_filter = $filter_alert['tag']; $action_filter = $filter_alert['action']; try { @@ -884,9 +884,9 @@ if ($get_agent_alerts_datatable === true) { users_get_groups($config['id_user'], $access, false) ); - $alerts['alerts_simple'] = get_group_alerts($id_groups, $filter_alert, $options_simple, $whereAlertSimple, false, false, $idGroup, false, $strict_user, $tag_filter, $action_filter); + $alerts['alerts_simple'] = get_group_alerts($id_groups, $filter_alert, $options_simple, $whereAlertSimple, false, false, $idGroup, false, $strict_user, $tag_filter, $action_filter, false); - $countAlertsSimple = get_group_alerts($id_groups, $filter_alert, false, $whereAlertSimple, false, false, $idGroup, true, $strict_user, $tag_filter, $action_filter); + $countAlertsSimple = get_group_alerts($id_groups, $filter_alert, false, $whereAlertSimple, false, false, $idGroup, true, $strict_user, $tag_filter, $action_filter, false); } } diff --git a/pandora_console/include/ajax/events.php b/pandora_console/include/ajax/events.php index bdba9acf39..2494a83634 100644 --- a/pandora_console/include/ajax/events.php +++ b/pandora_console/include/ajax/events.php @@ -2031,8 +2031,10 @@ if ($total_event_graph) { include_once $config['homedir'].'/include/functions_graph.php'; - $prueba = grafico_eventos_total('', 280, 150, false, true); - echo $prueba; + $out = '
'; + $out .= grafico_eventos_total('', 0, 0, false, true); + $out .= '
'; + echo $out; return; } @@ -2041,8 +2043,10 @@ if ($graphic_event_group) { include_once $config['homedir'].'/include/functions_graph.php'; - $prueba = grafico_eventos_grupo(280, 150, '', false, true); - echo $prueba; + $out = '
'; + $out .= grafico_eventos_grupo(0, 0, '', false, true); + $out .= '
'; + echo $out; return; } diff --git a/pandora_console/include/ajax/module.php b/pandora_console/include/ajax/module.php index 8efb03df12..61720a99a4 100755 --- a/pandora_console/include/ajax/module.php +++ b/pandora_console/include/ajax/module.php @@ -26,6 +26,8 @@ * ============================================================================ */ +use PandoraFMS\Enterprise\Metaconsole\Node; + // Begin. if (check_login()) { global $config; @@ -59,6 +61,11 @@ if (check_login()) { 0 ); + $get_data_dataMatrix = (bool) get_parameter( + 'get_data_dataMatrix', + 0 + ); + if ($get_agent_modules_json_by_name === true) { $agent_name = get_parameter('agent_name'); @@ -1436,4 +1443,190 @@ if (check_login()) { return; } + + if ($get_data_dataMatrix === true) { + global $config; + + $table_id = get_parameter('table_id', ''); + $modules = json_decode( + io_safe_output( + get_parameter('modules', '') + ), + true + ); + $period = get_parameter('period', 0); + $slice = get_parameter('slice', 0); + + // Datatables offset, limit. + $start = get_parameter('start', 0); + $formatData = (bool) get_parameter('formatData', 0); + $length = get_parameter( + 'length', + $config['block_size'] + ); + + $order = get_datatable_order(true); + + // Total time per page. + $time_all_box = ($length * $slice); + + // Total number of boxes. + $total_box = ceil($period / $slice); + + if ($start > 0) { + $start = ($start / $length); + } + + // Uncompress. + try { + ob_start(); + $dateNow = get_system_time(); + $final = ($dateNow - $period); + $date = ($dateNow - ($time_all_box * $start)); + + if (($date - $time_all_box) > $final) { + $datelimit = ($date - $time_all_box); + } else { + $datelimit = $final; + } + + foreach ($modules as $key => $value) { + if (is_metaconsole() === true) { + try { + $node = new Node((int) $value['id_node']); + $node->connect(); + } catch (\Exception $e) { + // Unexistent agent. + $node->disconnect(); + } + } + + $value['thresholds'] = [ + 'min_critical' => (empty($value['c_min']) === true) ? null : $value['c_min'], + 'max_critical' => (empty($value['c_max']) === true) ? null : $value['c_max'], + 'min_warning' => (empty($value['w_min']) === true) ? null : $value['w_min'], + 'max_warning' => (empty($value['w_max']) === true) ? null : $value['w_max'], + ]; + + $module_data = db_uncompress_module_data( + $value['id'], + $datelimit, + $date, + $slice, + true + ); + + $uncompressData[] = array_reduce( + $module_data, + function ($carry, $item) use ($value, $config, $formatData) { + // Last value. + $vdata = null; + if (is_array($item['data']) === true) { + foreach ($item['data'] as $v) { + $vdata = $v['datos']; + } + } + + $status = get_status_data_modules( + $value['id'], + $vdata, + $value['thresholds'] + ); + + $resultData = ''; + if ($vdata !== null && $vdata !== '' && $vdata !== false) { + if (isset($formatData) === true + && (bool) $formatData === true + ) { + $resultData .= format_for_graph( + $vdata, + $config['graph_precision'] + ); + } else { + $resultData .= sla_truncate( + $vdata, + $config['graph_precision'] + ); + } + + $resultData .= ' '.$value['unit']; + } else { + $resultData .= '--'; + } + + $resultData .= ''; + $carry[] = [ + 'utimestamp' => $item['utimestamp'], + 'Column-'.$value['id'] => $resultData, + ]; + + return $carry; + }, + [] + ); + + if (is_metaconsole() === true) { + $node->disconnect(); + } + } + + if (empty($uncompressData) === false) { + $data = array_reduce( + $uncompressData, + function ($carry, $item) { + foreach ($item as $data_module) { + foreach ($data_module as $key => $value) { + if ($key === 'utimestamp') { + $carry[$data_module['utimestamp']]['date'] = date('Y-m-d H:i', (int) $value); + } else { + $carry[$data_module['utimestamp']][$key] = $value; + } + } + } + + return $carry; + } + ); + } + + if (empty($data) === false) { + $data = array_reverse(array_values($data)); + } else { + $data = []; + } + + // RecordsTotal && recordsfiltered resultados totales. + echo json_encode( + [ + 'data' => $data, + 'recordsTotal' => $total_box, + 'recordsFiltered' => $total_box, + ] + ); + + $response = ob_get_clean(); + + // Clean output buffer. + while (ob_get_level() !== 0) { + ob_end_clean(); + } + } catch (Exception $e) { + echo json_encode( + ['error' => $e->getMessage()] + ); + } + + // If not valid it will throw an exception. + json_decode($response); + if (json_last_error() == JSON_ERROR_NONE) { + // If valid dump. + echo $response; + } else { + echo json_encode( + ['error' => $response] + ); + } + + return; + } } diff --git a/pandora_console/include/ajax/profile.php b/pandora_console/include/ajax/profile.php new file mode 100644 index 0000000000..56a29fd31d --- /dev/null +++ b/pandora_console/include/ajax/profile.php @@ -0,0 +1,29 @@ + md5($password_new), + 'password' => password_hash($password_new, PASSWORD_BCRYPT), 'last_pass_change' => date('Y/m/d H:i:s', get_system_time()), ], ['id_user' => $user] @@ -1050,7 +1061,7 @@ function create_user_and_permisions_ldap( $values['id_user'] = $id_user; if ($config['ldap_save_password'] || $config['ad_save_password']) { - $values['password'] = md5($password); + $values['password'] = password_hash($password, PASSWORD_BCRYPT); } $values['last_connect'] = 0; @@ -1482,9 +1493,9 @@ function change_local_user_pass_ldap($id_user, $password) $local_user_pass = db_get_value_filter('password', 'tusuario', ['id_user' => $id_user]); $return = false; - if (md5($password) !== $local_user_pass) { + if (password_hash($password, PASSWORD_BCRYPT) !== $local_user_pass) { $values_update = []; - $values_update['password'] = md5($password); + $values_update['password'] = password_hash($password, PASSWORD_BCRYPT); $return = db_process_sql_update('tusuario', $values_update, ['id_user' => $id_user]); } diff --git a/pandora_console/include/chart_generator.php b/pandora_console/include/chart_generator.php index 764f8ca3ce..c0ba8eb3ac 100644 --- a/pandora_console/include/chart_generator.php +++ b/pandora_console/include/chart_generator.php @@ -42,40 +42,23 @@ require_once $config['homedir'].'/include/functions_agents.php'; require_once $config['homedir'].'/include/functions_tags.php'; $data_raw = get_parameter('data'); -$data_decoded = json_decode(base64_decode($data_raw), true); +$data_decoded = json_decode(io_safe_output($data_raw), true); if (json_last_error() === JSON_ERROR_NONE) { - $data = urldecode($data_decoded['data']); - $session_id = urldecode($data_decoded['session_id']); - $data_combined = urldecode($data_decoded['data_combined']); - $data_module_list = urldecode($data_decoded['data_module_list']); - $type_graph_pdf = urldecode($data_decoded['type_graph_pdf']); - $viewport_width = urldecode($data_decoded['viewport_width']); + $data = $data_decoded['data']; + $session_id = $data_decoded['session_id']; + $type_graph_pdf = $data_decoded['type_graph_pdf']; + + $data_combined = []; + if (isset($data_decoded['data_combined']) === true) { + $data_combined = $data_decoded['data_combined']; + } + + $data_module_list = []; + if (isset($data_decoded['data_module_list']) === true) { + $data_module_list = $data_decoded['data_module_list']; + } } - -/** - * Echo to stdout a PhantomJS callback call. - * - * @return void - */ -function echoPhantomCallback() -{ - ?> - -

Access is not granted

- @@ -108,10 +90,16 @@ if (check_login(false) === false) { } // Access granted. -$params = json_decode($data, true); +$params = $data; +if (isset($params['backgroundColor']) === false) { + $params['backgroundColor'] = 'inherit'; +} // Metaconsole connection to the node. -$server_id = $params['server_id']; +$server_id = 0; +if (isset($params['server_id']) === true) { + $server_id = $params['server_id']; +} if (is_metaconsole() === true && empty($server_id) === false) { $server = metaconsole_get_connection_by_id($server_id); @@ -124,7 +112,6 @@ if (is_metaconsole() === true && empty($server_id) === false) { ui_print_error_message( __('There was a problem connecting with the node') ); - echoPhantomCallback(); ?> @@ -133,7 +120,6 @@ if (is_metaconsole() === true && empty($server_id) === false) { } } - $user_language = get_user_language($config['id_user']); if (file_exists('languages/'.$user_language.'.mo') === true) { $cfr = new CachedFileReader('languages/'.$user_language.'.mo'); @@ -146,16 +132,16 @@ if (file_exists('languages/'.$user_language.'.mo') === true) { - Pandora FMS Graph (<?php echo agents_get_alias($agent_id).' - '.$interface_name; ?>) + Pandora FMS Graph - + @@ -170,48 +156,46 @@ if (file_exists('languages/'.$user_language.'.mo') === true) { + + - ;'> + ;'> 0) { - $params['width'] = (int) $viewport_width; + $viewport = [ + 'width' => 0, + 'height' => 0, + ]; + + $style = 'width:100%;'; + if (isset($params['options']['viewport']) === true) { + $viewport = $params['options']['viewport']; + if (empty($viewport['width']) === false) { + $style .= 'width:'.$viewport['width'].'px;'; } - if ((isset($params['width']) === false - || ($params['width'] <= 0)) - ) { - if ((int) $params['width'] <= 0) { - $params['width'] = 650; - } - - if ((int) $params['landscape'] === 1) { - $params['width'] = 850; - } - - if ($type_graph_pdf === 'slicebar') { - $params['width'] = 100; - $params['height'] = 70; - } + if (empty($viewport['height']) === false) { + $style .= 'height:'.$viewport['height'].'px;'; } } - echo '
'; + echo '
'; switch ($type_graph_pdf) { case 'combined': $params['pdf'] = true; - echo graphic_combined_module( + $result = graphic_combined_module( $module_list, $params, $params_combined ); + + echo $result; break; case 'sparse': @@ -219,67 +203,40 @@ if (file_exists('languages/'.$user_language.'.mo') === true) { echo grafico_modulo_sparse($params); break; - case 'pie_chart': + case 'pie_graph': $params['pdf'] = true; - echo flot_pie_chart( - $params['values'], - $params['keys'], - $params['width'], - $params['height'], - $params['water_mark_url'], - $params['font'], - $config['font_size'], - $params['legend_position'], - $params['colors'], - $params['hide_labels'] + $chart = get_build_setup_charts( + 'PIE', + $params['options'], + $params['chart_data'] ); + + echo $chart->render(true); break; - case 'vbar': + case 'vbar_graph': $params['pdf'] = true; - echo flot_vcolumn_chart($params); - break; - - case 'hbar': - $params['pdf'] = true; - echo flot_hcolumn_chart( - $params['chart_data'], - $params['width'], - $params['height'], - $params['water_mark_url'], - $params['font'], - $config['font_size'], - $params['backgroundColor'], - $params['tick_color'], - $params['val_min'], - $params['val_max'], - $params['pdf'] + $chart = get_build_setup_charts( + 'BAR', + $params['options'], + $params['chart_data'] ); + + echo $chart->render(true); break; case 'ring_graph': $params['pdf'] = true; - echo flot_custom_pie_chart( - $params['chart_data'], - $params['width'], - $params['height'], - $params['colors'], - $params['module_name_list'], - $params['long_index'], - $params['no_data'], - false, - '', - $params['water_mark'], - $params['font'], - $config['font_size'], - $params['unit'], - $params['ttl'], - $params['homeurl'], - $params['background_color'], - $params['legend_position'], - $params['background_color'], - $params['pdf'] + $params['options']['width'] = 500; + $params['options']['height'] = 500; + + $chart = get_build_setup_charts( + 'DOUGHNUT', + $params['options'], + $params['chart_data'] ); + + echo $chart->render(true); break; case 'slicebar': @@ -313,7 +270,6 @@ if (file_exists('languages/'.$user_language.'.mo') === true) { } echo '
'; - echoPhantomCallback(); ?> diff --git a/pandora_console/include/class/ConsoleSupervisor.php b/pandora_console/include/class/ConsoleSupervisor.php index 37e71144ac..b6a6d3df43 100644 --- a/pandora_console/include/class/ConsoleSupervisor.php +++ b/pandora_console/include/class/ConsoleSupervisor.php @@ -167,7 +167,7 @@ class ConsoleSupervisor * NOTIF.PHP.UPLOAD_MAX_FILESIZE * NOTIF.PHP.MEMORY_LIMIT * NOTIF.PHP.DISABLE_FUNCTIONS - * NOTIF.PHP.PHANTOMJS + * NOTIF.PHP.CHROMIUM * NOTIF.PHP.VERSION */ @@ -349,7 +349,7 @@ class ConsoleSupervisor * NOTIF.PHP.UPLOAD_MAX_FILESIZE * NOTIF.PHP.MEMORY_LIMIT * NOTIF.PHP.DISABLE_FUNCTIONS - * NOTIF.PHP.PHANTOMJS + * NOTIF.PHP.CHROMIUM * NOTIF.PHP.VERSION */ @@ -528,11 +528,7 @@ class ConsoleSupervisor */ public function maintenanceOperations() { - /* - * Process cache clean if needed. - */ - $this->checkCleanPhantomCache(); } @@ -688,7 +684,7 @@ class ConsoleSupervisor case 'NOTIF.PHP.UPLOAD_MAX_FILESIZE': case 'NOTIF.PHP.MEMORY_LIMIT': case 'NOTIF.PHP.DISABLE_FUNCTIONS': - case 'NOTIF.PHP.PHANTOMJS': + case 'NOTIF.PHP.CHROMIUM': case 'NOTIF.PHP.VERSION': case 'NOTIF.HISTORYDB': case 'NOTIF.PANDORADB': @@ -1431,9 +1427,9 @@ class ConsoleSupervisor $PHPmemory_limit_min = config_return_in_bytes('-1'); } - // PhantomJS status. - $phantomjs_dir = io_safe_output($config['phantomjs_bin']); - $result_ejecution = exec($phantomjs_dir.'/phantomjs --version'); + // Chromium status. + $chromium_dir = io_safe_output($config['chromium_path']); + $result_ejecution = exec($chromium_dir.' --version'); // PHP version checks. $php_version = phpversion(); @@ -1577,21 +1573,20 @@ class ConsoleSupervisor } if (!isset($result_ejecution) || $result_ejecution == '') { - $url = 'https://pandorafms.com/manual/en/documentation/02_installation/04_configuration#Phantomjs'; - if ($config['language'] == 'es') { - $url = 'https://pandorafms.com/manual/es/documentation/02_installation/04_configuration#Phantomjs'; - } - + $url = 'https://www.chromium.org/getting-involved/download-chromium/'; + // if ($config['language'] == 'es') { + // $url = 'https://pandorafms.com/manual/es/documentation/02_installation/04_configuration#Phantomjs'; + // } $this->notify( [ - 'type' => 'NOTIF.PHP.PHANTOMJS', - 'title' => __('PhantomJS is not installed'), - 'message' => __('To be able to create images of the graphs for PDFs, please install the PhantomJS extension. For that, it is necessary to follow these steps:'), + 'type' => 'NOTIF.PHP.CHROMIUM', + 'title' => __('chromium is not installed'), + 'message' => __('To be able to create images of the graphs for PDFs, please install the chromium extension. For that, it is necessary to follow these steps:'), 'url' => $url, ] ); } else { - $this->cleanNotifications('NOTIF.PHP.PHANTOMJS'); + $this->cleanNotifications('NOTIF.PHP.CHROMIUM'); } if ($php_version_array[0] < 8) { @@ -2698,34 +2693,6 @@ class ConsoleSupervisor } - /** - * Clean Phantom cache if needed. - * - * @return void - */ - public function checkCleanPhantomCache() - { - global $config; - - if (isset($config['clean_phantomjs_cache']) !== true - || (int) $config['clean_phantomjs_cache'] !== 1 - ) { - return; - } - - $cache_dir = $config['homedir'].'/attachment/cache'; - if (is_dir($cache_dir) === true) { - Files::rmrf($cache_dir); - } - - // Clean process has ended. - config_update_value( - 'clean_phantomjs_cache', - 0 - ); - } - - /** * Verifies the status of synchronization queue and warns if something is * not working as expected. diff --git a/pandora_console/include/class/SnmpConsole.class.php b/pandora_console/include/class/SnmpConsole.class.php new file mode 100644 index 0000000000..9aae307bc5 --- /dev/null +++ b/pandora_console/include/class/SnmpConsole.class.php @@ -0,0 +1,1573 @@ +ajaxController = $ajaxController; + $this->filter_alert = $filter_alert; + $this->filter_severity = $filter_severity; + $this->filter_free_search = $filter_free_search; + $this->filter_status = $filter_status; + $this->filter_group_by = $filter_group_by; + $this->filter_hours_ago = $filter_hours_ago; + $this->filter_trap_type = $filter_trap_type; + $this->refr = $refr; + } + + + /** + * Run view + * + * @return void + */ + public function run() + { + global $config; + // Javascript. + ui_require_jquery_file('pandora'); + // CSS. + ui_require_css_file('wizard'); + ui_require_css_file('discovery'); + + $default_refr = 300; + + if (!isset($config['pure']) || $config['pure'] === false) { + $statistics['text'] = '
'.html_print_image( + 'images/op_reporting.png', + true, + [ + 'title' => __('Statistics'), + 'class' => 'invert_filter', + ] + ).''; + $list['text'] = ''.html_print_image( + 'images/op_snmp.png', + true, + [ + 'title' => __('List'), + 'class' => 'invert_filter', + ] + ).''; + $list['active'] = true; + + $screen['text'] = ''.html_print_image( + 'images/full_screen.png', + true, + [ + 'title' => __('List'), + 'class' => 'invert_filter', + ] + ).''; + + // Header. + ui_print_standard_header( + __('SNMP Console'), + 'images/op_snmp.png', + false, + 'snmp_console', + false, + [ + $screen, + $list, + $statistics, + ], + [ + [ + 'link' => '', + 'label' => __('Monitoring'), + ], + [ + 'link' => '', + 'label' => __('SNMP'), + ], + ] + ); + } else { + echo '
'; + + echo ''; + + echo '
'; + + ui_require_css_file('pandora_enterprise', ENTERPRISE_DIR.'/include/styles/'); + ui_require_css_file('pandora_dashboard', ENTERPRISE_DIR.'/include/styles/'); + ui_require_css_file('cluetip', 'include/styles/js/'); + + ui_require_jquery_file('countdown'); + ui_require_javascript_file('pandora_dashboard', ENTERPRISE_DIR.'/include/javascript/'); + ui_require_javascript_file('wz_jsgraphics'); + ui_require_javascript_file('pandora_visual_console'); + } + + // Datatables list. + try { + $checkbox_all = html_print_checkbox( + 'all_validate_box', + 1, + false, + true + ); + + $columns = [ + 'status', + [ + 'text' => 'snmp_agent', + 'class' => 'snmp-td', + ], + [ + 'text' => 'enterprise_string', + 'class' => 'snmp-td', + ], + [ + 'text' => 'count', + 'class' => 'snmp-td', + ], + [ + 'text' => 'trap_subtype', + 'class' => 'snmp-td', + ], + [ + 'text' => 'user_id', + 'class' => 'snmp-td', + ], + [ + 'text' => 'timestamp', + 'class' => 'snmp-td', + ], + 'alert', + 'action', + [ + 'text' => 'm', + 'class' => 'mw60px pdd_0px', + ], + ]; + + $column_names = [ + __('Status'), + __('SNMP Agent'), + __('Enterprise String'), + __('Count'), + __('Trap subtype'), + __('User ID'), + __('Timestamp'), + __('Alert'), + __('Actions'), + [ + 'text' => 'm', + 'extra' => $checkbox_all, + 'class' => 'w20px no-text-imp', + ], + ]; + + $show_alerts = [ + -1 => __('All'), + 0 => __('Not triggered'), + 1 => __('Triggered'), + ]; + + $severities = get_priorities(); + $severities[-1] = __('All'); + + $paginations = [ + $config['block_size'] => __('Default'), + 25 => '25', + 50 => '50', + 100 => '100', + 200 => '200', + 500 => '500', + ]; + + $status_array = [ + -1 => __('All'), + 0 => __('Not validated'), + 1 => __('Validated'), + ]; + + $trap_types = [ + -1 => __('None'), + 0 => __('Cold start (0)'), + 1 => __('Warm start (1)'), + 2 => __('Link down (2)'), + 3 => __('Link up (3)'), + 4 => __('Authentication failure (4)'), + 5 => __('Other'), + ]; + + $this->tableId = 'snmp_console'; + + // Load datatables user interface. + ui_print_datatable( + [ + 'id' => $this->tableId, + 'class' => 'info_table', + 'style' => 'width: 100%', + 'columns' => $columns, + 'column_names' => $column_names, + 'ajax_url' => $this->ajaxController, + 'ajax_data' => ['method' => 'draw'], + 'ajax_postprocces' => 'process_datatables_item(item)', + 'search_button_class' => 'sub filter float-right', + 'no_sortable_columns' => [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ], + 'form' => [ + 'class' => 'flex-row', + 'inputs' => [ + [ + 'label' => __('Alert'), + 'type' => 'select', + 'id' => 'filter_alert', + 'name' => 'filter_alert', + 'class' => 'w200px', + 'fields' => $show_alerts, + 'return' => true, + 'selected' => $this->filter_alert, + ], + [ + 'label' => __('Severity'), + 'type' => 'select', + 'id' => 'filter_severity', + 'name' => 'filter_severity', + 'class' => 'w200px', + 'fields' => $severities, + 'return' => true, + 'selected' => $this->filter_severity, + ], + [ + 'label' => __('Free search'), + 'type' => 'text', + 'class' => 'w400px', + 'id' => 'filter_free_search', + 'name' => 'filter_free_search', + 'value' => $this->filter_free_search, + ], + [ + 'label' => __('Status'), + 'type' => 'select', + 'id' => 'filter_status', + 'name' => 'filter_status', + 'class' => 'w200px', + 'fields' => $status_array, + 'return' => true, + 'selected' => $this->filter_status, + ], + [ + 'label' => __('Group by Enterprise String/IP'), + 'type' => 'select', + 'name' => 'filter_group_by', + 'selected' => $this->filter_group_by, + 'disabled' => false, + 'return' => true, + 'id' => 'filter_group_by', + 'fields' => [ + 0 => __('No'), + 1 => __('Yes'), + ], + ], + [ + 'label' => __('Max. hours old'), + 'type' => 'text', + 'class' => 'w200px', + 'id' => 'filter_hours_ago', + 'name' => 'filter_hours_ago', + 'value' => $this->filter_hours_ago, + ], + [ + 'label' => __('Trap type'), + 'type' => 'select', + 'id' => 'filter_trap_type', + 'name' => 'filter_trap_type', + 'class' => 'w200px', + 'fields' => $trap_types, + 'return' => true, + 'selected' => $this->filter_trap_type, + ], + ], + ], + ] + ); + } catch (Exception $e) { + echo $e->getMessage(); + } + + echo '
'; + html_print_submit_button(__('Validate'), 'updatebt', false, 'class="sub ok"'); + echo ' '; + html_print_submit_button( + __('Delete'), + 'deletebt', + false, + 'class="sub delete" onClick="javascript:return confirm(\''.__('Are you sure?').'\')"' + ); + echo '
'; + + echo '
'; + echo '

'.__('Status').'

'; + echo html_print_image( + 'images/pixel_green.png', + true, + [ + 'width' => '20', + 'height' => '20', + ] + ).' - '.__('Validated'); + echo '
'; + echo html_print_image( + 'images/pixel_red.png', + true, + [ + 'width' => '20', + 'height' => '20', + ] + ).' - '.__('Not validated'); + echo '
'; + echo '
'; + echo '

'.__('Alert').'

'; + echo html_print_image( + 'images/pixel_yellow.png', + true, + [ + 'width' => '20', + 'height' => '20', + ] + ).' - '.__('Fired'); + echo '
'; + echo html_print_image( + 'images/pixel_gray.png', + true, + [ + 'width' => '20', + 'height' => '20', + ] + ).' - '.__('Not fired'); + echo '
'; + echo '
'; + echo '

'.__('Action').'

'; + echo html_print_image('images/ok.png', true).' - '.__('Validate'); + echo '
'; + echo html_print_image('images/cross.png', true, ['class' => 'invert_filter']).' - '.__('Delete'); + echo '
'; + echo '
'; + echo '

'.__('Severity').'

'; + foreach (get_priorities() as $num => $name) { + echo ''.$name.''; + echo '
'; + } + + echo '
'; + + // Load own javascript file. + echo $this->loadJS(); + } + + + /** + * Get the data for draw the table. + * + * @return void. + */ + public function draw() + { + global $config; + + // Init data. + $data = []; + // Count of total records. + $count = 0; + // Catch post parameters. + $start = get_parameter('start', 0); + $length = get_parameter('length', $config['block_size']); + // There is a limit of (2^32)^2 (18446744073709551615) rows in a MyISAM table, show for show all use max nrows. + $length = ($length != '-1') ? $length : '18446744073709551615'; + $order = get_datatable_order(true); + $filters = get_parameter('filter', []); + + // Build ranges. + $now = new DateTime(); + $ago = new DateTime(); + $interval = new DateInterval(sprintf('PT%dH', $filters['filter_hours_ago'])); + $ago->sub($interval); + + $date_from_trap = $ago->format('Y/m/d'); + $date_to_trap = $now->format('Y/m/d'); + $time_from_trap = $ago->format('H:i:s'); + $time_to_trap = $now->format('H:i:s'); + + try { + ob_start(); + $data = []; + + $user_groups = users_get_groups($config['id_user'], 'AR', false); + $prea = array_keys($user_groups); + $ids = join(',', $prea); + + $user_in_group_wo_agents = db_get_value_sql('select count(DISTINCT(id_usuario)) from tusuario_perfil where id_usuario ="'.$config['id_user'].'" and id_perfil = 1 and id_grupo in (select id_grupo from tgrupo where id_grupo in ('.$ids.') and id_grupo not in (select id_grupo from tagente))'); + if ($user_in_group_wo_agents == 0) { + $rows = db_get_all_rows_filter( + 'tagente', + ['id_grupo' => array_keys($user_groups)], + ['id_agente'] + ); + $id_agents = []; + foreach ($rows as $row) { + $id_agents[] = $row['id_agente']; + } + + if (!empty($id_agents)) { + $address_by_user_groups = agents_get_addresses($id_agents); + foreach ($address_by_user_groups as $i => $a) { + $address_by_user_groups[$i] = '"'.$a.'"'; + } + } + } else { + $rows = db_get_all_rows_filter( + 'tagente', + [], + ['id_agente'] + ); + $id_agents = []; + foreach ($rows as $row) { + $id_agents[] = $row['id_agente']; + } + + $all_address_agents = agents_get_addresses($id_agents); + foreach ($all_address_agents as $i => $a) { + $all_address_agents[$i] = '"'.$a.'"'; + } + } + + if (empty($address_by_user_groups)) { + $address_by_user_groups = []; + array_unshift($address_by_user_groups, '""'); + } + + if (empty($all_address_agents)) { + $all_address_agents = []; + array_unshift($all_address_agents, '""'); + } + + $sql = 'SELECT * FROM ttrap + WHERE ( + `source` IN ('.implode(',', $address_by_user_groups).") OR + `source`='' OR + `source` NOT IN (".implode(',', $all_address_agents).') + ) + %s + ORDER BY timestamp DESC + LIMIT %d,%d'; + + $whereSubquery = ''; + if ($filters['filter_alert'] != -1) { + $whereSubquery .= ' AND alerted = '.$filters['filter_alert']; + } + + if ($filters['filter_severity'] != -1) { + // There are two special severity values aimed to match two different trap standard severities + // in database: warning/critical and critical/normal. + if ($filters['filter_severity'] != EVENT_CRIT_OR_NORMAL + && $filters['filter_severity'] != EVENT_CRIT_WARNING_OR_CRITICAL + ) { + // Test if enterprise is installed to search oid in text or oid field in ttrap. + if ($config['enterprise_installed']) { + $whereSubquery .= ' AND ( + (alerted = 0 AND severity = '.$filters['filter_severity'].') OR + (alerted = 1 AND priority = '.$filters['filter_severity'].'))'; + } else { + $whereSubquery .= ' AND ( + (alerted = 0 AND 1 = '.$filters['filter_severity'].') OR + (alerted = 1 AND priority = '.$filters['filter_severity'].'))'; + } + } else if ($filters['filter_severity'] === EVENT_CRIT_WARNING_OR_CRITICAL) { + // Test if enterprise is installed to search oid in text or oid field in ttrap. + if ($config['enterprise_installed']) { + $whereSubquery .= ' AND ( + (alerted = 0 AND (severity = '.EVENT_CRIT_WARNING.' OR severity = '.EVENT_CRIT_CRITICAL.')) OR + (alerted = 1 AND (priority = '.EVENT_CRIT_WARNING.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } else { + $whereSubquery .= ' AND ( + (alerted = 1 AND (priority = '.EVENT_CRIT_WARNING.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } + } else if ($filters['filter_severity'] === EVENT_CRIT_OR_NORMAL) { + // Test if enterprise is installed to search oid in text or oid field in ttrap. + if ($config['enterprise_installed']) { + $whereSubquery .= ' AND ( + (alerted = 0 AND (severity = '.EVENT_CRIT_NORMAL.' OR severity = '.EVENT_CRIT_CRITICAL.')) OR + (alerted = 1 AND (priority = '.EVENT_CRIT_NORMAL.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } else { + $whereSubquery .= ' AND ( + (alerted = 1 AND (priority = '.EVENT_CRIT_NORMAL.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } + } + } + + if ($filters['filter_free_search'] !== '') { + $whereSubquery .= ' + AND (source LIKE "%'.$filters['filter_free_search'].'%" OR + oid LIKE "%'.$filters['filter_free_search'].'%" OR + oid_custom LIKE "%'.$filters['filter_free_search'].'%" OR + type_custom LIKE "%'.$filters['filter_free_search'].'%" OR + value LIKE "%'.$filters['filter_free_search'].'%" OR + value_custom LIKE "%'.$filters['filter_free_search'].'%" OR + id_usuario LIKE "%'.$filters['filter_free_search'].'%" OR + text LIKE "%'.$filters['filter_free_search'].'%" OR + description LIKE "%'.$filters['filter_free_search'].'%")'; + } + + if ($filters['filter_status'] != -1) { + $whereSubquery .= ' AND status = '.$filters['filter_status']; + } + + if ($date_from_trap != '') { + if ($time_from_trap != '') { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) > UNIX_TIMESTAMP("'.$date_from_trap.' '.$time_from_trap.'")) + '; + } else { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) > UNIX_TIMESTAMP("'.$date_from_trap.' 23:59:59")) + '; + } + } + + if ($date_to_trap != '') { + if ($time_to_trap) { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) < UNIX_TIMESTAMP("'.$date_to_trap.' '.$time_to_trap.'")) + '; + } else { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) < UNIX_TIMESTAMP("'.$date_to_trap.' 23:59:59")) + '; + } + } + + if ($filters['filter_trap_type'] == 5) { + $whereSubquery .= ' AND type NOT IN (0, 1, 2, 3, 4)'; + } else if ($filters['filter_trap_type'] != -1) { + $whereSubquery .= ' AND type = '.$filters['filter_trap_type']; + } + + if ($filters['filter_group_by']) { + $where_without_group = $whereSubquery; + $whereSubquery .= ' GROUP BY source,oid'; + } + + $sql = sprintf($sql, $whereSubquery, $start, $length); + $sql_count = 'SELECT COUNT(id_trap) FROM ttrap + WHERE ( + source IN ('.implode(',', $address_by_user_groups).") OR + source='' OR + source NOT IN (".implode(',', $all_address_agents).') + ) + %s'; + + $sql_count = sprintf($sql_count, $whereSubquery); + + $traps = db_get_all_rows_sql($sql, true); + $total = (int) db_get_value_sql($sql_count, false, true); + + if (empty($traps) === false) { + $data = $traps; + $data = array_reduce( + $data, + function ($carry, $item) use ($filters, $where_without_group) { + global $config; + + if (empty($carry) === true) { + $count = 0; + } else { + $count = count($carry); + } + + // Transforms array of arrays $data into an array + // of objects, making a post-process of certain fields. + $tmp = (object) $item; + + $severity_class = get_priority_class($tmp->severity); + + $status = $tmp->status; + + // Status. + if ($status == 0) { + $tmp->status = html_print_image( + 'images/pixel_red.png', + true, + [ + 'title' => __('Not validated'), + 'width' => '20', + 'height' => '20', + ] + ); + } else { + $tmp->status = html_print_image( + 'images/pixel_green.png', + true, + [ + 'title' => __('Validated'), + 'width' => '20', + 'height' => '20', + ] + ); + } + + // SNMP Agent. + $agent = agents_get_agent_with_ip($tmp->source); + if ($agent === false) { + $tmp->snmp_agent .= ''.$tmp->source.''; + } else { + $tmp->snmp_agent .= ''; + } + + // Enterprise string. + if (empty($tmp->text) === false) { + $enterprise_string = $tmp->text; + } else if (empty($tmp->oid) === false) { + $enterprise_string = $tmp->oid; + } else { + $enterprise_string = __('N/A'); + } + + $tmp->enterprise_string = ''; + + // Count. + if ($filters['filter_group_by']) { + $sql = 'SELECT count(*) FROM ttrap WHERE 1=1 + '.$where_without_group.' + AND oid="'.$tmp->oid.'" + AND source="'.$tmp->source.'"'; + $group_traps = db_get_value_sql($sql); + $tmp->count = '
'.$group_traps.'
'; + } + + // Trap subtype. + $tmp->trap_subtype = '
'; + if (empty($tmp->value) === true) { + $tmp->trap_subtype .= __('N/A'); + } else { + $tmp->trap_subtype .= ui_print_truncate_text($tmp->value, GENERIC_SIZE_TEXT, false); + } + + $tmp->trap_subtype .= '
'; + + // User ID. + $tmp->user_id = '
'; + if (empty($status) === false) { + $tmp->user_id .= ''.substr($tmp->id_usuario, 0, 8).''; + if (!empty($tmp->id_usuario)) { + $tmp->user_id .= ui_print_help_tip(get_user_fullname($tmp->id_usuario), true); + } + } else { + $tmp->user_id .= '--'; + } + + $tmp->user_id .= '
'; + + // Timestamp. + $timestamp = $tmp->timestamp; + $tmp->timestamp = '
'; + $tmp->timestamp .= ''; + $tmp->timestamp .= ui_print_timestamp($timestamp, true); + $tmp->timestamp .= '
'; + + // Use alert severity if fired. + if (empty($tmp->alerted) === false) { + $tmp->alert = html_print_image('images/pixel_yellow.png', true, ['width' => '20', 'height' => '20', 'border' => '0', 'title' => __('Alert fired')]); + } else { + $tmp->alert = html_print_image('images/pixel_gray.png', true, ['width' => '20', 'height' => '20', 'border' => '0', 'title' => __('Alert not fired')]); + } + + // Actions. + $tmp->action = ''; + if ($status != 1) { + $tmp->action .= ''.html_print_image( + 'images/ok.png', + true, + [ + 'border' => '0', + 'title' => __('Validate'), + 'onclick' => 'validate_trap(\''.$tmp->id_trap.'\')', + ] + ).' '; + } + + if ($tmp->source === '') { + if (\users_is_admin()) { + $tmp->action .= ''.html_print_image( + 'images/cross.png', + true, + [ + 'border' => '0', + 'title' => __('Delete'), + 'class' => 'invert_filter', + 'onclick' => 'delete_trap(\''.$tmp->id_trap.'\')', + ] + ).' '; + } + } else { + $tmp->action .= ''.html_print_image( + 'images/cross.png', + true, + [ + 'border' => '0', + 'title' => __('Delete'), + 'class' => 'invert_filter', + 'onclick' => 'delete_trap(\''.$tmp->id_trap.'\')', + ] + ).' '; + } + + $tmp->action .= ''.html_print_image( + 'images/eye.png', + true, + [ + 'alt' => __('Show more'), + 'title' => __('Show more'), + 'class' => 'invert_filter', + ] + ).''; + $tmp->action .= ''.html_print_image('images/edit.png', true, ['alt' => __('SNMP trap editor'), 'title' => __('SNMP trap editor')]).''; + + $tmp->m = html_print_checkbox_extended('snmptrapid[]', $tmp->id_trap, false, false, '', 'class="chk"', true); + + $carry[] = $tmp; + return $carry; + }, + ); + } + + if (empty($data) === true) { + $total = 0; + $data = []; + } + + echo json_encode( + [ + 'data' => $data, + 'recordsTotal' => $total, + 'recordsFiltered' => $total, + ] + ); + // Capture output. + $response = ob_get_clean(); + } catch (Exception $e) { + echo json_encode(['error' => $e->getMessage()]); + exit; + } + + // If not valid, show error with issue. + json_decode($response); + if (json_last_error() === JSON_ERROR_NONE) { + // If valid dump. + echo $response; + } else { + echo json_encode( + ['error' => $response] + ); + } + + exit; + } + + + /** + * Checks if target method is available to be called using AJAX. + * + * @param string $method Target method. + * + * @return boolean True allowed, false not. + */ + public function ajaxMethod(string $method) + { + return in_array($method, $this->AJAXMethods); + } + + + /** + * Delete snmp trap. + * + * @return void + */ + public function deleteTrap() + { + $id_trap = get_parameter('id', 0); + $group_by = (bool) get_parameter('group_by', 0); + + if ($id_trap > 0) { + if ($group_by === true) { + $sql_ids_traps = 'SELECT id_trap, source FROM ttrap WHERE oid IN (SELECT oid FROM ttrap WHERE id_trap = '.$id_trap.') + AND source IN (SELECT source FROM ttrap WHERE id_trap = '.$id_trap.')'; + $ids_traps = db_get_all_rows_sql($sql_ids_traps); + + foreach ($ids_traps as $key => $value) { + $result = db_process_sql_delete('ttrap', ['id_trap' => $value['id_trap']]); + enterprise_hook('snmp_update_forwarded_modules', [$value]); + } + } else { + $forward_info = db_get_row('ttrap', 'id_trap', $id_trap); + $result = db_process_sql_delete('ttrap', ['id_trap' => $id_trap]); + enterprise_hook('snmp_update_forwarded_modules', [$forward_info]); + } + } + } + + + /** + * Delete snmp traps. + * + * @return void + */ + public function deleteTraps() + { + $ids = get_parameter('ids', []); + $group_by = (bool) get_parameter('group_by', false); + + if (empty($ids) === false) { + $string_ids = implode(',', $ids); + if ($group_by === true) { + $sql_ids_traps = 'SELECT id_trap, source FROM ttrap WHERE oid IN (SELECT oid FROM ttrap WHERE id_trap IN ('.$string_ids.')) + AND source IN (SELECT source FROM ttrap WHERE id_trap IN ('.$string_ids.'))'; + $ids_traps = db_get_all_rows_sql($sql_ids_traps); + + $array = array_column($ids_traps, 'id_trap'); + + $delete = sprintf( + 'DELETE FROM `ttrap` WHERE id_trap IN (%s)', + implode(',', $array), + ); + db_process_sql($delete); + + foreach ($ids_traps as $key => $value) { + enterprise_hook('snmp_update_forwarded_modules', [$value]); + } + } else { + $delete = sprintf( + 'DELETE FROM `ttrap` WHERE id_trap IN (%s)', + $string_ids, + ); + db_process_sql($delete); + foreach ($ids as $id_trap) { + enterprise_hook('snmp_update_forwarded_modules', [$id_trap]); + } + } + } + } + + + /** + * Validate snmp trap. + * + * @return void + */ + public function validateTrap() + { + global $config; + + $id_trap = get_parameter('id', 0); + + $values = [ + 'status' => 1, + 'id_usuario' => $config['id_user'], + ]; + + $result = db_process_sql_update('ttrap', $values, ['id_trap' => $id_trap]); + enterprise_hook('snmp_update_forwarded_modules', [$id_trap]); + } + + + /** + * Validate snmp traps. + * + * @return void + */ + public function validateTraps() + { + global $config; + + $ids = get_parameter('ids', []); + + if (empty($ids) === false) { + $update = sprintf( + 'UPDATE ttrap SET `status` = 1, `id_usuario` = "%s" WHERE id_trap IN (%s)', + $config['id_user'], + implode(',', $ids) + ); + db_process_sql($update); + + foreach ($ids as $id_trap) { + enterprise_hook('snmp_update_forwarded_modules', [$id_trap]); + } + } + } + + + /** + * VShow info trap. + * + * @return void + */ + public function showInfo() + { + global $config; + + $id_trap = get_parameter('id', 0); + $group_by = get_parameter('group_by', 0); + $alert = get_parameter('alert', -1); + $severity = get_parameter('severity', -1); + $search = get_parameter('search', ''); + $status = get_parameter('status', 0); + $hours_ago = get_parameter('hours_ago', 8); + $trap_type = get_parameter('trap_type', -1); + + $trap = db_get_row('ttrap', 'id_trap', $id_trap); + + if ($group_by) { + $now = new DateTime(); + $ago = new DateTime(); + $interval = new DateInterval(sprintf('PT%dH', $hours_ago)); + $ago->sub($interval); + + $date_from_trap = $ago->format('Y/m/d'); + $date_to_trap = $now->format('Y/m/d'); + $time_from_trap = $ago->format('H:i:s'); + $time_to_trap = $now->format('H:i:s'); + + $whereSubquery = ''; + if ($alert != -1) { + $whereSubquery .= ' AND alerted = '.$$alert; + } + + if ($severity != -1) { + // There are two special severity values aimed to match two different trap standard severities + // in database: warning/critical and critical/normal. + if ($severity != EVENT_CRIT_OR_NORMAL + && $severity != EVENT_CRIT_WARNING_OR_CRITICAL + ) { + // Test if enterprise is installed to search oid in text or oid field in ttrap. + if ($config['enterprise_installed']) { + $whereSubquery .= ' AND ( + (alerted = 0 AND severity = '.$severity.') OR + (alerted = 1 AND priority = '.$severity.'))'; + } else { + $whereSubquery .= ' AND ( + (alerted = 0 AND 1 = '.$severity.') OR + (alerted = 1 AND priority = '.$severity.'))'; + } + } else if ($severity === EVENT_CRIT_WARNING_OR_CRITICAL) { + // Test if enterprise is installed to search oid in text or oid field in ttrap. + if ($config['enterprise_installed']) { + $whereSubquery .= ' AND ( + (alerted = 0 AND (severity = '.EVENT_CRIT_WARNING.' OR severity = '.EVENT_CRIT_CRITICAL.')) OR + (alerted = 1 AND (priority = '.EVENT_CRIT_WARNING.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } else { + $whereSubquery .= ' AND ( + (alerted = 1 AND (priority = '.EVENT_CRIT_WARNING.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } + } else if ($severity === EVENT_CRIT_OR_NORMAL) { + // Test if enterprise is installed to search oid in text or oid field in ttrap. + if ($config['enterprise_installed']) { + $whereSubquery .= ' AND ( + (alerted = 0 AND (severity = '.EVENT_CRIT_NORMAL.' OR severity = '.EVENT_CRIT_CRITICAL.')) OR + (alerted = 1 AND (priority = '.EVENT_CRIT_NORMAL.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } else { + $whereSubquery .= ' AND ( + (alerted = 1 AND (priority = '.EVENT_CRIT_NORMAL.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; + } + } + } + + if ($search !== '') { + $whereSubquery .= ' + AND (source LIKE "%'.$search.'%" OR + oid LIKE "%'.$search.'%" OR + oid_custom LIKE "%'.$search.'%" OR + type_custom LIKE "%'.$search.'%" OR + value LIKE "%'.$search.'%" OR + value_custom LIKE "%'.$search.'%" OR + id_usuario LIKE "%'.$search.'%" OR + text LIKE "%'.$search.'%" OR + description LIKE "%'.$search.'%")'; + } + + if ($status != -1) { + $whereSubquery .= ' AND status = '.$status; + } + + if ($date_from_trap != '') { + if ($time_from_trap != '') { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) > UNIX_TIMESTAMP("'.$date_from_trap.' '.$time_from_trap.'")) + '; + } else { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) > UNIX_TIMESTAMP("'.$date_from_trap.' 23:59:59")) + '; + } + } + + if ($date_to_trap != '') { + if ($time_to_trap) { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) < UNIX_TIMESTAMP("'.$date_to_trap.' '.$time_to_trap.'")) + '; + } else { + $whereSubquery .= ' + AND (UNIX_TIMESTAMP(timestamp) < UNIX_TIMESTAMP("'.$date_to_trap.' 23:59:59")) + '; + } + } + + if ($trap_type == 5) { + $whereSubquery .= ' AND type NOT IN (0, 1, 2, 3, 4)'; + } else if ($trap_type != -1) { + $whereSubquery .= ' AND type = '.$trap_type; + } + + $sql = 'SELECT * FROM ttrap WHERE 1=1 + '.$whereSubquery.' + AND oid="'.$trap['oid'].'" + AND source="'.$trap['source'].'"'; + $group_traps = db_get_all_rows_sql($sql); + $count_group_traps = count($group_traps); + + $sql = 'SELECT timestamp FROM ttrap WHERE 1=1 + '.$whereSubquery.' + AND oid="'.$trap['oid'].'" + AND source="'.$trap['source'].'" + ORDER BY `timestamp` DESC'; + $last_trap = db_get_value_sql($sql); + + $sql = 'SELECT timestamp FROM ttrap WHERE 1=1 + '.$whereSubquery.' + AND oid="'.$trap['oid'].'" + AND source="'.$trap['source'].'" + ORDER BY `timestamp` ASC'; + $first_trap = db_get_value_sql($sql); + + $trap['count'] = $count_group_traps; + $trap['first'] = $first_trap; + $trap['last'] = $last_trap; + } + + echo json_encode($trap); + return; + } + + + /** + * Load Javascript code. + * + * @return string. + */ + public function loadJS() + { + // Nothing for this moment. + ob_start(); + + // Javascript content. + ?> + + $params, + 'session_id' => $session_id, + 'type_graph_pdf' => $type_graph_pdf, + 'data_module_list' => $module_list, + 'data_combined' => $params_combined, + ]; } else { - // To be used in PDF files. - $config['temp_images'][] = $img_path; - return ''; + $data = [ + 'data' => $params, + 'session_id' => $session_id, + 'type_graph_pdf' => $type_graph_pdf, + ]; + } + + // If not install chromium avoid 500 convert tu images no data to show. + $chromium_dir = io_safe_output($config['chromium_path']); + $result_ejecution = exec($chromium_dir.' --version'); + if (empty($result_ejecution) === true) { + if ($params['return_img_base_64']) { + $params['base64'] = true; + } + + return graph_nodata_image($params); + } + + try { + $browserFactory = new BrowserFactory($chromium_dir); + + // Starts headless chrome. + $browser = $browserFactory->createBrowser(['noSandbox' => true]); + + // Creates a new page. + $page = $browser->createPage(); + + // Navigate to an URL. + $navigation = $page->navigate($url.'?data='.urlencode(json_encode($data))); + $navigation->waitForNavigation(Page::DOM_CONTENT_LOADED); + + // Dynamic. + $dynamic_height = $page->evaluate('document.getElementById("container-chart-generator-item").clientHeight')->getReturnValue(); + if (empty($dynamic_height) === true) { + $dynamic_height = 200; + } + + if (isset($params['options']['viewport']) === true + && isset($params['options']['viewport']['height']) === true + && empty($params['options']['viewport']['height']) === false + ) { + $dynamic_height = $params['options']['viewport']['height']; + } + + $dynamic_width = $page->evaluate('document.getElementById("container-chart-generator-item").clientWidth')->getReturnValue(); + if (empty($dynamic_width) === true) { + $dynamic_width = 794; + } + + if (isset($params['options']['viewport']) === true + && isset($params['options']['viewport']['width']) === true + && empty($params['options']['viewport']['width']) === false + ) { + $dynamic_width = $params['options']['viewport']['width']; + } + + $clip = new Clip(0, 0, $dynamic_width, $dynamic_height); + + if ($params['return_img_base_64']) { + $b64 = $page->screenshot(['clip' => $clip])->getBase64(); + // To be used in alerts. + return $b64; + } else { + // To be used in PDF files. + $b64 = $page->screenshot(['clip' => $clip])->saveToFile($img_path); + $config['temp_images'][] = $img_path; + return ''; + } + } catch (\Throwable $th) { + error_log($th); + } finally { + $browser->close(); } } diff --git a/pandora_console/include/functions_alerts.php b/pandora_console/include/functions_alerts.php index 0a8628e54d..9921419c8b 100644 --- a/pandora_console/include/functions_alerts.php +++ b/pandora_console/include/functions_alerts.php @@ -2101,7 +2101,8 @@ function get_group_alerts( $count=false, $strict_user=false, $tag=false, - $action_filter=false + $action_filter=false, + $alert_action=true ) { global $config; @@ -2162,7 +2163,9 @@ function get_group_alerts( // WHEN SELECT ALL TAGS TO FILTER ALERTS if ($action_filter) { $filter .= ' AND (talert_template_modules.id IN (SELECT id_alert_template_module FROM talert_template_module_actions where id_alert_action = '.$action_filter.'))'; - $filter .= ' OR talert_template_modules.id_alert_template IN (SELECT talert_templates.id FROM talert_templates where talert_templates.id_alert_action = '.$action_filter.')'; + if ($alert_action) { + $filter .= ' OR talert_template_modules.id_alert_template IN (SELECT talert_templates.id FROM talert_templates where talert_templates.id_alert_action = '.$action_filter.')'; + } } if (is_array($options)) { @@ -2252,6 +2255,7 @@ function get_group_alerts( $filter, $orderbyText ); + $alerts = db_get_all_rows_sql($sql); if ($alerts === false) { diff --git a/pandora_console/include/functions_api.php b/pandora_console/include/functions_api.php index ce6387ffc7..21ced0498b 100644 --- a/pandora_console/include/functions_api.php +++ b/pandora_console/include/functions_api.php @@ -62,7 +62,7 @@ enterprise_include_once('include/functions_cron.php'); // Clases. use PandoraFMS\Agent; use PandoraFMS\Module; -use PandoraFMS\Enterprise\Cluster; +use PandoraFMS\Cluster; use PandoraFMS\Enterprise\Metaconsole\Node; use PandoraFMS\Event; use PandoraFMS\SpecialDay; @@ -9601,6 +9601,7 @@ function api_set_new_user($id, $thrash2, $other, $thrash3) $values['default_event_filter'] = $other['data'][10]; $values['section'] = $other['data'][11]; $values['session_time'] = $other['data'][12]; + $values['metaconsole_access_node'] = $other['data'][13]; if (empty($password) === true) { returnError('Password cannot be empty.'); @@ -10157,6 +10158,8 @@ function api_set_delete_module($id, $id2, $other, $trash1) } if (!$simulate) { + // Before delete the main module, check and delete the childrens from the original module. + module_check_childrens_and_delete($idAgentModule); $return = modules_delete_agent_module($idAgentModule); } else { $return = true; @@ -10182,6 +10185,8 @@ function api_set_delete_module($id, $id2, $other, $trash1) } if (!$simulate) { + // Before delete the main module, check and delete the childrens from the original module. + module_check_childrens_and_delete($idAgentModule); $return = modules_delete_agent_module($idAgentModule); } else { $return = true; diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php index cc49761959..f7b85e5137 100644 --- a/pandora_console/include/functions_config.php +++ b/pandora_console/include/functions_config.php @@ -184,8 +184,8 @@ function config_update_config() $error_update[] = __('Remote config directory'); } - if (config_update_value('phantomjs_bin', (string) get_parameter('phantomjs_bin'), true) === false) { - $error_update[] = __('phantomjs config directory'); + if (config_update_value('chromium_path', (string) get_parameter('chromium_path'), true) === false) { + $error_update[] = __('Chromium config directory'); } if (config_update_value('loginhash_pwd', io_input_password((string) get_parameter('loginhash_pwd')), true) === false) { @@ -523,6 +523,15 @@ function config_update_config() break; case 'auth': + $validatedCSRF = validate_csrf_code(); + + // CSRF Validation. + if ($validatedCSRF === false) { + include_once 'general/login_page.php'; + // Finish the execution. + exit(''); + } + // AUTHENTICATION SETUP. if (config_update_value('auth', get_parameter('auth'), true) === false) { $error_update[] = __('Authentication method'); @@ -926,25 +935,6 @@ function config_update_config() if (config_update_value('agent_wizard_defaults', json_encode($selectedAgentWizardOptions), true) === false) { $error_update[] = __('SNMP Interface Agent Wizard'); } - - $pjs = get_parameter('phantomjs_cache_interval'); - switch ($pjs) { - case $config['phantomjs_cache_interval']: - default; - // No changes. - break; - - case PHANTOM_CACHE_CLEANUP_ONCE: - case PHANTOM_CACHE_CLEANUP_DAILY: - case PHANTOM_CACHE_CLEANUP_WEEKLY: - enterprise_hook('phantomjs_cache_interval_schedule', [$pjs]); - break; - } - - if (config_update_value('phantomjs_cache_interval', get_parameter('phantomjs_cache_interval'), true) === false - ) { - $error_update[] = __('PhantomJS cache interval'); - } break; case 'vis': @@ -1975,14 +1965,9 @@ function config_process_config() config_update_value('remote_config', $default); } - if (!isset($config['phantomjs_bin'])) { - if ($is_windows) { - $default = 'C:\PandoraFMS\Pandora_Server\bin'; - } else { - $default = '/usr/bin'; - } - - config_update_value('phantomjs_bin', $default); + if (isset($config['chromium_path']) === false) { + $default = '/usr/bin/chromium-browser'; + config_update_value('chromium_path', $default); } if (!isset($config['date_format'])) { diff --git a/pandora_console/include/functions_db.php b/pandora_console/include/functions_db.php index 83b972ca75..bdc596234b 100644 --- a/pandora_console/include/functions_db.php +++ b/pandora_console/include/functions_db.php @@ -757,7 +757,8 @@ function db_uncompress_module_data( $id_agente_modulo, $tstart=false, $tend=false, - $slice_size=false + $slice_size=false, + $force_slice_not_data=false ) { global $config; @@ -860,7 +861,10 @@ function db_uncompress_module_data( $module_interval = modules_get_interval($id_agente_modulo); - if (($raw_data === false) && ( $first_utimestamp === false )) { + if (($force_slice_not_data === false) + && ($raw_data === false) + && ( $first_utimestamp === false ) + ) { // No data. return false; } @@ -2313,7 +2317,13 @@ function db_get_lock(string $lockname, int $expiration_time=86400) :?int } if ($lock_status === false) { - return null; + db_pandora_audit( + AUDIT_LOG_SYSTEM, + 'Issue in Database Lock', + 'system' + ); + + return (int) null; } return (int) $lock_status; diff --git a/pandora_console/include/functions_events.php b/pandora_console/include/functions_events.php index 60bc51e5b7..c5273baa63 100644 --- a/pandora_console/include/functions_events.php +++ b/pandora_console/include/functions_events.php @@ -2634,27 +2634,6 @@ function events_print_event_table( $events_table = html_print_table($table, true); $out = $events_table; - if (!$tactical_view) { - $out .= '
'; - if ($agent_id != 0) { - $out .= ''; - $out .= '
'; - $out .= '
- '.__('Events -by module-').''.graph_event_module(180, 100, $event['id_agente']).'
'; - $out .= '
'; - } else { - $out .= '
'; - $out .= '
'; - $out .= '
- '.__('Event graph').''.grafico_eventos_total('', 180, 60).'
'; - $out .= '
- '.__('Event graph by agent').''.grafico_eventos_grupo(180, 60).'
'; - $out .= '
'; - } - - $out .= '
'; - } - unset($table); if ($return) { @@ -5212,7 +5191,7 @@ function events_get_count_events_validated_by_user($data) ) { foreach ($fullnames as $value) { if (isset($data_graph_by_user[$value['id_user']]) === true) { - $data_graph_by_user[$value['fullname']] = $data_graph_by_user[$value['id_user']]; + $data_graph_by_user[io_safe_output($value['fullname'])] = $data_graph_by_user[$value['id_user']]; unset($data_graph_by_user[$value['id_user']]); } } diff --git a/pandora_console/include/functions_graph.php b/pandora_console/include/functions_graph.php index dc2616765c..ca9418374e 100644 --- a/pandora_console/include/functions_graph.php +++ b/pandora_console/include/functions_graph.php @@ -814,14 +814,7 @@ function grafico_modulo_sparse($params) } if (isset($params['agent_module_id']) === false) { - return graph_nodata_image( - $params['width'], - $params['height'], - 'area', - '', - false, - $params['pdf'] - ); + return graph_nodata_image($params); } else { $agent_module_id = $params['agent_module_id']; } @@ -1024,14 +1017,7 @@ function grafico_modulo_sparse($params) $array_events_alerts ); } else { - $return = graph_nodata_image( - $params['width'], - $params['height'], - 'area', - '', - false, - $params['pdf'] - ); + $return = graph_nodata_image($params); } $return .= '
'; @@ -1058,14 +1044,7 @@ function grafico_modulo_sparse($params) $array_events_alerts ); } else { - $return = graph_nodata_image( - $params['width'], - $params['height'], - 'area', - '', - false, - $params['pdf'] - ); + $return = graph_nodata_image($params); } } else { if (empty($array_data) === false) { @@ -1082,14 +1061,7 @@ function grafico_modulo_sparse($params) $array_events_alerts ); } else { - $return = graph_nodata_image( - $params['width'], - $params['height'], - 'area', - __('No data to display within the selected interval'), - false, - $params['pdf'] - ); + $return = graph_nodata_image($params); } } @@ -1550,6 +1522,12 @@ function graphic_combined_module( $height = $params['height']; $homeurl = $params['homeurl']; $ttl = $params['ttl']; + + $date_array = []; + $date_array['period'] = $params['period']; + $date_array['final_date'] = $params['date']; + $date_array['start_date'] = ($params['date'] - $params['period']); + $background_color = $params['backgroundColor']; $datelimit = $date_array['start_date']; $fixed_font_size = $config['font_size']; @@ -1733,10 +1711,10 @@ function graphic_combined_module( if (empty($array_data) === true) { if ($params_combined['return']) { - return graph_nodata_image($width, $height); + return graph_nodata_image($params); } - echo graph_nodata_image($width, $height); + echo graph_nodata_image($params); return false; } @@ -2235,10 +2213,6 @@ function graphic_combined_module( ); $temp_data = db_get_value_sql($query_last_value); - $agent_name = io_safe_output( - modules_get_agentmodule_agent_name($module) - ); - if (empty($params_combined['labels']) === false && isset($params_combined['labels'][$module]) === true ) { @@ -2250,24 +2224,21 @@ function graphic_combined_module( 'id_agente', $module_data['id_agente'] ); - if ($params['vconsole'] === true) { - if ($width < 250 || $height < 250) { - $label = \ui_print_truncate_text($module_data['nombre'], 3, false); - } else { - $label = $module_data['nombre']; - } - } else { - $label = $alias.' - '.$module_data['nombre']; - } + + $label = $alias.' - '.$module_data['nombre']; } - if ($params_combined['stacked'] == CUSTOM_GRAPH_VBARS) { - $temp[] = [ - 'tick' => $label, - 'data' => (int) round($temp_data, 4), + $graph_labels[] = io_safe_output($label); + if ($params_combined['stacked'] == CUSTOM_GRAPH_HBARS) { + $graph_values[] = [ + 'y' => io_safe_output($label), + 'x' => round($temp_data, 4), ]; } else { - $temp[$label]['g'] = round($temp_data, 4); + $graph_values[] = [ + 'x' => io_safe_output($label), + 'y' => round($temp_data, 4), + ]; } if (is_metaconsole() === true) { @@ -2279,69 +2250,50 @@ function graphic_combined_module( $color = color_graph_array(); - $graph_values = $temp; + if ($params['vconsole'] === true) { + $water_mark = ''; + } + + $options = [ + 'height' => $height, + 'waterMark' => $water_mark, + 'ttl' => $ttl, + 'pdf' => $params['pdf'], + 'legend' => ['display' => false], + 'scales' => [ + 'x' => [ + 'bounds' => 'data', + 'grid' => ['display' => false], + ], + 'y' => [ + 'grid' => ['display' => false], + ], + ], + 'labels' => $graph_labels, + ]; if ($params_combined['stacked'] == CUSTOM_GRAPH_HBARS) { - if ($params['vconsole'] === false) { - $width = 1024; - $height = 500; - } else { - $water_mark = false; - } - - $output = hbar_graph( - $graph_values, - $width, - $height, - $color, - $module_name_list, - $long_index, - ui_get_full_url( - 'images/image_problem_area_small.png', - false, - false, - false - ), - '', - '', - $water_mark, - $config['fontpath'], - $fixed_font_size, - '', - $ttl, - $homeurl, - $background_color, - '#c1c1c1', - null, - null, - false, - $params['pdf'] - ); + $options['axis'] = 'y'; } - if ($params_combined['stacked'] == CUSTOM_GRAPH_VBARS) { - $options = []; - $sizeLabelTickWidth = 85; - if ($params['vconsole'] === true) { - $water_mark = false; - if (isset($width) === true) { - $sizeLabelTickWidth = 30; - } + if ((bool) $params['pdf'] === true) { + $options['dataLabel'] = ['display' => 'auto']; + if ($params_combined['stacked'] == CUSTOM_GRAPH_HBARS) { + $options['layout'] = [ + 'padding' => ['right' => 35], + ]; } else { - $options['grid']['hoverable'] = true; + $options['layout'] = [ + 'padding' => ['top' => 35], + ]; } - - $options['generals']['rotate'] = true; - $options['generals']['forceTicks'] = true; - $options['x']['labelWidth'] = ($params['pdf'] === true) ? 30 : $sizeLabelTickWidth; - $options['generals']['arrayColors'] = $color; - $options['grid']['backgroundColor'] = $background_color; - $options['y']['color'] = $background_color; - $options['x']['color'] = $background_color; - $options['pdf'] = $params['pdf']; - - $output = vbar_graph($graph_values, $options, $ttl); } + + $output = '
'; + $output .= '
'; + $output .= vbar_graph($graph_values, $options); + $output .= '
'; + $output .= '
'; break; case CUSTOM_GRAPH_PIE: @@ -2361,18 +2313,15 @@ function graphic_combined_module( 'SELECT datos FROM tagente_datos WHERE id_agente_modulo = %d - AND utimestamp > %d AND utimestamp < %d ORDER BY utimestamp DESC', $module, - $datelimit, $params['date'] ); $temp_data = db_get_value_sql($query_last_value); - - if ($temp_data) { - if (is_numeric($temp_data)) { + if ($temp_data !== false) { + if (is_numeric($temp_data) === true) { $value = $temp_data; } else { $value = count($value); @@ -2383,8 +2332,8 @@ function graphic_combined_module( $total_modules += $value; - if (!empty($params_combined['labels']) - && isset($params_combined['labels'][$module]) + if (empty($params_combined['labels']) === false + && isset($params_combined['labels'][$module]) === true ) { $label = io_safe_output( $params_combined['labels'][$module] @@ -2399,45 +2348,50 @@ function graphic_combined_module( $label = io_safe_output($alias.': '.$data_module['nombre']); } - $temp[$label] = [ - 'value' => $value, - 'unit' => $data_module['unit'], - ]; + if ((bool) $params['pdf'] === true) { + $value = (empty($value) === false) ? $value : 0; + $label .= ' ('.$value.')'; + } + + $graph_labels[] = io_safe_output($label); + $graph_values[] = round($temp_data, 4); if (is_metaconsole() === true) { metaconsole_restore_db(); } } - $temp['total_modules'] = $total_modules; - - $graph_values = $temp; - - if ($params['vconsole'] === false) { - $width = $width; - $height = 500; - } else { + if ($params['vconsole'] === true) { $water_mark = false; } - $color = color_graph_array(); + $options = [ + 'waterMark' => $water_mark, + 'ttl' => $ttl, + 'pdf' => $params['pdf'], + 'legend' => [ + 'display' => (bool) $params['show_legend'], + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $graph_labels, + ]; - $output = ring_graph( - $graph_values, - $width, - $height, - $others_str, - $homeurl, - $water_mark, - $config['fontpath'], - ($config['font_size'] + 1), - $ttl, - false, - $color, - false, - $background_color, - $params['pdf'] - ); + if ((bool) $params['pdf'] === true) { + $options['dataLabel'] = ['display' => 'auto']; + $options['layout'] = [ + 'padding' => [ + 'top' => 20, + 'bottom' => 20, + ], + ]; + } + + $output = '
'; + $output .= '
'; + $output .= ring_graph($graph_values, $options); + $output .= '
'; + $output .= '
'; break; } @@ -2590,14 +2544,17 @@ function graphic_agentaccess( // Array data. $data_array = []; + $colors = []; if (isset($data) === true && is_array($data) === true) { - foreach ($data as $key => $value) { - $time = (date('H:m', $value['utimestamp'])); + foreach ($data as $value) { + $time = io_safe_output(date('H:m', $value['utimestamp'])); + $labels[] = $time; $data_array[] = [ - 'tick' => $time, - 'data' => (int) $value['data'], - 'color' => '#82b92f', + 'y' => (int) $value['data'], + 'x' => $time, ]; + + $colors[] = '#82b92f'; } } @@ -2608,17 +2565,29 @@ function graphic_agentaccess( $options['agent_view'] = true; } - if ($return === true) { - return vbar_graph($data_array, $options, 1); - } else { - $options['generals']['pdf']['width'] = 350; - $options['generals']['pdf']['height'] = 125; - $imgbase64 = ''; + $options = [ + 'width' => 350, + 'height' => 125, + 'colors' => $colors, + 'legend' => ['display' => false], + 'scales' => [ + 'x' => [ + 'grid' => ['display' => false], + 'ticks' => [ + 'fonts' => ['size' => 8], + ], + ], + 'y' => [ + 'grid' => ['display' => false], + 'ticks' => [ + 'fonts' => ['size' => 8], + ], + ], + ], + 'labels' => $labels, + ]; - return $imgbase64; - } + return vbar_graph($data_array, $options); } @@ -2635,38 +2604,33 @@ function graph_alert_status($defined_alerts, $fired_alerts, $width=300, $height= { global $config; + $labels = [ + __('Not fired alerts'), + __('Fired alerts'), + ]; $data = [ - __('Not fired alerts') => ($defined_alerts - $fired_alerts), - __('Fired alerts') => $fired_alerts, + ($defined_alerts - $fired_alerts), + $fired_alerts, ]; $colors = [ COL_NORMAL, COL_ALERTFIRED, ]; - 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), - ]; - } + $options = [ + 'width' => $width, + 'height' => $height, + 'colors' => $colors, + 'legend' => ['display' => false], + 'labels' => $labels, + ]; $out = pie_graph( $data, - $width, - $height, - __('other'), - '', - '', - $config['fontpath'], - $config['font_size'], - 1, - 'hidden', - $colors, - false + $options ); - if ($return) { + if ($return === true) { return $out; } else { echo $out; @@ -2776,30 +2740,23 @@ function graph_agent_status( $data = []; } + $options = [ + 'width' => $width, + 'height' => $height, + 'colors' => array_values($colors), + 'legend' => ['display' => false], + ]; + if ($donut_narrow_graph == true) { - $data_total = array_sum($data); - $out = print_donut_narrow_graph( - $colors, - $width, - $height, + $out = ring_graph( $data, - $data_total + $options ); return $out; } else { $out = pie_graph( $data, - $width, - $height, - __('other'), - ui_get_full_url(false, false, false, false), - '', - $config['fontpath'], - $config['font_size'], - 1, - 'hidden', - $colors, - 0 + $options ); if ($return) { @@ -2811,95 +2768,6 @@ function graph_agent_status( } -/** - * Print a pie graph with events data of agent - * - * @param integer width pie graph width - * @param integer height pie graph height - * @param integer id_agent Agent ID - */ -function graph_event_module($width=300, $height=200, $id_agent=null) -{ - global $config; - global $graphic_type; - - // Fix: tag filters implemented! for tag functionality groups have to be all user_groups (propagate ACL funct!) - $groups = users_get_groups($config['id_user']); - - $tags_condition = tags_get_acl_tags($config['id_user'], array_keys($groups), 'ER', 'event_condition', 'AND'); - - $data = []; - $max_items = 6; - switch ($config['dbtype']) { - case 'mysql': - case 'postgresql': - $sql = sprintf( - 'SELECT COUNT(id_evento) AS count_number, - id_agentmodule - FROM tevento - WHERE tevento.id_agente = %d %s - GROUP BY id_agentmodule ORDER BY count_number DESC LIMIT %d', - $id_agent, - $tags_condition, - $max_items - ); - break; - - case 'oracle': - $sql = sprintf( - 'SELECT COUNT(id_evento) AS count_number, - id_agentmodule - FROM tevento - WHERE tevento.id_agente = %d AND rownum <= %d - GROUP BY id_agentmodule ORDER BY count_number DESC', - $id_agent, - $max_items - ); - break; - } - - $events = db_get_all_rows_sql($sql); - if ($events === false) { - if (! $graphic_type) { - return fs_error_image(); - } - - graphic_error(); - return; - } - - foreach ($events as $event) { - if ($event['id_agentmodule'] == 0) { - $key = __('System').' ('.$event['count_number'].')'; - } else { - $key = modules_get_agentmodule_name($event['id_agentmodule']).' ('.$event['count_number'].')'; - } - - $data[$key] = $event['count_number']; - } - - 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), - ]; - } - - return pie_graph( - $data, - $width, - $height, - __('other'), - '', - $water_mark, - $config['fontpath'], - $config['font_size'], - 1, - 'bottom' - ); -} - - function progress_bar($progress, $width, $height, $title='', $mode=1, $value_text=false, $color=false, $options=false) { global $config; @@ -3049,10 +2917,11 @@ function grafico_incidente_prioridad() $integria_priorities_map_indexed_by_id = array_combine($integria_priorities_map_ids, $integria_priorities_map_names); $data = []; - + $labels = []; foreach ($integria_ticket_count_by_priority as $item) { $priority_name = $integria_priorities_map_indexed_by_id[$item['prioridad']]; - $data[__($priority_name)] = $item['n_incidents']; + $labels[] = io_safe_output($priority_name); + $data[] = $item['n_incidents']; } if ($config['fixed_graph'] == false) { @@ -3062,16 +2931,26 @@ function grafico_incidente_prioridad() ]; } - return pie_graph( + $options = [ + 'width' => 320, + 'height' => 200, + 'waterMark' => $water_mark, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + + $output = '
'; + $output .= pie_graph( $data, - 320, - 200, - __('Other'), - '', - '', - $config['fontpath'], - $config['font_size'] + $options ); + $output .= '
'; + + return $output; } @@ -3094,10 +2973,11 @@ function graph_incidents_status() $integria_status_map_indexed_by_id = array_combine($integria_status_map_ids, $integria_status_map_names); $data = []; - + $labels = []; foreach ($integria_ticket_count_by_status as $item) { $status_name = $integria_status_map_indexed_by_id[$item['estado']]; - $data[__($status_name)] = $item['n_incidents']; + $labels[] = io_safe_output($status_name); + $data[] = $item['n_incidents']; } if ($config['fixed_graph'] == false) { @@ -3107,16 +2987,26 @@ function graph_incidents_status() ]; } - return pie_graph( + $options = [ + 'width' => 320, + 'height' => 200, + 'waterMark' => $water_mark, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + + $output = '
'; + $output .= pie_graph( $data, - 320, - 200, - __('Other'), - '', - '', - $config['fontpath'], - $config['font_size'] + $options ); + $output .= '
'; + + return $output; } @@ -3135,10 +3025,11 @@ function graphic_incident_group() $integria_group_map = json_decode($integria_group_map_json, true); $data = []; - + $labels = []; foreach ($integria_ticket_count_by_group as $item) { $group_name = $integria_group_map[$item['id_grupo']]; - $data[__($group_name)] = $item['n_incidents']; + $labels[] = io_safe_output($group_name); + $data[] = $item['n_incidents']; } if ($config['fixed_graph'] == false) { @@ -3148,16 +3039,26 @@ function graphic_incident_group() ]; } - return pie_graph( + $options = [ + 'width' => 320, + 'height' => 200, + 'waterMark' => $water_mark, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + + $output = '
'; + $output .= pie_graph( $data, - 320, - 200, - __('Other'), - '', - '', - $config['fontpath'], - $config['font_size'] + $options ); + $output .= '
'; + + return $output; } @@ -3178,9 +3079,10 @@ function graphic_incident_user() $integria_ticket_count_by_user = json_decode($integria_ticket_count_by_user_json, true); $data = []; - + $labels = []; foreach ($integria_ticket_count_by_user as $item) { - $data[__($item['id_usuario'])] = $item['n_incidents']; + $labels[] = (empty($item['id_usuario']) === false) ? io_safe_output($item['id_usuario']) : '--'; + $data[] = $item['n_incidents']; } if ($config['fixed_graph'] == false) { @@ -3190,93 +3092,26 @@ function graphic_incident_user() ]; } - return pie_graph( + $options = [ + 'width' => 320, + 'height' => 200, + 'waterMark' => $water_mark, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + + $output = '
'; + $output .= pie_graph( $data, - 320, - 200, - __('Other'), - '', - '', - $config['fontpath'], - $config['font_size'] + $options ); -} + $output .= '
'; - -/** - * Print a pie graph with access data of incidents source - * - * @param integer width pie graph width - * @param integer height pie graph height - */ -function graphic_incident_source($width=320, $height=200) -{ - global $config; - global $graphic_type; - - $data = []; - $max_items = 5; - - switch ($config['dbtype']) { - case 'mysql': - $sql = sprintf( - 'SELECT COUNT(id_incidencia) n_incident, origen - FROM tincidencia - GROUP BY `origen` - ORDER BY 1 DESC LIMIT %d', - $max_items - ); - break; - - case 'postgresql': - $sql = sprintf( - 'SELECT COUNT(id_incidencia) n_incident, origen - FROM tincidencia - GROUP BY "origen" - ORDER BY 1 DESC LIMIT %d', - $max_items - ); - break; - - case 'oracle': - $sql = sprintf( - 'SELECT COUNT(id_incidencia) n_incident, origen - FROM tincidencia - WHERE rownum <= %d - GROUP BY origen - ORDER BY 1 DESC', - $max_items - ); - break; - } - - $origins = db_get_all_rows_sql($sql); - - if ($origins == false) { - $origins = []; - } - - foreach ($origins as $origin) { - $data[$origin['origen']] = $origin['n_incident']; - } - - 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), - ]; - } - - return pie_graph( - $data, - $width, - $height, - __('Other'), - '', - '', - $config['fontpath'], - $config['font_size'] - ); + return $output; } @@ -3310,6 +3145,7 @@ function grafico_eventos_grupo($width=300, $height=200, $url='', $noWaterMark=tr // It was urlencoded, so we urldecode it. $url = html_entity_decode(rawurldecode($url), ENT_QUOTES); $data = []; + $labels = []; $loop = 0; define('NUM_PIECES_PIE', 6); @@ -3388,7 +3224,8 @@ function grafico_eventos_grupo($width=300, $height=200, $url='', $noWaterMark=tr } else { $alias = agents_get_alias($row['id_agente']); $name = mb_substr($alias, 0, 25).' #'.$row['id_agente'].' ('.$row['count'].')'; - $data[$name] = $row['count']; + $labels[] = io_safe_output($name); + $data[] = $row['count']; } } @@ -3397,7 +3234,8 @@ function grafico_eventos_grupo($width=300, $height=200, $url='', $noWaterMark=tr if ($system_events > 0) { $name = __('SYSTEM').' ('.$system_events.')'; - $data[$name] = $system_events; + $labels[] = io_safe_output($name); + $data[] = $system_events; } // Sort the data. @@ -3411,17 +3249,21 @@ function grafico_eventos_grupo($width=300, $height=200, $url='', $noWaterMark=tr $water_mark = []; } + $options = [ + 'width' => $width, + 'height' => $height, + 'waterMark' => $water_mark, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + return pie_graph( $data, - $width, - $height, - __('Other'), - '', - $water_mark, - $config['fontpath'], - $config['font_size'], - 1, - 'bottom' + $options ); } @@ -3438,7 +3280,7 @@ function grafico_eventos_total($filter='', $width=320, $height=200, $noWaterMark $filter = str_replace('\\', '', $filter); - // Add tags condition to filter + // Add tags condition to filter. $tags_condition = tags_get_acl_tags($config['id_user'], 0, 'ER', 'event_condition', 'AND'); $filter .= $tags_condition; if ($time_limit && $config['event_view_hr']) { @@ -3446,8 +3288,7 @@ function grafico_eventos_total($filter='', $width=320, $height=200, $noWaterMark } $data = []; - $legend = []; - $total = 0; + $labels = []; $where = 'WHERE 1=1'; if (!users_is_admin()) { @@ -3466,7 +3307,7 @@ function grafico_eventos_total($filter='', $width=320, $height=200, $noWaterMark ); $criticities = db_get_all_rows_sql($sql, false, false); - if (empty($criticities)) { + if (empty($criticities) === true) { $criticities = []; $colors = []; } @@ -3474,127 +3315,77 @@ function grafico_eventos_total($filter='', $width=320, $height=200, $noWaterMark foreach ($criticities as $cr) { switch ($cr['criticity']) { case EVENT_CRIT_MAINTENANCE: - $data[__('Maintenance')] = $cr['events']; + $labels[] = __('Maintenance').' ('.$cr['events'].')'; + $data[] = $cr['events']; $colors[__('Maintenance')] = COL_MAINTENANCE; break; case EVENT_CRIT_INFORMATIONAL: - $data[__('Informational')] = $cr['events']; + $labels[] = __('Informational').' ('.$cr['events'].')'; + $data[] = $cr['events']; $colors[__('Informational')] = COL_INFORMATIONAL; break; case EVENT_CRIT_NORMAL: - $data[__('Normal')] = $cr['events']; + $labels[] = __('Normal').' ('.$cr['events'].')'; + $data[] = $cr['events']; $colors[__('Normal')] = COL_NORMAL; break; case EVENT_CRIT_MINOR: - $data[__('Minor')] = $cr['events']; + $labels[] = __('Minor').' ('.$cr['events'].')'; + $data[] = $cr['events']; $colors[__('Minor')] = COL_MINOR; break; case EVENT_CRIT_WARNING: - $data[__('Warning')] = $cr['events']; + $labels[] = __('Warning').' ('.$cr['events'].')'; + $data[] = $cr['events']; $colors[__('Warning')] = COL_WARNING; break; case EVENT_CRIT_MAJOR: - $data[__('Major')] = $cr['events']; + $labels[] = __('Major').' ('.$cr['events'].')'; + $data[] = $cr['events']; $colors[__('Major')] = COL_MAJOR; break; case EVENT_CRIT_CRITICAL: - $data[__('Critical')] = $cr['events']; + $labels[] = __('Critical').' ('.$cr['events'].')'; + $data[] = $cr['events']; $colors[__('Critical')] = COL_CRITICAL; break; + + default: + // Not possible. + break; } } - if ($noWaterMark) { + $water_mark = []; + if ($noWaterMark === true) { $water_mark = [ 'file' => $config['homedir'].'/images/logo_vertical_water.png', 'url' => ui_get_full_url('/images/logo_vertical_water.png', false, false, false), ]; - } else { - $water_mark = []; } - return pie_graph( - $data, - $width, - $height, - __('Other'), - '', - $water_mark, - $config['fontpath'], - $config['font_size'], - 1, - 'bottom', - $colors - ); -} - - -/** - * Print a pie graph with events data of users - * - * @param integer height pie graph height - * @param integer period time period - */ -function grafico_eventos_usuario($width, $height) -{ - global $config; - global $graphic_type; - - $data = []; - $max_items = 5; - - $where = ''; - if (!users_is_admin()) { - $where = 'WHERE event_type NOT IN (\'recon_host_detected\', \'system\',\'error\', \'new_agent\', \'configuration_change\')'; - } - - $sql = sprintf( - 'SELECT COUNT(id_evento) events, id_usuario - FROM tevento %s - GROUP BY id_usuario - ORDER BY 1 DESC LIMIT %d', - $where, - $max_items - ); - - $events = db_get_all_rows_sql($sql); - - if ($events === false) { - $events = []; - } - - foreach ($events as $event) { - if ($event['id_usuario'] == '0') { - $data[__('System')] = $event['events']; - } else if ($event['id_usuario'] == '') { - $data[__('System')] = $event['events']; - } else { - $data[$event['id_usuario']] = $event['events']; - } - } - - $water_mark = [ - 'file' => $config['homedir'].'/images/logo_vertical_water.png', - 'url' => ui_get_full_url('/images/logo_vertical_water.png', false, false, false), + $options = [ + 'width' => $width, + 'height' => $height, + 'waterMark' => $water_mark, + 'colors' => array_values($colors), + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, ]; return pie_graph( $data, - $width, - $height, - __('Other'), - '', - $water_mark, - $config['fontpath'], - $config['font_size'], - 1, - 'bottom' + $options ); } @@ -3699,9 +3490,9 @@ function graph_custom_sql_graph( } $data = []; - + $labels = []; $count = 0; - $flagOther = false; + $other = 0; foreach ($data_result as $data_item) { $count++; $value = 0; @@ -3720,72 +3511,57 @@ function graph_custom_sql_graph( 0, floor($SQL_GRAPH_MAX_LABEL_SIZE / 2) ); - $label .= '...
'; - $label .= substr( - $first_label, - floor(-$SQL_GRAPH_MAX_LABEL_SIZE / 2) - ); } } - switch ($type) { - case 'sql_graph_vbar': - default: - // Vertical bar. - $data[] = [ - 'tick' => $label.'_'.$count, - 'data' => $value, - ]; - break; - - case 'sql_graph_hbar': - // Horizontal bar. - $data[$label.'_'.$count]['g'] = $value; - break; - - case 'sql_graph_pie': - // Pie. - $data[$label.'_'.$count] = $value; - break; + $labels_bar[] = $label; + if ($type === 'sql_graph_hbar') { + $data_bar[] = [ + 'y' => $label, + 'x' => $value, + ]; + } else { + $data_bar[] = [ + 'x' => $label, + 'y' => $value, + ]; } + + if ((int) $ttl === 2 && $type === 'sql_graph_pie') { + $labels_pie[] = $label.'_'.$count.' ('.$value.')'; + } else { + $labels_pie[] = $label.'_'.$count; + } + + $data_pie[] = $value; } else { - switch ($type) { - case 'sql_graph_vbar': - default: - // Vertical bar. - if ($flagOther === false) { - $data[] = [ - 'tick' => __('Other'), - 'data' => $value, - ]; - - $flagOther = true; - } - - $data[(count($data) - 1)]['data'] += $value; - break; - - case 'sql_graph_hbar': - // Horizontal bar. - if (isset($data[__('Other')]['g']) === false) { - $data[__('Other')]['g'] = 0; - } - - $data[__('Other')]['g'] += $value; - break; - - case 'sql_graph_pie': - // Pie. - if (isset($data[__('Other')]) === false) { - $data[__('Other')] = 0; - } - - $data[__('Other')] += $value; - break; - } + $other += $value; } } + if (empty($other) === false) { + $label = __('Other'); + $labels_bar[] = $label; + if ($type === 'sql_graph_hbar') { + $data_bar[] = [ + 'y' => $label, + 'x' => $other, + ]; + } else { + $data_bar[] = [ + 'x' => $label, + 'y' => $other, + ]; + } + + if ((int) $ttl === 2 && $type === 'sql_graph_pie') { + $label .= ' ('.$other.')'; + } + + $labels_pie[] = $label; + $data_pie[] = $other; + } + if ($config['fixed_graph'] == false) { $water_mark = [ 'file' => $config['homedir'].'/images/logo_vertical_water.png', @@ -3799,76 +3575,82 @@ function graph_custom_sql_graph( } $output = ''; + $output .= '
'; + if ((int) $ttl === 2) { + $output .= ''; - $output .= vbar_graph($data, $options, $ttl); - $output .= '
'; - } - break; - case 'sql_graph_hbar': - // Horizontal bar. - $output .= hbar_graph( - $data, - $width, - $height, - [], - [], - '', - '', - '', - '', - $water_mark, - $config['fontpath'], - $config['font_size'], - false, - $ttl, - $homeurl, - 'white', - '#c1c1c1' + default: + $options = [ + 'height' => $height, + 'waterMark' => $water_mark, + 'ttl' => $ttl, + 'legend' => ['display' => false], + 'scales' => [ + 'x' => [ + 'grid' => ['display' => false], + ], + 'y' => [ + 'grid' => ['display' => false], + ], + ], + 'labels' => $labels_bar, + ]; + + if ($type === 'sql_graph_hbar') { + $options['axis'] = 'y'; + } + + if ((int) $ttl === 2) { + $options['dataLabel'] = ['display' => 'auto']; + } + + $output .= vbar_graph( + $data_bar, + $options ); break; case 'sql_graph_pie': + $options = [ + 'height' => $height, + 'waterMark' => $water_mark, + 'ttl' => $ttl, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels_pie, + ]; + + if ((int) $ttl === 2) { + $options['dataLabel'] = ['display' => 'auto']; + $options['layout'] = [ + 'padding' => [ + 'top' => 20, + 'bottom' => 20, + ], + ]; + } + // Pie. $output .= pie_graph( - $data, - $width, - $height, - __('other'), - $homeurl, - $water_mark, - $config['fontpath'], - $config['font_size'], - $ttl + $data_pie, + $options ); break; } + if ((int) $ttl === 2) { + $output .= '" />'; + } + + $output .= '
'; + return $output; } @@ -4129,8 +3911,7 @@ function graph_graphic_moduleevents( */ function fs_error_image($width=300, $height=110) { - global $config; - return graph_nodata_image($width, $height, 'area'); + return graph_nodata_image(['height' => $height]); } @@ -4768,6 +4549,9 @@ function graph_netflow_aggregate_pie($data, $aggregate, $ttl=1, $only_image=fals $i++; } + $labels = array_keys($values); + $values = array_values($values); + if ($config['fixed_graph'] == false) { $water_mark = [ 'file' => $config['homedir'].'/images/logo_vertical_water.png', @@ -4775,21 +4559,38 @@ function graph_netflow_aggregate_pie($data, $aggregate, $ttl=1, $only_image=fals ]; } - return pie_graph( + $options = [ + 'height' => 230, + 'waterMark' => $water_mark, + 'ttl' => $ttl, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + + $output = ''; + if ((int) $ttl === 2) { + $output .= ''; + } + + // Pie. + $output .= pie_graph( $values, - 370, - 200, - __('Other'), - $config['homeurl'], - $water_mark, - $config['fontpath'], - $config['font_size'], - $ttl, - false, - '', - false, - 6 + $options ); + + if ((int) $ttl === 2) { + $output .= '" />'; + } else { + $output .= '
'; + } + + return $output; } @@ -5003,52 +4804,39 @@ function graphic_module_events($id_module, $width, $height, $period=0, $homeurl= } -function graph_nodata_image( - $width=300, - $height=110, - $type='area', - $text='', - $percent=false, - $base64=false -) { +function graph_nodata_image($options) +{ global $config; - if ($base64 === true) { + + $height = 200; + if (isset($options['height']) === true + && empty($options['height']) === false + ) { + $height = $options['height']; + } + + $width_style = ''; + if (isset($options['width']) === true + && empty($options['width']) === false + ) { + $width_style = 'width:'.$options['width'].'px'; + } + + if ($options['base64'] === true) { $dataImg = file_get_contents( $config['homedir'].'/images/image_problem_area_150.png' ); return base64_encode($dataImg); } - $image = ui_get_full_url( + return html_print_image( 'images/image_problem_area.png', - false, - false, - false + true, + [ + 'title' => __('No data'), + 'style' => 'height:'.$height.'px;'.$width_style, + ] ); - - $style = 'text-align:center; padding: 30px 0; display:block; font-size:9.5pt;'; - $text_div = '
'; - $text_div .= $text; - $text_div .= '
'; - - $style = 'background-size: contain;background-image: url(\''.$image.'\');'; - $image_div = '
'; - - if ($percent === true) { - $div = $image_div; - } else { - if (strpos($width, '%') === false) { - $width = 'width: '.$width.'px;'; - } else { - $width = 'width: '.$width.';'; - } - - $style = $width.' height:'.$height.'px;'; - $style .= 'margin: 0 auto;'; - $div = '
'.$image_div.'
'; - } - - return $div; } diff --git a/pandora_console/include/functions_modules.php b/pandora_console/include/functions_modules.php index 59ead9b943..e414c64e39 100755 --- a/pandora_console/include/functions_modules.php +++ b/pandora_console/include/functions_modules.php @@ -3982,6 +3982,81 @@ function recursive_get_dt_from_modules_tree(&$f_modules, $modules, $deep) } +/** + * Get the module data from a children + * + * @param integer $id_module Id module + * @return array Children module data + */ +function get_children_module($id_module) +{ + $children_module_data = db_get_all_rows_sql( + 'SELECT * + FROM tagente_modulo + WHERE parent_module_id = '.$id_module + ); + + return $children_module_data; +} + + +/** + * Find and delete the childers modules from the $id_module + * + * @param mixed $id_module + * @return void + */ +function module_check_childrens_and_delete($id_module) +{ + $children_data = get_children_module($id_module); + // Check if exist have a childer + if ($children_data) { + // If have more than 1 children + if (is_array($children_data)) { + foreach ($children_data as $children_module_data) { + if ($children_module_data['parent_module_id']) { + // Search children and delete this module + // Before delete, lets check if exist (Just for cases it's already deleted) + if (modules_check_agentmodule_exists($children_module_data['parent_module_id'])) { + modules_delete_agent_module($children_module_data['parent_module_id']); + } + + module_check_childrens_and_delete($children_module_data['id_agente_modulo']); + } else { + // If haven't children just delete + // Before delete, lets check if exist (Just for cases it's already deleted) + if (modules_check_agentmodule_exists($children_module_data['id_agente_modulo'])) { + modules_delete_agent_module($children_module_data['id_agente_modulo']); + } + } + } + } else { + // If just have 1 children + if ($children_data['parent_module_id']) { + // Before delete, lets check if exist (Just for cases it's already deleted) + if (modules_check_agentmodule_exists($children_data['parent_module_id'])) { + modules_delete_agent_module($children_data['parent_module_id']); + } + + module_check_childrens_and_delete($children_data['id_agente_modulo']); + } else { + // If haven't children just delete + // Before delete, lets check if exist (Just for cases it's already deleted) + if (modules_check_agentmodule_exists($children_data['id_agente_modulo'])) { + modules_delete_agent_module($children_data['id_agente_modulo']); + } + } + } + } else { + // Haven't childrens, so delete + // Before delete, lets check if exist (Just for cases it's already deleted) + if (modules_check_agentmodule_exists($id_module)) { + modules_delete_agent_module($id_module); + } + } +} + + /** * @brief Get the button with the link to open realtime stats into a new window * Only to native (not satellite discovered) snmp modules. @@ -4336,3 +4411,129 @@ function modules_get_regex( return $result; } + + +/** + * Status for data thresholds modules. + * + * @param integer $id_module Module ID. + * @param mixed $data Data int, bool, null, etc. + * @param array $thresholds Array thresholds. + * + * @return array + */ +function get_status_data_modules(int $id_module, $data, $thresholds) +{ + // Check not init. + if ($data === false) { + return ['color' => COL_NOTINIT]; + } + + // Check boolean. + $is_bolean = modules_is_boolean($id_module); + if ($is_bolean === true) { + if ($data > 0) { + return ['color' => COL_CRITICAL]; + } else { + return ['color' => COL_NORMAL]; + } + } + + $thresholds = calculateThreshold($thresholds); + + foreach (getStatuses() as $status) { + if ($thresholds[$status]['min'] === null + && $thresholds[$status]['max'] === null + ) { + continue; + } + + if (($thresholds[$status]['min'] === null + && $thresholds[$status]['max'] >= $data) + || ($thresholds[$status]['max'] === null + && $thresholds[$status]['min'] <= $data) + || ($thresholds[$status]['min'] <= $data + && $thresholds[$status]['max'] >= $data) + ) { + if ($status === 'critical') { + return ['color' => COL_CRITICAL]; + } else if ($status === 'warning') { + return ['color' => COL_WARNING]; + } else { + return ['color' => COL_NORMAL]; + } + } + } + + return ['color' => COL_NORMAL]; +} + + +/** + * Calculate thresholds. + * + * @param array $thresholds_array + * + * @return array + */ +function calculateThreshold(array $thresholds_array) +{ + $nMax = null; + if ($thresholds_array['min_warning'] !== null) { + $nMax = $thresholds_array['min_warning']; + } else if ($thresholds_array['min_critical'] !== null) { + $nMax = $thresholds_array['min_critical']; + } + + $wMin = null; + if ($thresholds_array['min_warning'] !== null) { + $wMin = $thresholds_array['min_warning']; + } + + $wMax = null; + if ($thresholds_array['max_warning'] !== null) { + $wMax = $thresholds_array['max_warning']; + } + + $cMin = null; + if ($thresholds_array['min_critical'] !== null) { + $cMin = $thresholds_array['min_critical']; + } + + $cMax = null; + if ($thresholds_array['max_critical'] !== null) { + $cMax = $thresholds_array['max_critical']; + } + + $thresholds = [ + 'normal' => [ + 'min' => null, + 'max' => $nMax, + ], + 'warning' => [ + 'min' => $wMin, + 'max' => $wMax, + ], + 'critical' => [ + 'min' => $cMin, + 'max' => $cMax, + ], + ]; + + return $thresholds; +} + + +/** + * Get status. + * + * @return array + */ +function getStatuses() +{ + return [ + 'critical', + 'warning', + 'normal', + ]; +} diff --git a/pandora_console/include/functions_netflow.php b/pandora_console/include/functions_netflow.php index 6a0c68d3d7..e4ac71e690 100644 --- a/pandora_console/include/functions_netflow.php +++ b/pandora_console/include/functions_netflow.php @@ -1479,7 +1479,7 @@ function netflow_draw_item( } if ($output === 'HTML' || $output === 'PDF') { - return graph_nodata_image(300, 110, 'data'); + return graph_nodata_image(['height' => 110]); } } diff --git a/pandora_console/include/functions_networkmap.php b/pandora_console/include/functions_networkmap.php index 54005a571a..f5dd4cfe96 100644 --- a/pandora_console/include/functions_networkmap.php +++ b/pandora_console/include/functions_networkmap.php @@ -3399,7 +3399,10 @@ function get_status_color_networkmap_fictional_point($id_networkmap, $parent='') if ($agent['source_data'] == -2) { if (empty($parent) === true) { - $option = json_decode($agent, true); + if (is_array($agent) === false) { + $option = json_decode($agent, true); + } + if ($option['networkmap'] == 0) { $status = 0; } else { diff --git a/pandora_console/include/functions_notifications.php b/pandora_console/include/functions_notifications.php index a0cb16faa0..705d67ab3b 100644 --- a/pandora_console/include/functions_notifications.php +++ b/pandora_console/include/functions_notifications.php @@ -134,7 +134,7 @@ function notifications_get_subtypes(?string $source=null) 'NOTIF.PHP.UPLOAD_MAX_FILESIZE', 'NOTIF.PHP.MEMORY_LIMIT', 'NOTIF.PHP.DISABLE_FUNCTIONS', - 'NOTIF.PHP.PHANTOMJS', + 'NOTIF.PHP.CHROMIUM', 'NOTIF.PHP.VERSION', 'NOTIF.HISTORYDB', 'NOTIF.PANDORADB', diff --git a/pandora_console/include/functions_reporting.php b/pandora_console/include/functions_reporting.php index 9cd403d69c..81c9c2df1b 100755 --- a/pandora_console/include/functions_reporting.php +++ b/pandora_console/include/functions_reporting.php @@ -844,7 +844,8 @@ function reporting_make_reporting_data( $content, $type, $force_width_chart, - $force_height_chart + $force_height_chart, + $pdf ); if ($report_control['total_events'] == 0 && $content['hide_no_data'] == 1) { break; @@ -1780,7 +1781,9 @@ function reporting_event_top_n( if ($order_uptodown == 1 || $order_uptodown == 2) { $i = 0; - $data_pie_graph = []; + $labels_pie = []; + $labels_hbar = []; + $data_pie = []; $data_hbar = []; foreach ($data_top as $key_dt => $dt) { $item_name = ui_print_truncate_text( @@ -1801,28 +1804,14 @@ function reporting_event_top_n( '...' ); - $item_name_key_pie = $item_name; - $exist_key = true; - while ($exist_key) { - if (isset($data_pie_graph[$item_name_key_pie])) { - $item_name_key_pie .= ' '; - } else { - $exist_key = false; - } - } + $labels_hbar[] = io_safe_output($item_name); + $data_hbar[] = [ + 'y' => io_safe_output($item_name), + 'x' => $dt, + ]; - $item_name_key_hbar = $item_name; - $exist_key = true; - while ($exist_key) { - if (isset($data_hbar[$item_name_key_hbar])) { - $item_name_key_hbar = ' '.$item_name_key_hbar; - } else { - $exist_key = false; - } - } - - $data_hbar[$item_name]['g'] = $dt; - $data_pie_graph[$item_name] = $dt; + $labels_pie[] = io_safe_output($item_name); + $data_pie[] = $dt; if ($show_graph == 0 || $show_graph == 1) { $data = []; @@ -1861,7 +1850,9 @@ function reporting_event_top_n( } } else if ($order_uptodown == 0 || $order_uptodown == 3) { $i = 0; - $data_pie_graph = []; + $labels_pie = []; + $labels_hbar = []; + $data_pie = []; $data_hbar = []; foreach ($agent_name as $key_an => $an) { $item_name = ''; @@ -1881,16 +1872,6 @@ function reporting_event_top_n( '...' ); - $item_name_key_pie = $item_name; - $exist_key = true; - while ($exist_key) { - if (isset($data_pie_graph[$item_name_key_pie])) { - $item_name_key_pie .= ' '; - } else { - $exist_key = false; - } - } - $item_name_key_hbar = $item_name; $exist_key = true; while ($exist_key) { @@ -1901,8 +1882,18 @@ function reporting_event_top_n( } } - $data_pie_graph[$item_name] = $data_top[$key_an]; - $data_hbar[$item_name]['g'] = $data_top[$key_an]; + $labels_hbar[] = io_safe_output($item_name); + $data_hbar[] = [ + 'y' => io_safe_output($item_name), + 'x' => $data_top[$key_an], + ]; + if ((int) $ttl === 2) { + $data_top[$key_an] = (empty($data_top[$key_an]) === false) ? $data_top[$key_an] : 0; + $item_name .= ' ('.$data_top[$key_an].')'; + } + + $labels_pie[] = io_safe_output($item_name); + $data_pie[] = $data_top[$key_an]; $divisor = get_data_multiplier($units[$key_an]); @@ -1944,39 +1935,80 @@ function reporting_event_top_n( $return['charts']['pie'] = null; if ($show_graph != REPORT_TOP_N_ONLY_TABLE) { - arsort($data_pie_graph); - $return['charts']['pie'] = pie_graph( - $data_pie_graph, - $width, - $height, - __('other'), - ui_get_full_url(false, true, false, false).'/', - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + $options_charts = [ + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'ttl' => $ttl, + 'labels' => $labels_pie, + ]; + + if ((int) $ttl === 2) { + $options_charts['dataLabel'] = ['display' => 'auto']; + $options_charts['layout'] = [ + 'padding' => [ + 'top' => 15, + 'bottom' => 15, + ], + ]; + } + + $return['charts']['pie'] = '
'; + if ((int) $ttl === 2) { + $return['charts']['pie'] .= ''; + } + + $return['charts']['pie'] .= '
'; + + $options = [ + 'height' => (count($data_hbar) * 30), + 'ttl' => $ttl, + 'axis' => 'y', + 'legend' => ['display' => false], + 'scales' => [ + 'x' => [ + 'grid' => ['display' => false], + ], + 'y' => [ + 'grid' => ['display' => false], + ], + ], + 'labels' => $labels_hbar, + ]; + + if ((int) $ttl === 2) { + $options['dataLabel'] = ['display' => 'auto']; + $options['layout'] = [ + 'padding' => ['right' => 35], + ]; + } + + $return['charts']['bars'] = '
'; + if ((int) $ttl === 2) { + $return['charts']['bars'] .= ''; + } + + $return['charts']['bars'] .= '
'; } $return['resume'] = null; @@ -2029,7 +2061,8 @@ function reporting_event_report_group( $content, $type='dinamic', $force_width_chart=null, - $force_height_chart=null + $force_height_chart=null, + $pdf=false ) { global $config; @@ -2175,6 +2208,28 @@ function reporting_event_report_group( $return['chart']['by_criticity'] = null; $return['chart']['validated_vs_unvalidated'] = null; + $options_charts = [ + 'width' => 500, + 'height' => 150, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'pdf' => $pdf, + 'ttl' => $ttl, + ]; + + if ($pdf === true) { + $options_charts['dataLabel'] = ['display' => 'auto']; + $options_charts['layout'] = [ + 'padding' => [ + 'top' => 15, + 'bottom' => 15, + ], + ]; + } + if ($event_graph_by_agent) { $data_graph_by_agent = []; if (empty($data) === false) { @@ -2184,70 +2239,118 @@ function reporting_event_report_group( $k = '('.$value['server_name'].') '.$value['alias']; } + $k = io_safe_output($k); + if (isset($data_graph_by_agent[$k]) === true) { $data_graph_by_agent[$k]++; } else { $data_graph_by_agent[$k] = 1; } } + + if ($pdf === true) { + $result_data_graph_by_agent = []; + foreach ($data_graph_by_agent as $key => $value) { + $result_data_graph_by_agent[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_agent = $result_data_graph_by_agent; + } } - $return['chart']['by_agent'] = pie_graph( - $data_graph_by_agent, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + if ($pdf === true) { + $return['chart']['by_agent'] = ''; + } + + $options_charts['labels'] = array_keys($data_graph_by_agent); + $return['chart']['by_agent'] .= pie_graph( + array_values($data_graph_by_agent), + $options_charts ); + + if ($pdf === true) { + $return['chart']['by_agent'] .= '" />'; + } else { + $return['chart']['by_agent'] .= '
'; + } } if ($event_graph_by_user_validator) { $data_graph_by_user = events_get_count_events_validated_by_user($data); - $return['chart']['by_user_validator'] = pie_graph( - $data_graph_by_user, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + if ($pdf === true) { + $result_data_graph_by_user = []; + foreach ($data_graph_by_user as $key => $value) { + $result_data_graph_by_user[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_user = $result_data_graph_by_user; + } + + if ($pdf === true) { + $return['chart']['by_user_validator'] = ''; + } + + $options_charts['labels'] = array_keys($data_graph_by_user); + $return['chart']['by_user_validator'] .= pie_graph( + array_values($data_graph_by_user), + $options_charts ); + + if ($pdf === true) { + $return['chart']['by_user_validator'] .= '" />'; + } else { + $return['chart']['by_user_validator'] .= '
'; + } } if ($event_graph_by_criticity) { $data_graph_by_criticity = []; if (empty($data) === false) { foreach ($data as $value) { - $k = get_priority_name($value['criticity']); + $k = io_safe_output(get_priority_name($value['criticity'])); if (isset($data_graph_by_criticity[$k]) === true) { $data_graph_by_criticity[$k]++; } else { $data_graph_by_criticity[$k] = 1; } } + + $colors = get_criticity_pie_colors($data_graph_by_criticity); + $options_charts['colors'] = array_values($colors); + + if ($pdf === true) { + $result_data_graph_by_criticity = []; + foreach ($data_graph_by_criticity as $key => $value) { + $result_data_graph_by_criticity[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_criticity = $result_data_graph_by_criticity; + } } - $colors = get_criticity_pie_colors($data_graph_by_criticity); + if ($pdf === true) { + $return['chart']['by_criticity'] = ''; + } - $return['chart']['by_criticity'] = pie_graph( - $data_graph_by_criticity, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl, - false, - $colors + $options_charts['labels'] = array_keys($data_graph_by_criticity); + $return['chart']['by_criticity'] .= pie_graph( + array_values($data_graph_by_criticity), + $options_charts ); + + if ($pdf === true) { + $return['chart']['by_criticity'] .= '" />'; + } else { + $return['chart']['by_criticity'] .= '
'; + } + + unset($options_charts['colors']); } if ($event_graph_validated_vs_unvalidated) { @@ -2265,19 +2368,34 @@ function reporting_event_report_group( $data_graph_by_status[$k] = 1; } } + + if ($pdf === true) { + $result_data_graph_by_status = []; + foreach ($data_graph_by_status as $key => $value) { + $result_data_graph_by_status[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_status = $result_data_graph_by_status; + } } - $return['chart']['validated_vs_unvalidated'] = pie_graph( - $data_graph_by_status, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + if ($pdf === true) { + $return['chart']['validated_vs_unvalidated'] = ''; + } + + $options_charts['labels'] = array_keys($data_graph_by_status); + $return['chart']['validated_vs_unvalidated'] .= pie_graph( + array_values($data_graph_by_status), + $options_charts ); + + if ($pdf === true) { + $return['chart']['validated_vs_unvalidated'] .= '" />'; + } else { + $return['chart']['validated_vs_unvalidated'] .= '
'; + } } // Total events. @@ -3788,26 +3906,6 @@ function reporting_exception( } ); - $data_pie_graph = []; - $data_hbar = []; - foreach ($items as $key => $item) { - if ($show_graph == 1 || $show_graph == 2) { - // TODO: Find a better way to show the graphs - $data_hbar[$item['agent'].' - '.$item['operation']]['g'] = $item['value']; - $data_pie_graph[$item['agent'].' - '.$item['operation']] = $item['value']; - } - - if ($show_graph == 0 || $show_graph == 1) { - $data = []; - $data['agent'] = $item['agent']; - $data['module'] = $item['module']; - $data['operation'] = __($item['operation']); - $data['value'] = $item['value']; - $data['formated_value'] = format_for_graph($item['value'], 2).' '.$item['unit']; - $return['data'][] = $data; - } - } - if ($show_graph == 1 || $show_graph == 2) { reporting_set_conf_charts( $width, @@ -3818,6 +3916,61 @@ function reporting_exception( $ttl ); + $data_pie = []; + $labels_pie = []; + $data_hbar = []; + $labels_hbar = []; + + $other = 0; + foreach ($items as $key => $item) { + if ($show_graph == 1 || $show_graph == 2) { + if ($key <= 10) { + $label = $item['agent'].' - '.$item['operation']; + $labels_hbar[] = io_safe_output($label); + $data_hbar[] = [ + 'x' => $item['value'], + 'y' => io_safe_output($label), + ]; + + if ((int) $ttl === 2) { + $item['value'] = (empty($item['value']) === false) ? $item['value'] : 0; + $label .= ' ('.$item['value'].')'; + } + + $labels_pie[] = io_safe_output($label); + $data_pie[] = $item['value']; + } else { + $other += $item['value']; + } + } + + if ($show_graph == 0 || $show_graph == 1) { + $data = []; + $data['agent'] = $item['agent']; + $data['module'] = $item['module']; + $data['operation'] = __($item['operation']); + $data['value'] = $item['value']; + $data['formated_value'] = format_for_graph($item['value'], 2).' '.$item['unit']; + $return['data'][] = $data; + } + } + + if (empty($other) === false) { + $label = __('Others'); + $labels_hbar[] = $label; + $data_hbar[] = [ + 'x' => $other, + 'y' => $label, + ]; + + if ((int) $ttl === 2) { + $label .= ' ('.$other.')'; + } + + $labels_pie[] = $label; + $data_pie[] = $other; + } + if (!empty($force_width_chart)) { $width = $force_width_chart; } @@ -3826,40 +3979,82 @@ function reporting_exception( $height = $force_height_chart; } - $return['chart']['pie'] = pie_graph( - $data_pie_graph, - 600, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + $options_charts = [ + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'ttl' => $ttl, + 'labels' => $labels_pie, + ]; + + if ((int) $ttl === 2) { + $options_charts['dataLabel'] = ['display' => 'auto']; + $options_charts['layout'] = [ + 'padding' => [ + 'top' => 15, + 'bottom' => 15, + ], + ]; + } + + if ((int) $ttl === 2) { + $return['chart']['pie'] = ''; + } + + $return['chart']['pie'] .= pie_graph( + $data_pie, + $options_charts ); - $params = [ - 'chart_data' => $data_hbar, - 'width' => 600, - 'height' => (25 * count($data_hbar)), - 'color' => [], - 'legend' => [], - 'long_index' => [], - 'no_data_image' => ui_get_full_url('images/image_problem_area_small.png', false, false, false), - 'xaxisname' => '', - 'yaxisname' => '', - 'water_mark' => ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - 'font' => '', - 'font_size' => '', - 'unit' => '', - 'ttl' => $ttl, - 'homeurl' => ui_get_full_url(false, false, false, false), - 'backgroundColor' => 'white', + if ((int) $ttl === 2) { + $return['chart']['pie'] .= '" />'; + } else { + $return['chart']['pie'] .= '
'; + } + + if ((int) $ttl === 2) { + $return['chart']['hbar'] = ''; + } + + $options = [ + 'height' => (count($data_hbar) * 30), + 'ttl' => $ttl, + 'axis' => 'y', + 'legend' => ['display' => false], + 'scales' => [ + 'x' => [ + 'grid' => ['display' => false], + ], + 'y' => [ + 'grid' => ['display' => false], + ], + ], + 'labels' => $labels_hbar, ]; - $return['chart']['hbar'] = call_user_func_array( - 'hbar_graph', - array_values(($params ?? [])) + + if ((int) $ttl === 2) { + $options['dataLabel'] = ['display' => 'auto']; + $options['layout'] = [ + 'padding' => ['right' => 35], + ]; + } + + $return['chart']['hbar'] .= vbar_graph( + $data_hbar, + $options ); + + if ((int) $ttl === 2) { + $return['chart']['hbar'] .= '" />'; + } else { + $return['chart']['hbar'] .= ''; + } } if ($content['show_resume'] && $i > 0) { @@ -4192,49 +4387,102 @@ function reporting_event_report_agent( $return['chart']['by_criticity'] = null; $return['chart']['validated_vs_unvalidated'] = null; + $options_charts = [ + 'width' => 500, + 'height' => 150, + 'radius' => null, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'ttl' => $ttl, + ]; + + if ((int) $ttl === 2) { + $options_charts['dataLabel'] = ['display' => 'auto']; + $options_charts['layout'] = [ + 'padding' => [ + 'top' => 15, + 'bottom' => 15, + ], + ]; + } + if ($event_graph_by_user_validator) { $data_graph_by_user = events_get_count_events_validated_by_user($return['data']); - $return['chart']['by_user_validator'] = pie_graph( - $data_graph_by_user, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + if ((int) $ttl === 2) { + $result_data_graph_by_user = []; + foreach ($data_graph_by_user as $key => $value) { + $result_data_graph_by_user[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_user = $result_data_graph_by_user; + } + + if ((int) $ttl === 2) { + $return['chart']['by_user_validator'] = ''; + } + + $options_charts['labels'] = array_keys($data_graph_by_user); + $return['chart']['by_user_validator'] .= pie_graph( + array_values($data_graph_by_user), + $options_charts ); + + if ((int) $ttl === 2) { + $return['chart']['by_user_validator'] .= '" />'; + } else { + $return['chart']['by_user_validator'] .= ''; + } } if ($event_graph_by_criticity) { $data_graph_by_criticity = []; if (empty($return['data']) === false) { foreach ($return['data'] as $value) { - $k = get_priority_name($value['criticity']); + $k = io_safe_output(get_priority_name($value['criticity'])); if (isset($data_graph_by_criticity[$k]) === true) { $data_graph_by_criticity[$k]++; } else { $data_graph_by_criticity[$k] = 1; } } + + $colors = get_criticity_pie_colors($data_graph_by_criticity); + $options_charts['colors'] = array_values($colors); + + if ((int) $ttl === 2) { + $result_data_graph_by_criticity = []; + foreach ($data_graph_by_criticity as $key => $value) { + $result_data_graph_by_criticity[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_criticity = $result_data_graph_by_criticity; + } } - $colors = get_criticity_pie_colors($data_graph_by_criticity); + if ((int) $ttl === 2) { + $return['chart']['by_criticity'] = ''; + } - $return['chart']['by_criticity'] = pie_graph( - $data_graph_by_criticity, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl, - false, - $colors + $options_charts['labels'] = array_keys($data_graph_by_criticity); + $return['chart']['by_criticity'] .= pie_graph( + array_values($data_graph_by_criticity), + $options_charts ); + + if ((int) $ttl === 2) { + $return['chart']['by_criticity'] .= '" />'; + } else { + $return['chart']['by_criticity'] .= ''; + } + + unset($options_charts['colors']); } if ($event_graph_validated_vs_unvalidated) { @@ -4245,26 +4493,41 @@ function reporting_event_report_agent( 0 => __('Not validated'), ]; foreach ($return['data'] as $value) { - $k = $status[$value['estado']]; + $k = $status[$value['status']]; if (isset($data_graph_by_status[$k]) === true) { $data_graph_by_status[$k]++; } else { $data_graph_by_status[$k] = 1; } } + + if ((int) $ttl === 2) { + $result_data_graph_by_status = []; + foreach ($data_graph_by_status as $key => $value) { + $result_data_graph_by_status[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_status = $result_data_graph_by_status; + } } - $return['chart']['validated_vs_unvalidated'] = pie_graph( - $data_graph_by_status, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + if ((int) $ttl === 2) { + $return['chart']['validated_vs_unvalidated'] = ''; + } + + $options_charts['labels'] = array_keys($data_graph_by_status); + $return['chart']['validated_vs_unvalidated'] .= pie_graph( + array_values($data_graph_by_status), + $options_charts ); + + if ((int) $ttl === 2) { + $return['chart']['validated_vs_unvalidated'] .= '" />'; + } else { + $return['chart']['validated_vs_unvalidated'] .= ''; + } } // Total events. @@ -5136,111 +5399,59 @@ function reporting_custom_render($report, $content, $type='dinamic', $pdf=0) $height = $data_macro['height']; } - // TODO: Allow to paint horizontal and vertical bar graphs for the moment only pie graphs. - $type = 'sql_graph_pie'; + $options = [ + 'width' => $width, + 'height' => $height, + 'ttl' => ($pdf === true) ? 2 : 1, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + ]; - $SQL_GRAPH_MAX_LABEL_SIZE = 5; - - $count = 0; - $flagOther = false; - foreach ($data_query as $data_item) { - $count++; - $value = 0; - if (empty($data_item['value']) === false) { - $value = $data_item['value']; - } - - if ($count <= 5) { - $label = __('Data'); - if (empty($data_item['label']) === false) { - $label = io_safe_output($data_item['label']); - if (strlen($label) > $SQL_GRAPH_MAX_LABEL_SIZE) { - $first_label = $label; - $label = substr( - $first_label, - 0, - floor($SQL_GRAPH_MAX_LABEL_SIZE / 2) - ); - $label .= '...
'; - $label .= substr( - $first_label, - floor(-$SQL_GRAPH_MAX_LABEL_SIZE / 2) - ); - } - } - - switch ($type) { - case 'sql_graph_vbar': - default: - // Vertical bar. - $data[] = [ - 'tick' => $label.'_'.$count, - 'data' => $value, - ]; - break; - - case 'sql_graph_hbar': - // Horizontal bar. - $data[$label.'_'.$count]['g'] = $value; - break; - - case 'sql_graph_pie': - // Pie. - $data[$label.'_'.$count] = $value; - break; - } - } else { - switch ($type) { - case 'sql_graph_vbar': - default: - // Vertical bar. - if ($flagOther === false) { - $data[] = [ - 'tick' => __('Other'), - 'data' => $value, - ]; - - $flagOther = true; - } - - $data[(count($data) - 1)]['data'] += $value; - break; - - case 'sql_graph_hbar': - // Horizontal bar. - if (isset($data[__('Other')]['g']) === false) { - $data[__('Other')]['g'] = 0; - } - - $data[__('Other')]['g'] += $value; - break; - - case 'sql_graph_pie': - // Pie. - if (isset($data[__('Other')]) === false) { - $data[__('Other')] = 0; - } - - $data[__('Other')] += $value; - break; - } - } + if ($pdf === true) { + $options['dataLabel'] = ['display' => 'auto']; + $options['layout'] = [ + 'padding' => [ + 'top' => 15, + 'bottom' => 15, + ], + ]; } - $value_query = pie_graph( - $data, - $width, - $height, - __('other'), - ui_get_full_url(false, false, false, false), - '', - $config['fontpath'], - $config['font_size'], - ($pdf === true) ? 2 : 1, - 'hidden', - '', - true + $data = array_reduce( + $data_query, + function ($carry, $item) use ($pdf) { + if ($pdf === true) { + $carry['labels'][] = io_safe_output($item['label']).' ('.$item['value'].')'; + } else { + $carry['labels'][] = io_safe_output($item['label']); + } + + $carry['data'][] = $item['value']; + + return $carry; + }, + [] ); + + $value_query = '
'; + if ($pdf === true) { + $value_query .= ''; + } + + $value_query .= '
'; } } @@ -5352,6 +5563,10 @@ function agents_get_network_interfaces_array( $row_data['agent'] = $agent['name']; $row_data['interfaces'] = []; foreach ($agent['interfaces'] as $interface_name => $interface) { + if (isset($interface['traffic']) === false) { + $interface['traffic'] = []; + } + $row_interface = []; $row_interface['name'] = $interface_name; $row_interface['ip'] = $interface['ip']; @@ -8330,14 +8545,17 @@ function reporting_advanced_sla( $sla_check_value_warning = false; if ($sla_check_value === true) { - // Warning SLA check. - $sla_check_value_warning = sla_check_value( - $current_data['datos'], - $min_value_warning, - $max_value_warning, - $inverse_interval_warning, - 1 - ); + if ((isset($min_value_warning) === false + && isset($max_value_warning) === false) === false + ) { + // Warning SLA check. + $sla_check_value_warning = sla_check_value( + $current_data['datos'], + $min_value_warning, + $max_value_warning, + $inverse_interval_warning + ); + } } } @@ -10872,49 +11090,102 @@ function reporting_get_module_detailed_event( $height = $force_height_chart; } + $options_charts = [ + 'width' => 500, + 'height' => 150, + 'radius' => null, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'ttl' => $ttl, + ]; + + if ((int) $ttl === 2) { + $options_charts['dataLabel'] = ['display' => 'auto']; + $options_charts['layout'] = [ + 'padding' => [ + 'top' => 15, + 'bottom' => 15, + ], + ]; + } + if ($event_graph_by_user_validator) { $data_graph_by_user = events_get_count_events_validated_by_user($event['data']); - $event['chart']['by_user_validator'] = pie_graph( - $data_graph_by_user, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + if ((int) $ttl === 2) { + $result_data_graph_by_user = []; + foreach ($data_graph_by_user as $key => $value) { + $result_data_graph_by_user[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_user = $result_data_graph_by_user; + } + + if ((int) $ttl === 2) { + $event['chart']['by_user_validator'] = ''; + } + + $options_charts['labels'] = array_keys($data_graph_by_user); + $event['chart']['by_user_validator'] .= pie_graph( + array_values($data_graph_by_user), + $options_charts ); + + if ((int) $ttl === 2) { + $event['chart']['by_user_validator'] .= '" />'; + } else { + $event['chart']['by_user_validator'] .= ''; + } } if ($event_graph_by_criticity) { $data_graph_by_criticity = []; if (empty($event['data']) === false) { foreach ($event['data'] as $value) { - $k = get_priority_name($value['criticity']); + $k = io_safe_output(get_priority_name($value['criticity'])); if (isset($data_graph_by_criticity[$k]) === true) { $data_graph_by_criticity[$k]++; } else { $data_graph_by_criticity[$k] = 1; } } + + $colors = get_criticity_pie_colors($data_graph_by_criticity); + $options_charts['colors'] = array_values($colors); + + if ((int) $ttl === 2) { + $result_data_graph_by_criticity = []; + foreach ($data_graph_by_criticity as $key => $value) { + $result_data_graph_by_criticity[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_criticity = $result_data_graph_by_criticity; + } } - $colors = get_criticity_pie_colors($data_graph_by_criticity); + if ((int) $ttl === 2) { + $event['chart']['by_criticity'] = ''; + } - $event['chart']['by_criticity'] = pie_graph( - $data_graph_by_criticity, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl, - false, - $colors + $options_charts['labels'] = array_keys($data_graph_by_criticity); + $event['chart']['by_criticity'] .= pie_graph( + array_values($data_graph_by_criticity), + $options_charts ); + + if ((int) $ttl === 2) { + $event['chart']['by_criticity'] .= '" />'; + } else { + $event['chart']['by_criticity'] .= ''; + } + + unset($options_charts['colors']); } if ($event_graph_validated_vs_unvalidated) { @@ -10932,19 +11203,34 @@ function reporting_get_module_detailed_event( $data_graph_by_status[$k] = 1; } } + + if ((int) $ttl === 2) { + $result_data_graph_by_status = []; + foreach ($data_graph_by_status as $key => $value) { + $result_data_graph_by_status[$key.' ('.$value.')'] = $value; + } + + $data_graph_by_status = $result_data_graph_by_status; + } } - $event['chart']['validated_vs_unvalidated'] = pie_graph( - $data_graph_by_status, - 500, - 150, - __('other'), - ui_get_full_url(false, false, false, false), - ui_get_full_url(false, false, false, false).'/images/logo_vertical_water.png', - $config['fontpath'], - $config['font_size'], - $ttl + if ((int) $ttl === 2) { + $event['chart']['validated_vs_unvalidated'] = ''; + } + + $options_charts['labels'] = array_keys($data_graph_by_status); + $event['chart']['validated_vs_unvalidated'] .= pie_graph( + array_values($data_graph_by_status), + $options_charts ); + + if ((int) $ttl === 2) { + $event['chart']['validated_vs_unvalidated'] .= '" />'; + } else { + $event['chart']['validated_vs_unvalidated'] .= ''; + } } if (!empty($event)) { @@ -14986,8 +15272,6 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) $return = []; - $urlImage = ui_get_full_url(false, true, false, false); - $return['type'] = $content['type']; $ttl = 1; @@ -15096,10 +15380,24 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) return false; } - $uncompress_module = db_uncompress_module_data( + $module_interval = modules_get_interval( + $content['id_agent_module'] + ); + $slice = ($content['period'] / $module_interval); + + $result_sla = reporting_advanced_sla( $content['id_agent_module'], ($report['datetime'] - $content['period']), - $report['datetime'] + $report['datetime'], + null, + null, + 0, + null, + null, + null, + $slice, + 1, + true ); // Select Warning and critical values. @@ -15148,125 +15446,84 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) $inverse_warning = $agentmodule_info['warning_inverse']; - // Initialize vars. - $tstart = 0; - $tend = 0; - $tacum = 0; - $tacum_data = 0; + $data = []; + $data['time_total'] = 0; + $data['time_ok'] = 0; + $data['time_error'] = 0; + $data['time_warning'] = 0; + $data['time_unknown'] = 0; + $data['time_not_init'] = 0; + $data['time_downtime'] = 0; + $data['checks_total'] = 0; + $data['checks_ok'] = 0; + $data['checks_error'] = 0; + $data['checks_warning'] = 0; + $data['checks_unknown'] = 0; + $data['checks_not_init'] = 0; $array_graph = []; + $i = 0; + foreach ($result_sla as $value_sla) { + $data['time_total'] += $value_sla['time_total']; + $data['time_ok'] += $value_sla['time_ok']; + $data['time_error'] += $value_sla['time_error']; + $data['time_warning'] += $value_sla['time_warning']; + $data['time_unknown'] += $value_sla['time_unknown']; + $data['time_downtime'] += $value_sla['time_downtime']; + $data['time_not_init'] += $value_sla['time_not_init']; + $data['checks_total'] += $value_sla['checks_total']; + $data['checks_ok'] += $value_sla['checks_ok']; + $data['checks_error'] += $value_sla['checks_error']; + $data['checks_warning'] += $value_sla['checks_warning']; + $data['checks_unknown'] += $value_sla['checks_unknown']; + $data['checks_not_init'] += $value_sla['checks_not_init']; - $data_not_init = 0; - $data_unknown = 0; - $data_critical = 0; - $data_warning = 0; - $data_ok = 0; - $data_total = 0; - - $time_not_init = 0; - $time_unknown = 0; - $time_critical = 0; - $time_warning = 0; - $time_ok = 0; - - $legend = []; - foreach ($uncompress_module as $data) { - foreach ($data['data'] as $key => $value) { - if ($tacum == 0) { - // Initialize the accumulators. - $tacum = $value['utimestamp']; - $tacum_data = $value['datos']; + // Generate raw data for graph. + if ($value_sla['time_total'] != 0) { + if ($value_sla['time_error'] > 0) { + // ERR. + $array_graph[$i]['data'] = 3; + } else if ($value_sla['time_unknown'] > 0) { + // UNKNOWN. + $array_graph[$i]['data'] = 4; + } else if ($value_sla['time_warning'] > 0) { + // Warning. + $array_graph[$i]['data'] = 2; + } else if ($value_sla['time_not_init'] == $value_sla['time_total']) { + // NOT INIT. + $array_graph[$i]['data'] = 6; } else { - // Utimestand end and final. - $tstart = $tacum; - $tend = $value['utimestamp']; - - // Module type isn't string. - $sla_check_value_critical = sla_check_value( - $tacum_data, - $min_value_critical, - $max_value_critical, - $inverse_critical - ); - $sla_check_value_warning = sla_check_value( - $tacum_data, - $min_value_warning, - $max_value_warning, - $inverse_warning - ); - - // Module type is string. - $string_check_value_critical = preg_match('/'.$max_value_critical.'/', $tacum_data); - $string_check_value_warning = preg_match('/'.$max_value_warning.'/', $tacum_data); - - if ($inverse_critical) { - $string_check_value_critical = !preg_match('/'.$max_value_critical.'/', $tacum_data); - } - - if ($string_check_value_warning) { - $string_check_value_warning = !preg_match('/'.$max_value_warning.'/', $tacum_data); - } - - // Contruct array period and data. - if ($tacum_data === false) { - $array_graph[$data_total]['data'] = AGENT_MODULE_STATUS_NOT_INIT; - // NOT INIT. - $time_not_init = ($time_not_init + ($tend - $tstart)); - $data_not_init++; - } else if ($tacum_data === null) { - $array_graph[$data_total]['data'] = AGENT_MODULE_STATUS_UNKNOWN; - // UNKNOWN. - $time_unknown = ($time_unknown + ($tend - $tstart)); - $data_unknown++; - } else if (( (isset($min_value_critical) || isset($max_value_critical)) && ($modules_is_string === false) && ($sla_check_value_critical == true) ) - || ( isset($max_value_critical) && ($modules_is_string === true) && $string_check_value_critical ) - ) { - $array_graph[$data_total]['data'] = AGENT_MODULE_STATUS_CRITICAL_BAD; - // CRITICAL. - $time_critical = ($time_critical + ($tend - $tstart)); - $data_critical++; - } else if (( (isset($min_value_warning) || isset($max_value_warning)) && ($modules_is_string === false) && ($sla_check_value_warning == true) ) - || ( isset($max_value_warning) && ($modules_is_string === true) && $sla_check_value_warning ) - ) { - $array_graph[$data_total]['data'] = AGENT_MODULE_STATUS_WARNING; - // WARNING. - $time_warning = ($time_warning + ($tend - $tstart)); - $data_warning++; - } else { - $array_graph[$data_total]['data'] = AGENT_MODULE_STATUS_NORMAL; - // OK. - $time_ok = ($time_ok + ($tend - $tstart)); - $data_ok++; - } - - $array_graph[$data_total]['utimestamp'] = ($tend - $tstart); - $array_graph[$data_total]['real_data'] = $tacum_data; - - // Reassign accumulators. - $tacum = $value['utimestamp']; - $tacum_data = $value['datos']; - $data_total++; + $array_graph[$i]['data'] = 1; } + } else { + $array_graph[$i]['data'] = 7; } + + $array_graph[$i]['utimestamp'] = ($value_sla['date_to'] - $value_sla['date_from']); + $i++; } + $data['sla_value'] = reporting_sla_get_compliance_from_array( + $data + ); + + $data['sla_fixed'] = sla_truncate( + $data['sla_value'], + $config['graph_precision'] + ); + $data_init = -1; $acum = 0; $sum = 0; $array_result = []; $i = 0; - foreach ($array_graph as $key => $value) { + foreach ($array_graph as $value) { if ($data_init == -1) { $data_init = $value['data']; $acum = $value['utimestamp']; } else { if ($data_init == $value['data']) { $acum = ($acum + $value['utimestamp']); - if ($modules_is_string === false) { - $sum = ($sum + $value['real_data']); - } else { - $sum = $value['real_data']; - } } else { $array_result[$i]['data'] = $data_init; $array_result[$i]['utimestamp'] = $acum; @@ -15274,7 +15531,6 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) $i++; $data_init = $value['data']; $acum = $value['utimestamp']; - $sum = $value['real_data']; } } } @@ -15287,39 +15543,42 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) $array_result[$i]['real_data'] = $sum; } - $time_total = ($time_not_init + $time_unknown + $time_critical + $time_warning + $time_ok); + $time_total = $data['time_total']; // Slice graphs calculation. - $return['agent'] = modules_get_agentmodule_agent_alias( + $return['agent'] = modules_get_agentmodule_agent_alias( $content['id_agent_module'] ); - $return['module'] = modules_get_agentmodule_name( + $return['module'] = modules_get_agentmodule_name( $content['id_agent_module'] ); - $return['max_critical'] = $max_value_critical; - $return['min_critical'] = $min_value_critical; + + $return['max_critical'] = $max_value_critical; + $return['min_critical'] = $min_value_critical; $return['critical_inverse'] = $inverse_critical; - $return['max_warning'] = $max_value_warning; - $return['min_warning'] = $min_value_warning; - $return['warning_inverse'] = $inverse_warning; - $return['data_not_init'] = $data_not_init; - $return['data_unknown'] = $data_unknown; - $return['data_critical'] = $data_critical; - $return['data_warning'] = $data_warning; - $return['data_ok'] = $data_ok; - $return['data_total'] = $data_total; - $return['time_not_init'] = $time_not_init; - $return['time_unknown'] = $time_unknown; - $return['time_critical'] = $time_critical; - $return['time_warning'] = $time_warning; - $return['time_ok'] = $time_ok; - $return['percent_ok'] = (($data_ok * 100) / $data_total); + $return['max_warning'] = $max_value_warning; + $return['min_warning'] = $min_value_warning; + $return['warning_inverse'] = $inverse_warning; + $return['data_not_init'] = $data['checks_not_init']; + $return['data_unknown'] = $data['checks_unknown']; + $return['data_critical'] = $data['checks_error']; + $return['data_warning'] = $data['checks_warning']; + $return['data_ok'] = $data['checks_ok']; + $return['data_total'] = $data['checks_total']; + $return['time_not_init'] = $data['time_not_init']; + $return['time_unknown'] = $data['time_unknown']; + $return['time_critical'] = $data['time_error']; + $return['time_warning'] = $data['time_warning']; + $return['time_ok'] = $data['time_ok']; + $return['percent_ok'] = (($data['checks_ok'] * 100) / $data['checks_total']); $colors = [ - AGENT_MODULE_STATUS_NORMAL => COL_NORMAL, - AGENT_MODULE_STATUS_WARNING => COL_WARNING, - AGENT_MODULE_STATUS_CRITICAL_BAD => COL_CRITICAL, - AGENT_MODULE_STATUS_UNKNOWN => COL_UNKNOWN, - AGENT_MODULE_STATUS_NOT_INIT => COL_NOTINIT, + 1 => COL_NORMAL, + 2 => COL_WARNING, + 3 => COL_CRITICAL, + 4 => COL_UNKNOWN, + 5 => COL_DOWNTIME, + 6 => COL_NOTINIT, + 7 => COL_IGNORED, ]; $width_graph = 100; @@ -15329,7 +15588,7 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) $time_total, $width_graph, $height_graph, - $legend, + [], $colors, $config['fontpath'], $config['round_corner'], @@ -15346,11 +15605,7 @@ function reporting_module_histogram_graph($report, $content, $pdf=0) $report['datetime'] ); } else { - $return['chart'] = graph_nodata_image( - $width_graph, - $height_graph, - 'area' - ); + $return['chart'] = graph_nodata_image(['height' => $height_graph]); } if ($metaconsole_on && $server_name != '') { diff --git a/pandora_console/include/functions_reporting_html.php b/pandora_console/include/functions_reporting_html.php index c116c3faa8..77c6db2807 100644 --- a/pandora_console/include/functions_reporting_html.php +++ b/pandora_console/include/functions_reporting_html.php @@ -961,7 +961,7 @@ function reporting_html_top_n($table, $item, $pdf=0) $table->data['top_n']['cell'] = html_print_table($table1, true); } - if (!empty($item['charts']['pie'])) { + if (empty($item['charts']['pie']) === false) { if ($pdf !== 0) { $return_pdf .= $item['charts']['pie']; } else { @@ -970,16 +970,15 @@ function reporting_html_top_n($table, $item, $pdf=0) } } - if (!empty($item['charts']['bars'])) { + if (empty($item['charts']['bars']) === false) { if ($pdf !== 0) { $return_pdf .= $item['charts']['bars']; } else { - // $table->colspan['char_bars']['cell'] = 3; $table->data['char_pie'][1] = $item['charts']['bars']; } } - if (!empty($item['resume'])) { + if (empty($item['resume']) === false) { $table1 = new stdClass(); $table1->width = '99%'; @@ -5141,7 +5140,17 @@ function reporting_get_stats_summary($data, $graph_width, $graph_height) // Fixed width non interactive charts. $status_chart_width = $graph_width; - $tdata[0] = '
'.graph_agent_status(false, $graph_width, $graph_height, true, true).'
'; + $tdata[0] = '
'; + $tdata[0] .= '
'; + $tdata[0] .= graph_agent_status( + false, + $graph_width, + $graph_height, + true, + true + ); + $tdata[0] .= '
'; + $tdata[0] .= '
'; } else { $tdata[2] = html_print_image( 'images/image_problem_area_small.png', @@ -5151,7 +5160,16 @@ function reporting_get_stats_summary($data, $graph_width, $graph_height) } if ($data['monitor_alerts'] > 0) { - $tdata[2] = '
'.graph_alert_status($data['monitor_alerts'], $data['monitor_alerts_fired'], $graph_width, $graph_height, true, true).'
'; + $tdata[2] = '
'; + $tdata[2] .= graph_alert_status( + $data['monitor_alerts'], + $data['monitor_alerts_fired'], + $graph_width, + $graph_height, + true, + true + ); + $tdata[2] .= '
'; } else { $tdata[2] = html_print_image( 'images/image_problem_area_small.png', @@ -5160,8 +5178,8 @@ function reporting_get_stats_summary($data, $graph_width, $graph_height) ); } - $table_sum->rowclass[] = ''; - $table_sum->data[] = $tdata; + $table_sum->rowclass[] = ''; + $table_sum->data[] = $tdata; $output = '
'.__('Summary').''.html_print_table($table_sum, true).'
'; diff --git a/pandora_console/include/functions_snmp_browser.php b/pandora_console/include/functions_snmp_browser.php index cbf812d2bd..b97f6cc313 100644 --- a/pandora_console/include/functions_snmp_browser.php +++ b/pandora_console/include/functions_snmp_browser.php @@ -460,7 +460,7 @@ function snmp_browser_get_oid( $snmptranslate_bin = $config['snmptranslate']; } - if ($server_to_exec != 0 && enterprise_installed()) { + if (empty($server_to_exec) === false && enterprise_installed()) { $server_data = db_get_row('tserver', 'id_server', $server_to_exec); $command_output = $snmptranslate_bin.' -m ALL -M +'.escapeshellarg($config['homedir'].'/attachment/mibs').' -Td '.escapeshellarg($oid); diff --git a/pandora_console/include/functions_treeview.php b/pandora_console/include/functions_treeview.php index be76db9459..f6f6ec6a7a 100755 --- a/pandora_console/include/functions_treeview.php +++ b/pandora_console/include/functions_treeview.php @@ -690,18 +690,11 @@ function treeview_printTable($id_agente, $server_data=[], $no_head=false) $go_to_agent = '
'; if ($agent['id_os'] == CLUSTER_OS_ID) { - if (enterprise_installed()) { - $cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId( - $agent['id_agente'] - ); - $url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR; - $url .= '/operation/cluster/cluster'; - $url = ui_get_full_url( - $url.'&op=update&id='.$cluster->id() - ); - $go_to_agent .= ''; - $go_to_agent .= html_print_submit_button(__('Edit cluster'), 'upd_button', false, 'class="sub config"', true); - } + $cluster = PandoraFMS\Cluster::loadFromAgentId( + $agent['id_agente'] + ); + $go_to_agent .= ''; + $go_to_agent .= html_print_submit_button(__('Go to cluster edition'), 'upd_button', false, 'class="sub config"', true); } else { $go_to_agent .= ''; $go_to_agent .= html_print_submit_button(__('Go to agent edition'), 'upd_button', false, 'class="sub config"', true); diff --git a/pandora_console/include/functions_visual_map.php b/pandora_console/include/functions_visual_map.php index d1b2e60fdc..b6e3932293 100755 --- a/pandora_console/include/functions_visual_map.php +++ b/pandora_console/include/functions_visual_map.php @@ -1227,7 +1227,7 @@ function visual_map_print_item( $module_data = get_bars_module_data( $id_module, - ($layoutData['type_graph'] !== 'horizontal') + $layoutData['type_graph'] ); $options = []; $options['generals']['rotate'] = true; @@ -2344,7 +2344,7 @@ function get_if_module_is_image($id_module) } -function get_bars_module_data($id_module, $vBars=false) +function get_bars_module_data($id_module, $typeGraph='horizontal') { // This charts is only serialize graphs. // In other string show image no data to show. @@ -2355,7 +2355,7 @@ function get_bars_module_data($id_module, $vBars=false) ); $values = false; - // avoid showing the image type modules. WUX + // Avoid showing the image type modules. WUX. if (strpos($mod_values, 'data:image/png;base64') !== 0) { if (preg_match("/\r\n/", $mod_values)) { $values = explode("\r\n", $mod_values); @@ -2364,26 +2364,28 @@ function get_bars_module_data($id_module, $vBars=false) } } - $values_to_return = []; - $index = 0; - $color_index = 0; - $total = 0; - if (!$values) { return false; } - if ($vBars === false) { - foreach ($values as $val) { - $data = explode(',', $val); - $values_to_return[$data[0]] = ['g' => $data[1]]; + $values_to_return = []; + foreach ($values as $val) { + $data = explode(',', $val); + + if ($data[0] === $val) { + continue; } - } else { - foreach ($values as $val) { - $data = explode(',', $val); - $values_to_return[] = [ - 'tick' => $data[0], - 'data' => $data[1], + + $values_to_return['labels'][] = io_safe_output($data[0]); + if ($typeGraph === 'horizontal') { + $values_to_return['data'][] = [ + 'y' => io_safe_output($data[0]), + 'x' => $data[1], + ]; + } else { + $values_to_return['data'][] = [ + 'x' => io_safe_output($data[0]), + 'y' => $data[1], ]; } } @@ -2842,74 +2844,23 @@ function get_donut_module_data($id_module) $no_data_to_show = true; } - $colors = []; - $colors[] = '#aa3333'; - $colors[] = '#045FB4'; - $colors[] = '#8181F7'; - $colors[] = '#F78181'; - $colors[] = '#D0A9F5'; - $colors[] = '#BDBDBD'; - $colors[] = '#6AB277'; - - $max_elements = 6; $values_to_return = []; - $index = 0; - $total = 0; foreach ($values as $val) { - if ($index < $max_elements) { - $data = explode(',', $val); + $data = explode(',', $val); - if ($no_data_to_show) { - $values_to_return[$index]['tag_name'] = $data[0]; - } else { - $values_to_return[$index]['tag_name'] = $data[0].': '.$data[1]; - } + if ($data[0] === $val) { + continue; + } - $values_to_return[$index]['color'] = $colors[$index]; - $values_to_return[$index]['value'] = (int) $data[1]; - $total += (int) $data[1]; - $index++; + if ($no_data_to_show) { + $values_to_return['labels'][] = $data[0]; } else { - $data = explode(',', $val); - $values_to_return[$index]['tag_name'] = __('Others').': '.$data[1]; - $values_to_return[$index]['color'] = $colors[$index]; - $values_to_return[$index]['value'] += (int) $data[1]; - $total += (int) $data[1]; - } - } - - foreach ($values_to_return as $ind => $donut_data) { - $values_to_return[$ind]['percent'] = (($donut_data['value'] * 100) / $total); - } - - // sort array - $new_values_to_return = []; - while (!empty($values_to_return)) { - $first = true; - $max_elem = 0; - $max_elem_array = []; - $index_to_del = 0; - foreach ($values_to_return as $i => $val) { - if ($first) { - $max_elem = $val['value']; - $max_elem_array = $val; - $index_to_del = $i; - $first = false; - } else { - if ($val['value'] > $max_elem) { - $max_elem = $val['value']; - $max_elem_array = $val; - $index_to_del = $i; - } - } + $values_to_return['labels'][] = $data[0].': '.$data[1]; } - $new_values_to_return[] = $max_elem_array; - unset($values_to_return[$index_to_del]); + $values_to_return['data'][] = (int) $data[1]; } - $values_to_return = $new_values_to_return; - return $values_to_return; } diff --git a/pandora_console/include/graphs/chartjs/chart.js b/pandora_console/include/graphs/chartjs/chart.js new file mode 100644 index 0000000000..cf279cc849 --- /dev/null +++ b/pandora_console/include/graphs/chartjs/chart.js @@ -0,0 +1,11526 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/chart.js@4.0.1/dist/chart.umd.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +/*! + * Chart.js v4.0.1 + * https://www.chartjs.org + * (c) 2022 Chart.js Contributors + * Released under the MIT License + */ +!(function(t, e) { + "object" == typeof exports && "undefined" != typeof module + ? (module.exports = e()) + : "function" == typeof define && define.amd + ? define(e) + : ((t = + "undefined" != typeof globalThis ? globalThis : t || self).Chart = e()); +})(this, function() { + "use strict"; + function t() {} + const e = (() => { + let t = 0; + return () => t++; + })(); + function i(t) { + return null == t; + } + function s(t) { + if (Array.isArray && Array.isArray(t)) return !0; + const e = Object.prototype.toString.call(t); + return "[object" === e.slice(0, 7) && "Array]" === e.slice(-6); + } + function n(t) { + return ( + null !== t && "[object Object]" === Object.prototype.toString.call(t) + ); + } + function o(t) { + return ("number" == typeof t || t instanceof Number) && isFinite(+t); + } + function a(t, e) { + return o(t) ? t : e; + } + function r(t, e) { + return void 0 === t ? e : t; + } + const l = (t, e) => + "string" == typeof t && t.endsWith("%") ? parseFloat(t) / 100 : +t / e, + h = (t, e) => + "string" == typeof t && t.endsWith("%") ? (parseFloat(t) / 100) * e : +t; + function c(t, e, i) { + if (t && "function" == typeof t.call) return t.apply(i, e); + } + function d(t, e, i, o) { + let a, r, l; + if (s(t)) + if (((r = t.length), o)) for (a = r - 1; a >= 0; a--) e.call(i, t[a], a); + else for (a = 0; a < r; a++) e.call(i, t[a], a); + else if (n(t)) + for (l = Object.keys(t), r = l.length, a = 0; a < r; a++) + e.call(i, t[l[a]], l[a]); + } + function u(t, e) { + let i, s, n, o; + if (!t || !e || t.length !== e.length) return !1; + for (i = 0, s = t.length; i < s; ++i) + if ( + ((n = t[i]), + (o = e[i]), + n.datasetIndex !== o.datasetIndex || n.index !== o.index) + ) + return !1; + return !0; + } + function f(t) { + if (s(t)) return t.map(f); + if (n(t)) { + const e = Object.create(null), + i = Object.keys(t), + s = i.length; + let n = 0; + for (; n < s; ++n) e[i[n]] = f(t[i[n]]); + return e; + } + return t; + } + function g(t) { + return -1 === ["__proto__", "prototype", "constructor"].indexOf(t); + } + function p(t, e, i, s) { + if (!g(t)) return; + const o = e[t], + a = i[t]; + n(o) && n(a) ? m(o, a, s) : (e[t] = f(a)); + } + function m(t, e, i) { + const o = s(e) ? e : [e], + a = o.length; + if (!n(t)) return t; + const r = (i = i || {}).merger || p; + let l; + for (let e = 0; e < a; ++e) { + if (((l = o[e]), !n(l))) continue; + const s = Object.keys(l); + for (let e = 0, n = s.length; e < n; ++e) r(s[e], t, l, i); + } + return t; + } + function b(t, e) { + return m(t, e, { merger: x }); + } + function x(t, e, i) { + if (!g(t)) return; + const s = e[t], + o = i[t]; + n(s) && n(o) + ? b(s, o) + : Object.prototype.hasOwnProperty.call(e, t) || (e[t] = f(o)); + } + const _ = { "": t => t, x: t => t.x, y: t => t.y }; + function y(t) { + const e = t.split("."), + i = []; + let s = ""; + for (const t of e) + (s += t), + s.endsWith("\\") ? (s = s.slice(0, -1) + ".") : (i.push(s), (s = "")); + return i; + } + function v(t, e) { + const i = + _[e] || + (_[e] = (function(t) { + const e = y(t); + return t => { + for (const i of e) { + if ("" === i) break; + t = t && t[i]; + } + return t; + }; + })(e)); + return i(t); + } + function M(t) { + return t.charAt(0).toUpperCase() + t.slice(1); + } + const w = t => void 0 !== t, + k = t => "function" == typeof t, + S = (t, e) => { + if (t.size !== e.size) return !1; + for (const i of t) if (!e.has(i)) return !1; + return !0; + }; + function P(t) { + return ( + "mouseup" === t.type || "click" === t.type || "contextmenu" === t.type + ); + } + const D = Math.PI, + C = 2 * D, + O = C + D, + A = Number.POSITIVE_INFINITY, + T = D / 180, + L = D / 2, + E = D / 4, + R = (2 * D) / 3, + I = Math.log10, + z = Math.sign; + function F(t, e, i) { + return Math.abs(t - e) < i; + } + function V(t) { + const e = Math.round(t); + t = F(t, e, t / 1e3) ? e : t; + const i = Math.pow(10, Math.floor(I(t))), + s = t / i; + return (s <= 1 ? 1 : s <= 2 ? 2 : s <= 5 ? 5 : 10) * i; + } + function B(t) { + const e = [], + i = Math.sqrt(t); + let s; + for (s = 1; s < i; s++) t % s == 0 && (e.push(s), e.push(t / s)); + return i === (0 | i) && e.push(i), e.sort((t, e) => t - e).pop(), e; + } + function N(t) { + return !isNaN(parseFloat(t)) && isFinite(t); + } + function W(t, e) { + const i = Math.round(t); + return i - e <= t && i + e >= t; + } + function H(t, e, i) { + let s, n, o; + for (s = 0, n = t.length; s < n; s++) + (o = t[s][i]), + isNaN(o) || + ((e.min = Math.min(e.min, o)), (e.max = Math.max(e.max, o))); + } + function j(t) { + return t * (D / 180); + } + function $(t) { + return t * (180 / D); + } + function Y(t) { + if (!o(t)) return; + let e = 1, + i = 0; + for (; Math.round(t * e) / e !== t; ) (e *= 10), i++; + return i; + } + function U(t, e) { + const i = e.x - t.x, + s = e.y - t.y, + n = Math.sqrt(i * i + s * s); + let o = Math.atan2(s, i); + return o < -0.5 * D && (o += C), { angle: o, distance: n }; + } + function X(t, e) { + return Math.sqrt(Math.pow(e.x - t.x, 2) + Math.pow(e.y - t.y, 2)); + } + function q(t, e) { + return ((t - e + O) % C) - D; + } + function K(t) { + return ((t % C) + C) % C; + } + function G(t, e, i, s) { + const n = K(t), + o = K(e), + a = K(i), + r = K(o - n), + l = K(a - n), + h = K(n - o), + c = K(n - a); + return n === o || n === a || (s && o === a) || (r > l && h < c); + } + function Z(t, e, i) { + return Math.max(e, Math.min(i, t)); + } + function J(t) { + return Z(t, -32768, 32767); + } + function Q(t, e, i, s = 1e-6) { + return t >= Math.min(e, i) - s && t <= Math.max(e, i) + s; + } + function tt(t, e, i) { + i = i || (i => t[i] < e); + let s, + n = t.length - 1, + o = 0; + for (; n - o > 1; ) (s = (o + n) >> 1), i(s) ? (o = s) : (n = s); + return { lo: o, hi: n }; + } + const et = (t, e, i, s) => + tt( + t, + i, + s + ? s => { + const n = t[s][e]; + return n < i || (n === i && t[s + 1][e] === i); + } + : s => t[s][e] < i + ), + it = (t, e, i) => tt(t, i, s => t[s][e] >= i); + function st(t, e, i) { + let s = 0, + n = t.length; + for (; s < n && t[s] < e; ) s++; + for (; n > s && t[n - 1] > i; ) n--; + return s > 0 || n < t.length ? t.slice(s, n) : t; + } + const nt = ["push", "pop", "shift", "splice", "unshift"]; + function ot(t, e) { + t._chartjs + ? t._chartjs.listeners.push(e) + : (Object.defineProperty(t, "_chartjs", { + configurable: !0, + enumerable: !1, + value: { listeners: [e] } + }), + nt.forEach(e => { + const i = "_onData" + M(e), + s = t[e]; + Object.defineProperty(t, e, { + configurable: !0, + enumerable: !1, + value(...e) { + const n = s.apply(this, e); + return ( + t._chartjs.listeners.forEach(t => { + "function" == typeof t[i] && t[i](...e); + }), + n + ); + } + }); + })); + } + function at(t, e) { + const i = t._chartjs; + if (!i) return; + const s = i.listeners, + n = s.indexOf(e); + -1 !== n && s.splice(n, 1), + s.length > 0 || + (nt.forEach(e => { + delete t[e]; + }), + delete t._chartjs); + } + function rt(t) { + const e = new Set(); + let i, s; + for (i = 0, s = t.length; i < s; ++i) e.add(t[i]); + return e.size === s ? t : Array.from(e); + } + const lt = + "undefined" == typeof window + ? function(t) { + return t(); + } + : window.requestAnimationFrame; + function ht(t, e) { + let i = !1; + return function(...s) { + i || + ((i = !0), + lt.call(window, () => { + (i = !1), t.apply(e, s); + })); + }; + } + function ct(t, e) { + let i; + return function(...s) { + return ( + e ? (clearTimeout(i), (i = setTimeout(t, e, s))) : t.apply(this, s), e + ); + }; + } + const dt = t => ("start" === t ? "left" : "end" === t ? "right" : "center"), + ut = (t, e, i) => ("start" === t ? e : "end" === t ? i : (e + i) / 2), + ft = (t, e, i, s) => + t === (s ? "left" : "right") ? i : "center" === t ? (e + i) / 2 : e; + function gt(t, e, i) { + const s = e.length; + let n = 0, + o = s; + if (t._sorted) { + const { iScale: a, _parsed: r } = t, + l = a.axis, + { min: h, max: c, minDefined: d, maxDefined: u } = a.getUserBounds(); + d && + (n = Z( + Math.min( + et(r, a.axis, h).lo, + i ? s : et(e, l, a.getPixelForValue(h)).lo + ), + 0, + s - 1 + )), + (o = u + ? Z( + Math.max( + et(r, a.axis, c, !0).hi + 1, + i ? 0 : et(e, l, a.getPixelForValue(c), !0).hi + 1 + ), + n, + s + ) - n + : s - n); + } + return { start: n, count: o }; + } + function pt(t) { + const { xScale: e, yScale: i, _scaleRanges: s } = t, + n = { xmin: e.min, xmax: e.max, ymin: i.min, ymax: i.max }; + if (!s) return (t._scaleRanges = n), !0; + const o = + s.xmin !== e.min || + s.xmax !== e.max || + s.ymin !== i.min || + s.ymax !== i.max; + return Object.assign(s, n), o; + } + class mt { + constructor() { + (this._request = null), + (this._charts = new Map()), + (this._running = !1), + (this._lastDate = void 0); + } + _notify(t, e, i, s) { + const n = e.listeners[s], + o = e.duration; + n.forEach(s => + s({ + chart: t, + initial: e.initial, + numSteps: o, + currentStep: Math.min(i - e.start, o) + }) + ); + } + _refresh() { + this._request || + ((this._running = !0), + (this._request = lt.call(window, () => { + this._update(), + (this._request = null), + this._running && this._refresh(); + }))); + } + _update(t = Date.now()) { + let e = 0; + this._charts.forEach((i, s) => { + if (!i.running || !i.items.length) return; + const n = i.items; + let o, + a = n.length - 1, + r = !1; + for (; a >= 0; --a) + (o = n[a]), + o._active + ? (o._total > i.duration && (i.duration = o._total), + o.tick(t), + (r = !0)) + : ((n[a] = n[n.length - 1]), n.pop()); + r && (s.draw(), this._notify(s, i, t, "progress")), + n.length || + ((i.running = !1), + this._notify(s, i, t, "complete"), + (i.initial = !1)), + (e += n.length); + }), + (this._lastDate = t), + 0 === e && (this._running = !1); + } + _getAnims(t) { + const e = this._charts; + let i = e.get(t); + return ( + i || + ((i = { + running: !1, + initial: !0, + items: [], + listeners: { complete: [], progress: [] } + }), + e.set(t, i)), + i + ); + } + listen(t, e, i) { + this._getAnims(t).listeners[e].push(i); + } + add(t, e) { + e && e.length && this._getAnims(t).items.push(...e); + } + has(t) { + return this._getAnims(t).items.length > 0; + } + start(t) { + const e = this._charts.get(t); + e && + ((e.running = !0), + (e.start = Date.now()), + (e.duration = e.items.reduce((t, e) => Math.max(t, e._duration), 0)), + this._refresh()); + } + running(t) { + if (!this._running) return !1; + const e = this._charts.get(t); + return !!(e && e.running && e.items.length); + } + stop(t) { + const e = this._charts.get(t); + if (!e || !e.items.length) return; + const i = e.items; + let s = i.length - 1; + for (; s >= 0; --s) i[s].cancel(); + (e.items = []), this._notify(t, e, Date.now(), "complete"); + } + remove(t) { + return this._charts.delete(t); + } + } + var bt = new mt(); + /*! + * @kurkle/color v0.2.1 + * https://github.com/kurkle/color#readme + * (c) 2022 Jukka Kurkela + * Released under the MIT License + */ function xt(t) { + return (t + 0.5) | 0; + } + const _t = (t, e, i) => Math.max(Math.min(t, i), e); + function yt(t) { + return _t(xt(2.55 * t), 0, 255); + } + function vt(t) { + return _t(xt(255 * t), 0, 255); + } + function Mt(t) { + return _t(xt(t / 2.55) / 100, 0, 1); + } + function wt(t) { + return _t(xt(100 * t), 0, 100); + } + const kt = { + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + A: 10, + B: 11, + C: 12, + D: 13, + E: 14, + F: 15, + a: 10, + b: 11, + c: 12, + d: 13, + e: 14, + f: 15 + }, + St = [..."0123456789ABCDEF"], + Pt = t => St[15 & t], + Dt = t => St[(240 & t) >> 4] + St[15 & t], + Ct = t => (240 & t) >> 4 == (15 & t); + function Ot(t) { + var e = (t => Ct(t.r) && Ct(t.g) && Ct(t.b) && Ct(t.a))(t) ? Pt : Dt; + return t + ? "#" + + e(t.r) + + e(t.g) + + e(t.b) + + ((t, e) => (t < 255 ? e(t) : ""))(t.a, e) + : void 0; + } + const At = /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/; + function Tt(t, e, i) { + const s = e * Math.min(i, 1 - i), + n = (e, n = (e + t / 30) % 12) => + i - s * Math.max(Math.min(n - 3, 9 - n, 1), -1); + return [n(0), n(8), n(4)]; + } + function Lt(t, e, i) { + const s = (s, n = (s + t / 60) % 6) => + i - i * e * Math.max(Math.min(n, 4 - n, 1), 0); + return [s(5), s(3), s(1)]; + } + function Et(t, e, i) { + const s = Tt(t, 1, 0.5); + let n; + for ( + e + i > 1 && ((n = 1 / (e + i)), (e *= n), (i *= n)), n = 0; + n < 3; + n++ + ) + (s[n] *= 1 - e - i), (s[n] += e); + return s; + } + function Rt(t) { + const e = t.r / 255, + i = t.g / 255, + s = t.b / 255, + n = Math.max(e, i, s), + o = Math.min(e, i, s), + a = (n + o) / 2; + let r, l, h; + return ( + n !== o && + ((h = n - o), + (l = a > 0.5 ? h / (2 - n - o) : h / (n + o)), + (r = (function(t, e, i, s, n) { + return t === n + ? (e - i) / s + (e < i ? 6 : 0) + : e === n + ? (i - t) / s + 2 + : (t - e) / s + 4; + })(e, i, s, h, n)), + (r = 60 * r + 0.5)), + [0 | r, l || 0, a] + ); + } + function It(t, e, i, s) { + return (Array.isArray(e) ? t(e[0], e[1], e[2]) : t(e, i, s)).map(vt); + } + function zt(t, e, i) { + return It(Tt, t, e, i); + } + function Ft(t) { + return ((t % 360) + 360) % 360; + } + function Vt(t) { + const e = At.exec(t); + let i, + s = 255; + if (!e) return; + e[5] !== i && (s = e[6] ? yt(+e[5]) : vt(+e[5])); + const n = Ft(+e[2]), + o = +e[3] / 100, + a = +e[4] / 100; + return ( + (i = + "hwb" === e[1] + ? (function(t, e, i) { + return It(Et, t, e, i); + })(n, o, a) + : "hsv" === e[1] + ? (function(t, e, i) { + return It(Lt, t, e, i); + })(n, o, a) + : zt(n, o, a)), + { r: i[0], g: i[1], b: i[2], a: s } + ); + } + const Bt = { + x: "dark", + Z: "light", + Y: "re", + X: "blu", + W: "gr", + V: "medium", + U: "slate", + A: "ee", + T: "ol", + S: "or", + B: "ra", + C: "lateg", + D: "ights", + R: "in", + Q: "turquois", + E: "hi", + P: "ro", + O: "al", + N: "le", + M: "de", + L: "yello", + F: "en", + K: "ch", + G: "arks", + H: "ea", + I: "ightg", + J: "wh" + }, + Nt = { + OiceXe: "f0f8ff", + antiquewEte: "faebd7", + aqua: "ffff", + aquamarRe: "7fffd4", + azuY: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "0", + blanKedOmond: "ffebcd", + Xe: "ff", + XeviTet: "8a2be2", + bPwn: "a52a2a", + burlywood: "deb887", + caMtXe: "5f9ea0", + KartYuse: "7fff00", + KocTate: "d2691e", + cSO: "ff7f50", + cSnflowerXe: "6495ed", + cSnsilk: "fff8dc", + crimson: "dc143c", + cyan: "ffff", + xXe: "8b", + xcyan: "8b8b", + xgTMnPd: "b8860b", + xWay: "a9a9a9", + xgYF: "6400", + xgYy: "a9a9a9", + xkhaki: "bdb76b", + xmagFta: "8b008b", + xTivegYF: "556b2f", + xSange: "ff8c00", + xScEd: "9932cc", + xYd: "8b0000", + xsOmon: "e9967a", + xsHgYF: "8fbc8f", + xUXe: "483d8b", + xUWay: "2f4f4f", + xUgYy: "2f4f4f", + xQe: "ced1", + xviTet: "9400d3", + dAppRk: "ff1493", + dApskyXe: "bfff", + dimWay: "696969", + dimgYy: "696969", + dodgerXe: "1e90ff", + fiYbrick: "b22222", + flSOwEte: "fffaf0", + foYstWAn: "228b22", + fuKsia: "ff00ff", + gaRsbSo: "dcdcdc", + ghostwEte: "f8f8ff", + gTd: "ffd700", + gTMnPd: "daa520", + Way: "808080", + gYF: "8000", + gYFLw: "adff2f", + gYy: "808080", + honeyMw: "f0fff0", + hotpRk: "ff69b4", + RdianYd: "cd5c5c", + Rdigo: "4b0082", + ivSy: "fffff0", + khaki: "f0e68c", + lavFMr: "e6e6fa", + lavFMrXsh: "fff0f5", + lawngYF: "7cfc00", + NmoncEffon: "fffacd", + ZXe: "add8e6", + ZcSO: "f08080", + Zcyan: "e0ffff", + ZgTMnPdLw: "fafad2", + ZWay: "d3d3d3", + ZgYF: "90ee90", + ZgYy: "d3d3d3", + ZpRk: "ffb6c1", + ZsOmon: "ffa07a", + ZsHgYF: "20b2aa", + ZskyXe: "87cefa", + ZUWay: "778899", + ZUgYy: "778899", + ZstAlXe: "b0c4de", + ZLw: "ffffe0", + lime: "ff00", + limegYF: "32cd32", + lRF: "faf0e6", + magFta: "ff00ff", + maPon: "800000", + VaquamarRe: "66cdaa", + VXe: "cd", + VScEd: "ba55d3", + VpurpN: "9370db", + VsHgYF: "3cb371", + VUXe: "7b68ee", + VsprRggYF: "fa9a", + VQe: "48d1cc", + VviTetYd: "c71585", + midnightXe: "191970", + mRtcYam: "f5fffa", + mistyPse: "ffe4e1", + moccasR: "ffe4b5", + navajowEte: "ffdead", + navy: "80", + Tdlace: "fdf5e6", + Tive: "808000", + TivedBb: "6b8e23", + Sange: "ffa500", + SangeYd: "ff4500", + ScEd: "da70d6", + pOegTMnPd: "eee8aa", + pOegYF: "98fb98", + pOeQe: "afeeee", + pOeviTetYd: "db7093", + papayawEp: "ffefd5", + pHKpuff: "ffdab9", + peru: "cd853f", + pRk: "ffc0cb", + plum: "dda0dd", + powMrXe: "b0e0e6", + purpN: "800080", + YbeccapurpN: "663399", + Yd: "ff0000", + Psybrown: "bc8f8f", + PyOXe: "4169e1", + saddNbPwn: "8b4513", + sOmon: "fa8072", + sandybPwn: "f4a460", + sHgYF: "2e8b57", + sHshell: "fff5ee", + siFna: "a0522d", + silver: "c0c0c0", + skyXe: "87ceeb", + UXe: "6a5acd", + UWay: "708090", + UgYy: "708090", + snow: "fffafa", + sprRggYF: "ff7f", + stAlXe: "4682b4", + tan: "d2b48c", + teO: "8080", + tEstN: "d8bfd8", + tomato: "ff6347", + Qe: "40e0d0", + viTet: "ee82ee", + JHt: "f5deb3", + wEte: "ffffff", + wEtesmoke: "f5f5f5", + Lw: "ffff00", + LwgYF: "9acd32" + }; + let Wt; + function Ht(t) { + Wt || + ((Wt = (function() { + const t = {}, + e = Object.keys(Nt), + i = Object.keys(Bt); + let s, n, o, a, r; + for (s = 0; s < e.length; s++) { + for (a = r = e[s], n = 0; n < i.length; n++) + (o = i[n]), (r = r.replace(o, Bt[o])); + (o = parseInt(Nt[a], 16)), + (t[r] = [(o >> 16) & 255, (o >> 8) & 255, 255 & o]); + } + return t; + })()), + (Wt.transparent = [0, 0, 0, 0])); + const e = Wt[t.toLowerCase()]; + return e && { r: e[0], g: e[1], b: e[2], a: 4 === e.length ? e[3] : 255 }; + } + const jt = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/; + const $t = t => + t <= 0.0031308 ? 12.92 * t : 1.055 * Math.pow(t, 1 / 2.4) - 0.055, + Yt = t => (t <= 0.04045 ? t / 12.92 : Math.pow((t + 0.055) / 1.055, 2.4)); + function Ut(t, e, i) { + if (t) { + let s = Rt(t); + (s[e] = Math.max(0, Math.min(s[e] + s[e] * i, 0 === e ? 360 : 1))), + (s = zt(s)), + (t.r = s[0]), + (t.g = s[1]), + (t.b = s[2]); + } + } + function Xt(t, e) { + return t ? Object.assign(e || {}, t) : t; + } + function qt(t) { + var e = { r: 0, g: 0, b: 0, a: 255 }; + return ( + Array.isArray(t) + ? t.length >= 3 && + ((e = { r: t[0], g: t[1], b: t[2], a: 255 }), + t.length > 3 && (e.a = vt(t[3]))) + : ((e = Xt(t, { r: 0, g: 0, b: 0, a: 1 })).a = vt(e.a)), + e + ); + } + function Kt(t) { + return "r" === t.charAt(0) + ? (function(t) { + const e = jt.exec(t); + let i, + s, + n, + o = 255; + if (e) { + if (e[7] !== i) { + const t = +e[7]; + o = e[8] ? yt(t) : _t(255 * t, 0, 255); + } + return ( + (i = +e[1]), + (s = +e[3]), + (n = +e[5]), + (i = 255 & (e[2] ? yt(i) : _t(i, 0, 255))), + (s = 255 & (e[4] ? yt(s) : _t(s, 0, 255))), + (n = 255 & (e[6] ? yt(n) : _t(n, 0, 255))), + { r: i, g: s, b: n, a: o } + ); + } + })(t) + : Vt(t); + } + class Gt { + constructor(t) { + if (t instanceof Gt) return t; + const e = typeof t; + let i; + var s, n, o; + "object" === e + ? (i = qt(t)) + : "string" === e && + ((o = (s = t).length), + "#" === s[0] && + (4 === o || 5 === o + ? (n = { + r: 255 & (17 * kt[s[1]]), + g: 255 & (17 * kt[s[2]]), + b: 255 & (17 * kt[s[3]]), + a: 5 === o ? 17 * kt[s[4]] : 255 + }) + : (7 !== o && 9 !== o) || + (n = { + r: (kt[s[1]] << 4) | kt[s[2]], + g: (kt[s[3]] << 4) | kt[s[4]], + b: (kt[s[5]] << 4) | kt[s[6]], + a: 9 === o ? (kt[s[7]] << 4) | kt[s[8]] : 255 + })), + (i = n || Ht(t) || Kt(t))), + (this._rgb = i), + (this._valid = !!i); + } + get valid() { + return this._valid; + } + get rgb() { + var t = Xt(this._rgb); + return t && (t.a = Mt(t.a)), t; + } + set rgb(t) { + this._rgb = qt(t); + } + rgbString() { + return this._valid + ? (t = this._rgb) && + (t.a < 255 + ? `rgba(${t.r}, ${t.g}, ${t.b}, ${Mt(t.a)})` + : `rgb(${t.r}, ${t.g}, ${t.b})`) + : void 0; + var t; + } + hexString() { + return this._valid ? Ot(this._rgb) : void 0; + } + hslString() { + return this._valid + ? (function(t) { + if (!t) return; + const e = Rt(t), + i = e[0], + s = wt(e[1]), + n = wt(e[2]); + return t.a < 255 + ? `hsla(${i}, ${s}%, ${n}%, ${Mt(t.a)})` + : `hsl(${i}, ${s}%, ${n}%)`; + })(this._rgb) + : void 0; + } + mix(t, e) { + if (t) { + const i = this.rgb, + s = t.rgb; + let n; + const o = e === n ? 0.5 : e, + a = 2 * o - 1, + r = i.a - s.a, + l = ((a * r == -1 ? a : (a + r) / (1 + a * r)) + 1) / 2; + (n = 1 - l), + (i.r = 255 & (l * i.r + n * s.r + 0.5)), + (i.g = 255 & (l * i.g + n * s.g + 0.5)), + (i.b = 255 & (l * i.b + n * s.b + 0.5)), + (i.a = o * i.a + (1 - o) * s.a), + (this.rgb = i); + } + return this; + } + interpolate(t, e) { + return ( + t && + (this._rgb = (function(t, e, i) { + const s = Yt(Mt(t.r)), + n = Yt(Mt(t.g)), + o = Yt(Mt(t.b)); + return { + r: vt($t(s + i * (Yt(Mt(e.r)) - s))), + g: vt($t(n + i * (Yt(Mt(e.g)) - n))), + b: vt($t(o + i * (Yt(Mt(e.b)) - o))), + a: t.a + i * (e.a - t.a) + }; + })(this._rgb, t._rgb, e)), + this + ); + } + clone() { + return new Gt(this.rgb); + } + alpha(t) { + return (this._rgb.a = vt(t)), this; + } + clearer(t) { + return (this._rgb.a *= 1 - t), this; + } + greyscale() { + const t = this._rgb, + e = xt(0.3 * t.r + 0.59 * t.g + 0.11 * t.b); + return (t.r = t.g = t.b = e), this; + } + opaquer(t) { + return (this._rgb.a *= 1 + t), this; + } + negate() { + const t = this._rgb; + return (t.r = 255 - t.r), (t.g = 255 - t.g), (t.b = 255 - t.b), this; + } + lighten(t) { + return Ut(this._rgb, 2, t), this; + } + darken(t) { + return Ut(this._rgb, 2, -t), this; + } + saturate(t) { + return Ut(this._rgb, 1, t), this; + } + desaturate(t) { + return Ut(this._rgb, 1, -t), this; + } + rotate(t) { + return ( + (function(t, e) { + var i = Rt(t); + (i[0] = Ft(i[0] + e)), + (i = zt(i)), + (t.r = i[0]), + (t.g = i[1]), + (t.b = i[2]); + })(this._rgb, t), + this + ); + } + } + function Zt(t) { + return new Gt(t); + } + function Jt(t) { + if (t && "object" == typeof t) { + const e = t.toString(); + return "[object CanvasPattern]" === e || "[object CanvasGradient]" === e; + } + return !1; + } + function Qt(t) { + return Jt(t) ? t : Zt(t); + } + function te(t) { + return Jt(t) + ? t + : Zt(t) + .saturate(0.5) + .darken(0.1) + .hexString(); + } + const ee = ["x", "y", "borderWidth", "radius", "tension"], + ie = ["color", "borderColor", "backgroundColor"]; + const se = new Map(); + function ne(t, e, i) { + return (function(t, e) { + e = e || {}; + const i = t + JSON.stringify(e); + let s = se.get(i); + return s || ((s = new Intl.NumberFormat(t, e)), se.set(i, s)), s; + })(e, i).format(t); + } + const oe = { + values: t => (s(t) ? t : "" + t), + numeric(t, e, i) { + if (0 === t) return "0"; + const s = this.chart.options.locale; + let n, + o = t; + if (i.length > 1) { + const e = Math.max( + Math.abs(i[0].value), + Math.abs(i[i.length - 1].value) + ); + (e < 1e-4 || e > 1e15) && (n = "scientific"), + (o = (function(t, e) { + let i = + e.length > 3 ? e[2].value - e[1].value : e[1].value - e[0].value; + Math.abs(i) >= 1 && t !== Math.floor(t) && (i = t - Math.floor(t)); + return i; + })(t, i)); + } + const a = I(Math.abs(o)), + r = Math.max(Math.min(-1 * Math.floor(a), 20), 0), + l = { notation: n, minimumFractionDigits: r, maximumFractionDigits: r }; + return Object.assign(l, this.options.ticks.format), ne(t, s, l); + }, + logarithmic(t, e, i) { + if (0 === t) return "0"; + const s = i[e].significand || t / Math.pow(10, Math.floor(I(t))); + return [1, 2, 3, 5, 10, 15].includes(s) || e > 0.8 * i.length + ? oe.numeric.call(this, t, e, i) + : ""; + } + }; + var ae = { formatters: oe }; + const re = Object.create(null), + le = Object.create(null); + function he(t, e) { + if (!e) return t; + const i = e.split("."); + for (let e = 0, s = i.length; e < s; ++e) { + const s = i[e]; + t = t[s] || (t[s] = Object.create(null)); + } + return t; + } + function ce(t, e, i) { + return "string" == typeof e ? m(he(t, e), i) : m(he(t, ""), e); + } + class de { + constructor(t, e) { + (this.animation = void 0), + (this.backgroundColor = "rgba(0,0,0,0.1)"), + (this.borderColor = "rgba(0,0,0,0.1)"), + (this.color = "#666"), + (this.datasets = {}), + (this.devicePixelRatio = t => t.chart.platform.getDevicePixelRatio()), + (this.elements = {}), + (this.events = [ + "mousemove", + "mouseout", + "click", + "touchstart", + "touchmove" + ]), + (this.font = { + family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + size: 12, + style: "normal", + lineHeight: 1.2, + weight: null + }), + (this.hover = {}), + (this.hoverBackgroundColor = (t, e) => te(e.backgroundColor)), + (this.hoverBorderColor = (t, e) => te(e.borderColor)), + (this.hoverColor = (t, e) => te(e.color)), + (this.indexAxis = "x"), + (this.interaction = { + mode: "nearest", + intersect: !0, + includeInvisible: !1 + }), + (this.maintainAspectRatio = !0), + (this.onHover = null), + (this.onClick = null), + (this.parsing = !0), + (this.plugins = {}), + (this.responsive = !0), + (this.scale = void 0), + (this.scales = {}), + (this.showLine = !0), + (this.drawActiveElementsOnTop = !0), + this.describe(t), + this.apply(e); + } + set(t, e) { + return ce(this, t, e); + } + get(t) { + return he(this, t); + } + describe(t, e) { + return ce(le, t, e); + } + override(t, e) { + return ce(re, t, e); + } + route(t, e, i, s) { + const o = he(this, t), + a = he(this, i), + l = "_" + e; + Object.defineProperties(o, { + [l]: { value: o[e], writable: !0 }, + [e]: { + enumerable: !0, + get() { + const t = this[l], + e = a[s]; + return n(t) ? Object.assign({}, e, t) : r(t, e); + }, + set(t) { + this[l] = t; + } + } + }); + } + apply(t) { + t.forEach(t => t(this)); + } + } + var ue = new de( + { + _scriptable: t => !t.startsWith("on"), + _indexable: t => "events" !== t, + hover: { _fallback: "interaction" }, + interaction: { _scriptable: !1, _indexable: !1 } + }, + [ + function(t) { + t.set("animation", { + delay: void 0, + duration: 1e3, + easing: "easeOutQuart", + fn: void 0, + from: void 0, + loop: void 0, + to: void 0, + type: void 0 + }), + t.describe("animation", { + _fallback: !1, + _indexable: !1, + _scriptable: t => + "onProgress" !== t && "onComplete" !== t && "fn" !== t + }), + t.set("animations", { + colors: { type: "color", properties: ie }, + numbers: { type: "number", properties: ee } + }), + t.describe("animations", { _fallback: "animation" }), + t.set("transitions", { + active: { animation: { duration: 400 } }, + resize: { animation: { duration: 0 } }, + show: { + animations: { + colors: { from: "transparent" }, + visible: { type: "boolean", duration: 0 } + } + }, + hide: { + animations: { + colors: { to: "transparent" }, + visible: { type: "boolean", easing: "linear", fn: t => 0 | t } + } + } + }); + }, + function(t) { + t.set("layout", { + autoPadding: !0, + padding: { top: 0, right: 0, bottom: 0, left: 0 } + }); + }, + function(t) { + t.set("scale", { + display: !0, + offset: !1, + reverse: !1, + beginAtZero: !1, + bounds: "ticks", + grace: 0, + grid: { + display: !0, + lineWidth: 1, + drawOnChartArea: !0, + drawTicks: !0, + tickLength: 8, + tickWidth: (t, e) => e.lineWidth, + tickColor: (t, e) => e.color, + offset: !1 + }, + border: { display: !0, dash: [], dashOffset: 0, width: 1 }, + title: { display: !1, text: "", padding: { top: 4, bottom: 4 } }, + ticks: { + minRotation: 0, + maxRotation: 50, + mirror: !1, + textStrokeWidth: 0, + textStrokeColor: "", + padding: 3, + display: !0, + autoSkip: !0, + autoSkipPadding: 3, + labelOffset: 0, + callback: ae.formatters.values, + minor: {}, + major: {}, + align: "center", + crossAlign: "near", + showLabelBackdrop: !1, + backdropColor: "rgba(255, 255, 255, 0.75)", + backdropPadding: 2 + } + }), + t.route("scale.ticks", "color", "", "color"), + t.route("scale.grid", "color", "", "borderColor"), + t.route("scale.border", "color", "", "borderColor"), + t.route("scale.title", "color", "", "color"), + t.describe("scale", { + _fallback: !1, + _scriptable: t => + !t.startsWith("before") && + !t.startsWith("after") && + "callback" !== t && + "parser" !== t, + _indexable: t => + "borderDash" !== t && "tickBorderDash" !== t && "dash" !== t + }), + t.describe("scales", { _fallback: "scale" }), + t.describe("scale.ticks", { + _scriptable: t => "backdropPadding" !== t && "callback" !== t, + _indexable: t => "backdropPadding" !== t + }); + } + ] + ); + function fe() { + return "undefined" != typeof window && "undefined" != typeof document; + } + function ge(t) { + let e = t.parentNode; + return e && "[object ShadowRoot]" === e.toString() && (e = e.host), e; + } + function pe(t, e, i) { + let s; + return ( + "string" == typeof t + ? ((s = parseInt(t, 10)), + -1 !== t.indexOf("%") && (s = (s / 100) * e.parentNode[i])) + : (s = t), + s + ); + } + const me = t => t.ownerDocument.defaultView.getComputedStyle(t, null); + function be(t, e) { + return me(t).getPropertyValue(e); + } + const xe = ["top", "right", "bottom", "left"]; + function _e(t, e, i) { + const s = {}; + i = i ? "-" + i : ""; + for (let n = 0; n < 4; n++) { + const o = xe[n]; + s[o] = parseFloat(t[e + "-" + o + i]) || 0; + } + return (s.width = s.left + s.right), (s.height = s.top + s.bottom), s; + } + function ye(t, e) { + if ("native" in t) return t; + const { canvas: i, currentDevicePixelRatio: s } = e, + n = me(i), + o = "border-box" === n.boxSizing, + a = _e(n, "padding"), + r = _e(n, "border", "width"), + { x: l, y: h, box: c } = (function(t, e) { + const i = t.touches, + s = i && i.length ? i[0] : t, + { offsetX: n, offsetY: o } = s; + let a, + r, + l = !1; + if ( + ((t, e, i) => (t > 0 || e > 0) && (!i || !i.shadowRoot))( + n, + o, + t.target + ) + ) + (a = n), (r = o); + else { + const t = e.getBoundingClientRect(); + (a = s.clientX - t.left), (r = s.clientY - t.top), (l = !0); + } + return { x: a, y: r, box: l }; + })(t, i), + d = a.left + (c && r.left), + u = a.top + (c && r.top); + let { width: f, height: g } = e; + return ( + o && ((f -= a.width + r.width), (g -= a.height + r.height)), + { + x: Math.round((((l - d) / f) * i.width) / s), + y: Math.round((((h - u) / g) * i.height) / s) + } + ); + } + const ve = t => Math.round(10 * t) / 10; + function Me(t, e, i, s) { + const n = me(t), + o = _e(n, "margin"), + a = pe(n.maxWidth, t, "clientWidth") || A, + r = pe(n.maxHeight, t, "clientHeight") || A, + l = (function(t, e, i) { + let s, n; + if (void 0 === e || void 0 === i) { + const o = ge(t); + if (o) { + const t = o.getBoundingClientRect(), + a = me(o), + r = _e(a, "border", "width"), + l = _e(a, "padding"); + (e = t.width - l.width - r.width), + (i = t.height - l.height - r.height), + (s = pe(a.maxWidth, o, "clientWidth")), + (n = pe(a.maxHeight, o, "clientHeight")); + } else (e = t.clientWidth), (i = t.clientHeight); + } + return { width: e, height: i, maxWidth: s || A, maxHeight: n || A }; + })(t, e, i); + let { width: h, height: c } = l; + if ("content-box" === n.boxSizing) { + const t = _e(n, "border", "width"), + e = _e(n, "padding"); + (h -= e.width + t.width), (c -= e.height + t.height); + } + (h = Math.max(0, h - o.width)), + (c = Math.max(0, s ? Math.floor(h / s) : c - o.height)), + (h = ve(Math.min(h, a, l.maxWidth))), + (c = ve(Math.min(c, r, l.maxHeight))), + h && !c && (c = ve(h / 2)); + return ( + (void 0 !== e || void 0 !== i) && + s && + l.height && + c > l.height && + ((c = l.height), (h = ve(Math.floor(c * s)))), + { width: h, height: c } + ); + } + function we(t, e, i) { + const s = e || 1, + n = Math.floor(t.height * s), + o = Math.floor(t.width * s); + (t.height = n / s), (t.width = o / s); + const a = t.canvas; + return ( + a.style && + (i || (!a.style.height && !a.style.width)) && + ((a.style.height = `${t.height}px`), (a.style.width = `${t.width}px`)), + (t.currentDevicePixelRatio !== s || a.height !== n || a.width !== o) && + ((t.currentDevicePixelRatio = s), + (a.height = n), + (a.width = o), + t.ctx.setTransform(s, 0, 0, s, 0, 0), + !0) + ); + } + const ke = (function() { + let t = !1; + try { + const e = { + get passive() { + return (t = !0), !1; + } + }; + window.addEventListener("test", null, e), + window.removeEventListener("test", null, e); + } catch (t) {} + return t; + })(); + function Se(t, e) { + const i = be(t, e), + s = i && i.match(/^(\d+)(\.\d+)?px$/); + return s ? +s[1] : void 0; + } + function Pe(t) { + return !t || i(t.size) || i(t.family) + ? null + : (t.style ? t.style + " " : "") + + (t.weight ? t.weight + " " : "") + + t.size + + "px " + + t.family; + } + function De(t, e, i, s, n) { + let o = e[n]; + return ( + o || ((o = e[n] = t.measureText(n).width), i.push(n)), o > s && (s = o), s + ); + } + function Ce(t, e, i, n) { + let o = ((n = n || {}).data = n.data || {}), + a = (n.garbageCollect = n.garbageCollect || []); + n.font !== e && + ((o = n.data = {}), (a = n.garbageCollect = []), (n.font = e)), + t.save(), + (t.font = e); + let r = 0; + const l = i.length; + let h, c, d, u, f; + for (h = 0; h < l; h++) + if (((u = i[h]), null != u && !0 !== s(u))) r = De(t, o, a, r, u); + else if (s(u)) + for (c = 0, d = u.length; c < d; c++) + (f = u[c]), null == f || s(f) || (r = De(t, o, a, r, f)); + t.restore(); + const g = a.length / 2; + if (g > i.length) { + for (h = 0; h < g; h++) delete o[a[h]]; + a.splice(0, g); + } + return r; + } + function Oe(t, e, i) { + const s = t.currentDevicePixelRatio, + n = 0 !== i ? Math.max(i / 2, 0.5) : 0; + return Math.round((e - n) * s) / s + n; + } + function Ae(t, e) { + (e = e || t.getContext("2d")).save(), + e.resetTransform(), + e.clearRect(0, 0, t.width, t.height), + e.restore(); + } + function Te(t, e, i, s) { + Le(t, e, i, s, null); + } + function Le(t, e, i, s, n) { + let o, a, r, l, h, c, d, u; + const f = e.pointStyle, + g = e.rotation, + p = e.radius; + let m = (g || 0) * T; + if ( + f && + "object" == typeof f && + ((o = f.toString()), + "[object HTMLImageElement]" === o || "[object HTMLCanvasElement]" === o) + ) + return ( + t.save(), + t.translate(i, s), + t.rotate(m), + t.drawImage(f, -f.width / 2, -f.height / 2, f.width, f.height), + void t.restore() + ); + if (!(isNaN(p) || p <= 0)) { + switch ((t.beginPath(), f)) { + default: + n ? t.ellipse(i, s, n / 2, p, 0, 0, C) : t.arc(i, s, p, 0, C), + t.closePath(); + break; + case "triangle": + (c = n ? n / 2 : p), + t.moveTo(i + Math.sin(m) * c, s - Math.cos(m) * p), + (m += R), + t.lineTo(i + Math.sin(m) * c, s - Math.cos(m) * p), + (m += R), + t.lineTo(i + Math.sin(m) * c, s - Math.cos(m) * p), + t.closePath(); + break; + case "rectRounded": + (h = 0.516 * p), + (l = p - h), + (a = Math.cos(m + E) * l), + (d = Math.cos(m + E) * (n ? n / 2 - h : l)), + (r = Math.sin(m + E) * l), + (u = Math.sin(m + E) * (n ? n / 2 - h : l)), + t.arc(i - d, s - r, h, m - D, m - L), + t.arc(i + u, s - a, h, m - L, m), + t.arc(i + d, s + r, h, m, m + L), + t.arc(i - u, s + a, h, m + L, m + D), + t.closePath(); + break; + case "rect": + if (!g) { + (l = Math.SQRT1_2 * p), + (c = n ? n / 2 : l), + t.rect(i - c, s - l, 2 * c, 2 * l); + break; + } + m += E; + case "rectRot": + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + u, s - a), + t.lineTo(i + d, s + r), + t.lineTo(i - u, s + a), + t.closePath(); + break; + case "crossRot": + m += E; + case "cross": + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + d, s + r), + t.moveTo(i + u, s - a), + t.lineTo(i - u, s + a); + break; + case "star": + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + d, s + r), + t.moveTo(i + u, s - a), + t.lineTo(i - u, s + a), + (m += E), + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + d, s + r), + t.moveTo(i + u, s - a), + t.lineTo(i - u, s + a); + break; + case "line": + (a = n ? n / 2 : Math.cos(m) * p), + (r = Math.sin(m) * p), + t.moveTo(i - a, s - r), + t.lineTo(i + a, s + r); + break; + case "dash": + t.moveTo(i, s), + t.lineTo(i + Math.cos(m) * (n ? n / 2 : p), s + Math.sin(m) * p); + } + t.fill(), e.borderWidth > 0 && t.stroke(); + } + } + function Ee(t, e, i) { + return ( + (i = i || 0.5), + !e || + (t && + t.x > e.left - i && + t.x < e.right + i && + t.y > e.top - i && + t.y < e.bottom + i) + ); + } + function Re(t, e) { + t.save(), + t.beginPath(), + t.rect(e.left, e.top, e.right - e.left, e.bottom - e.top), + t.clip(); + } + function Ie(t) { + t.restore(); + } + function ze(t, e, i, s, n) { + if (!e) return t.lineTo(i.x, i.y); + if ("middle" === n) { + const s = (e.x + i.x) / 2; + t.lineTo(s, e.y), t.lineTo(s, i.y); + } else ("after" === n) != !!s ? t.lineTo(e.x, i.y) : t.lineTo(i.x, e.y); + t.lineTo(i.x, i.y); + } + function Fe(t, e, i, s) { + if (!e) return t.lineTo(i.x, i.y); + t.bezierCurveTo( + s ? e.cp1x : e.cp2x, + s ? e.cp1y : e.cp2y, + s ? i.cp2x : i.cp1x, + s ? i.cp2y : i.cp1y, + i.x, + i.y + ); + } + function Ve(t, e, n, o, a, r = {}) { + const l = s(e) ? e : [e], + h = r.strokeWidth > 0 && "" !== r.strokeColor; + let c, d; + for ( + t.save(), + t.font = a.string, + (function(t, e) { + e.translation && t.translate(e.translation[0], e.translation[1]); + i(e.rotation) || t.rotate(e.rotation); + e.color && (t.fillStyle = e.color); + e.textAlign && (t.textAlign = e.textAlign); + e.textBaseline && (t.textBaseline = e.textBaseline); + })(t, r), + c = 0; + c < l.length; + ++c + ) + (d = l[c]), + r.backdrop && Ne(t, r.backdrop), + h && + (r.strokeColor && (t.strokeStyle = r.strokeColor), + i(r.strokeWidth) || (t.lineWidth = r.strokeWidth), + t.strokeText(d, n, o, r.maxWidth)), + t.fillText(d, n, o, r.maxWidth), + Be(t, n, o, d, r), + (o += a.lineHeight); + t.restore(); + } + function Be(t, e, i, s, n) { + if (n.strikethrough || n.underline) { + const o = t.measureText(s), + a = e - o.actualBoundingBoxLeft, + r = e + o.actualBoundingBoxRight, + l = i - o.actualBoundingBoxAscent, + h = i + o.actualBoundingBoxDescent, + c = n.strikethrough ? (l + h) / 2 : h; + (t.strokeStyle = t.fillStyle), + t.beginPath(), + (t.lineWidth = n.decorationWidth || 2), + t.moveTo(a, c), + t.lineTo(r, c), + t.stroke(); + } + } + function Ne(t, e) { + const i = t.fillStyle; + (t.fillStyle = e.color), + t.fillRect(e.left, e.top, e.width, e.height), + (t.fillStyle = i); + } + function We(t, e) { + const { x: i, y: s, w: n, h: o, radius: a } = e; + t.arc(i + a.topLeft, s + a.topLeft, a.topLeft, -L, D, !0), + t.lineTo(i, s + o - a.bottomLeft), + t.arc(i + a.bottomLeft, s + o - a.bottomLeft, a.bottomLeft, D, L, !0), + t.lineTo(i + n - a.bottomRight, s + o), + t.arc( + i + n - a.bottomRight, + s + o - a.bottomRight, + a.bottomRight, + L, + 0, + !0 + ), + t.lineTo(i + n, s + a.topRight), + t.arc(i + n - a.topRight, s + a.topRight, a.topRight, 0, -L, !0), + t.lineTo(i + a.topLeft, s); + } + function He(t, e = [""], i = t, s, n = () => t[0]) { + w(s) || (s = Qe("_fallback", t)); + const o = { + [Symbol.toStringTag]: "Object", + _cacheable: !0, + _scopes: t, + _rootScopes: i, + _fallback: s, + _getTarget: n, + override: n => He([n, ...t], e, i, s) + }; + return new Proxy(o, { + deleteProperty: (e, i) => ( + delete e[i], delete e._keys, delete t[0][i], !0 + ), + get: (i, s) => + Xe(i, s, () => + (function(t, e, i, s) { + let n; + for (const o of e) + if (((n = Qe(Ye(o, t), i)), w(n))) + return Ue(t, n) ? Ze(i, s, t, n) : n; + })(s, e, t, i) + ), + getOwnPropertyDescriptor: (t, e) => + Reflect.getOwnPropertyDescriptor(t._scopes[0], e), + getPrototypeOf: () => Reflect.getPrototypeOf(t[0]), + has: (t, e) => ti(t).includes(e), + ownKeys: t => ti(t), + set(t, e, i) { + const s = t._storage || (t._storage = n()); + return (t[e] = s[e] = i), delete t._keys, !0; + } + }); + } + function je(t, e, i, o) { + const a = { + _cacheable: !1, + _proxy: t, + _context: e, + _subProxy: i, + _stack: new Set(), + _descriptors: $e(t, o), + setContext: e => je(t, e, i, o), + override: s => je(t.override(s), e, i, o) + }; + return new Proxy(a, { + deleteProperty: (e, i) => (delete e[i], delete t[i], !0), + get: (t, e, i) => + Xe(t, e, () => + (function(t, e, i) { + const { _proxy: o, _context: a, _subProxy: r, _descriptors: l } = t; + let h = o[e]; + k(h) && + l.isScriptable(e) && + (h = (function(t, e, i, s) { + const { _proxy: n, _context: o, _subProxy: a, _stack: r } = i; + if (r.has(t)) + throw new Error( + "Recursion detected: " + Array.from(r).join("->") + "->" + t + ); + r.add(t), + (e = e(o, a || s)), + r.delete(t), + Ue(t, e) && (e = Ze(n._scopes, n, t, e)); + return e; + })(e, h, t, i)); + s(h) && + h.length && + (h = (function(t, e, i, s) { + const { + _proxy: o, + _context: a, + _subProxy: r, + _descriptors: l + } = i; + if (w(a.index) && s(t)) e = e[a.index % e.length]; + else if (n(e[0])) { + const i = e, + s = o._scopes.filter(t => t !== i); + e = []; + for (const n of i) { + const i = Ze(s, o, t, n); + e.push(je(i, a, r && r[t], l)); + } + } + return e; + })(e, h, t, l.isIndexable)); + Ue(e, h) && (h = je(h, a, r && r[e], l)); + return h; + })(t, e, i) + ), + getOwnPropertyDescriptor: (e, i) => + e._descriptors.allKeys + ? Reflect.has(t, i) + ? { enumerable: !0, configurable: !0 } + : void 0 + : Reflect.getOwnPropertyDescriptor(t, i), + getPrototypeOf: () => Reflect.getPrototypeOf(t), + has: (e, i) => Reflect.has(t, i), + ownKeys: () => Reflect.ownKeys(t), + set: (e, i, s) => ((t[i] = s), delete e[i], !0) + }); + } + function $e(t, e = { scriptable: !0, indexable: !0 }) { + const { + _scriptable: i = e.scriptable, + _indexable: s = e.indexable, + _allKeys: n = e.allKeys + } = t; + return { + allKeys: n, + scriptable: i, + indexable: s, + isScriptable: k(i) ? i : () => i, + isIndexable: k(s) ? s : () => s + }; + } + const Ye = (t, e) => (t ? t + M(e) : e), + Ue = (t, e) => + n(e) && + "adapters" !== t && + (null === Object.getPrototypeOf(e) || e.constructor === Object); + function Xe(t, e, i) { + if (Object.prototype.hasOwnProperty.call(t, e)) return t[e]; + const s = i(); + return (t[e] = s), s; + } + function qe(t, e, i) { + return k(t) ? t(e, i) : t; + } + const Ke = (t, e) => (!0 === t ? e : "string" == typeof t ? v(e, t) : void 0); + function Ge(t, e, i, s, n) { + for (const o of e) { + const e = Ke(i, o); + if (e) { + t.add(e); + const o = qe(e._fallback, i, n); + if (w(o) && o !== i && o !== s) return o; + } else if (!1 === e && w(s) && i !== s) return null; + } + return !1; + } + function Ze(t, e, i, o) { + const a = e._rootScopes, + r = qe(e._fallback, i, o), + l = [...t, ...a], + h = new Set(); + h.add(o); + let c = Je(h, l, i, r || i, o); + return ( + null !== c && + (!w(r) || r === i || ((c = Je(h, l, r, c, o)), null !== c)) && + He(Array.from(h), [""], a, r, () => + (function(t, e, i) { + const o = t._getTarget(); + e in o || (o[e] = {}); + const a = o[e]; + if (s(a) && n(i)) return i; + return a || {}; + })(e, i, o) + ) + ); + } + function Je(t, e, i, s, n) { + for (; i; ) i = Ge(t, e, i, s, n); + return i; + } + function Qe(t, e) { + for (const i of e) { + if (!i) continue; + const e = i[t]; + if (w(e)) return e; + } + } + function ti(t) { + let e = t._keys; + return ( + e || + (e = t._keys = (function(t) { + const e = new Set(); + for (const i of t) + for (const t of Object.keys(i).filter(t => !t.startsWith("_"))) + e.add(t); + return Array.from(e); + })(t._scopes)), + e + ); + } + function ei(t, e, i, s) { + const { iScale: n } = t, + { key: o = "r" } = this._parsing, + a = new Array(s); + let r, l, h, c; + for (r = 0, l = s; r < l; ++r) + (h = r + i), (c = e[h]), (a[r] = { r: n.parse(v(c, o), h) }); + return a; + } + const ii = Number.EPSILON || 1e-14, + si = (t, e) => e < t.length && !t[e].skip && t[e], + ni = t => ("x" === t ? "y" : "x"); + function oi(t, e, i, s) { + const n = t.skip ? e : t, + o = e, + a = i.skip ? e : i, + r = X(o, n), + l = X(a, o); + let h = r / (r + l), + c = l / (r + l); + (h = isNaN(h) ? 0 : h), (c = isNaN(c) ? 0 : c); + const d = s * h, + u = s * c; + return { + previous: { x: o.x - d * (a.x - n.x), y: o.y - d * (a.y - n.y) }, + next: { x: o.x + u * (a.x - n.x), y: o.y + u * (a.y - n.y) } + }; + } + function ai(t, e = "x") { + const i = ni(e), + s = t.length, + n = Array(s).fill(0), + o = Array(s); + let a, + r, + l, + h = si(t, 0); + for (a = 0; a < s; ++a) + if (((r = l), (l = h), (h = si(t, a + 1)), l)) { + if (h) { + const t = h[e] - l[e]; + n[a] = 0 !== t ? (h[i] - l[i]) / t : 0; + } + o[a] = r + ? h + ? z(n[a - 1]) !== z(n[a]) + ? 0 + : (n[a - 1] + n[a]) / 2 + : n[a - 1] + : n[a]; + } + !(function(t, e, i) { + const s = t.length; + let n, + o, + a, + r, + l, + h = si(t, 0); + for (let c = 0; c < s - 1; ++c) + (l = h), + (h = si(t, c + 1)), + l && + h && + (F(e[c], 0, ii) + ? (i[c] = i[c + 1] = 0) + : ((n = i[c] / e[c]), + (o = i[c + 1] / e[c]), + (r = Math.pow(n, 2) + Math.pow(o, 2)), + r <= 9 || + ((a = 3 / Math.sqrt(r)), + (i[c] = n * a * e[c]), + (i[c + 1] = o * a * e[c])))); + })(t, n, o), + (function(t, e, i = "x") { + const s = ni(i), + n = t.length; + let o, + a, + r, + l = si(t, 0); + for (let h = 0; h < n; ++h) { + if (((a = r), (r = l), (l = si(t, h + 1)), !r)) continue; + const n = r[i], + c = r[s]; + a && + ((o = (n - a[i]) / 3), + (r[`cp1${i}`] = n - o), + (r[`cp1${s}`] = c - o * e[h])), + l && + ((o = (l[i] - n) / 3), + (r[`cp2${i}`] = n + o), + (r[`cp2${s}`] = c + o * e[h])); + } + })(t, o, e); + } + function ri(t, e, i) { + return Math.max(Math.min(t, i), e); + } + function li(t, e, i, s, n) { + let o, a, r, l; + if ( + (e.spanGaps && (t = t.filter(t => !t.skip)), + "monotone" === e.cubicInterpolationMode) + ) + ai(t, n); + else { + let i = s ? t[t.length - 1] : t[0]; + for (o = 0, a = t.length; o < a; ++o) + (r = t[o]), + (l = oi(i, r, t[Math.min(o + 1, a - (s ? 0 : 1)) % a], e.tension)), + (r.cp1x = l.previous.x), + (r.cp1y = l.previous.y), + (r.cp2x = l.next.x), + (r.cp2y = l.next.y), + (i = r); + } + e.capBezierPoints && + (function(t, e) { + let i, + s, + n, + o, + a, + r = Ee(t[0], e); + for (i = 0, s = t.length; i < s; ++i) + (a = o), + (o = r), + (r = i < s - 1 && Ee(t[i + 1], e)), + o && + ((n = t[i]), + a && + ((n.cp1x = ri(n.cp1x, e.left, e.right)), + (n.cp1y = ri(n.cp1y, e.top, e.bottom))), + r && + ((n.cp2x = ri(n.cp2x, e.left, e.right)), + (n.cp2y = ri(n.cp2y, e.top, e.bottom)))); + })(t, i); + } + const hi = t => 0 === t || 1 === t, + ci = (t, e, i) => -Math.pow(2, 10 * (t -= 1)) * Math.sin(((t - e) * C) / i), + di = (t, e, i) => Math.pow(2, -10 * t) * Math.sin(((t - e) * C) / i) + 1, + ui = { + linear: t => t, + easeInQuad: t => t * t, + easeOutQuad: t => -t * (t - 2), + easeInOutQuad: t => + (t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1), + easeInCubic: t => t * t * t, + easeOutCubic: t => (t -= 1) * t * t + 1, + easeInOutCubic: t => + (t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2), + easeInQuart: t => t * t * t * t, + easeOutQuart: t => -((t -= 1) * t * t * t - 1), + easeInOutQuart: t => + (t /= 0.5) < 1 + ? 0.5 * t * t * t * t + : -0.5 * ((t -= 2) * t * t * t - 2), + easeInQuint: t => t * t * t * t * t, + easeOutQuint: t => (t -= 1) * t * t * t * t + 1, + easeInOutQuint: t => + (t /= 0.5) < 1 + ? 0.5 * t * t * t * t * t + : 0.5 * ((t -= 2) * t * t * t * t + 2), + easeInSine: t => 1 - Math.cos(t * L), + easeOutSine: t => Math.sin(t * L), + easeInOutSine: t => -0.5 * (Math.cos(D * t) - 1), + easeInExpo: t => (0 === t ? 0 : Math.pow(2, 10 * (t - 1))), + easeOutExpo: t => (1 === t ? 1 : 1 - Math.pow(2, -10 * t)), + easeInOutExpo: t => + hi(t) + ? t + : t < 0.5 + ? 0.5 * Math.pow(2, 10 * (2 * t - 1)) + : 0.5 * (2 - Math.pow(2, -10 * (2 * t - 1))), + easeInCirc: t => (t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1)), + easeOutCirc: t => Math.sqrt(1 - (t -= 1) * t), + easeInOutCirc: t => + (t /= 0.5) < 1 + ? -0.5 * (Math.sqrt(1 - t * t) - 1) + : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1), + easeInElastic: t => (hi(t) ? t : ci(t, 0.075, 0.3)), + easeOutElastic: t => (hi(t) ? t : di(t, 0.075, 0.3)), + easeInOutElastic(t) { + const e = 0.1125; + return hi(t) + ? t + : t < 0.5 + ? 0.5 * ci(2 * t, e, 0.45) + : 0.5 + 0.5 * di(2 * t - 1, e, 0.45); + }, + easeInBack(t) { + const e = 1.70158; + return t * t * ((e + 1) * t - e); + }, + easeOutBack(t) { + const e = 1.70158; + return (t -= 1) * t * ((e + 1) * t + e) + 1; + }, + easeInOutBack(t) { + let e = 1.70158; + return (t /= 0.5) < 1 + ? t * t * ((1 + (e *= 1.525)) * t - e) * 0.5 + : 0.5 * ((t -= 2) * t * ((1 + (e *= 1.525)) * t + e) + 2); + }, + easeInBounce: t => 1 - ui.easeOutBounce(1 - t), + easeOutBounce(t) { + const e = 7.5625, + i = 2.75; + return t < 1 / i + ? e * t * t + : t < 2 / i + ? e * (t -= 1.5 / i) * t + 0.75 + : t < 2.5 / i + ? e * (t -= 2.25 / i) * t + 0.9375 + : e * (t -= 2.625 / i) * t + 0.984375; + }, + easeInOutBounce: t => + t < 0.5 + ? 0.5 * ui.easeInBounce(2 * t) + : 0.5 * ui.easeOutBounce(2 * t - 1) + 0.5 + }; + var fi = ui; + function gi(t, e, i, s) { + return { x: t.x + i * (e.x - t.x), y: t.y + i * (e.y - t.y) }; + } + function pi(t, e, i, s) { + return { + x: t.x + i * (e.x - t.x), + y: + "middle" === s + ? i < 0.5 + ? t.y + : e.y + : "after" === s + ? i < 1 + ? t.y + : e.y + : i > 0 + ? e.y + : t.y + }; + } + function mi(t, e, i, s) { + const n = { x: t.cp2x, y: t.cp2y }, + o = { x: e.cp1x, y: e.cp1y }, + a = gi(t, n, i), + r = gi(n, o, i), + l = gi(o, e, i), + h = gi(a, r, i), + c = gi(r, l, i); + return gi(h, c, i); + } + const bi = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/, + xi = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/; + function _i(t, e) { + const i = ("" + t).match(bi); + if (!i || "normal" === i[1]) return 1.2 * e; + switch (((t = +i[2]), i[3])) { + case "px": + return t; + case "%": + t /= 100; + } + return e * t; + } + function yi(t, e) { + const i = {}, + s = n(e), + o = s ? Object.keys(e) : e, + a = n(t) ? (s ? i => r(t[i], t[e[i]]) : e => t[e]) : () => t; + for (const t of o) i[t] = +a(t) || 0; + return i; + } + function vi(t) { + return yi(t, { top: "y", right: "x", bottom: "y", left: "x" }); + } + function Mi(t) { + return yi(t, ["topLeft", "topRight", "bottomLeft", "bottomRight"]); + } + function wi(t) { + const e = vi(t); + return (e.width = e.left + e.right), (e.height = e.top + e.bottom), e; + } + function ki(t, e) { + (t = t || {}), (e = e || ue.font); + let i = r(t.size, e.size); + "string" == typeof i && (i = parseInt(i, 10)); + let s = r(t.style, e.style); + s && + !("" + s).match(xi) && + (console.warn('Invalid font style specified: "' + s + '"'), (s = void 0)); + const n = { + family: r(t.family, e.family), + lineHeight: _i(r(t.lineHeight, e.lineHeight), i), + size: i, + style: s, + weight: r(t.weight, e.weight), + string: "" + }; + return (n.string = Pe(n)), n; + } + function Si(t, e, i, n) { + let o, + a, + r, + l = !0; + for (o = 0, a = t.length; o < a; ++o) + if ( + ((r = t[o]), + void 0 !== r && + (void 0 !== e && "function" == typeof r && ((r = r(e)), (l = !1)), + void 0 !== i && s(r) && ((r = r[i % r.length]), (l = !1)), + void 0 !== r)) + ) + return n && !l && (n.cacheable = !1), r; + } + function Pi(t, e, i) { + const { min: s, max: n } = t, + o = h(e, (n - s) / 2), + a = (t, e) => (i && 0 === t ? 0 : t + e); + return { min: a(s, -Math.abs(o)), max: a(n, o) }; + } + function Di(t, e) { + return Object.assign(Object.create(t), e); + } + function Ci(t, e, i) { + return t + ? (function(t, e) { + return { + x: i => t + t + e - i, + setWidth(t) { + e = t; + }, + textAlign: t => + "center" === t ? t : "right" === t ? "left" : "right", + xPlus: (t, e) => t - e, + leftForLtr: (t, e) => t - e + }; + })(e, i) + : { + x: t => t, + setWidth(t) {}, + textAlign: t => t, + xPlus: (t, e) => t + e, + leftForLtr: (t, e) => t + }; + } + function Oi(t, e) { + let i, s; + ("ltr" !== e && "rtl" !== e) || + ((i = t.canvas.style), + (s = [ + i.getPropertyValue("direction"), + i.getPropertyPriority("direction") + ]), + i.setProperty("direction", e, "important"), + (t.prevTextDirection = s)); + } + function Ai(t, e) { + void 0 !== e && + (delete t.prevTextDirection, + t.canvas.style.setProperty("direction", e[0], e[1])); + } + function Ti(t) { + return "angle" === t + ? { between: G, compare: q, normalize: K } + : { between: Q, compare: (t, e) => t - e, normalize: t => t }; + } + function Li({ start: t, end: e, count: i, loop: s, style: n }) { + return { + start: t % i, + end: e % i, + loop: s && (e - t + 1) % i == 0, + style: n + }; + } + function Ei(t, e, i) { + if (!i) return [t]; + const { property: s, start: n, end: o } = i, + a = e.length, + { compare: r, between: l, normalize: h } = Ti(s), + { start: c, end: d, loop: u, style: f } = (function(t, e, i) { + const { property: s, start: n, end: o } = i, + { between: a, normalize: r } = Ti(s), + l = e.length; + let h, + c, + { start: d, end: u, loop: f } = t; + if (f) { + for ( + d += l, u += l, h = 0, c = l; + h < c && a(r(e[d % l][s]), n, o); + ++h + ) + d--, u--; + (d %= l), (u %= l); + } + return u < d && (u += l), { start: d, end: u, loop: f, style: t.style }; + })(t, e, i), + g = []; + let p, + m, + b, + x = !1, + _ = null; + const y = () => x || (l(n, b, p) && 0 !== r(n, b)), + v = () => !x || 0 === r(o, p) || l(o, b, p); + for (let t = c, i = c; t <= d; ++t) + (m = e[t % a]), + m.skip || + ((p = h(m[s])), + p !== b && + ((x = l(p, n, o)), + null === _ && y() && (_ = 0 === r(p, n) ? t : i), + null !== _ && + v() && + (g.push(Li({ start: _, end: t, loop: u, count: a, style: f })), + (_ = null)), + (i = t), + (b = p))); + return ( + null !== _ && + g.push(Li({ start: _, end: d, loop: u, count: a, style: f })), + g + ); + } + function Ri(t, e) { + const i = [], + s = t.segments; + for (let n = 0; n < s.length; n++) { + const o = Ei(s[n], t.points, e); + o.length && i.push(...o); + } + return i; + } + function Ii(t, e) { + const i = t.points, + s = t.options.spanGaps, + n = i.length; + if (!n) return []; + const o = !!t._loop, + { start: a, end: r } = (function(t, e, i, s) { + let n = 0, + o = e - 1; + if (i && !s) for (; n < e && !t[n].skip; ) n++; + for (; n < e && t[n].skip; ) n++; + for (n %= e, i && (o += n); o > n && t[o % e].skip; ) o--; + return (o %= e), { start: n, end: o }; + })(i, n, o, s); + if (!0 === s) return zi(t, [{ start: a, end: r, loop: o }], i, e); + return zi( + t, + (function(t, e, i, s) { + const n = t.length, + o = []; + let a, + r = e, + l = t[e]; + for (a = e + 1; a <= i; ++a) { + const i = t[a % n]; + i.skip || i.stop + ? l.skip || + ((s = !1), + o.push({ start: e % n, end: (a - 1) % n, loop: s }), + (e = r = i.stop ? a : null)) + : ((r = a), l.skip && (e = a)), + (l = i); + } + return null !== r && o.push({ start: e % n, end: r % n, loop: s }), o; + })(i, a, r < a ? r + n : r, !!t._fullLoop && 0 === a && r === n - 1), + i, + e + ); + } + function zi(t, e, i, s) { + return s && s.setContext && i + ? (function(t, e, i, s) { + const n = t._chart.getContext(), + o = Fi(t.options), + { + _datasetIndex: a, + options: { spanGaps: r } + } = t, + l = i.length, + h = []; + let c = o, + d = e[0].start, + u = d; + function f(t, e, s, n) { + const o = r ? -1 : 1; + if (t !== e) { + for (t += l; i[t % l].skip; ) t -= o; + for (; i[e % l].skip; ) e += o; + t % l != e % l && + (h.push({ start: t % l, end: e % l, loop: s, style: n }), + (c = n), + (d = e % l)); + } + } + for (const t of e) { + d = r ? d : t.start; + let e, + o = i[d % l]; + for (u = d + 1; u <= t.end; u++) { + const r = i[u % l]; + (e = Fi( + s.setContext( + Di(n, { + type: "segment", + p0: o, + p1: r, + p0DataIndex: (u - 1) % l, + p1DataIndex: u % l, + datasetIndex: a + }) + ) + )), + Vi(e, c) && f(d, u - 1, t.loop, c), + (o = r), + (c = e); + } + d < u - 1 && f(d, u - 1, t.loop, c); + } + return h; + })(t, e, i, s) + : e; + } + function Fi(t) { + return { + backgroundColor: t.backgroundColor, + borderCapStyle: t.borderCapStyle, + borderDash: t.borderDash, + borderDashOffset: t.borderDashOffset, + borderJoinStyle: t.borderJoinStyle, + borderWidth: t.borderWidth, + borderColor: t.borderColor + }; + } + function Vi(t, e) { + return e && JSON.stringify(t) !== JSON.stringify(e); + } + var Bi = Object.freeze({ + __proto__: null, + easingEffects: fi, + isPatternOrGradient: Jt, + color: Qt, + getHoverColor: te, + noop: t, + uid: e, + isNullOrUndef: i, + isArray: s, + isObject: n, + isFinite: o, + finiteOrDefault: a, + valueOrDefault: r, + toPercentage: l, + toDimension: h, + callback: c, + each: d, + _elementsEqual: u, + clone: f, + _merger: p, + merge: m, + mergeIf: b, + _mergerIf: x, + _deprecated: function(t, e, i, s) { + void 0 !== e && + console.warn( + t + ': "' + i + '" is deprecated. Please use "' + s + '" instead' + ); + }, + _splitKey: y, + resolveObjectKey: v, + _capitalize: M, + defined: w, + isFunction: k, + setsEqual: S, + _isClickEvent: P, + toFontString: Pe, + _measureText: De, + _longestText: Ce, + _alignPixel: Oe, + clearCanvas: Ae, + drawPoint: Te, + drawPointLegend: Le, + _isPointInArea: Ee, + clipArea: Re, + unclipArea: Ie, + _steppedLineTo: ze, + _bezierCurveTo: Fe, + renderText: Ve, + addRoundedRectPath: We, + _lookup: tt, + _lookupByKey: et, + _rlookupByKey: it, + _filterBetween: st, + listenArrayEvents: ot, + unlistenArrayEvents: at, + _arrayUnique: rt, + _createResolver: He, + _attachContext: je, + _descriptors: $e, + _parseObjectDataRadialScale: ei, + splineCurve: oi, + splineCurveMonotone: ai, + _updateBezierControlPoints: li, + _isDomSupported: fe, + _getParentNode: ge, + getStyle: be, + getRelativePosition: ye, + getMaximumSize: Me, + retinaScale: we, + supportsEventListenerOptions: ke, + readUsedSize: Se, + fontString: function(t, e, i) { + return e + " " + t + "px " + i; + }, + requestAnimFrame: lt, + throttled: ht, + debounce: ct, + _toLeftRightCenter: dt, + _alignStartEnd: ut, + _textX: ft, + _getStartAndCountOfVisiblePoints: gt, + _scaleRangesChanged: pt, + _pointInLine: gi, + _steppedInterpolation: pi, + _bezierInterpolation: mi, + formatNumber: ne, + toLineHeight: _i, + _readValueToProps: yi, + toTRBL: vi, + toTRBLCorners: Mi, + toPadding: wi, + toFont: ki, + resolve: Si, + _addGrace: Pi, + createContext: Di, + PI: D, + TAU: C, + PITAU: O, + INFINITY: A, + RAD_PER_DEG: T, + HALF_PI: L, + QUARTER_PI: E, + TWO_THIRDS_PI: R, + log10: I, + sign: z, + almostEquals: F, + niceNum: V, + _factorize: B, + isNumber: N, + almostWhole: W, + _setMinAndMaxByKey: H, + toRadians: j, + toDegrees: $, + _decimalPlaces: Y, + getAngleFromPoint: U, + distanceBetweenPoints: X, + _angleDiff: q, + _normalizeAngle: K, + _angleBetween: G, + _limitValue: Z, + _int16Range: J, + _isBetween: Q, + getRtlAdapter: Ci, + overrideTextDirection: Oi, + restoreTextDirection: Ai, + _boundSegment: Ei, + _boundSegments: Ri, + _computeSegments: Ii + }); + function Ni(t, e, i, s) { + const { controller: n, data: o, _sorted: a } = t, + r = n._cachedMeta.iScale; + if (r && e === r.axis && "r" !== e && a && o.length) { + const t = r._reversePixels ? it : et; + if (!s) return t(o, e, i); + if (n._sharedOptions) { + const s = o[0], + n = "function" == typeof s.getRange && s.getRange(e); + if (n) { + const s = t(o, e, i - n), + a = t(o, e, i + n); + return { lo: s.lo, hi: a.hi }; + } + } + } + return { lo: 0, hi: o.length - 1 }; + } + function Wi(t, e, i, s, n) { + const o = t.getSortedVisibleDatasetMetas(), + a = i[e]; + for (let t = 0, i = o.length; t < i; ++t) { + const { index: i, data: r } = o[t], + { lo: l, hi: h } = Ni(o[t], e, a, n); + for (let t = l; t <= h; ++t) { + const e = r[t]; + e.skip || s(e, i, t); + } + } + } + function Hi(t, e, i, s, n) { + const o = []; + if (!n && !t.isPointInArea(e)) return o; + return ( + Wi( + t, + i, + e, + function(i, a, r) { + (n || Ee(i, t.chartArea, 0)) && + i.inRange(e.x, e.y, s) && + o.push({ element: i, datasetIndex: a, index: r }); + }, + !0 + ), + o + ); + } + function ji(t, e, i, s, n, o) { + let a = []; + const r = (function(t) { + const e = -1 !== t.indexOf("x"), + i = -1 !== t.indexOf("y"); + return function(t, s) { + const n = e ? Math.abs(t.x - s.x) : 0, + o = i ? Math.abs(t.y - s.y) : 0; + return Math.sqrt(Math.pow(n, 2) + Math.pow(o, 2)); + }; + })(i); + let l = Number.POSITIVE_INFINITY; + return ( + Wi(t, i, e, function(i, h, c) { + const d = i.inRange(e.x, e.y, n); + if (s && !d) return; + const u = i.getCenterPoint(n); + if (!(!!o || t.isPointInArea(u)) && !d) return; + const f = r(e, u); + f < l + ? ((a = [{ element: i, datasetIndex: h, index: c }]), (l = f)) + : f === l && a.push({ element: i, datasetIndex: h, index: c }); + }), + a + ); + } + function $i(t, e, i, s, n, o) { + return o || t.isPointInArea(e) + ? "r" !== i || s + ? ji(t, e, i, s, n, o) + : (function(t, e, i, s) { + let n = []; + return ( + Wi(t, i, e, function(t, i, o) { + const { startAngle: a, endAngle: r } = t.getProps( + ["startAngle", "endAngle"], + s + ), + { angle: l } = U(t, { x: e.x, y: e.y }); + G(l, a, r) && n.push({ element: t, datasetIndex: i, index: o }); + }), + n + ); + })(t, e, i, n) + : []; + } + function Yi(t, e, i, s, n) { + const o = [], + a = "x" === i ? "inXRange" : "inYRange"; + let r = !1; + return ( + Wi(t, i, e, (t, s, l) => { + t[a](e[i], n) && + (o.push({ element: t, datasetIndex: s, index: l }), + (r = r || t.inRange(e.x, e.y, n))); + }), + s && !r ? [] : o + ); + } + var Ui = { + evaluateInteractionItems: Wi, + modes: { + index(t, e, i, s) { + const n = ye(e, t), + o = i.axis || "x", + a = i.includeInvisible || !1, + r = i.intersect ? Hi(t, n, o, s, a) : $i(t, n, o, !1, s, a), + l = []; + return r.length + ? (t.getSortedVisibleDatasetMetas().forEach(t => { + const e = r[0].index, + i = t.data[e]; + i && + !i.skip && + l.push({ element: i, datasetIndex: t.index, index: e }); + }), + l) + : []; + }, + dataset(t, e, i, s) { + const n = ye(e, t), + o = i.axis || "xy", + a = i.includeInvisible || !1; + let r = i.intersect ? Hi(t, n, o, s, a) : $i(t, n, o, !1, s, a); + if (r.length > 0) { + const e = r[0].datasetIndex, + i = t.getDatasetMeta(e).data; + r = []; + for (let t = 0; t < i.length; ++t) + r.push({ element: i[t], datasetIndex: e, index: t }); + } + return r; + }, + point: (t, e, i, s) => + Hi(t, ye(e, t), i.axis || "xy", s, i.includeInvisible || !1), + nearest(t, e, i, s) { + const n = ye(e, t), + o = i.axis || "xy", + a = i.includeInvisible || !1; + return $i(t, n, o, i.intersect, s, a); + }, + x: (t, e, i, s) => Yi(t, ye(e, t), "x", i.intersect, s), + y: (t, e, i, s) => Yi(t, ye(e, t), "y", i.intersect, s) + } + }; + const Xi = ["left", "top", "right", "bottom"]; + function qi(t, e) { + return t.filter(t => t.pos === e); + } + function Ki(t, e) { + return t.filter(t => -1 === Xi.indexOf(t.pos) && t.box.axis === e); + } + function Gi(t, e) { + return t.sort((t, i) => { + const s = e ? i : t, + n = e ? t : i; + return s.weight === n.weight ? s.index - n.index : s.weight - n.weight; + }); + } + function Zi(t, e) { + const i = (function(t) { + const e = {}; + for (const i of t) { + const { stack: t, pos: s, stackWeight: n } = i; + if (!t || !Xi.includes(s)) continue; + const o = + e[t] || (e[t] = { count: 0, placed: 0, weight: 0, size: 0 }); + o.count++, (o.weight += n); + } + return e; + })(t), + { vBoxMaxWidth: s, hBoxMaxHeight: n } = e; + let o, a, r; + for (o = 0, a = t.length; o < a; ++o) { + r = t[o]; + const { fullSize: a } = r.box, + l = i[r.stack], + h = l && r.stackWeight / l.weight; + r.horizontal + ? ((r.width = h ? h * s : a && e.availableWidth), (r.height = n)) + : ((r.width = s), (r.height = h ? h * n : a && e.availableHeight)); + } + return i; + } + function Ji(t, e, i, s) { + return Math.max(t[i], e[i]) + Math.max(t[s], e[s]); + } + function Qi(t, e) { + (t.top = Math.max(t.top, e.top)), + (t.left = Math.max(t.left, e.left)), + (t.bottom = Math.max(t.bottom, e.bottom)), + (t.right = Math.max(t.right, e.right)); + } + function ts(t, e, i, s) { + const { pos: o, box: a } = i, + r = t.maxPadding; + if (!n(o)) { + i.size && (t[o] -= i.size); + const e = s[i.stack] || { size: 0, count: 1 }; + (e.size = Math.max(e.size, i.horizontal ? a.height : a.width)), + (i.size = e.size / e.count), + (t[o] += i.size); + } + a.getPadding && Qi(r, a.getPadding()); + const l = Math.max(0, e.outerWidth - Ji(r, t, "left", "right")), + h = Math.max(0, e.outerHeight - Ji(r, t, "top", "bottom")), + c = l !== t.w, + d = h !== t.h; + return ( + (t.w = l), + (t.h = h), + i.horizontal ? { same: c, other: d } : { same: d, other: c } + ); + } + function es(t, e) { + const i = e.maxPadding; + function s(t) { + const s = { left: 0, top: 0, right: 0, bottom: 0 }; + return ( + t.forEach(t => { + s[t] = Math.max(e[t], i[t]); + }), + s + ); + } + return s(t ? ["left", "right"] : ["top", "bottom"]); + } + function is(t, e, i, s) { + const n = []; + let o, a, r, l, h, c; + for (o = 0, a = t.length, h = 0; o < a; ++o) { + (r = t[o]), + (l = r.box), + l.update(r.width || e.w, r.height || e.h, es(r.horizontal, e)); + const { same: a, other: d } = ts(e, i, r, s); + (h |= a && n.length), (c = c || d), l.fullSize || n.push(r); + } + return (h && is(n, e, i, s)) || c; + } + function ss(t, e, i, s, n) { + (t.top = i), + (t.left = e), + (t.right = e + s), + (t.bottom = i + n), + (t.width = s), + (t.height = n); + } + function ns(t, e, i, s) { + const n = i.padding; + let { x: o, y: a } = e; + for (const r of t) { + const t = r.box, + l = s[r.stack] || { count: 1, placed: 0, weight: 1 }, + h = r.stackWeight / l.weight || 1; + if (r.horizontal) { + const s = e.w * h, + o = l.size || t.height; + w(l.start) && (a = l.start), + t.fullSize + ? ss(t, n.left, a, i.outerWidth - n.right - n.left, o) + : ss(t, e.left + l.placed, a, s, o), + (l.start = a), + (l.placed += s), + (a = t.bottom); + } else { + const s = e.h * h, + a = l.size || t.width; + w(l.start) && (o = l.start), + t.fullSize + ? ss(t, o, n.top, a, i.outerHeight - n.bottom - n.top) + : ss(t, o, e.top + l.placed, a, s), + (l.start = o), + (l.placed += s), + (o = t.right); + } + } + (e.x = o), (e.y = a); + } + var os = { + addBox(t, e) { + t.boxes || (t.boxes = []), + (e.fullSize = e.fullSize || !1), + (e.position = e.position || "top"), + (e.weight = e.weight || 0), + (e._layers = + e._layers || + function() { + return [ + { + z: 0, + draw(t) { + e.draw(t); + } + } + ]; + }), + t.boxes.push(e); + }, + removeBox(t, e) { + const i = t.boxes ? t.boxes.indexOf(e) : -1; + -1 !== i && t.boxes.splice(i, 1); + }, + configure(t, e, i) { + (e.fullSize = i.fullSize), + (e.position = i.position), + (e.weight = i.weight); + }, + update(t, e, i, s) { + if (!t) return; + const n = wi(t.options.layout.padding), + o = Math.max(e - n.width, 0), + a = Math.max(i - n.height, 0), + r = (function(t) { + const e = (function(t) { + const e = []; + let i, s, n, o, a, r; + for (i = 0, s = (t || []).length; i < s; ++i) + (n = t[i]), + ({ + position: o, + options: { stack: a, stackWeight: r = 1 } + } = n), + e.push({ + index: i, + box: n, + pos: o, + horizontal: n.isHorizontal(), + weight: n.weight, + stack: a && o + a, + stackWeight: r + }); + return e; + })(t), + i = Gi( + e.filter(t => t.box.fullSize), + !0 + ), + s = Gi(qi(e, "left"), !0), + n = Gi(qi(e, "right")), + o = Gi(qi(e, "top"), !0), + a = Gi(qi(e, "bottom")), + r = Ki(e, "x"), + l = Ki(e, "y"); + return { + fullSize: i, + leftAndTop: s.concat(o), + rightAndBottom: n + .concat(l) + .concat(a) + .concat(r), + chartArea: qi(e, "chartArea"), + vertical: s.concat(n).concat(l), + horizontal: o.concat(a).concat(r) + }; + })(t.boxes), + l = r.vertical, + h = r.horizontal; + d(t.boxes, t => { + "function" == typeof t.beforeLayout && t.beforeLayout(); + }); + const c = + l.reduce( + (t, e) => + e.box.options && !1 === e.box.options.display ? t : t + 1, + 0 + ) || 1, + u = Object.freeze({ + outerWidth: e, + outerHeight: i, + padding: n, + availableWidth: o, + availableHeight: a, + vBoxMaxWidth: o / 2 / c, + hBoxMaxHeight: a / 2 + }), + f = Object.assign({}, n); + Qi(f, wi(s)); + const g = Object.assign( + { maxPadding: f, w: o, h: a, x: n.left, y: n.top }, + n + ), + p = Zi(l.concat(h), u); + is(r.fullSize, g, u, p), + is(l, g, u, p), + is(h, g, u, p) && is(l, g, u, p), + (function(t) { + const e = t.maxPadding; + function i(i) { + const s = Math.max(e[i] - t[i], 0); + return (t[i] += s), s; + } + (t.y += i("top")), (t.x += i("left")), i("right"), i("bottom"); + })(g), + ns(r.leftAndTop, g, u, p), + (g.x += g.w), + (g.y += g.h), + ns(r.rightAndBottom, g, u, p), + (t.chartArea = { + left: g.left, + top: g.top, + right: g.left + g.w, + bottom: g.top + g.h, + height: g.h, + width: g.w + }), + d(r.chartArea, e => { + const i = e.box; + Object.assign(i, t.chartArea), + i.update(g.w, g.h, { left: 0, top: 0, right: 0, bottom: 0 }); + }); + } + }; + class as { + acquireContext(t, e) {} + releaseContext(t) { + return !1; + } + addEventListener(t, e, i) {} + removeEventListener(t, e, i) {} + getDevicePixelRatio() { + return 1; + } + getMaximumSize(t, e, i, s) { + return ( + (e = Math.max(0, e || t.width)), + (i = i || t.height), + { width: e, height: Math.max(0, s ? Math.floor(e / s) : i) } + ); + } + isAttached(t) { + return !0; + } + updateConfig(t) {} + } + class rs extends as { + acquireContext(t) { + return (t && t.getContext && t.getContext("2d")) || null; + } + updateConfig(t) { + t.options.animation = !1; + } + } + const ls = { + touchstart: "mousedown", + touchmove: "mousemove", + touchend: "mouseup", + pointerenter: "mouseenter", + pointerdown: "mousedown", + pointermove: "mousemove", + pointerup: "mouseup", + pointerleave: "mouseout", + pointerout: "mouseout" + }, + hs = t => null === t || "" === t; + const cs = !!ke && { passive: !0 }; + function ds(t, e, i) { + t.canvas.removeEventListener(e, i, cs); + } + function us(t, e) { + for (const i of t) if (i === e || i.contains(e)) return !0; + } + function fs(t, e, i) { + const s = t.canvas, + n = new MutationObserver(t => { + let e = !1; + for (const i of t) + (e = e || us(i.addedNodes, s)), (e = e && !us(i.removedNodes, s)); + e && i(); + }); + return n.observe(document, { childList: !0, subtree: !0 }), n; + } + function gs(t, e, i) { + const s = t.canvas, + n = new MutationObserver(t => { + let e = !1; + for (const i of t) + (e = e || us(i.removedNodes, s)), (e = e && !us(i.addedNodes, s)); + e && i(); + }); + return n.observe(document, { childList: !0, subtree: !0 }), n; + } + const ps = new Map(); + let ms = 0; + function bs() { + const t = window.devicePixelRatio; + t !== ms && + ((ms = t), + ps.forEach((e, i) => { + i.currentDevicePixelRatio !== t && e(); + })); + } + function xs(t, e, i) { + const s = t.canvas, + n = s && ge(s); + if (!n) return; + const o = ht((t, e) => { + const s = n.clientWidth; + i(t, e), s < n.clientWidth && i(); + }, window), + a = new ResizeObserver(t => { + const e = t[0], + i = e.contentRect.width, + s = e.contentRect.height; + (0 === i && 0 === s) || o(i, s); + }); + return ( + a.observe(n), + (function(t, e) { + ps.size || window.addEventListener("resize", bs), ps.set(t, e); + })(t, o), + a + ); + } + function _s(t, e, i) { + i && i.disconnect(), + "resize" === e && + (function(t) { + ps.delete(t), ps.size || window.removeEventListener("resize", bs); + })(t); + } + function ys(t, e, i) { + const s = t.canvas, + n = ht(e => { + null !== t.ctx && + i( + (function(t, e) { + const i = ls[t.type] || t.type, + { x: s, y: n } = ye(t, e); + return { + type: i, + chart: e, + native: t, + x: void 0 !== s ? s : null, + y: void 0 !== n ? n : null + }; + })(e, t) + ); + }, t); + return ( + (function(t, e, i) { + t.addEventListener(e, i, cs); + })(s, e, n), + n + ); + } + class vs extends as { + acquireContext(t, e) { + const i = t && t.getContext && t.getContext("2d"); + return i && i.canvas === t + ? ((function(t, e) { + const i = t.style, + s = t.getAttribute("height"), + n = t.getAttribute("width"); + if ( + ((t.$chartjs = { + initial: { + height: s, + width: n, + style: { + display: i.display, + height: i.height, + width: i.width + } + } + }), + (i.display = i.display || "block"), + (i.boxSizing = i.boxSizing || "border-box"), + hs(n)) + ) { + const e = Se(t, "width"); + void 0 !== e && (t.width = e); + } + if (hs(s)) + if ("" === t.style.height) t.height = t.width / (e || 2); + else { + const e = Se(t, "height"); + void 0 !== e && (t.height = e); + } + })(t, e), + i) + : null; + } + releaseContext(t) { + const e = t.canvas; + if (!e.$chartjs) return !1; + const s = e.$chartjs.initial; + ["height", "width"].forEach(t => { + const n = s[t]; + i(n) ? e.removeAttribute(t) : e.setAttribute(t, n); + }); + const n = s.style || {}; + return ( + Object.keys(n).forEach(t => { + e.style[t] = n[t]; + }), + (e.width = e.width), + delete e.$chartjs, + !0 + ); + } + addEventListener(t, e, i) { + this.removeEventListener(t, e); + const s = t.$proxies || (t.$proxies = {}), + n = { attach: fs, detach: gs, resize: xs }[e] || ys; + s[e] = n(t, e, i); + } + removeEventListener(t, e) { + const i = t.$proxies || (t.$proxies = {}), + s = i[e]; + if (!s) return; + (({ attach: _s, detach: _s, resize: _s }[e] || ds)(t, e, s), + (i[e] = void 0)); + } + getDevicePixelRatio() { + return window.devicePixelRatio; + } + getMaximumSize(t, e, i, s) { + return Me(t, e, i, s); + } + isAttached(t) { + const e = ge(t); + return !(!e || !e.isConnected); + } + } + function Ms(t) { + return !fe() || + ("undefined" != typeof OffscreenCanvas && t instanceof OffscreenCanvas) + ? rs + : vs; + } + var ws = Object.freeze({ + __proto__: null, + _detectPlatform: Ms, + BasePlatform: as, + BasicPlatform: rs, + DomPlatform: vs + }); + const ks = "transparent", + Ss = { + boolean: (t, e, i) => (i > 0.5 ? e : t), + color(t, e, i) { + const s = Qt(t || ks), + n = s.valid && Qt(e || ks); + return n && n.valid ? n.mix(s, i).hexString() : e; + }, + number: (t, e, i) => t + (e - t) * i + }; + class Ps { + constructor(t, e, i, s) { + const n = e[i]; + s = Si([t.to, s, n, t.from]); + const o = Si([t.from, n, s]); + (this._active = !0), + (this._fn = t.fn || Ss[t.type || typeof o]), + (this._easing = fi[t.easing] || fi.linear), + (this._start = Math.floor(Date.now() + (t.delay || 0))), + (this._duration = this._total = Math.floor(t.duration)), + (this._loop = !!t.loop), + (this._target = e), + (this._prop = i), + (this._from = o), + (this._to = s), + (this._promises = void 0); + } + active() { + return this._active; + } + update(t, e, i) { + if (this._active) { + this._notify(!1); + const s = this._target[this._prop], + n = i - this._start, + o = this._duration - n; + (this._start = i), + (this._duration = Math.floor(Math.max(o, t.duration))), + (this._total += n), + (this._loop = !!t.loop), + (this._to = Si([t.to, e, s, t.from])), + (this._from = Si([t.from, s, e])); + } + } + cancel() { + this._active && + (this.tick(Date.now()), (this._active = !1), this._notify(!1)); + } + tick(t) { + const e = t - this._start, + i = this._duration, + s = this._prop, + n = this._from, + o = this._loop, + a = this._to; + let r; + if (((this._active = n !== a && (o || e < i)), !this._active)) + return (this._target[s] = a), void this._notify(!0); + e < 0 + ? (this._target[s] = n) + : ((r = (e / i) % 2), + (r = o && r > 1 ? 2 - r : r), + (r = this._easing(Math.min(1, Math.max(0, r)))), + (this._target[s] = this._fn(n, a, r))); + } + wait() { + const t = this._promises || (this._promises = []); + return new Promise((e, i) => { + t.push({ res: e, rej: i }); + }); + } + _notify(t) { + const e = t ? "res" : "rej", + i = this._promises || []; + for (let t = 0; t < i.length; t++) i[t][e](); + } + } + class Ds { + constructor(t, e) { + (this._chart = t), (this._properties = new Map()), this.configure(e); + } + configure(t) { + if (!n(t)) return; + const e = Object.keys(ue.animation), + i = this._properties; + Object.getOwnPropertyNames(t).forEach(o => { + const a = t[o]; + if (!n(a)) return; + const r = {}; + for (const t of e) r[t] = a[t]; + ((s(a.properties) && a.properties) || [o]).forEach(t => { + (t !== o && i.has(t)) || i.set(t, r); + }); + }); + } + _animateOptions(t, e) { + const i = e.options, + s = (function(t, e) { + if (!e) return; + let i = t.options; + if (!i) return void (t.options = e); + i.$shared && + (t.options = i = Object.assign({}, i, { + $shared: !1, + $animations: {} + })); + return i; + })(t, i); + if (!s) return []; + const n = this._createAnimations(s, i); + return ( + i.$shared && + (function(t, e) { + const i = [], + s = Object.keys(e); + for (let e = 0; e < s.length; e++) { + const n = t[s[e]]; + n && n.active() && i.push(n.wait()); + } + return Promise.all(i); + })(t.options.$animations, i).then( + () => { + t.options = i; + }, + () => {} + ), + n + ); + } + _createAnimations(t, e) { + const i = this._properties, + s = [], + n = t.$animations || (t.$animations = {}), + o = Object.keys(e), + a = Date.now(); + let r; + for (r = o.length - 1; r >= 0; --r) { + const l = o[r]; + if ("$" === l.charAt(0)) continue; + if ("options" === l) { + s.push(...this._animateOptions(t, e)); + continue; + } + const h = e[l]; + let c = n[l]; + const d = i.get(l); + if (c) { + if (d && c.active()) { + c.update(d, h, a); + continue; + } + c.cancel(); + } + d && d.duration + ? ((n[l] = c = new Ps(d, t, l, h)), s.push(c)) + : (t[l] = h); + } + return s; + } + update(t, e) { + if (0 === this._properties.size) return void Object.assign(t, e); + const i = this._createAnimations(t, e); + return i.length ? (bt.add(this._chart, i), !0) : void 0; + } + } + function Cs(t, e) { + const i = (t && t.options) || {}, + s = i.reverse, + n = void 0 === i.min ? e : 0, + o = void 0 === i.max ? e : 0; + return { start: s ? o : n, end: s ? n : o }; + } + function Os(t, e) { + const i = [], + s = t._getSortedDatasetMetas(e); + let n, o; + for (n = 0, o = s.length; n < o; ++n) i.push(s[n].index); + return i; + } + function As(t, e, i, s = {}) { + const n = t.keys, + a = "single" === s.mode; + let r, l, h, c; + if (null !== e) { + for (r = 0, l = n.length; r < l; ++r) { + if (((h = +n[r]), h === i)) { + if (s.all) continue; + break; + } + (c = t.values[h]), o(c) && (a || 0 === e || z(e) === z(c)) && (e += c); + } + return e; + } + } + function Ts(t, e) { + const i = t && t.options.stacked; + return i || (void 0 === i && void 0 !== e.stack); + } + function Ls(t, e, i) { + const s = t[e] || (t[e] = {}); + return s[i] || (s[i] = {}); + } + function Es(t, e, i, s) { + for (const n of e.getMatchingVisibleMetas(s).reverse()) { + const e = t[n.index]; + if ((i && e > 0) || (!i && e < 0)) return n.index; + } + return null; + } + function Rs(t, e) { + const { chart: i, _cachedMeta: s } = t, + n = i._stacks || (i._stacks = {}), + { iScale: o, vScale: a, index: r } = s, + l = o.axis, + h = a.axis, + c = (function(t, e, i) { + return `${t.id}.${e.id}.${i.stack || i.type}`; + })(o, a, s), + d = e.length; + let u; + for (let t = 0; t < d; ++t) { + const i = e[t], + { [l]: o, [h]: d } = i; + (u = (i._stacks || (i._stacks = {}))[h] = Ls(n, c, o)), + (u[r] = d), + (u._top = Es(u, a, !0, s.type)), + (u._bottom = Es(u, a, !1, s.type)); + } + } + function Is(t, e) { + const i = t.scales; + return Object.keys(i) + .filter(t => i[t].axis === e) + .shift(); + } + function zs(t, e) { + const i = t.controller.index, + s = t.vScale && t.vScale.axis; + if (s) { + e = e || t._parsed; + for (const t of e) { + const e = t._stacks; + if (!e || void 0 === e[s] || void 0 === e[s][i]) return; + delete e[s][i]; + } + } + } + const Fs = t => "reset" === t || "none" === t, + Vs = (t, e) => (e ? t : Object.assign({}, t)); + class Bs { + static defaults = {}; + static datasetElementType = null; + static dataElementType = null; + constructor(t, e) { + (this.chart = t), + (this._ctx = t.ctx), + (this.index = e), + (this._cachedDataOpts = {}), + (this._cachedMeta = this.getMeta()), + (this._type = this._cachedMeta.type), + (this.options = void 0), + (this._parsing = !1), + (this._data = void 0), + (this._objectData = void 0), + (this._sharedOptions = void 0), + (this._drawStart = void 0), + (this._drawCount = void 0), + (this.enableOptionSharing = !1), + (this.supportsDecimation = !1), + (this.$context = void 0), + (this._syncList = []), + (this.datasetElementType = new.target.datasetElementType), + (this.dataElementType = new.target.dataElementType), + this.initialize(); + } + initialize() { + const t = this._cachedMeta; + this.configure(), + this.linkScales(), + (t._stacked = Ts(t.vScale, t)), + this.addElements(), + this.options.fill && + !this.chart.isPluginEnabled("filler") && + console.warn( + "Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options" + ); + } + updateIndex(t) { + this.index !== t && zs(this._cachedMeta), (this.index = t); + } + linkScales() { + const t = this.chart, + e = this._cachedMeta, + i = this.getDataset(), + s = (t, e, i, s) => ("x" === t ? e : "r" === t ? s : i), + n = (e.xAxisID = r(i.xAxisID, Is(t, "x"))), + o = (e.yAxisID = r(i.yAxisID, Is(t, "y"))), + a = (e.rAxisID = r(i.rAxisID, Is(t, "r"))), + l = e.indexAxis, + h = (e.iAxisID = s(l, n, o, a)), + c = (e.vAxisID = s(l, o, n, a)); + (e.xScale = this.getScaleForId(n)), + (e.yScale = this.getScaleForId(o)), + (e.rScale = this.getScaleForId(a)), + (e.iScale = this.getScaleForId(h)), + (e.vScale = this.getScaleForId(c)); + } + getDataset() { + return this.chart.data.datasets[this.index]; + } + getMeta() { + return this.chart.getDatasetMeta(this.index); + } + getScaleForId(t) { + return this.chart.scales[t]; + } + _getOtherScale(t) { + const e = this._cachedMeta; + return t === e.iScale ? e.vScale : e.iScale; + } + reset() { + this._update("reset"); + } + _destroy() { + const t = this._cachedMeta; + this._data && at(this._data, this), t._stacked && zs(t); + } + _dataCheck() { + const t = this.getDataset(), + e = t.data || (t.data = []), + i = this._data; + if (n(e)) + this._data = (function(t) { + const e = Object.keys(t), + i = new Array(e.length); + let s, n, o; + for (s = 0, n = e.length; s < n; ++s) + (o = e[s]), (i[s] = { x: o, y: t[o] }); + return i; + })(e); + else if (i !== e) { + if (i) { + at(i, this); + const t = this._cachedMeta; + zs(t), (t._parsed = []); + } + e && Object.isExtensible(e) && ot(e, this), + (this._syncList = []), + (this._data = e); + } + } + addElements() { + const t = this._cachedMeta; + this._dataCheck(), + this.datasetElementType && (t.dataset = new this.datasetElementType()); + } + buildOrUpdateElements(t) { + const e = this._cachedMeta, + i = this.getDataset(); + let s = !1; + this._dataCheck(); + const n = e._stacked; + (e._stacked = Ts(e.vScale, e)), + e.stack !== i.stack && ((s = !0), zs(e), (e.stack = i.stack)), + this._resyncElements(t), + (s || n !== e._stacked) && Rs(this, e._parsed); + } + configure() { + const t = this.chart.config, + e = t.datasetScopeKeys(this._type), + i = t.getOptionScopes(this.getDataset(), e, !0); + (this.options = t.createResolver(i, this.getContext())), + (this._parsing = this.options.parsing), + (this._cachedDataOpts = {}); + } + parse(t, e) { + const { _cachedMeta: i, _data: o } = this, + { iScale: a, _stacked: r } = i, + l = a.axis; + let h, + c, + d, + u = (0 === t && e === o.length) || i._sorted, + f = t > 0 && i._parsed[t - 1]; + if (!1 === this._parsing) (i._parsed = o), (i._sorted = !0), (d = o); + else { + d = s(o[t]) + ? this.parseArrayData(i, o, t, e) + : n(o[t]) + ? this.parseObjectData(i, o, t, e) + : this.parsePrimitiveData(i, o, t, e); + const a = () => null === c[l] || (f && c[l] < f[l]); + for (h = 0; h < e; ++h) + (i._parsed[h + t] = c = d[h]), u && (a() && (u = !1), (f = c)); + i._sorted = u; + } + r && Rs(this, d); + } + parsePrimitiveData(t, e, i, s) { + const { iScale: n, vScale: o } = t, + a = n.axis, + r = o.axis, + l = n.getLabels(), + h = n === o, + c = new Array(s); + let d, u, f; + for (d = 0, u = s; d < u; ++d) + (f = d + i), + (c[d] = { [a]: h || n.parse(l[f], f), [r]: o.parse(e[f], f) }); + return c; + } + parseArrayData(t, e, i, s) { + const { xScale: n, yScale: o } = t, + a = new Array(s); + let r, l, h, c; + for (r = 0, l = s; r < l; ++r) + (h = r + i), + (c = e[h]), + (a[r] = { x: n.parse(c[0], h), y: o.parse(c[1], h) }); + return a; + } + parseObjectData(t, e, i, s) { + const { xScale: n, yScale: o } = t, + { xAxisKey: a = "x", yAxisKey: r = "y" } = this._parsing, + l = new Array(s); + let h, c, d, u; + for (h = 0, c = s; h < c; ++h) + (d = h + i), + (u = e[d]), + (l[h] = { x: n.parse(v(u, a), d), y: o.parse(v(u, r), d) }); + return l; + } + getParsed(t) { + return this._cachedMeta._parsed[t]; + } + getDataElement(t) { + return this._cachedMeta.data[t]; + } + applyStack(t, e, i) { + const s = this.chart, + n = this._cachedMeta, + o = e[t.axis]; + return As({ keys: Os(s, !0), values: e._stacks[t.axis] }, o, n.index, { + mode: i + }); + } + updateRangeFromParsed(t, e, i, s) { + const n = i[e.axis]; + let o = null === n ? NaN : n; + const a = s && i._stacks[e.axis]; + s && a && ((s.values = a), (o = As(s, n, this._cachedMeta.index))), + (t.min = Math.min(t.min, o)), + (t.max = Math.max(t.max, o)); + } + getMinMax(t, e) { + const i = this._cachedMeta, + s = i._parsed, + n = i._sorted && t === i.iScale, + a = s.length, + r = this._getOtherScale(t), + l = ((t, e, i) => + t && !e.hidden && e._stacked && { keys: Os(i, !0), values: null })( + e, + i, + this.chart + ), + h = { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY }, + { min: c, max: d } = (function(t) { + const { + min: e, + max: i, + minDefined: s, + maxDefined: n + } = t.getUserBounds(); + return { + min: s ? e : Number.NEGATIVE_INFINITY, + max: n ? i : Number.POSITIVE_INFINITY + }; + })(r); + let u, f; + function g() { + f = s[u]; + const e = f[r.axis]; + return !o(f[t.axis]) || c > e || d < e; + } + for ( + u = 0; + u < a && (g() || (this.updateRangeFromParsed(h, t, f, l), !n)); + ++u + ); + if (n) + for (u = a - 1; u >= 0; --u) + if (!g()) { + this.updateRangeFromParsed(h, t, f, l); + break; + } + return h; + } + getAllParsedValues(t) { + const e = this._cachedMeta._parsed, + i = []; + let s, n, a; + for (s = 0, n = e.length; s < n; ++s) + (a = e[s][t.axis]), o(a) && i.push(a); + return i; + } + getMaxOverflow() { + return !1; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = e.iScale, + s = e.vScale, + n = this.getParsed(t); + return { + label: i ? "" + i.getLabelForValue(n[i.axis]) : "", + value: s ? "" + s.getLabelForValue(n[s.axis]) : "" + }; + } + _update(t) { + const e = this._cachedMeta; + this.update(t || "default"), + (e._clip = (function(t) { + let e, i, s, o; + return ( + n(t) + ? ((e = t.top), (i = t.right), (s = t.bottom), (o = t.left)) + : (e = i = s = o = t), + { top: e, right: i, bottom: s, left: o, disabled: !1 === t } + ); + })( + r( + this.options.clip, + (function(t, e, i) { + if (!1 === i) return !1; + const s = Cs(t, i), + n = Cs(e, i); + return { + top: n.end, + right: s.end, + bottom: n.start, + left: s.start + }; + })(e.xScale, e.yScale, this.getMaxOverflow()) + ) + )); + } + update(t) {} + draw() { + const t = this._ctx, + e = this.chart, + i = this._cachedMeta, + s = i.data || [], + n = e.chartArea, + o = [], + a = this._drawStart || 0, + r = this._drawCount || s.length - a, + l = this.options.drawActiveElementsOnTop; + let h; + for (i.dataset && i.dataset.draw(t, n, a, r), h = a; h < a + r; ++h) { + const e = s[h]; + e.hidden || (e.active && l ? o.push(e) : e.draw(t, n)); + } + for (h = 0; h < o.length; ++h) o[h].draw(t, n); + } + getStyle(t, e) { + const i = e ? "active" : "default"; + return void 0 === t && this._cachedMeta.dataset + ? this.resolveDatasetElementOptions(i) + : this.resolveDataElementOptions(t || 0, i); + } + getContext(t, e, i) { + const s = this.getDataset(); + let n; + if (t >= 0 && t < this._cachedMeta.data.length) { + const e = this._cachedMeta.data[t]; + (n = + e.$context || + (e.$context = (function(t, e, i) { + return Di(t, { + active: !1, + dataIndex: e, + parsed: void 0, + raw: void 0, + element: i, + index: e, + mode: "default", + type: "data" + }); + })(this.getContext(), t, e))), + (n.parsed = this.getParsed(t)), + (n.raw = s.data[t]), + (n.index = n.dataIndex = t); + } else + (n = + this.$context || + (this.$context = (function(t, e) { + return Di(t, { + active: !1, + dataset: void 0, + datasetIndex: e, + index: e, + mode: "default", + type: "dataset" + }); + })(this.chart.getContext(), this.index))), + (n.dataset = s), + (n.index = n.datasetIndex = this.index); + return (n.active = !!e), (n.mode = i), n; + } + resolveDatasetElementOptions(t) { + return this._resolveElementOptions(this.datasetElementType.id, t); + } + resolveDataElementOptions(t, e) { + return this._resolveElementOptions(this.dataElementType.id, e, t); + } + _resolveElementOptions(t, e = "default", i) { + const s = "active" === e, + n = this._cachedDataOpts, + o = t + "-" + e, + a = n[o], + r = this.enableOptionSharing && w(i); + if (a) return Vs(a, r); + const l = this.chart.config, + h = l.datasetElementScopeKeys(this._type, t), + c = s ? [`${t}Hover`, "hover", t, ""] : [t, ""], + d = l.getOptionScopes(this.getDataset(), h), + u = Object.keys(ue.elements[t]), + f = l.resolveNamedOptions(d, u, () => this.getContext(i, s), c); + return ( + f.$shared && ((f.$shared = r), (n[o] = Object.freeze(Vs(f, r)))), f + ); + } + _resolveAnimations(t, e, i) { + const s = this.chart, + n = this._cachedDataOpts, + o = `animation-${e}`, + a = n[o]; + if (a) return a; + let r; + if (!1 !== s.options.animation) { + const s = this.chart.config, + n = s.datasetAnimationScopeKeys(this._type, e), + o = s.getOptionScopes(this.getDataset(), n); + r = s.createResolver(o, this.getContext(t, i, e)); + } + const l = new Ds(s, r && r.animations); + return r && r._cacheable && (n[o] = Object.freeze(l)), l; + } + getSharedOptions(t) { + if (t.$shared) + return ( + this._sharedOptions || (this._sharedOptions = Object.assign({}, t)) + ); + } + includeOptions(t, e) { + return !e || Fs(t) || this.chart._animationsDisabled; + } + _getSharedOptions(t, e) { + const i = this.resolveDataElementOptions(t, e), + s = this._sharedOptions, + n = this.getSharedOptions(i), + o = this.includeOptions(e, n) || n !== s; + return ( + this.updateSharedOptions(n, e, i), + { sharedOptions: n, includeOptions: o } + ); + } + updateElement(t, e, i, s) { + Fs(s) ? Object.assign(t, i) : this._resolveAnimations(e, s).update(t, i); + } + updateSharedOptions(t, e, i) { + t && !Fs(e) && this._resolveAnimations(void 0, e).update(t, i); + } + _setStyle(t, e, i, s) { + t.active = s; + const n = this.getStyle(e, s); + this._resolveAnimations(e, i, s).update(t, { + options: (!s && this.getSharedOptions(n)) || n + }); + } + removeHoverStyle(t, e, i) { + this._setStyle(t, i, "active", !1); + } + setHoverStyle(t, e, i) { + this._setStyle(t, i, "active", !0); + } + _removeDatasetHoverStyle() { + const t = this._cachedMeta.dataset; + t && this._setStyle(t, void 0, "active", !1); + } + _setDatasetHoverStyle() { + const t = this._cachedMeta.dataset; + t && this._setStyle(t, void 0, "active", !0); + } + _resyncElements(t) { + const e = this._data, + i = this._cachedMeta.data; + for (const [t, e, i] of this._syncList) this[t](e, i); + this._syncList = []; + const s = i.length, + n = e.length, + o = Math.min(n, s); + o && this.parse(0, o), + n > s + ? this._insertElements(s, n - s, t) + : n < s && this._removeElements(n, s - n); + } + _insertElements(t, e, i = !0) { + const s = this._cachedMeta, + n = s.data, + o = t + e; + let a; + const r = t => { + for (t.length += e, a = t.length - 1; a >= o; a--) t[a] = t[a - e]; + }; + for (r(n), a = t; a < o; ++a) n[a] = new this.dataElementType(); + this._parsing && r(s._parsed), + this.parse(t, e), + i && this.updateElements(n, t, e, "reset"); + } + updateElements(t, e, i, s) {} + _removeElements(t, e) { + const i = this._cachedMeta; + if (this._parsing) { + const s = i._parsed.splice(t, e); + i._stacked && zs(i, s); + } + i.data.splice(t, e); + } + _sync(t) { + if (this._parsing) this._syncList.push(t); + else { + const [e, i, s] = t; + this[e](i, s); + } + this.chart._dataChanges.push([this.index, ...t]); + } + _onDataPush() { + const t = arguments.length; + this._sync(["_insertElements", this.getDataset().data.length - t, t]); + } + _onDataPop() { + this._sync(["_removeElements", this._cachedMeta.data.length - 1, 1]); + } + _onDataShift() { + this._sync(["_removeElements", 0, 1]); + } + _onDataSplice(t, e) { + e && this._sync(["_removeElements", t, e]); + const i = arguments.length - 2; + i && this._sync(["_insertElements", t, i]); + } + _onDataUnshift() { + this._sync(["_insertElements", 0, arguments.length]); + } + } + class Ns { + static defaults = {}; + static defaultRoutes = void 0; + active = !1; + tooltipPosition(t) { + const { x: e, y: i } = this.getProps(["x", "y"], t); + return { x: e, y: i }; + } + hasValue() { + return N(this.x) && N(this.y); + } + getProps(t, e) { + const i = this.$animations; + if (!e || !i) return this; + const s = {}; + return ( + t.forEach(t => { + s[t] = i[t] && i[t].active() ? i[t]._to : this[t]; + }), + s + ); + } + } + function Ws(t, e) { + const s = t.options.ticks, + n = (function(t) { + const e = t.options.offset, + i = t._tickSize(), + s = t._length / i + (e ? 0 : 1), + n = t._maxLength / i; + return Math.floor(Math.min(s, n)); + })(t), + o = Math.min(s.maxTicksLimit || n, n), + a = s.major.enabled + ? (function(t) { + const e = []; + let i, s; + for (i = 0, s = t.length; i < s; i++) t[i].major && e.push(i); + return e; + })(e) + : [], + r = a.length, + l = a[0], + h = a[r - 1], + c = []; + if (r > o) + return ( + (function(t, e, i, s) { + let n, + o = 0, + a = i[0]; + for (s = Math.ceil(s), n = 0; n < t.length; n++) + n === a && (e.push(t[n]), o++, (a = i[o * s])); + })(e, c, a, r / o), + c + ); + const d = (function(t, e, i) { + const s = (function(t) { + const e = t.length; + let i, s; + if (e < 2) return !1; + for (s = t[0], i = 1; i < e; ++i) + if (t[i] - t[i - 1] !== s) return !1; + return s; + })(t), + n = e.length / i; + if (!s) return Math.max(n, 1); + const o = B(s); + for (let t = 0, e = o.length - 1; t < e; t++) { + const e = o[t]; + if (e > n) return e; + } + return Math.max(n, 1); + })(a, e, o); + if (r > 0) { + let t, s; + const n = r > 1 ? Math.round((h - l) / (r - 1)) : null; + for (Hs(e, c, d, i(n) ? 0 : l - n, l), t = 0, s = r - 1; t < s; t++) + Hs(e, c, d, a[t], a[t + 1]); + return Hs(e, c, d, h, i(n) ? e.length : h + n), c; + } + return Hs(e, c, d), c; + } + function Hs(t, e, i, s, n) { + const o = r(s, 0), + a = Math.min(r(n, t.length), t.length); + let l, + h, + c, + d = 0; + for ( + i = Math.ceil(i), n && ((l = n - s), (i = l / Math.floor(l / i))), c = o; + c < 0; + + ) + d++, (c = Math.round(o + d * i)); + for (h = Math.max(o, 0); h < a; h++) + h === c && (e.push(t[h]), d++, (c = Math.round(o + d * i))); + } + const js = (t, e, i) => ("top" === e || "left" === e ? t[e] + i : t[e] - i); + function $s(t, e) { + const i = [], + s = t.length / e, + n = t.length; + let o = 0; + for (; o < n; o += s) i.push(t[Math.floor(o)]); + return i; + } + function Ys(t, e, i) { + const s = t.ticks.length, + n = Math.min(e, s - 1), + o = t._startPixel, + a = t._endPixel, + r = 1e-6; + let l, + h = t.getPixelForTick(n); + if ( + !( + i && + ((l = + 1 === s + ? Math.max(h - o, a - h) + : 0 === e + ? (t.getPixelForTick(1) - h) / 2 + : (h - t.getPixelForTick(n - 1)) / 2), + (h += n < e ? l : -l), + h < o - r || h > a + r) + ) + ) + return h; + } + function Us(t) { + return t.drawTicks ? t.tickLength : 0; + } + function Xs(t, e) { + if (!t.display) return 0; + const i = ki(t.font, e), + n = wi(t.padding); + return (s(t.text) ? t.text.length : 1) * i.lineHeight + n.height; + } + function qs(t, e, i) { + let s = dt(t); + return ( + ((i && "right" !== e) || (!i && "right" === e)) && + (s = (t => ("left" === t ? "right" : "right" === t ? "left" : t))(s)), + s + ); + } + class Ks extends Ns { + constructor(t) { + super(), + (this.id = t.id), + (this.type = t.type), + (this.options = void 0), + (this.ctx = t.ctx), + (this.chart = t.chart), + (this.top = void 0), + (this.bottom = void 0), + (this.left = void 0), + (this.right = void 0), + (this.width = void 0), + (this.height = void 0), + (this._margins = { left: 0, right: 0, top: 0, bottom: 0 }), + (this.maxWidth = void 0), + (this.maxHeight = void 0), + (this.paddingTop = void 0), + (this.paddingBottom = void 0), + (this.paddingLeft = void 0), + (this.paddingRight = void 0), + (this.axis = void 0), + (this.labelRotation = void 0), + (this.min = void 0), + (this.max = void 0), + (this._range = void 0), + (this.ticks = []), + (this._gridLineItems = null), + (this._labelItems = null), + (this._labelSizes = null), + (this._length = 0), + (this._maxLength = 0), + (this._longestTextCache = {}), + (this._startPixel = void 0), + (this._endPixel = void 0), + (this._reversePixels = !1), + (this._userMax = void 0), + (this._userMin = void 0), + (this._suggestedMax = void 0), + (this._suggestedMin = void 0), + (this._ticksLength = 0), + (this._borderValue = 0), + (this._cache = {}), + (this._dataLimitsCached = !1), + (this.$context = void 0); + } + init(t) { + (this.options = t.setContext(this.getContext())), + (this.axis = t.axis), + (this._userMin = this.parse(t.min)), + (this._userMax = this.parse(t.max)), + (this._suggestedMin = this.parse(t.suggestedMin)), + (this._suggestedMax = this.parse(t.suggestedMax)); + } + parse(t, e) { + return t; + } + getUserBounds() { + let { + _userMin: t, + _userMax: e, + _suggestedMin: i, + _suggestedMax: s + } = this; + return ( + (t = a(t, Number.POSITIVE_INFINITY)), + (e = a(e, Number.NEGATIVE_INFINITY)), + (i = a(i, Number.POSITIVE_INFINITY)), + (s = a(s, Number.NEGATIVE_INFINITY)), + { min: a(t, i), max: a(e, s), minDefined: o(t), maxDefined: o(e) } + ); + } + getMinMax(t) { + let e, + { min: i, max: s, minDefined: n, maxDefined: o } = this.getUserBounds(); + if (n && o) return { min: i, max: s }; + const r = this.getMatchingVisibleMetas(); + for (let a = 0, l = r.length; a < l; ++a) + (e = r[a].controller.getMinMax(this, t)), + n || (i = Math.min(i, e.min)), + o || (s = Math.max(s, e.max)); + return ( + (i = o && i > s ? s : i), + (s = n && i > s ? i : s), + { min: a(i, a(s, i)), max: a(s, a(i, s)) } + ); + } + getPadding() { + return { + left: this.paddingLeft || 0, + top: this.paddingTop || 0, + right: this.paddingRight || 0, + bottom: this.paddingBottom || 0 + }; + } + getTicks() { + return this.ticks; + } + getLabels() { + const t = this.chart.data; + return ( + this.options.labels || + (this.isHorizontal() ? t.xLabels : t.yLabels) || + t.labels || + [] + ); + } + beforeLayout() { + (this._cache = {}), (this._dataLimitsCached = !1); + } + beforeUpdate() { + c(this.options.beforeUpdate, [this]); + } + update(t, e, i) { + const { beginAtZero: s, grace: n, ticks: o } = this.options, + a = o.sampleSize; + this.beforeUpdate(), + (this.maxWidth = t), + (this.maxHeight = e), + (this._margins = i = Object.assign( + { left: 0, right: 0, top: 0, bottom: 0 }, + i + )), + (this.ticks = null), + (this._labelSizes = null), + (this._gridLineItems = null), + (this._labelItems = null), + this.beforeSetDimensions(), + this.setDimensions(), + this.afterSetDimensions(), + (this._maxLength = this.isHorizontal() + ? this.width + i.left + i.right + : this.height + i.top + i.bottom), + this._dataLimitsCached || + (this.beforeDataLimits(), + this.determineDataLimits(), + this.afterDataLimits(), + (this._range = Pi(this, n, s)), + (this._dataLimitsCached = !0)), + this.beforeBuildTicks(), + (this.ticks = this.buildTicks() || []), + this.afterBuildTicks(); + const r = a < this.ticks.length; + this._convertTicksToLabels(r ? $s(this.ticks, a) : this.ticks), + this.configure(), + this.beforeCalculateLabelRotation(), + this.calculateLabelRotation(), + this.afterCalculateLabelRotation(), + o.display && + (o.autoSkip || "auto" === o.source) && + ((this.ticks = Ws(this, this.ticks)), + (this._labelSizes = null), + this.afterAutoSkip()), + r && this._convertTicksToLabels(this.ticks), + this.beforeFit(), + this.fit(), + this.afterFit(), + this.afterUpdate(); + } + configure() { + let t, + e, + i = this.options.reverse; + this.isHorizontal() + ? ((t = this.left), (e = this.right)) + : ((t = this.top), (e = this.bottom), (i = !i)), + (this._startPixel = t), + (this._endPixel = e), + (this._reversePixels = i), + (this._length = e - t), + (this._alignToPixels = this.options.alignToPixels); + } + afterUpdate() { + c(this.options.afterUpdate, [this]); + } + beforeSetDimensions() { + c(this.options.beforeSetDimensions, [this]); + } + setDimensions() { + this.isHorizontal() + ? ((this.width = this.maxWidth), + (this.left = 0), + (this.right = this.width)) + : ((this.height = this.maxHeight), + (this.top = 0), + (this.bottom = this.height)), + (this.paddingLeft = 0), + (this.paddingTop = 0), + (this.paddingRight = 0), + (this.paddingBottom = 0); + } + afterSetDimensions() { + c(this.options.afterSetDimensions, [this]); + } + _callHooks(t) { + this.chart.notifyPlugins(t, this.getContext()), + c(this.options[t], [this]); + } + beforeDataLimits() { + this._callHooks("beforeDataLimits"); + } + determineDataLimits() {} + afterDataLimits() { + this._callHooks("afterDataLimits"); + } + beforeBuildTicks() { + this._callHooks("beforeBuildTicks"); + } + buildTicks() { + return []; + } + afterBuildTicks() { + this._callHooks("afterBuildTicks"); + } + beforeTickToLabelConversion() { + c(this.options.beforeTickToLabelConversion, [this]); + } + generateTickLabels(t) { + const e = this.options.ticks; + let i, s, n; + for (i = 0, s = t.length; i < s; i++) + (n = t[i]), (n.label = c(e.callback, [n.value, i, t], this)); + } + afterTickToLabelConversion() { + c(this.options.afterTickToLabelConversion, [this]); + } + beforeCalculateLabelRotation() { + c(this.options.beforeCalculateLabelRotation, [this]); + } + calculateLabelRotation() { + const t = this.options, + e = t.ticks, + i = this.ticks.length, + s = e.minRotation || 0, + n = e.maxRotation; + let o, + a, + r, + l = s; + if ( + !this._isVisible() || + !e.display || + s >= n || + i <= 1 || + !this.isHorizontal() + ) + return void (this.labelRotation = s); + const h = this._getLabelSizes(), + c = h.widest.width, + d = h.highest.height, + u = Z(this.chart.width - c, 0, this.maxWidth); + (o = t.offset ? this.maxWidth / i : u / (i - 1)), + c + 6 > o && + ((o = u / (i - (t.offset ? 0.5 : 1))), + (a = + this.maxHeight - + Us(t.grid) - + e.padding - + Xs(t.title, this.chart.options.font)), + (r = Math.sqrt(c * c + d * d)), + (l = $( + Math.min( + Math.asin(Z((h.highest.height + 6) / o, -1, 1)), + Math.asin(Z(a / r, -1, 1)) - Math.asin(Z(d / r, -1, 1)) + ) + )), + (l = Math.max(s, Math.min(n, l)))), + (this.labelRotation = l); + } + afterCalculateLabelRotation() { + c(this.options.afterCalculateLabelRotation, [this]); + } + afterAutoSkip() {} + beforeFit() { + c(this.options.beforeFit, [this]); + } + fit() { + const t = { width: 0, height: 0 }, + { + chart: e, + options: { ticks: i, title: s, grid: n } + } = this, + o = this._isVisible(), + a = this.isHorizontal(); + if (o) { + const o = Xs(s, e.options.font); + if ( + (a + ? ((t.width = this.maxWidth), (t.height = Us(n) + o)) + : ((t.height = this.maxHeight), (t.width = Us(n) + o)), + i.display && this.ticks.length) + ) { + const { + first: e, + last: s, + widest: n, + highest: o + } = this._getLabelSizes(), + r = 2 * i.padding, + l = j(this.labelRotation), + h = Math.cos(l), + c = Math.sin(l); + if (a) { + const e = i.mirror ? 0 : c * n.width + h * o.height; + t.height = Math.min(this.maxHeight, t.height + e + r); + } else { + const e = i.mirror ? 0 : h * n.width + c * o.height; + t.width = Math.min(this.maxWidth, t.width + e + r); + } + this._calculatePadding(e, s, c, h); + } + } + this._handleMargins(), + a + ? ((this.width = this._length = + e.width - this._margins.left - this._margins.right), + (this.height = t.height)) + : ((this.width = t.width), + (this.height = this._length = + e.height - this._margins.top - this._margins.bottom)); + } + _calculatePadding(t, e, i, s) { + const { + ticks: { align: n, padding: o }, + position: a + } = this.options, + r = 0 !== this.labelRotation, + l = "top" !== a && "x" === this.axis; + if (this.isHorizontal()) { + const a = this.getPixelForTick(0) - this.left, + h = this.right - this.getPixelForTick(this.ticks.length - 1); + let c = 0, + d = 0; + r + ? l + ? ((c = s * t.width), (d = i * e.height)) + : ((c = i * t.height), (d = s * e.width)) + : "start" === n + ? (d = e.width) + : "end" === n + ? (c = t.width) + : "inner" !== n && ((c = t.width / 2), (d = e.width / 2)), + (this.paddingLeft = Math.max( + ((c - a + o) * this.width) / (this.width - a), + 0 + )), + (this.paddingRight = Math.max( + ((d - h + o) * this.width) / (this.width - h), + 0 + )); + } else { + let i = e.height / 2, + s = t.height / 2; + "start" === n + ? ((i = 0), (s = t.height)) + : "end" === n && ((i = e.height), (s = 0)), + (this.paddingTop = i + o), + (this.paddingBottom = s + o); + } + } + _handleMargins() { + this._margins && + ((this._margins.left = Math.max(this.paddingLeft, this._margins.left)), + (this._margins.top = Math.max(this.paddingTop, this._margins.top)), + (this._margins.right = Math.max( + this.paddingRight, + this._margins.right + )), + (this._margins.bottom = Math.max( + this.paddingBottom, + this._margins.bottom + ))); + } + afterFit() { + c(this.options.afterFit, [this]); + } + isHorizontal() { + const { axis: t, position: e } = this.options; + return "top" === e || "bottom" === e || "x" === t; + } + isFullSize() { + return this.options.fullSize; + } + _convertTicksToLabels(t) { + let e, s; + for ( + this.beforeTickToLabelConversion(), + this.generateTickLabels(t), + e = 0, + s = t.length; + e < s; + e++ + ) + i(t[e].label) && (t.splice(e, 1), s--, e--); + this.afterTickToLabelConversion(); + } + _getLabelSizes() { + let t = this._labelSizes; + if (!t) { + const e = this.options.ticks.sampleSize; + let i = this.ticks; + e < i.length && (i = $s(i, e)), + (this._labelSizes = t = this._computeLabelSizes(i, i.length)); + } + return t; + } + _computeLabelSizes(t, e) { + const { ctx: n, _longestTextCache: o } = this, + a = [], + r = []; + let l, + h, + c, + u, + f, + g, + p, + m, + b, + x, + _, + y = 0, + v = 0; + for (l = 0; l < e; ++l) { + if ( + ((u = t[l].label), + (f = this._resolveTickFontOptions(l)), + (n.font = g = f.string), + (p = o[g] = o[g] || { data: {}, gc: [] }), + (m = f.lineHeight), + (b = x = 0), + i(u) || s(u)) + ) { + if (s(u)) + for (h = 0, c = u.length; h < c; ++h) + (_ = u[h]), + i(_) || s(_) || ((b = De(n, p.data, p.gc, b, _)), (x += m)); + } else (b = De(n, p.data, p.gc, b, u)), (x = m); + a.push(b), r.push(x), (y = Math.max(b, y)), (v = Math.max(x, v)); + } + !(function(t, e) { + d(t, t => { + const i = t.gc, + s = i.length / 2; + let n; + if (s > e) { + for (n = 0; n < s; ++n) delete t.data[i[n]]; + i.splice(0, s); + } + }); + })(o, e); + const M = a.indexOf(y), + w = r.indexOf(v), + k = t => ({ width: a[t] || 0, height: r[t] || 0 }); + return { + first: k(0), + last: k(e - 1), + widest: k(M), + highest: k(w), + widths: a, + heights: r + }; + } + getLabelForValue(t) { + return t; + } + getPixelForValue(t, e) { + return NaN; + } + getValueForPixel(t) {} + getPixelForTick(t) { + const e = this.ticks; + return t < 0 || t > e.length - 1 + ? null + : this.getPixelForValue(e[t].value); + } + getPixelForDecimal(t) { + this._reversePixels && (t = 1 - t); + const e = this._startPixel + t * this._length; + return J(this._alignToPixels ? Oe(this.chart, e, 0) : e); + } + getDecimalForPixel(t) { + const e = (t - this._startPixel) / this._length; + return this._reversePixels ? 1 - e : e; + } + getBasePixel() { + return this.getPixelForValue(this.getBaseValue()); + } + getBaseValue() { + const { min: t, max: e } = this; + return t < 0 && e < 0 ? e : t > 0 && e > 0 ? t : 0; + } + getContext(t) { + const e = this.ticks || []; + if (t >= 0 && t < e.length) { + const i = e[t]; + return ( + i.$context || + (i.$context = (function(t, e, i) { + return Di(t, { tick: i, index: e, type: "tick" }); + })(this.getContext(), t, i)) + ); + } + return ( + this.$context || + (this.$context = Di(this.chart.getContext(), { + scale: this, + type: "scale" + })) + ); + } + _tickSize() { + const t = this.options.ticks, + e = j(this.labelRotation), + i = Math.abs(Math.cos(e)), + s = Math.abs(Math.sin(e)), + n = this._getLabelSizes(), + o = t.autoSkipPadding || 0, + a = n ? n.widest.width + o : 0, + r = n ? n.highest.height + o : 0; + return this.isHorizontal() + ? r * i > a * s + ? a / i + : r / s + : r * s < a * i + ? r / i + : a / s; + } + _isVisible() { + const t = this.options.display; + return "auto" !== t ? !!t : this.getMatchingVisibleMetas().length > 0; + } + _computeGridLineItems(t) { + const e = this.axis, + i = this.chart, + s = this.options, + { grid: o, position: a, border: l } = s, + h = o.offset, + c = this.isHorizontal(), + d = this.ticks.length + (h ? 1 : 0), + u = Us(o), + f = [], + g = l.setContext(this.getContext()), + p = g.display ? g.width : 0, + m = p / 2, + b = function(t) { + return Oe(i, t, p); + }; + let x, _, y, v, M, w, k, S, P, D, C, O; + if ("top" === a) + (x = b(this.bottom)), + (w = this.bottom - u), + (S = x - m), + (D = b(t.top) + m), + (O = t.bottom); + else if ("bottom" === a) + (x = b(this.top)), + (D = t.top), + (O = b(t.bottom) - m), + (w = x + m), + (S = this.top + u); + else if ("left" === a) + (x = b(this.right)), + (M = this.right - u), + (k = x - m), + (P = b(t.left) + m), + (C = t.right); + else if ("right" === a) + (x = b(this.left)), + (P = t.left), + (C = b(t.right) - m), + (M = x + m), + (k = this.left + u); + else if ("x" === e) { + if ("center" === a) x = b((t.top + t.bottom) / 2 + 0.5); + else if (n(a)) { + const t = Object.keys(a)[0], + e = a[t]; + x = b(this.chart.scales[t].getPixelForValue(e)); + } + (D = t.top), (O = t.bottom), (w = x + m), (S = w + u); + } else if ("y" === e) { + if ("center" === a) x = b((t.left + t.right) / 2); + else if (n(a)) { + const t = Object.keys(a)[0], + e = a[t]; + x = b(this.chart.scales[t].getPixelForValue(e)); + } + (M = x - m), (k = M - u), (P = t.left), (C = t.right); + } + const A = r(s.ticks.maxTicksLimit, d), + T = Math.max(1, Math.ceil(d / A)); + for (_ = 0; _ < d; _ += T) { + const t = this.getContext(_), + e = o.setContext(t), + s = l.setContext(t), + n = e.lineWidth, + a = e.color, + r = s.dash || [], + d = s.dashOffset, + u = e.tickWidth, + g = e.tickColor, + p = e.tickBorderDash || [], + m = e.tickBorderDashOffset; + (y = Ys(this, _, h)), + void 0 !== y && + ((v = Oe(i, y, n)), + c ? (M = k = P = C = v) : (w = S = D = O = v), + f.push({ + tx1: M, + ty1: w, + tx2: k, + ty2: S, + x1: P, + y1: D, + x2: C, + y2: O, + width: n, + color: a, + borderDash: r, + borderDashOffset: d, + tickWidth: u, + tickColor: g, + tickBorderDash: p, + tickBorderDashOffset: m + })); + } + return (this._ticksLength = d), (this._borderValue = x), f; + } + _computeLabelItems(t) { + const e = this.axis, + i = this.options, + { position: o, ticks: a } = i, + r = this.isHorizontal(), + l = this.ticks, + { align: h, crossAlign: c, padding: d, mirror: u } = a, + f = Us(i.grid), + g = f + d, + p = u ? -d : g, + m = -j(this.labelRotation), + b = []; + let x, + _, + y, + v, + M, + w, + k, + S, + P, + D, + C, + O, + A = "middle"; + if ("top" === o) + (w = this.bottom - p), (k = this._getXAxisLabelAlignment()); + else if ("bottom" === o) + (w = this.top + p), (k = this._getXAxisLabelAlignment()); + else if ("left" === o) { + const t = this._getYAxisLabelAlignment(f); + (k = t.textAlign), (M = t.x); + } else if ("right" === o) { + const t = this._getYAxisLabelAlignment(f); + (k = t.textAlign), (M = t.x); + } else if ("x" === e) { + if ("center" === o) w = (t.top + t.bottom) / 2 + g; + else if (n(o)) { + const t = Object.keys(o)[0], + e = o[t]; + w = this.chart.scales[t].getPixelForValue(e) + g; + } + k = this._getXAxisLabelAlignment(); + } else if ("y" === e) { + if ("center" === o) M = (t.left + t.right) / 2 - g; + else if (n(o)) { + const t = Object.keys(o)[0], + e = o[t]; + M = this.chart.scales[t].getPixelForValue(e); + } + k = this._getYAxisLabelAlignment(f).textAlign; + } + "y" === e && + ("start" === h ? (A = "top") : "end" === h && (A = "bottom")); + const T = this._getLabelSizes(); + for (x = 0, _ = l.length; x < _; ++x) { + (y = l[x]), (v = y.label); + const t = a.setContext(this.getContext(x)); + (S = this.getPixelForTick(x) + a.labelOffset), + (P = this._resolveTickFontOptions(x)), + (D = P.lineHeight), + (C = s(v) ? v.length : 1); + const e = C / 2, + i = t.color, + n = t.textStrokeColor, + h = t.textStrokeWidth; + let d, + f = k; + if ( + (r + ? ((M = S), + "inner" === k && + (f = + x === _ - 1 + ? this.options.reverse + ? "left" + : "right" + : 0 === x + ? this.options.reverse + ? "right" + : "left" + : "center"), + (O = + "top" === o + ? "near" === c || 0 !== m + ? -C * D + D / 2 + : "center" === c + ? -T.highest.height / 2 - e * D + D + : -T.highest.height + D / 2 + : "near" === c || 0 !== m + ? D / 2 + : "center" === c + ? T.highest.height / 2 - e * D + : T.highest.height - C * D), + u && (O *= -1), + 0 === m || t.showLabelBackdrop || (M += (D / 2) * Math.sin(m))) + : ((w = S), (O = ((1 - C) * D) / 2)), + t.showLabelBackdrop) + ) { + const e = wi(t.backdropPadding), + i = T.heights[x], + s = T.widths[x]; + let n = O - e.top, + o = 0 - e.left; + switch (A) { + case "middle": + n -= i / 2; + break; + case "bottom": + n -= i; + } + switch (k) { + case "center": + o -= s / 2; + break; + case "right": + o -= s; + } + d = { + left: o, + top: n, + width: s + e.width, + height: i + e.height, + color: t.backdropColor + }; + } + b.push({ + rotation: m, + label: v, + font: P, + color: i, + strokeColor: n, + strokeWidth: h, + textOffset: O, + textAlign: f, + textBaseline: A, + translation: [M, w], + backdrop: d + }); + } + return b; + } + _getXAxisLabelAlignment() { + const { position: t, ticks: e } = this.options; + if (-j(this.labelRotation)) return "top" === t ? "left" : "right"; + let i = "center"; + return ( + "start" === e.align + ? (i = "left") + : "end" === e.align + ? (i = "right") + : "inner" === e.align && (i = "inner"), + i + ); + } + _getYAxisLabelAlignment(t) { + const { + position: e, + ticks: { crossAlign: i, mirror: s, padding: n } + } = this.options, + o = t + n, + a = this._getLabelSizes().widest.width; + let r, l; + return ( + "left" === e + ? s + ? ((l = this.right + n), + "near" === i + ? (r = "left") + : "center" === i + ? ((r = "center"), (l += a / 2)) + : ((r = "right"), (l += a))) + : ((l = this.right - o), + "near" === i + ? (r = "right") + : "center" === i + ? ((r = "center"), (l -= a / 2)) + : ((r = "left"), (l = this.left))) + : "right" === e + ? s + ? ((l = this.left + n), + "near" === i + ? (r = "right") + : "center" === i + ? ((r = "center"), (l -= a / 2)) + : ((r = "left"), (l -= a))) + : ((l = this.left + o), + "near" === i + ? (r = "left") + : "center" === i + ? ((r = "center"), (l += a / 2)) + : ((r = "right"), (l = this.right))) + : (r = "right"), + { textAlign: r, x: l } + ); + } + _computeLabelArea() { + if (this.options.ticks.mirror) return; + const t = this.chart, + e = this.options.position; + return "left" === e || "right" === e + ? { top: 0, left: this.left, bottom: t.height, right: this.right } + : "top" === e || "bottom" === e + ? { top: this.top, left: 0, bottom: this.bottom, right: t.width } + : void 0; + } + drawBackground() { + const { + ctx: t, + options: { backgroundColor: e }, + left: i, + top: s, + width: n, + height: o + } = this; + e && (t.save(), (t.fillStyle = e), t.fillRect(i, s, n, o), t.restore()); + } + getLineWidthForValue(t) { + const e = this.options.grid; + if (!this._isVisible() || !e.display) return 0; + const i = this.ticks.findIndex(e => e.value === t); + if (i >= 0) { + return e.setContext(this.getContext(i)).lineWidth; + } + return 0; + } + drawGrid(t) { + const e = this.options.grid, + i = this.ctx, + s = + this._gridLineItems || + (this._gridLineItems = this._computeGridLineItems(t)); + let n, o; + const a = (t, e, s) => { + s.width && + s.color && + (i.save(), + (i.lineWidth = s.width), + (i.strokeStyle = s.color), + i.setLineDash(s.borderDash || []), + (i.lineDashOffset = s.borderDashOffset), + i.beginPath(), + i.moveTo(t.x, t.y), + i.lineTo(e.x, e.y), + i.stroke(), + i.restore()); + }; + if (e.display) + for (n = 0, o = s.length; n < o; ++n) { + const t = s[n]; + e.drawOnChartArea && a({ x: t.x1, y: t.y1 }, { x: t.x2, y: t.y2 }, t), + e.drawTicks && + a( + { x: t.tx1, y: t.ty1 }, + { x: t.tx2, y: t.ty2 }, + { + color: t.tickColor, + width: t.tickWidth, + borderDash: t.tickBorderDash, + borderDashOffset: t.tickBorderDashOffset + } + ); + } + } + drawBorder() { + const { + chart: t, + ctx: e, + options: { border: i, grid: s } + } = this, + n = i.setContext(this.getContext()), + o = i.display ? n.width : 0; + if (!o) return; + const a = s.setContext(this.getContext(0)).lineWidth, + r = this._borderValue; + let l, h, c, d; + this.isHorizontal() + ? ((l = Oe(t, this.left, o) - o / 2), + (h = Oe(t, this.right, a) + a / 2), + (c = d = r)) + : ((c = Oe(t, this.top, o) - o / 2), + (d = Oe(t, this.bottom, a) + a / 2), + (l = h = r)), + e.save(), + (e.lineWidth = n.width), + (e.strokeStyle = n.color), + e.beginPath(), + e.moveTo(l, c), + e.lineTo(h, d), + e.stroke(), + e.restore(); + } + drawLabels(t) { + if (!this.options.ticks.display) return; + const e = this.ctx, + i = this._computeLabelArea(); + i && Re(e, i); + const s = + this._labelItems || (this._labelItems = this._computeLabelItems(t)); + let n, o; + for (n = 0, o = s.length; n < o; ++n) { + const t = s[n], + i = t.font; + Ve(e, t.label, 0, t.textOffset, i, t); + } + i && Ie(e); + } + drawTitle() { + const { + ctx: t, + options: { position: e, title: i, reverse: o } + } = this; + if (!i.display) return; + const a = ki(i.font), + r = wi(i.padding), + l = i.align; + let h = a.lineHeight / 2; + "bottom" === e || "center" === e || n(e) + ? ((h += r.bottom), + s(i.text) && (h += a.lineHeight * (i.text.length - 1))) + : (h += r.top); + const { titleX: c, titleY: d, maxWidth: u, rotation: f } = (function( + t, + e, + i, + s + ) { + const { top: o, left: a, bottom: r, right: l, chart: h } = t, + { chartArea: c, scales: d } = h; + let u, + f, + g, + p = 0; + const m = r - o, + b = l - a; + if (t.isHorizontal()) { + if (((f = ut(s, a, l)), n(i))) { + const t = Object.keys(i)[0], + s = i[t]; + g = d[t].getPixelForValue(s) + m - e; + } else + g = "center" === i ? (c.bottom + c.top) / 2 + m - e : js(t, i, e); + u = l - a; + } else { + if (n(i)) { + const t = Object.keys(i)[0], + s = i[t]; + f = d[t].getPixelForValue(s) - b + e; + } else + f = "center" === i ? (c.left + c.right) / 2 - b + e : js(t, i, e); + (g = ut(s, r, o)), (p = "left" === i ? -L : L); + } + return { titleX: f, titleY: g, maxWidth: u, rotation: p }; + })(this, h, e, l); + Ve(t, i.text, 0, 0, a, { + color: i.color, + maxWidth: u, + rotation: f, + textAlign: qs(l, e, o), + textBaseline: "middle", + translation: [c, d] + }); + } + draw(t) { + this._isVisible() && + (this.drawBackground(), + this.drawGrid(t), + this.drawBorder(), + this.drawTitle(), + this.drawLabels(t)); + } + _layers() { + const t = this.options, + e = (t.ticks && t.ticks.z) || 0, + i = r(t.grid && t.grid.z, -1), + s = r(t.border && t.border.z, 0); + return this._isVisible() && this.draw === Ks.prototype.draw + ? [ + { + z: i, + draw: t => { + this.drawBackground(), this.drawGrid(t), this.drawTitle(); + } + }, + { + z: s, + draw: () => { + this.drawBorder(); + } + }, + { + z: e, + draw: t => { + this.drawLabels(t); + } + } + ] + : [ + { + z: e, + draw: t => { + this.draw(t); + } + } + ]; + } + getMatchingVisibleMetas(t) { + const e = this.chart.getSortedVisibleDatasetMetas(), + i = this.axis + "AxisID", + s = []; + let n, o; + for (n = 0, o = e.length; n < o; ++n) { + const o = e[n]; + o[i] !== this.id || (t && o.type !== t) || s.push(o); + } + return s; + } + _resolveTickFontOptions(t) { + return ki(this.options.ticks.setContext(this.getContext(t)).font); + } + _maxDigits() { + const t = this._resolveTickFontOptions(0).lineHeight; + return (this.isHorizontal() ? this.width : this.height) / t; + } + } + class Gs { + constructor(t, e, i) { + (this.type = t), + (this.scope = e), + (this.override = i), + (this.items = Object.create(null)); + } + isForType(t) { + return Object.prototype.isPrototypeOf.call( + this.type.prototype, + t.prototype + ); + } + register(t) { + const e = Object.getPrototypeOf(t); + let i; + (function(t) { + return "id" in t && "defaults" in t; + })(e) && (i = this.register(e)); + const s = this.items, + n = t.id, + o = this.scope + "." + n; + if (!n) throw new Error("class does not have id: " + t); + return ( + n in s || + ((s[n] = t), + (function(t, e, i) { + const s = m(Object.create(null), [ + i ? ue.get(i) : {}, + ue.get(e), + t.defaults + ]); + ue.set(e, s), + t.defaultRoutes && + (function(t, e) { + Object.keys(e).forEach(i => { + const s = i.split("."), + n = s.pop(), + o = [t].concat(s).join("."), + a = e[i].split("."), + r = a.pop(), + l = a.join("."); + ue.route(o, n, l, r); + }); + })(e, t.defaultRoutes); + t.descriptors && ue.describe(e, t.descriptors); + })(t, o, i), + this.override && ue.override(t.id, t.overrides)), + o + ); + } + get(t) { + return this.items[t]; + } + unregister(t) { + const e = this.items, + i = t.id, + s = this.scope; + i in e && delete e[i], + s && i in ue[s] && (delete ue[s][i], this.override && delete re[i]); + } + } + class Zs { + constructor() { + (this.controllers = new Gs(Bs, "datasets", !0)), + (this.elements = new Gs(Ns, "elements")), + (this.plugins = new Gs(Object, "plugins")), + (this.scales = new Gs(Ks, "scales")), + (this._typedRegistries = [ + this.controllers, + this.scales, + this.elements + ]); + } + add(...t) { + this._each("register", t); + } + remove(...t) { + this._each("unregister", t); + } + addControllers(...t) { + this._each("register", t, this.controllers); + } + addElements(...t) { + this._each("register", t, this.elements); + } + addPlugins(...t) { + this._each("register", t, this.plugins); + } + addScales(...t) { + this._each("register", t, this.scales); + } + getController(t) { + return this._get(t, this.controllers, "controller"); + } + getElement(t) { + return this._get(t, this.elements, "element"); + } + getPlugin(t) { + return this._get(t, this.plugins, "plugin"); + } + getScale(t) { + return this._get(t, this.scales, "scale"); + } + removeControllers(...t) { + this._each("unregister", t, this.controllers); + } + removeElements(...t) { + this._each("unregister", t, this.elements); + } + removePlugins(...t) { + this._each("unregister", t, this.plugins); + } + removeScales(...t) { + this._each("unregister", t, this.scales); + } + _each(t, e, i) { + [...e].forEach(e => { + const s = i || this._getRegistryForType(e); + i || s.isForType(e) || (s === this.plugins && e.id) + ? this._exec(t, s, e) + : d(e, e => { + const s = i || this._getRegistryForType(e); + this._exec(t, s, e); + }); + }); + } + _exec(t, e, i) { + const s = M(t); + c(i["before" + s], [], i), e[t](i), c(i["after" + s], [], i); + } + _getRegistryForType(t) { + for (let e = 0; e < this._typedRegistries.length; e++) { + const i = this._typedRegistries[e]; + if (i.isForType(t)) return i; + } + return this.plugins; + } + _get(t, e, i) { + const s = e.get(t); + if (void 0 === s) + throw new Error('"' + t + '" is not a registered ' + i + "."); + return s; + } + } + var Js = new Zs(); + class Qs { + constructor() { + this._init = []; + } + notify(t, e, i, s) { + "beforeInit" === e && + ((this._init = this._createDescriptors(t, !0)), + this._notify(this._init, t, "install")); + const n = s ? this._descriptors(t).filter(s) : this._descriptors(t), + o = this._notify(n, t, e, i); + return ( + "afterDestroy" === e && + (this._notify(n, t, "stop"), + this._notify(this._init, t, "uninstall")), + o + ); + } + _notify(t, e, i, s) { + s = s || {}; + for (const n of t) { + const t = n.plugin; + if (!1 === c(t[i], [e, s, n.options], t) && s.cancelable) return !1; + } + return !0; + } + invalidate() { + i(this._cache) || + ((this._oldCache = this._cache), (this._cache = void 0)); + } + _descriptors(t) { + if (this._cache) return this._cache; + const e = (this._cache = this._createDescriptors(t)); + return this._notifyStateChanges(t), e; + } + _createDescriptors(t, e) { + const i = t && t.config, + s = r(i.options && i.options.plugins, {}), + n = (function(t) { + const e = {}, + i = [], + s = Object.keys(Js.plugins.items); + for (let t = 0; t < s.length; t++) i.push(Js.getPlugin(s[t])); + const n = t.plugins || []; + for (let t = 0; t < n.length; t++) { + const s = n[t]; + -1 === i.indexOf(s) && (i.push(s), (e[s.id] = !0)); + } + return { plugins: i, localIds: e }; + })(i); + return !1 !== s || e + ? (function(t, { plugins: e, localIds: i }, s, n) { + const o = [], + a = t.getContext(); + for (const r of e) { + const e = r.id, + l = tn(s[e], n); + null !== l && + o.push({ + plugin: r, + options: en(t.config, { plugin: r, local: i[e] }, l, a) + }); + } + return o; + })(t, n, s, e) + : []; + } + _notifyStateChanges(t) { + const e = this._oldCache || [], + i = this._cache, + s = (t, e) => t.filter(t => !e.some(e => t.plugin.id === e.plugin.id)); + this._notify(s(e, i), t, "stop"), this._notify(s(i, e), t, "start"); + } + } + function tn(t, e) { + return e || !1 !== t ? (!0 === t ? {} : t) : null; + } + function en(t, { plugin: e, local: i }, s, n) { + const o = t.pluginScopeKeys(e), + a = t.getOptionScopes(s, o); + return ( + i && e.defaults && a.push(e.defaults), + t.createResolver(a, n, [""], { + scriptable: !1, + indexable: !1, + allKeys: !0 + }) + ); + } + function sn(t, e) { + const i = ue.datasets[t] || {}; + return ( + ((e.datasets || {})[t] || {}).indexAxis || + e.indexAxis || + i.indexAxis || + "x" + ); + } + function nn(t, e) { + if ("x" === t || "y" === t || "r" === t) return t; + var i; + if ( + (t = + e.axis || + ("top" === (i = e.position) || "bottom" === i + ? "x" + : "left" === i || "right" === i + ? "y" + : void 0) || + (t.length > 1 && nn(t[0].toLowerCase(), e))) + ) + return t; + throw new Error( + `Cannot determine type of '${name}' axis. Please provide 'axis' or 'position' option.` + ); + } + function on(t) { + const e = t.options || (t.options = {}); + (e.plugins = r(e.plugins, {})), + (e.scales = (function(t, e) { + const i = re[t.type] || { scales: {} }, + s = e.scales || {}, + o = sn(t.type, e), + a = Object.create(null); + return ( + Object.keys(s).forEach(t => { + const e = s[t]; + if (!n(e)) + return console.error( + `Invalid scale configuration for scale: ${t}` + ); + if (e._proxy) + return console.warn( + `Ignoring resolver passed as options for scale: ${t}` + ); + const r = nn(t, e), + l = (function(t, e) { + return t === e ? "_index_" : "_value_"; + })(r, o), + h = i.scales || {}; + a[t] = b(Object.create(null), [{ axis: r }, e, h[r], h[l]]); + }), + t.data.datasets.forEach(i => { + const n = i.type || t.type, + o = i.indexAxis || sn(n, e), + r = (re[n] || {}).scales || {}; + Object.keys(r).forEach(t => { + const e = (function(t, e) { + let i = t; + return ( + "_index_" === t + ? (i = e) + : "_value_" === t && (i = "x" === e ? "y" : "x"), + i + ); + })(t, o), + n = i[e + "AxisID"] || e; + (a[n] = a[n] || Object.create(null)), + b(a[n], [{ axis: e }, s[n], r[t]]); + }); + }), + Object.keys(a).forEach(t => { + const e = a[t]; + b(e, [ue.scales[e.type], ue.scale]); + }), + a + ); + })(t, e)); + } + function an(t) { + return ( + ((t = t || {}).datasets = t.datasets || []), + (t.labels = t.labels || []), + t + ); + } + const rn = new Map(), + ln = new Set(); + function hn(t, e) { + let i = rn.get(t); + return i || ((i = e()), rn.set(t, i), ln.add(i)), i; + } + const cn = (t, e, i) => { + const s = v(e, i); + void 0 !== s && t.add(s); + }; + class dn { + constructor(t) { + (this._config = (function(t) { + return ((t = t || {}).data = an(t.data)), on(t), t; + })(t)), + (this._scopeCache = new Map()), + (this._resolverCache = new Map()); + } + get platform() { + return this._config.platform; + } + get type() { + return this._config.type; + } + set type(t) { + this._config.type = t; + } + get data() { + return this._config.data; + } + set data(t) { + this._config.data = an(t); + } + get options() { + return this._config.options; + } + set options(t) { + this._config.options = t; + } + get plugins() { + return this._config.plugins; + } + update() { + const t = this._config; + this.clearCache(), on(t); + } + clearCache() { + this._scopeCache.clear(), this._resolverCache.clear(); + } + datasetScopeKeys(t) { + return hn(t, () => [[`datasets.${t}`, ""]]); + } + datasetAnimationScopeKeys(t, e) { + return hn(`${t}.transition.${e}`, () => [ + [`datasets.${t}.transitions.${e}`, `transitions.${e}`], + [`datasets.${t}`, ""] + ]); + } + datasetElementScopeKeys(t, e) { + return hn(`${t}-${e}`, () => [ + [`datasets.${t}.elements.${e}`, `datasets.${t}`, `elements.${e}`, ""] + ]); + } + pluginScopeKeys(t) { + const e = t.id; + return hn(`${this.type}-plugin-${e}`, () => [ + [`plugins.${e}`, ...(t.additionalOptionScopes || [])] + ]); + } + _cachedScopes(t, e) { + const i = this._scopeCache; + let s = i.get(t); + return (s && !e) || ((s = new Map()), i.set(t, s)), s; + } + getOptionScopes(t, e, i) { + const { options: s, type: n } = this, + o = this._cachedScopes(t, i), + a = o.get(e); + if (a) return a; + const r = new Set(); + e.forEach(e => { + t && (r.add(t), e.forEach(e => cn(r, t, e))), + e.forEach(t => cn(r, s, t)), + e.forEach(t => cn(r, re[n] || {}, t)), + e.forEach(t => cn(r, ue, t)), + e.forEach(t => cn(r, le, t)); + }); + const l = Array.from(r); + return ( + 0 === l.length && l.push(Object.create(null)), + ln.has(e) && o.set(e, l), + l + ); + } + chartOptionScopes() { + const { options: t, type: e } = this; + return [t, re[e] || {}, ue.datasets[e] || {}, { type: e }, ue, le]; + } + resolveNamedOptions(t, e, i, n = [""]) { + const o = { $shared: !0 }, + { resolver: a, subPrefixes: r } = un(this._resolverCache, t, n); + let l = a; + if ( + (function(t, e) { + const { isScriptable: i, isIndexable: n } = $e(t); + for (const o of e) { + const e = i(o), + a = n(o), + r = (a || e) && t[o]; + if ((e && (k(r) || fn(r))) || (a && s(r))) return !0; + } + return !1; + })(a, e) + ) { + o.$shared = !1; + l = je(a, (i = k(i) ? i() : i), this.createResolver(t, i, r)); + } + for (const t of e) o[t] = l[t]; + return o; + } + createResolver(t, e, i = [""], s) { + const { resolver: o } = un(this._resolverCache, t, i); + return n(e) ? je(o, e, void 0, s) : o; + } + } + function un(t, e, i) { + let s = t.get(e); + s || ((s = new Map()), t.set(e, s)); + const n = i.join(); + let o = s.get(n); + if (!o) { + (o = { + resolver: He(e, i), + subPrefixes: i.filter(t => !t.toLowerCase().includes("hover")) + }), + s.set(n, o); + } + return o; + } + const fn = t => + n(t) && Object.getOwnPropertyNames(t).reduce((e, i) => e || k(t[i]), !1); + const gn = ["top", "bottom", "left", "right", "chartArea"]; + function pn(t, e) { + return "top" === t || "bottom" === t || (-1 === gn.indexOf(t) && "x" === e); + } + function mn(t, e) { + return function(i, s) { + return i[t] === s[t] ? i[e] - s[e] : i[t] - s[t]; + }; + } + function bn(t) { + const e = t.chart, + i = e.options.animation; + e.notifyPlugins("afterRender"), c(i && i.onComplete, [t], e); + } + function xn(t) { + const e = t.chart, + i = e.options.animation; + c(i && i.onProgress, [t], e); + } + function _n(t) { + return ( + fe() && "string" == typeof t + ? (t = document.getElementById(t)) + : t && t.length && (t = t[0]), + t && t.canvas && (t = t.canvas), + t + ); + } + const yn = {}, + vn = t => { + const e = _n(t); + return Object.values(yn) + .filter(t => t.canvas === e) + .pop(); + }; + function Mn(t, e, i) { + const s = Object.keys(t); + for (const n of s) { + const s = +n; + if (s >= e) { + const o = t[n]; + delete t[n], (i > 0 || s > e) && (t[s + i] = o); + } + } + } + class wn { + static defaults = ue; + static instances = yn; + static overrides = re; + static registry = Js; + static version = "4.0.1"; + static getChart = vn; + static register(...t) { + Js.add(...t), kn(); + } + static unregister(...t) { + Js.remove(...t), kn(); + } + constructor(t, i) { + const s = (this.config = new dn(i)), + n = _n(t), + o = vn(n); + if (o) + throw new Error( + "Canvas is already in use. Chart with ID '" + + o.id + + "' must be destroyed before the canvas with ID '" + + o.canvas.id + + "' can be reused." + ); + const a = s.createResolver(s.chartOptionScopes(), this.getContext()); + (this.platform = new (s.platform || Ms(n))()), + this.platform.updateConfig(s); + const r = this.platform.acquireContext(n, a.aspectRatio), + l = r && r.canvas, + h = l && l.height, + c = l && l.width; + (this.id = e()), + (this.ctx = r), + (this.canvas = l), + (this.width = c), + (this.height = h), + (this._options = a), + (this._aspectRatio = this.aspectRatio), + (this._layers = []), + (this._metasets = []), + (this._stacks = void 0), + (this.boxes = []), + (this.currentDevicePixelRatio = void 0), + (this.chartArea = void 0), + (this._active = []), + (this._lastEvent = void 0), + (this._listeners = {}), + (this._responsiveListeners = void 0), + (this._sortedMetasets = []), + (this.scales = {}), + (this._plugins = new Qs()), + (this.$proxies = {}), + (this._hiddenIndices = {}), + (this.attached = !1), + (this._animationsDisabled = void 0), + (this.$context = void 0), + (this._doResize = ct(t => this.update(t), a.resizeDelay || 0)), + (this._dataChanges = []), + (yn[this.id] = this), + r && l + ? (bt.listen(this, "complete", bn), + bt.listen(this, "progress", xn), + this._initialize(), + this.attached && this.update()) + : console.error( + "Failed to create chart: can't acquire context from the given item" + ); + } + get aspectRatio() { + const { + options: { aspectRatio: t, maintainAspectRatio: e }, + width: s, + height: n, + _aspectRatio: o + } = this; + return i(t) ? (e && o ? o : n ? s / n : null) : t; + } + get data() { + return this.config.data; + } + set data(t) { + this.config.data = t; + } + get options() { + return this._options; + } + set options(t) { + this.config.options = t; + } + get registry() { + return Js; + } + _initialize() { + return ( + this.notifyPlugins("beforeInit"), + this.options.responsive + ? this.resize() + : we(this, this.options.devicePixelRatio), + this.bindEvents(), + this.notifyPlugins("afterInit"), + this + ); + } + clear() { + return Ae(this.canvas, this.ctx), this; + } + stop() { + return bt.stop(this), this; + } + resize(t, e) { + bt.running(this) + ? (this._resizeBeforeDraw = { width: t, height: e }) + : this._resize(t, e); + } + _resize(t, e) { + const i = this.options, + s = this.canvas, + n = i.maintainAspectRatio && this.aspectRatio, + o = this.platform.getMaximumSize(s, t, e, n), + a = i.devicePixelRatio || this.platform.getDevicePixelRatio(), + r = this.width ? "resize" : "attach"; + (this.width = o.width), + (this.height = o.height), + (this._aspectRatio = this.aspectRatio), + we(this, a, !0) && + (this.notifyPlugins("resize", { size: o }), + c(i.onResize, [this, o], this), + this.attached && this._doResize(r) && this.render()); + } + ensureScalesHaveIDs() { + d(this.options.scales || {}, (t, e) => { + t.id = e; + }); + } + buildOrUpdateScales() { + const t = this.options, + e = t.scales, + i = this.scales, + s = Object.keys(i).reduce((t, e) => ((t[e] = !1), t), {}); + let n = []; + e && + (n = n.concat( + Object.keys(e).map(t => { + const i = e[t], + s = nn(t, i), + n = "r" === s, + o = "x" === s; + return { + options: i, + dposition: n ? "chartArea" : o ? "bottom" : "left", + dtype: n ? "radialLinear" : o ? "category" : "linear" + }; + }) + )), + d(n, e => { + const n = e.options, + o = n.id, + a = nn(o, n), + l = r(n.type, e.dtype); + (void 0 !== n.position && pn(n.position, a) === pn(e.dposition)) || + (n.position = e.dposition), + (s[o] = !0); + let h = null; + if (o in i && i[o].type === l) h = i[o]; + else { + (h = new (Js.getScale(l))({ + id: o, + type: l, + ctx: this.ctx, + chart: this + })), + (i[h.id] = h); + } + h.init(n, t); + }), + d(s, (t, e) => { + t || delete i[e]; + }), + d(i, t => { + os.configure(this, t, t.options), os.addBox(this, t); + }); + } + _updateMetasets() { + const t = this._metasets, + e = this.data.datasets.length, + i = t.length; + if ((t.sort((t, e) => t.index - e.index), i > e)) { + for (let t = e; t < i; ++t) this._destroyDatasetMeta(t); + t.splice(e, i - e); + } + this._sortedMetasets = t.slice(0).sort(mn("order", "index")); + } + _removeUnreferencedMetasets() { + const { + _metasets: t, + data: { datasets: e } + } = this; + t.length > e.length && delete this._stacks, + t.forEach((t, i) => { + 0 === e.filter(e => e === t._dataset).length && + this._destroyDatasetMeta(i); + }); + } + buildOrUpdateControllers() { + const t = [], + e = this.data.datasets; + let i, s; + for ( + this._removeUnreferencedMetasets(), i = 0, s = e.length; + i < s; + i++ + ) { + const s = e[i]; + let n = this.getDatasetMeta(i); + const o = s.type || this.config.type; + if ( + (n.type && + n.type !== o && + (this._destroyDatasetMeta(i), (n = this.getDatasetMeta(i))), + (n.type = o), + (n.indexAxis = s.indexAxis || sn(o, this.options)), + (n.order = s.order || 0), + (n.index = i), + (n.label = "" + s.label), + (n.visible = this.isDatasetVisible(i)), + n.controller) + ) + n.controller.updateIndex(i), n.controller.linkScales(); + else { + const e = Js.getController(o), + { datasetElementType: s, dataElementType: a } = ue.datasets[o]; + Object.assign(e, { + dataElementType: Js.getElement(a), + datasetElementType: s && Js.getElement(s) + }), + (n.controller = new e(this, i)), + t.push(n.controller); + } + } + return this._updateMetasets(), t; + } + _resetElements() { + d( + this.data.datasets, + (t, e) => { + this.getDatasetMeta(e).controller.reset(); + }, + this + ); + } + reset() { + this._resetElements(), this.notifyPlugins("reset"); + } + update(t) { + const e = this.config; + e.update(); + const i = (this._options = e.createResolver( + e.chartOptionScopes(), + this.getContext() + )), + s = (this._animationsDisabled = !i.animation); + if ( + (this._updateScales(), + this._checkEventBindings(), + this._updateHiddenIndices(), + this._plugins.invalidate(), + !1 === this.notifyPlugins("beforeUpdate", { mode: t, cancelable: !0 })) + ) + return; + const n = this.buildOrUpdateControllers(); + this.notifyPlugins("beforeElementsUpdate"); + let o = 0; + for (let t = 0, e = this.data.datasets.length; t < e; t++) { + const { controller: e } = this.getDatasetMeta(t), + i = !s && -1 === n.indexOf(e); + e.buildOrUpdateElements(i), (o = Math.max(+e.getMaxOverflow(), o)); + } + (o = this._minPadding = i.layout.autoPadding ? o : 0), + this._updateLayout(o), + s || + d(n, t => { + t.reset(); + }), + this._updateDatasets(t), + this.notifyPlugins("afterUpdate", { mode: t }), + this._layers.sort(mn("z", "_idx")); + const { _active: a, _lastEvent: r } = this; + r + ? this._eventHandler(r, !0) + : a.length && this._updateHoverStyles(a, a, !0), + this.render(); + } + _updateScales() { + d(this.scales, t => { + os.removeBox(this, t); + }), + this.ensureScalesHaveIDs(), + this.buildOrUpdateScales(); + } + _checkEventBindings() { + const t = this.options, + e = new Set(Object.keys(this._listeners)), + i = new Set(t.events); + (S(e, i) && !!this._responsiveListeners === t.responsive) || + (this.unbindEvents(), this.bindEvents()); + } + _updateHiddenIndices() { + const { _hiddenIndices: t } = this, + e = this._getUniformDataChanges() || []; + for (const { method: i, start: s, count: n } of e) { + Mn(t, s, "_removeElements" === i ? -n : n); + } + } + _getUniformDataChanges() { + const t = this._dataChanges; + if (!t || !t.length) return; + this._dataChanges = []; + const e = this.data.datasets.length, + i = e => + new Set( + t + .filter(t => t[0] === e) + .map((t, e) => e + "," + t.splice(1).join(",")) + ), + s = i(0); + for (let t = 1; t < e; t++) if (!S(s, i(t))) return; + return Array.from(s) + .map(t => t.split(",")) + .map(t => ({ method: t[1], start: +t[2], count: +t[3] })); + } + _updateLayout(t) { + if (!1 === this.notifyPlugins("beforeLayout", { cancelable: !0 })) return; + os.update(this, this.width, this.height, t); + const e = this.chartArea, + i = e.width <= 0 || e.height <= 0; + (this._layers = []), + d( + this.boxes, + t => { + (i && "chartArea" === t.position) || + (t.configure && t.configure(), this._layers.push(...t._layers())); + }, + this + ), + this._layers.forEach((t, e) => { + t._idx = e; + }), + this.notifyPlugins("afterLayout"); + } + _updateDatasets(t) { + if ( + !1 !== + this.notifyPlugins("beforeDatasetsUpdate", { mode: t, cancelable: !0 }) + ) { + for (let t = 0, e = this.data.datasets.length; t < e; ++t) + this.getDatasetMeta(t).controller.configure(); + for (let e = 0, i = this.data.datasets.length; e < i; ++e) + this._updateDataset(e, k(t) ? t({ datasetIndex: e }) : t); + this.notifyPlugins("afterDatasetsUpdate", { mode: t }); + } + } + _updateDataset(t, e) { + const i = this.getDatasetMeta(t), + s = { meta: i, index: t, mode: e, cancelable: !0 }; + !1 !== this.notifyPlugins("beforeDatasetUpdate", s) && + (i.controller._update(e), + (s.cancelable = !1), + this.notifyPlugins("afterDatasetUpdate", s)); + } + render() { + !1 !== this.notifyPlugins("beforeRender", { cancelable: !0 }) && + (bt.has(this) + ? this.attached && !bt.running(this) && bt.start(this) + : (this.draw(), bn({ chart: this }))); + } + draw() { + let t; + if (this._resizeBeforeDraw) { + const { width: t, height: e } = this._resizeBeforeDraw; + this._resize(t, e), (this._resizeBeforeDraw = null); + } + if ((this.clear(), this.width <= 0 || this.height <= 0)) return; + if (!1 === this.notifyPlugins("beforeDraw", { cancelable: !0 })) return; + const e = this._layers; + for (t = 0; t < e.length && e[t].z <= 0; ++t) e[t].draw(this.chartArea); + for (this._drawDatasets(); t < e.length; ++t) e[t].draw(this.chartArea); + this.notifyPlugins("afterDraw"); + } + _getSortedDatasetMetas(t) { + const e = this._sortedMetasets, + i = []; + let s, n; + for (s = 0, n = e.length; s < n; ++s) { + const n = e[s]; + (t && !n.visible) || i.push(n); + } + return i; + } + getSortedVisibleDatasetMetas() { + return this._getSortedDatasetMetas(!0); + } + _drawDatasets() { + if (!1 === this.notifyPlugins("beforeDatasetsDraw", { cancelable: !0 })) + return; + const t = this.getSortedVisibleDatasetMetas(); + for (let e = t.length - 1; e >= 0; --e) this._drawDataset(t[e]); + this.notifyPlugins("afterDatasetsDraw"); + } + _drawDataset(t) { + const e = this.ctx, + i = t._clip, + s = !i.disabled, + n = + (function(t) { + const { xScale: e, yScale: i } = t; + if (e && i) + return { + left: e.left, + right: e.right, + top: i.top, + bottom: i.bottom + }; + })(t) || this.chartArea, + o = { meta: t, index: t.index, cancelable: !0 }; + !1 !== this.notifyPlugins("beforeDatasetDraw", o) && + (s && + Re(e, { + left: !1 === i.left ? 0 : n.left - i.left, + right: !1 === i.right ? this.width : n.right + i.right, + top: !1 === i.top ? 0 : n.top - i.top, + bottom: !1 === i.bottom ? this.height : n.bottom + i.bottom + }), + t.controller.draw(), + s && Ie(e), + (o.cancelable = !1), + this.notifyPlugins("afterDatasetDraw", o)); + } + isPointInArea(t) { + return Ee(t, this.chartArea, this._minPadding); + } + getElementsAtEventForMode(t, e, i, s) { + const n = Ui.modes[e]; + return "function" == typeof n ? n(this, t, i, s) : []; + } + getDatasetMeta(t) { + const e = this.data.datasets[t], + i = this._metasets; + let s = i.filter(t => t && t._dataset === e).pop(); + return ( + s || + ((s = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, + xAxisID: null, + yAxisID: null, + order: (e && e.order) || 0, + index: t, + _dataset: e, + _parsed: [], + _sorted: !1 + }), + i.push(s)), + s + ); + } + getContext() { + return ( + this.$context || + (this.$context = Di(null, { chart: this, type: "chart" })) + ); + } + getVisibleDatasetCount() { + return this.getSortedVisibleDatasetMetas().length; + } + isDatasetVisible(t) { + const e = this.data.datasets[t]; + if (!e) return !1; + const i = this.getDatasetMeta(t); + return "boolean" == typeof i.hidden ? !i.hidden : !e.hidden; + } + setDatasetVisibility(t, e) { + this.getDatasetMeta(t).hidden = !e; + } + toggleDataVisibility(t) { + this._hiddenIndices[t] = !this._hiddenIndices[t]; + } + getDataVisibility(t) { + return !this._hiddenIndices[t]; + } + _updateVisibility(t, e, i) { + const s = i ? "show" : "hide", + n = this.getDatasetMeta(t), + o = n.controller._resolveAnimations(void 0, s); + w(e) + ? ((n.data[e].hidden = !i), this.update()) + : (this.setDatasetVisibility(t, i), + o.update(n, { visible: i }), + this.update(e => (e.datasetIndex === t ? s : void 0))); + } + hide(t, e) { + this._updateVisibility(t, e, !1); + } + show(t, e) { + this._updateVisibility(t, e, !0); + } + _destroyDatasetMeta(t) { + const e = this._metasets[t]; + e && e.controller && e.controller._destroy(), delete this._metasets[t]; + } + _stop() { + let t, e; + for ( + this.stop(), bt.remove(this), t = 0, e = this.data.datasets.length; + t < e; + ++t + ) + this._destroyDatasetMeta(t); + } + destroy() { + this.notifyPlugins("beforeDestroy"); + const { canvas: t, ctx: e } = this; + this._stop(), + this.config.clearCache(), + t && + (this.unbindEvents(), + Ae(t, e), + this.platform.releaseContext(e), + (this.canvas = null), + (this.ctx = null)), + delete yn[this.id], + this.notifyPlugins("afterDestroy"); + } + toBase64Image(...t) { + return this.canvas.toDataURL(...t); + } + bindEvents() { + this.bindUserEvents(), + this.options.responsive + ? this.bindResponsiveEvents() + : (this.attached = !0); + } + bindUserEvents() { + const t = this._listeners, + e = this.platform, + i = (i, s) => { + e.addEventListener(this, i, s), (t[i] = s); + }, + s = (t, e, i) => { + (t.offsetX = e), (t.offsetY = i), this._eventHandler(t); + }; + d(this.options.events, t => i(t, s)); + } + bindResponsiveEvents() { + this._responsiveListeners || (this._responsiveListeners = {}); + const t = this._responsiveListeners, + e = this.platform, + i = (i, s) => { + e.addEventListener(this, i, s), (t[i] = s); + }, + s = (i, s) => { + t[i] && (e.removeEventListener(this, i, s), delete t[i]); + }, + n = (t, e) => { + this.canvas && this.resize(t, e); + }; + let o; + const a = () => { + s("attach", a), + (this.attached = !0), + this.resize(), + i("resize", n), + i("detach", o); + }; + (o = () => { + (this.attached = !1), + s("resize", n), + this._stop(), + this._resize(0, 0), + i("attach", a); + }), + e.isAttached(this.canvas) ? a() : o(); + } + unbindEvents() { + d(this._listeners, (t, e) => { + this.platform.removeEventListener(this, e, t); + }), + (this._listeners = {}), + d(this._responsiveListeners, (t, e) => { + this.platform.removeEventListener(this, e, t); + }), + (this._responsiveListeners = void 0); + } + updateHoverStyle(t, e, i) { + const s = i ? "set" : "remove"; + let n, o, a, r; + for ( + "dataset" === e && + ((n = this.getDatasetMeta(t[0].datasetIndex)), + n.controller["_" + s + "DatasetHoverStyle"]()), + a = 0, + r = t.length; + a < r; + ++a + ) { + o = t[a]; + const e = o && this.getDatasetMeta(o.datasetIndex).controller; + e && e[s + "HoverStyle"](o.element, o.datasetIndex, o.index); + } + } + getActiveElements() { + return this._active || []; + } + setActiveElements(t) { + const e = this._active || [], + i = t.map(({ datasetIndex: t, index: e }) => { + const i = this.getDatasetMeta(t); + if (!i) throw new Error("No dataset found at index " + t); + return { datasetIndex: t, element: i.data[e], index: e }; + }); + !u(i, e) && + ((this._active = i), + (this._lastEvent = null), + this._updateHoverStyles(i, e)); + } + notifyPlugins(t, e, i) { + return this._plugins.notify(this, t, e, i); + } + isPluginEnabled(t) { + return 1 === this._plugins._cache.filter(e => e.plugin.id === t).length; + } + _updateHoverStyles(t, e, i) { + const s = this.options.hover, + n = (t, e) => + t.filter( + t => + !e.some( + e => t.datasetIndex === e.datasetIndex && t.index === e.index + ) + ), + o = n(e, t), + a = i ? t : n(t, e); + o.length && this.updateHoverStyle(o, s.mode, !1), + a.length && s.mode && this.updateHoverStyle(a, s.mode, !0); + } + _eventHandler(t, e) { + const i = { + event: t, + replay: e, + cancelable: !0, + inChartArea: this.isPointInArea(t) + }, + s = e => + (e.options.events || this.options.events).includes(t.native.type); + if (!1 === this.notifyPlugins("beforeEvent", i, s)) return; + const n = this._handleEvent(t, e, i.inChartArea); + return ( + (i.cancelable = !1), + this.notifyPlugins("afterEvent", i, s), + (n || i.changed) && this.render(), + this + ); + } + _handleEvent(t, e, i) { + const { _active: s = [], options: n } = this, + o = e, + a = this._getActiveElements(t, s, i, o), + r = P(t), + l = (function(t, e, i, s) { + return i && "mouseout" !== t.type ? (s ? e : t) : null; + })(t, this._lastEvent, i, r); + i && + ((this._lastEvent = null), + c(n.onHover, [t, a, this], this), + r && c(n.onClick, [t, a, this], this)); + const h = !u(a, s); + return ( + (h || e) && ((this._active = a), this._updateHoverStyles(a, s, e)), + (this._lastEvent = l), + h + ); + } + _getActiveElements(t, e, i, s) { + if ("mouseout" === t.type) return []; + if (!i) return e; + const n = this.options.hover; + return this.getElementsAtEventForMode(t, n.mode, n, s); + } + } + function kn() { + return d(wn.instances, t => t._plugins.invalidate()); + } + var Sn = wn; + function Pn() { + throw new Error( + "This method is not implemented: Check that a complete date adapter is provided." + ); + } + class Dn { + static override(t) { + Object.assign(Dn.prototype, t); + } + constructor(t) { + this.options = t || {}; + } + init() {} + formats() { + return Pn(); + } + parse() { + return Pn(); + } + format() { + return Pn(); + } + add() { + return Pn(); + } + diff() { + return Pn(); + } + startOf() { + return Pn(); + } + endOf() { + return Pn(); + } + } + var Cn = { _date: Dn }; + function On(t) { + const e = t.iScale, + i = (function(t, e) { + if (!t._cache.$bar) { + const i = t.getMatchingVisibleMetas(e); + let s = []; + for (let e = 0, n = i.length; e < n; e++) + s = s.concat(i[e].controller.getAllParsedValues(t)); + t._cache.$bar = rt(s.sort((t, e) => t - e)); + } + return t._cache.$bar; + })(e, t.type); + let s, + n, + o, + a, + r = e._length; + const l = () => { + 32767 !== o && + -32768 !== o && + (w(a) && (r = Math.min(r, Math.abs(o - a) || r)), (a = o)); + }; + for (s = 0, n = i.length; s < n; ++s) (o = e.getPixelForValue(i[s])), l(); + for (a = void 0, s = 0, n = e.ticks.length; s < n; ++s) + (o = e.getPixelForTick(s)), l(); + return r; + } + function An(t, e, i, n) { + return ( + s(t) + ? (function(t, e, i, s) { + const n = i.parse(t[0], s), + o = i.parse(t[1], s), + a = Math.min(n, o), + r = Math.max(n, o); + let l = a, + h = r; + Math.abs(a) > Math.abs(r) && ((l = r), (h = a)), + (e[i.axis] = h), + (e._custom = { + barStart: l, + barEnd: h, + start: n, + end: o, + min: a, + max: r + }); + })(t, e, i, n) + : (e[i.axis] = i.parse(t, n)), + e + ); + } + function Tn(t, e, i, s) { + const n = t.iScale, + o = t.vScale, + a = n.getLabels(), + r = n === o, + l = []; + let h, c, d, u; + for (h = i, c = i + s; h < c; ++h) + (u = e[h]), + (d = {}), + (d[n.axis] = r || n.parse(a[h], h)), + l.push(An(u, d, o, h)); + return l; + } + function Ln(t) { + return t && void 0 !== t.barStart && void 0 !== t.barEnd; + } + function En(t, e, i, s) { + let n = e.borderSkipped; + const o = {}; + if (!n) return void (t.borderSkipped = o); + if (!0 === n) + return void (t.borderSkipped = { + top: !0, + right: !0, + bottom: !0, + left: !0 + }); + const { start: a, end: r, reverse: l, top: h, bottom: c } = (function(t) { + let e, i, s, n, o; + return ( + t.horizontal + ? ((e = t.base > t.x), (i = "left"), (s = "right")) + : ((e = t.base < t.y), (i = "bottom"), (s = "top")), + e ? ((n = "end"), (o = "start")) : ((n = "start"), (o = "end")), + { start: i, end: s, reverse: e, top: n, bottom: o } + ); + })(t); + "middle" === n && + i && + ((t.enableBorderRadius = !0), + (i._top || 0) === s + ? (n = h) + : (i._bottom || 0) === s + ? (n = c) + : ((o[Rn(c, a, r, l)] = !0), (n = h))), + (o[Rn(n, a, r, l)] = !0), + (t.borderSkipped = o); + } + function Rn(t, e, i, s) { + var n, o, a; + return ( + s + ? ((a = i), + (t = In((t = (n = t) === (o = e) ? a : n === a ? o : n), i, e))) + : (t = In(t, e, i)), + t + ); + } + function In(t, e, i) { + return "start" === t ? e : "end" === t ? i : t; + } + function zn(t, { inflateAmount: e }, i) { + t.inflateAmount = "auto" === e ? (1 === i ? 0.33 : 0) : e; + } + class Fn extends Bs { + static id = "doughnut"; + static defaults = { + datasetElementType: !1, + dataElementType: "arc", + animation: { animateRotate: !0, animateScale: !1 }, + animations: { + numbers: { + type: "number", + properties: [ + "circumference", + "endAngle", + "innerRadius", + "outerRadius", + "startAngle", + "x", + "y", + "offset", + "borderWidth", + "spacing" + ] + } + }, + cutout: "50%", + rotation: 0, + circumference: 360, + radius: "100%", + spacing: 0, + indexAxis: "r" + }; + static descriptors = { + _scriptable: t => "spacing" !== t, + _indexable: t => "spacing" !== t + }; + static overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(t) { + const e = t.data; + if (e.labels.length && e.datasets.length) { + const { + labels: { pointStyle: i, color: s } + } = t.legend.options; + return e.labels.map((e, n) => { + const o = t.getDatasetMeta(0).controller.getStyle(n); + return { + text: e, + fillStyle: o.backgroundColor, + strokeStyle: o.borderColor, + fontColor: s, + lineWidth: o.borderWidth, + pointStyle: i, + hidden: !t.getDataVisibility(n), + index: n + }; + }); + } + return []; + } + }, + onClick(t, e, i) { + i.chart.toggleDataVisibility(e.index), i.chart.update(); + } + } + } + }; + constructor(t, e) { + super(t, e), + (this.enableOptionSharing = !0), + (this.innerRadius = void 0), + (this.outerRadius = void 0), + (this.offsetX = void 0), + (this.offsetY = void 0); + } + linkScales() {} + parse(t, e) { + const i = this.getDataset().data, + s = this._cachedMeta; + if (!1 === this._parsing) s._parsed = i; + else { + let o, + a, + r = t => +i[t]; + if (n(i[t])) { + const { key: t = "value" } = this._parsing; + r = e => +v(i[e], t); + } + for (o = t, a = t + e; o < a; ++o) s._parsed[o] = r(o); + } + } + _getRotation() { + return j(this.options.rotation - 90); + } + _getCircumference() { + return j(this.options.circumference); + } + _getRotationExtents() { + let t = C, + e = -C; + for (let i = 0; i < this.chart.data.datasets.length; ++i) + if ( + this.chart.isDatasetVisible(i) && + this.chart.getDatasetMeta(i).type === this._type + ) { + const s = this.chart.getDatasetMeta(i).controller, + n = s._getRotation(), + o = s._getCircumference(); + (t = Math.min(t, n)), (e = Math.max(e, n + o)); + } + return { rotation: t, circumference: e - t }; + } + update(t) { + const e = this.chart, + { chartArea: i } = e, + s = this._cachedMeta, + n = s.data, + o = + this.getMaxBorderWidth() + + this.getMaxOffset(n) + + this.options.spacing, + a = Math.max((Math.min(i.width, i.height) - o) / 2, 0), + r = Math.min(l(this.options.cutout, a), 1), + c = this._getRingWeight(this.index), + { circumference: d, rotation: u } = this._getRotationExtents(), + { ratioX: f, ratioY: g, offsetX: p, offsetY: m } = (function(t, e, i) { + let s = 1, + n = 1, + o = 0, + a = 0; + if (e < C) { + const r = t, + l = r + e, + h = Math.cos(r), + c = Math.sin(r), + d = Math.cos(l), + u = Math.sin(l), + f = (t, e, s) => + G(t, r, l, !0) ? 1 : Math.max(e, e * i, s, s * i), + g = (t, e, s) => + G(t, r, l, !0) ? -1 : Math.min(e, e * i, s, s * i), + p = f(0, h, d), + m = f(L, c, u), + b = g(D, h, d), + x = g(D + L, c, u); + (s = (p - b) / 2), + (n = (m - x) / 2), + (o = -(p + b) / 2), + (a = -(m + x) / 2); + } + return { ratioX: s, ratioY: n, offsetX: o, offsetY: a }; + })(u, d, r), + b = (i.width - o) / f, + x = (i.height - o) / g, + _ = Math.max(Math.min(b, x) / 2, 0), + y = h(this.options.radius, _), + v = (y - Math.max(y * r, 0)) / this._getVisibleDatasetWeightTotal(); + (this.offsetX = p * y), + (this.offsetY = m * y), + (s.total = this.calculateTotal()), + (this.outerRadius = y - v * this._getRingWeightOffset(this.index)), + (this.innerRadius = Math.max(this.outerRadius - v * c, 0)), + this.updateElements(n, 0, n.length, t); + } + _circumference(t, e) { + const i = this.options, + s = this._cachedMeta, + n = this._getCircumference(); + return (e && i.animation.animateRotate) || + !this.chart.getDataVisibility(t) || + null === s._parsed[t] || + s.data[t].hidden + ? 0 + : this.calculateCircumference((s._parsed[t] * n) / C); + } + updateElements(t, e, i, s) { + const n = "reset" === s, + o = this.chart, + a = o.chartArea, + r = o.options.animation, + l = (a.left + a.right) / 2, + h = (a.top + a.bottom) / 2, + c = n && r.animateScale, + d = c ? 0 : this.innerRadius, + u = c ? 0 : this.outerRadius, + { sharedOptions: f, includeOptions: g } = this._getSharedOptions(e, s); + let p, + m = this._getRotation(); + for (p = 0; p < e; ++p) m += this._circumference(p, n); + for (p = e; p < e + i; ++p) { + const e = this._circumference(p, n), + i = t[p], + o = { + x: l + this.offsetX, + y: h + this.offsetY, + startAngle: m, + endAngle: m + e, + circumference: e, + outerRadius: u, + innerRadius: d + }; + g && + (o.options = + f || this.resolveDataElementOptions(p, i.active ? "active" : s)), + (m += e), + this.updateElement(i, p, o, s); + } + } + calculateTotal() { + const t = this._cachedMeta, + e = t.data; + let i, + s = 0; + for (i = 0; i < e.length; i++) { + const n = t._parsed[i]; + null === n || + isNaN(n) || + !this.chart.getDataVisibility(i) || + e[i].hidden || + (s += Math.abs(n)); + } + return s; + } + calculateCircumference(t) { + const e = this._cachedMeta.total; + return e > 0 && !isNaN(t) ? C * (Math.abs(t) / e) : 0; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart, + s = i.data.labels || [], + n = ne(e._parsed[t], i.options.locale); + return { label: s[t] || "", value: n }; + } + getMaxBorderWidth(t) { + let e = 0; + const i = this.chart; + let s, n, o, a, r; + if (!t) + for (s = 0, n = i.data.datasets.length; s < n; ++s) + if (i.isDatasetVisible(s)) { + (o = i.getDatasetMeta(s)), (t = o.data), (a = o.controller); + break; + } + if (!t) return 0; + for (s = 0, n = t.length; s < n; ++s) + (r = a.resolveDataElementOptions(s)), + "inner" !== r.borderAlign && + (e = Math.max(e, r.borderWidth || 0, r.hoverBorderWidth || 0)); + return e; + } + getMaxOffset(t) { + let e = 0; + for (let i = 0, s = t.length; i < s; ++i) { + const t = this.resolveDataElementOptions(i); + e = Math.max(e, t.offset || 0, t.hoverOffset || 0); + } + return e; + } + _getRingWeightOffset(t) { + let e = 0; + for (let i = 0; i < t; ++i) + this.chart.isDatasetVisible(i) && (e += this._getRingWeight(i)); + return e; + } + _getRingWeight(t) { + return Math.max(r(this.chart.data.datasets[t].weight, 1), 0); + } + _getVisibleDatasetWeightTotal() { + return this._getRingWeightOffset(this.chart.data.datasets.length) || 1; + } + } + var Vn = Object.freeze({ + __proto__: null, + BarController: class extends Bs { + static id = "bar"; + static defaults = { + datasetElementType: !1, + dataElementType: "bar", + categoryPercentage: 0.8, + barPercentage: 0.9, + grouped: !0, + animations: { + numbers: { + type: "number", + properties: ["x", "y", "base", "width", "height"] + } + } + }; + static overrides = { + scales: { + _index_: { type: "category", offset: !0, grid: { offset: !0 } }, + _value_: { type: "linear", beginAtZero: !0 } + } + }; + parsePrimitiveData(t, e, i, s) { + return Tn(t, e, i, s); + } + parseArrayData(t, e, i, s) { + return Tn(t, e, i, s); + } + parseObjectData(t, e, i, s) { + const { iScale: n, vScale: o } = t, + { xAxisKey: a = "x", yAxisKey: r = "y" } = this._parsing, + l = "x" === n.axis ? a : r, + h = "x" === o.axis ? a : r, + c = []; + let d, u, f, g; + for (d = i, u = i + s; d < u; ++d) + (g = e[d]), + (f = {}), + (f[n.axis] = n.parse(v(g, l), d)), + c.push(An(v(g, h), f, o, d)); + return c; + } + updateRangeFromParsed(t, e, i, s) { + super.updateRangeFromParsed(t, e, i, s); + const n = i._custom; + n && + e === this._cachedMeta.vScale && + ((t.min = Math.min(t.min, n.min)), (t.max = Math.max(t.max, n.max))); + } + getMaxOverflow() { + return 0; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + { iScale: i, vScale: s } = e, + n = this.getParsed(t), + o = n._custom, + a = Ln(o) + ? "[" + o.start + ", " + o.end + "]" + : "" + s.getLabelForValue(n[s.axis]); + return { label: "" + i.getLabelForValue(n[i.axis]), value: a }; + } + initialize() { + (this.enableOptionSharing = !0), super.initialize(); + this._cachedMeta.stack = this.getDataset().stack; + } + update(t) { + const e = this._cachedMeta; + this.updateElements(e.data, 0, e.data.length, t); + } + updateElements(t, e, s, n) { + const o = "reset" === n, + { + index: a, + _cachedMeta: { vScale: r } + } = this, + l = r.getBasePixel(), + h = r.isHorizontal(), + c = this._getRuler(), + { sharedOptions: d, includeOptions: u } = this._getSharedOptions( + e, + n + ); + for (let f = e; f < e + s; f++) { + const e = this.getParsed(f), + s = + o || i(e[r.axis]) + ? { base: l, head: l } + : this._calculateBarValuePixels(f), + g = this._calculateBarIndexPixels(f, c), + p = (e._stacks || {})[r.axis], + m = { + horizontal: h, + base: s.base, + enableBorderRadius: + !p || Ln(e._custom) || a === p._top || a === p._bottom, + x: h ? s.head : g.center, + y: h ? g.center : s.head, + height: h ? g.size : Math.abs(s.size), + width: h ? Math.abs(s.size) : g.size + }; + u && + (m.options = + d || + this.resolveDataElementOptions(f, t[f].active ? "active" : n)); + const b = m.options || t[f].options; + En(m, b, p, a), zn(m, b, c.ratio), this.updateElement(t[f], f, m, n); + } + } + _getStacks(t, e) { + const { iScale: s } = this._cachedMeta, + n = s + .getMatchingVisibleMetas(this._type) + .filter(t => t.controller.options.grouped), + o = s.options.stacked, + a = [], + r = t => { + const s = t.controller.getParsed(e), + n = s && s[t.vScale.axis]; + if (i(n) || isNaN(n)) return !0; + }; + for (const i of n) + if ( + (void 0 === e || !r(i)) && + ((!1 === o || + -1 === a.indexOf(i.stack) || + (void 0 === o && void 0 === i.stack)) && + a.push(i.stack), + i.index === t) + ) + break; + return a.length || a.push(void 0), a; + } + _getStackCount(t) { + return this._getStacks(void 0, t).length; + } + _getStackIndex(t, e, i) { + const s = this._getStacks(t, i), + n = void 0 !== e ? s.indexOf(e) : -1; + return -1 === n ? s.length - 1 : n; + } + _getRuler() { + const t = this.options, + e = this._cachedMeta, + i = e.iScale, + s = []; + let n, o; + for (n = 0, o = e.data.length; n < o; ++n) + s.push(i.getPixelForValue(this.getParsed(n)[i.axis], n)); + const a = t.barThickness; + return { + min: a || On(e), + pixels: s, + start: i._startPixel, + end: i._endPixel, + stackCount: this._getStackCount(), + scale: i, + grouped: t.grouped, + ratio: a ? 1 : t.categoryPercentage * t.barPercentage + }; + } + _calculateBarValuePixels(t) { + const { + _cachedMeta: { vScale: e, _stacked: s }, + options: { base: n, minBarLength: o } + } = this, + a = n || 0, + r = this.getParsed(t), + l = r._custom, + h = Ln(l); + let c, + d, + u = r[e.axis], + f = 0, + g = s ? this.applyStack(e, r, s) : u; + g !== u && ((f = g - u), (g = u)), + h && + ((u = l.barStart), + (g = l.barEnd - l.barStart), + 0 !== u && z(u) !== z(l.barEnd) && (f = 0), + (f += u)); + const p = i(n) || h ? f : n; + let m = e.getPixelForValue(p); + if ( + ((c = this.chart.getDataVisibility(t) + ? e.getPixelForValue(f + g) + : m), + (d = c - m), + Math.abs(d) < o) + ) { + (d = + (function(t, e, i) { + return 0 !== t + ? z(t) + : (e.isHorizontal() ? 1 : -1) * (e.min >= i ? 1 : -1); + })(d, e, a) * o), + u === a && (m -= d / 2); + const t = e.getPixelForDecimal(0), + i = e.getPixelForDecimal(1), + s = Math.min(t, i), + n = Math.max(t, i); + (m = Math.max(Math.min(m, n), s)), (c = m + d); + } + if (m === e.getPixelForValue(a)) { + const t = (z(d) * e.getLineWidthForValue(a)) / 2; + (m += t), (d -= t); + } + return { size: d, base: m, head: c, center: c + d / 2 }; + } + _calculateBarIndexPixels(t, e) { + const s = e.scale, + n = this.options, + o = n.skipNull, + a = r(n.maxBarThickness, 1 / 0); + let l, h; + if (e.grouped) { + const s = o ? this._getStackCount(t) : e.stackCount, + r = + "flex" === n.barThickness + ? (function(t, e, i, s) { + const n = e.pixels, + o = n[t]; + let a = t > 0 ? n[t - 1] : null, + r = t < n.length - 1 ? n[t + 1] : null; + const l = i.categoryPercentage; + null === a && + (a = o - (null === r ? e.end - e.start : r - o)), + null === r && (r = o + o - a); + const h = o - ((o - Math.min(a, r)) / 2) * l; + return { + chunk: ((Math.abs(r - a) / 2) * l) / s, + ratio: i.barPercentage, + start: h + }; + })(t, e, n, s) + : (function(t, e, s, n) { + const o = s.barThickness; + let a, r; + return ( + i(o) + ? ((a = e.min * s.categoryPercentage), + (r = s.barPercentage)) + : ((a = o * n), (r = 1)), + { chunk: a / n, ratio: r, start: e.pixels[t] - a / 2 } + ); + })(t, e, n, s), + c = this._getStackIndex( + this.index, + this._cachedMeta.stack, + o ? t : void 0 + ); + (l = r.start + r.chunk * c + r.chunk / 2), + (h = Math.min(a, r.chunk * r.ratio)); + } else + (l = s.getPixelForValue(this.getParsed(t)[s.axis], t)), + (h = Math.min(a, e.min * e.ratio)); + return { base: l - h / 2, head: l + h / 2, center: l, size: h }; + } + draw() { + const t = this._cachedMeta, + e = t.vScale, + i = t.data, + s = i.length; + let n = 0; + for (; n < s; ++n) + null !== this.getParsed(n)[e.axis] && i[n].draw(this._ctx); + } + }, + BubbleController: class extends Bs { + static id = "bubble"; + static defaults = { + datasetElementType: !1, + dataElementType: "point", + animations: { + numbers: { + type: "number", + properties: ["x", "y", "borderWidth", "radius"] + } + } + }; + static overrides = { + scales: { x: { type: "linear" }, y: { type: "linear" } } + }; + initialize() { + (this.enableOptionSharing = !0), super.initialize(); + } + parsePrimitiveData(t, e, i, s) { + const n = super.parsePrimitiveData(t, e, i, s); + for (let t = 0; t < n.length; t++) + n[t]._custom = this.resolveDataElementOptions(t + i).radius; + return n; + } + parseArrayData(t, e, i, s) { + const n = super.parseArrayData(t, e, i, s); + for (let t = 0; t < n.length; t++) { + const s = e[i + t]; + n[t]._custom = r(s[2], this.resolveDataElementOptions(t + i).radius); + } + return n; + } + parseObjectData(t, e, i, s) { + const n = super.parseObjectData(t, e, i, s); + for (let t = 0; t < n.length; t++) { + const s = e[i + t]; + n[t]._custom = r( + s && s.r && +s.r, + this.resolveDataElementOptions(t + i).radius + ); + } + return n; + } + getMaxOverflow() { + const t = this._cachedMeta.data; + let e = 0; + for (let i = t.length - 1; i >= 0; --i) + e = Math.max(e, t[i].size(this.resolveDataElementOptions(i)) / 2); + return e > 0 && e; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart.data.labels || [], + { xScale: s, yScale: n } = e, + o = this.getParsed(t), + a = s.getLabelForValue(o.x), + r = n.getLabelForValue(o.y), + l = o._custom; + return { + label: i[t] || "", + value: "(" + a + ", " + r + (l ? ", " + l : "") + ")" + }; + } + update(t) { + const e = this._cachedMeta.data; + this.updateElements(e, 0, e.length, t); + } + updateElements(t, e, i, s) { + const n = "reset" === s, + { iScale: o, vScale: a } = this._cachedMeta, + { sharedOptions: r, includeOptions: l } = this._getSharedOptions( + e, + s + ), + h = o.axis, + c = a.axis; + for (let d = e; d < e + i; d++) { + const e = t[d], + i = !n && this.getParsed(d), + u = {}, + f = (u[h] = n + ? o.getPixelForDecimal(0.5) + : o.getPixelForValue(i[h])), + g = (u[c] = n ? a.getBasePixel() : a.getPixelForValue(i[c])); + (u.skip = isNaN(f) || isNaN(g)), + l && + ((u.options = + r || + this.resolveDataElementOptions(d, e.active ? "active" : s)), + n && (u.options.radius = 0)), + this.updateElement(e, d, u, s); + } + } + resolveDataElementOptions(t, e) { + const i = this.getParsed(t); + let s = super.resolveDataElementOptions(t, e); + s.$shared && (s = Object.assign({}, s, { $shared: !1 })); + const n = s.radius; + return ( + "active" !== e && (s.radius = 0), + (s.radius += r(i && i._custom, n)), + s + ); + } + }, + DoughnutController: Fn, + LineController: class extends Bs { + static id = "line"; + static defaults = { + datasetElementType: "line", + dataElementType: "point", + showLine: !0, + spanGaps: !1 + }; + static overrides = { + scales: { _index_: { type: "category" }, _value_: { type: "linear" } } + }; + initialize() { + (this.enableOptionSharing = !0), + (this.supportsDecimation = !0), + super.initialize(); + } + update(t) { + const e = this._cachedMeta, + { dataset: i, data: s = [], _dataset: n } = e, + o = this.chart._animationsDisabled; + let { start: a, count: r } = gt(e, s, o); + (this._drawStart = a), + (this._drawCount = r), + pt(e) && ((a = 0), (r = s.length)), + (i._chart = this.chart), + (i._datasetIndex = this.index), + (i._decimated = !!n._decimated), + (i.points = s); + const l = this.resolveDatasetElementOptions(t); + this.options.showLine || (l.borderWidth = 0), + (l.segment = this.options.segment), + this.updateElement(i, void 0, { animated: !o, options: l }, t), + this.updateElements(s, a, r, t); + } + updateElements(t, e, s, n) { + const o = "reset" === n, + { iScale: a, vScale: r, _stacked: l, _dataset: h } = this._cachedMeta, + { sharedOptions: c, includeOptions: d } = this._getSharedOptions( + e, + n + ), + u = a.axis, + f = r.axis, + { spanGaps: g, segment: p } = this.options, + m = N(g) ? g : Number.POSITIVE_INFINITY, + b = this.chart._animationsDisabled || o || "none" === n, + x = e + s, + _ = t.length; + let y = e > 0 && this.getParsed(e - 1); + for (let s = 0; s < _; ++s) { + const g = t[s], + _ = b ? g : {}; + if (s < e || s >= x) { + _.skip = !0; + continue; + } + const v = this.getParsed(s), + M = i(v[f]), + w = (_[u] = a.getPixelForValue(v[u], s)), + k = (_[f] = + o || M + ? r.getBasePixel() + : r.getPixelForValue(l ? this.applyStack(r, v, l) : v[f], s)); + (_.skip = isNaN(w) || isNaN(k) || M), + (_.stop = s > 0 && Math.abs(v[u] - y[u]) > m), + p && ((_.parsed = v), (_.raw = h.data[s])), + d && + (_.options = + c || + this.resolveDataElementOptions(s, g.active ? "active" : n)), + b || this.updateElement(g, s, _, n), + (y = v); + } + } + getMaxOverflow() { + const t = this._cachedMeta, + e = t.dataset, + i = (e.options && e.options.borderWidth) || 0, + s = t.data || []; + if (!s.length) return i; + const n = s[0].size(this.resolveDataElementOptions(0)), + o = s[s.length - 1].size( + this.resolveDataElementOptions(s.length - 1) + ); + return Math.max(i, n, o) / 2; + } + draw() { + const t = this._cachedMeta; + t.dataset.updateControlPoints(this.chart.chartArea, t.iScale.axis), + super.draw(); + } + }, + PolarAreaController: class extends Bs { + static id = "polarArea"; + static defaults = { + dataElementType: "arc", + animation: { animateRotate: !0, animateScale: !0 }, + animations: { + numbers: { + type: "number", + properties: [ + "x", + "y", + "startAngle", + "endAngle", + "innerRadius", + "outerRadius" + ] + } + }, + indexAxis: "r", + startAngle: 0 + }; + static overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(t) { + const e = t.data; + if (e.labels.length && e.datasets.length) { + const { + labels: { pointStyle: i, color: s } + } = t.legend.options; + return e.labels.map((e, n) => { + const o = t.getDatasetMeta(0).controller.getStyle(n); + return { + text: e, + fillStyle: o.backgroundColor, + strokeStyle: o.borderColor, + fontColor: s, + lineWidth: o.borderWidth, + pointStyle: i, + hidden: !t.getDataVisibility(n), + index: n + }; + }); + } + return []; + } + }, + onClick(t, e, i) { + i.chart.toggleDataVisibility(e.index), i.chart.update(); + } + } + }, + scales: { + r: { + type: "radialLinear", + angleLines: { display: !1 }, + beginAtZero: !0, + grid: { circular: !0 }, + pointLabels: { display: !1 }, + startAngle: 0 + } + } + }; + constructor(t, e) { + super(t, e), (this.innerRadius = void 0), (this.outerRadius = void 0); + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart, + s = i.data.labels || [], + n = ne(e._parsed[t].r, i.options.locale); + return { label: s[t] || "", value: n }; + } + parseObjectData(t, e, i, s) { + return ei.bind(this)(t, e, i, s); + } + update(t) { + const e = this._cachedMeta.data; + this._updateRadius(), this.updateElements(e, 0, e.length, t); + } + getMinMax() { + const t = this._cachedMeta, + e = { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY }; + return ( + t.data.forEach((t, i) => { + const s = this.getParsed(i).r; + !isNaN(s) && + this.chart.getDataVisibility(i) && + (s < e.min && (e.min = s), s > e.max && (e.max = s)); + }), + e + ); + } + _updateRadius() { + const t = this.chart, + e = t.chartArea, + i = t.options, + s = Math.min(e.right - e.left, e.bottom - e.top), + n = Math.max(s / 2, 0), + o = + (n - + Math.max( + i.cutoutPercentage ? (n / 100) * i.cutoutPercentage : 1, + 0 + )) / + t.getVisibleDatasetCount(); + (this.outerRadius = n - o * this.index), + (this.innerRadius = this.outerRadius - o); + } + updateElements(t, e, i, s) { + const n = "reset" === s, + o = this.chart, + a = o.options.animation, + r = this._cachedMeta.rScale, + l = r.xCenter, + h = r.yCenter, + c = r.getIndexAngle(0) - 0.5 * D; + let d, + u = c; + const f = 360 / this.countVisibleElements(); + for (d = 0; d < e; ++d) u += this._computeAngle(d, s, f); + for (d = e; d < e + i; d++) { + const e = t[d]; + let i = u, + g = u + this._computeAngle(d, s, f), + p = o.getDataVisibility(d) + ? r.getDistanceFromCenterForValue(this.getParsed(d).r) + : 0; + (u = g), + n && (a.animateScale && (p = 0), a.animateRotate && (i = g = c)); + const m = { + x: l, + y: h, + innerRadius: 0, + outerRadius: p, + startAngle: i, + endAngle: g, + options: this.resolveDataElementOptions(d, e.active ? "active" : s) + }; + this.updateElement(e, d, m, s); + } + } + countVisibleElements() { + const t = this._cachedMeta; + let e = 0; + return ( + t.data.forEach((t, i) => { + !isNaN(this.getParsed(i).r) && + this.chart.getDataVisibility(i) && + e++; + }), + e + ); + } + _computeAngle(t, e, i) { + return this.chart.getDataVisibility(t) + ? j(this.resolveDataElementOptions(t, e).angle || i) + : 0; + } + }, + PieController: class extends Fn { + static id = "pie"; + static defaults = { + cutout: 0, + rotation: 0, + circumference: 360, + radius: "100%" + }; + }, + RadarController: class extends Bs { + static id = "radar"; + static defaults = { + datasetElementType: "line", + dataElementType: "point", + indexAxis: "r", + showLine: !0, + elements: { line: { fill: "start" } } + }; + static overrides = { + aspectRatio: 1, + scales: { r: { type: "radialLinear" } } + }; + getLabelAndValue(t) { + const e = this._cachedMeta.vScale, + i = this.getParsed(t); + return { + label: e.getLabels()[t], + value: "" + e.getLabelForValue(i[e.axis]) + }; + } + parseObjectData(t, e, i, s) { + return ei.bind(this)(t, e, i, s); + } + update(t) { + const e = this._cachedMeta, + i = e.dataset, + s = e.data || [], + n = e.iScale.getLabels(); + if (((i.points = s), "resize" !== t)) { + const e = this.resolveDatasetElementOptions(t); + this.options.showLine || (e.borderWidth = 0); + const o = { _loop: !0, _fullLoop: n.length === s.length, options: e }; + this.updateElement(i, void 0, o, t); + } + this.updateElements(s, 0, s.length, t); + } + updateElements(t, e, i, s) { + const n = this._cachedMeta.rScale, + o = "reset" === s; + for (let a = e; a < e + i; a++) { + const e = t[a], + i = this.resolveDataElementOptions(a, e.active ? "active" : s), + r = n.getPointPositionForValue(a, this.getParsed(a).r), + l = o ? n.xCenter : r.x, + h = o ? n.yCenter : r.y, + c = { + x: l, + y: h, + angle: r.angle, + skip: isNaN(l) || isNaN(h), + options: i + }; + this.updateElement(e, a, c, s); + } + } + }, + ScatterController: class extends Bs { + static id = "scatter"; + static defaults = { + datasetElementType: !1, + dataElementType: "point", + showLine: !1, + fill: !1 + }; + static overrides = { + interaction: { mode: "point" }, + scales: { x: { type: "linear" }, y: { type: "linear" } } + }; + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart.data.labels || [], + { xScale: s, yScale: n } = e, + o = this.getParsed(t), + a = s.getLabelForValue(o.x), + r = n.getLabelForValue(o.y); + return { label: i[t] || "", value: "(" + a + ", " + r + ")" }; + } + update(t) { + const e = this._cachedMeta, + { data: i = [] } = e, + s = this.chart._animationsDisabled; + let { start: n, count: o } = gt(e, i, s); + if ( + ((this._drawStart = n), + (this._drawCount = o), + pt(e) && ((n = 0), (o = i.length)), + this.options.showLine) + ) { + const { dataset: n, _dataset: o } = e; + (n._chart = this.chart), + (n._datasetIndex = this.index), + (n._decimated = !!o._decimated), + (n.points = i); + const a = this.resolveDatasetElementOptions(t); + (a.segment = this.options.segment), + this.updateElement(n, void 0, { animated: !s, options: a }, t); + } + this.updateElements(i, n, o, t); + } + addElements() { + const { showLine: t } = this.options; + !this.datasetElementType && + t && + (this.datasetElementType = this.chart.registry.getElement("line")), + super.addElements(); + } + updateElements(t, e, s, n) { + const o = "reset" === n, + { iScale: a, vScale: r, _stacked: l, _dataset: h } = this._cachedMeta, + c = this.resolveDataElementOptions(e, n), + d = this.getSharedOptions(c), + u = this.includeOptions(n, d), + f = a.axis, + g = r.axis, + { spanGaps: p, segment: m } = this.options, + b = N(p) ? p : Number.POSITIVE_INFINITY, + x = this.chart._animationsDisabled || o || "none" === n; + let _ = e > 0 && this.getParsed(e - 1); + for (let c = e; c < e + s; ++c) { + const e = t[c], + s = this.getParsed(c), + p = x ? e : {}, + y = i(s[g]), + v = (p[f] = a.getPixelForValue(s[f], c)), + M = (p[g] = + o || y + ? r.getBasePixel() + : r.getPixelForValue(l ? this.applyStack(r, s, l) : s[g], c)); + (p.skip = isNaN(v) || isNaN(M) || y), + (p.stop = c > 0 && Math.abs(s[f] - _[f]) > b), + m && ((p.parsed = s), (p.raw = h.data[c])), + u && + (p.options = + d || + this.resolveDataElementOptions(c, e.active ? "active" : n)), + x || this.updateElement(e, c, p, n), + (_ = s); + } + this.updateSharedOptions(d, n, c); + } + getMaxOverflow() { + const t = this._cachedMeta, + e = t.data || []; + if (!this.options.showLine) { + let t = 0; + for (let i = e.length - 1; i >= 0; --i) + t = Math.max(t, e[i].size(this.resolveDataElementOptions(i)) / 2); + return t > 0 && t; + } + const i = t.dataset, + s = (i.options && i.options.borderWidth) || 0; + if (!e.length) return s; + const n = e[0].size(this.resolveDataElementOptions(0)), + o = e[e.length - 1].size( + this.resolveDataElementOptions(e.length - 1) + ); + return Math.max(s, n, o) / 2; + } + } + }); + function Bn(t, e, i, s) { + const n = yi(t.options.borderRadius, [ + "outerStart", + "outerEnd", + "innerStart", + "innerEnd" + ]); + const o = (i - e) / 2, + a = Math.min(o, (s * e) / 2), + r = t => { + const e = ((i - Math.min(o, t)) * s) / 2; + return Z(t, 0, Math.min(o, e)); + }; + return { + outerStart: r(n.outerStart), + outerEnd: r(n.outerEnd), + innerStart: Z(n.innerStart, 0, a), + innerEnd: Z(n.innerEnd, 0, a) + }; + } + function Nn(t, e, i, s) { + return { x: i + t * Math.cos(e), y: s + t * Math.sin(e) }; + } + function Wn(t, e, i, s, n, o) { + const { x: a, y: r, startAngle: l, pixelMargin: h, innerRadius: c } = e, + d = Math.max(e.outerRadius + s + i - h, 0), + u = c > 0 ? c + s + i + h : 0; + let f = 0; + const g = n - l; + if (s) { + const t = ((c > 0 ? c - s : 0) + (d > 0 ? d - s : 0)) / 2; + f = (g - (0 !== t ? (g * t) / (t + s) : g)) / 2; + } + const p = (g - Math.max(0.001, g * d - i / D) / d) / 2, + m = l + p + f, + b = n - p - f, + { outerStart: x, outerEnd: _, innerStart: y, innerEnd: v } = Bn( + e, + u, + d, + b - m + ), + M = d - x, + w = d - _, + k = m + x / M, + S = b - _ / w, + P = u + y, + C = u + v, + O = m + y / P, + A = b - v / C; + if ((t.beginPath(), o)) { + const e = (k + S) / 2; + if ((t.arc(a, r, d, k, e), t.arc(a, r, d, e, S), _ > 0)) { + const e = Nn(w, S, a, r); + t.arc(e.x, e.y, _, S, b + L); + } + const i = Nn(C, b, a, r); + if ((t.lineTo(i.x, i.y), v > 0)) { + const e = Nn(C, A, a, r); + t.arc(e.x, e.y, v, b + L, A + Math.PI); + } + const s = (b - v / u + (m + y / u)) / 2; + if ( + (t.arc(a, r, u, b - v / u, s, !0), + t.arc(a, r, u, s, m + y / u, !0), + y > 0) + ) { + const e = Nn(P, O, a, r); + t.arc(e.x, e.y, y, O + Math.PI, m - L); + } + const n = Nn(M, m, a, r); + if ((t.lineTo(n.x, n.y), x > 0)) { + const e = Nn(M, k, a, r); + t.arc(e.x, e.y, x, m - L, k); + } + } else { + t.moveTo(a, r); + const e = Math.cos(k) * d + a, + i = Math.sin(k) * d + r; + t.lineTo(e, i); + const s = Math.cos(S) * d + a, + n = Math.sin(S) * d + r; + t.lineTo(s, n); + } + t.closePath(); + } + function Hn(t, e, i, s, n) { + const { fullCircles: o, startAngle: a, circumference: r, options: l } = e, + { borderWidth: h, borderJoinStyle: c } = l, + d = "inner" === l.borderAlign; + if (!h) return; + d + ? ((t.lineWidth = 2 * h), (t.lineJoin = c || "round")) + : ((t.lineWidth = h), (t.lineJoin = c || "bevel")); + let u = e.endAngle; + if (o) { + Wn(t, e, i, s, u, n); + for (let e = 0; e < o; ++e) t.stroke(); + isNaN(r) || (u = a + (r % C || C)); + } + d && + (function(t, e, i) { + const { + startAngle: s, + pixelMargin: n, + x: o, + y: a, + outerRadius: r, + innerRadius: l + } = e; + let h = n / r; + t.beginPath(), + t.arc(o, a, r, s - h, i + h), + l > n + ? ((h = n / l), t.arc(o, a, l, i + h, s - h, !0)) + : t.arc(o, a, n, i + L, s - L), + t.closePath(), + t.clip(); + })(t, e, u), + o || (Wn(t, e, i, s, u, n), t.stroke()); + } + function jn(t, e, i = e) { + (t.lineCap = r(i.borderCapStyle, e.borderCapStyle)), + t.setLineDash(r(i.borderDash, e.borderDash)), + (t.lineDashOffset = r(i.borderDashOffset, e.borderDashOffset)), + (t.lineJoin = r(i.borderJoinStyle, e.borderJoinStyle)), + (t.lineWidth = r(i.borderWidth, e.borderWidth)), + (t.strokeStyle = r(i.borderColor, e.borderColor)); + } + function $n(t, e, i) { + t.lineTo(i.x, i.y); + } + function Yn(t, e, i = {}) { + const s = t.length, + { start: n = 0, end: o = s - 1 } = i, + { start: a, end: r } = e, + l = Math.max(n, a), + h = Math.min(o, r), + c = (n < a && o < a) || (n > r && o > r); + return { + count: s, + start: l, + loop: e.loop, + ilen: h < l && !c ? s + h - l : h - l + }; + } + function Un(t, e, i, s) { + const { points: n, options: o } = e, + { count: a, start: r, loop: l, ilen: h } = Yn(n, i, s), + c = (function(t) { + return t.stepped + ? ze + : t.tension || "monotone" === t.cubicInterpolationMode + ? Fe + : $n; + })(o); + let d, + u, + f, + { move: g = !0, reverse: p } = s || {}; + for (d = 0; d <= h; ++d) + (u = n[(r + (p ? h - d : d)) % a]), + u.skip || + (g ? (t.moveTo(u.x, u.y), (g = !1)) : c(t, f, u, p, o.stepped), + (f = u)); + return l && ((u = n[(r + (p ? h : 0)) % a]), c(t, f, u, p, o.stepped)), !!l; + } + function Xn(t, e, i, s) { + const n = e.points, + { count: o, start: a, ilen: r } = Yn(n, i, s), + { move: l = !0, reverse: h } = s || {}; + let c, + d, + u, + f, + g, + p, + m = 0, + b = 0; + const x = t => (a + (h ? r - t : t)) % o, + _ = () => { + f !== g && (t.lineTo(m, g), t.lineTo(m, f), t.lineTo(m, p)); + }; + for (l && ((d = n[x(0)]), t.moveTo(d.x, d.y)), c = 0; c <= r; ++c) { + if (((d = n[x(c)]), d.skip)) continue; + const e = d.x, + i = d.y, + s = 0 | e; + s === u + ? (i < f ? (f = i) : i > g && (g = i), (m = (b * m + e) / ++b)) + : (_(), t.lineTo(e, i), (u = s), (b = 0), (f = g = i)), + (p = i); + } + _(); + } + function qn(t) { + const e = t.options, + i = e.borderDash && e.borderDash.length; + return !( + t._decimated || + t._loop || + e.tension || + "monotone" === e.cubicInterpolationMode || + e.stepped || + i + ) + ? Xn + : Un; + } + const Kn = "function" == typeof Path2D; + function Gn(t, e, i, s) { + Kn && !e.options.segment + ? (function(t, e, i, s) { + let n = e._path; + n || ((n = e._path = new Path2D()), e.path(n, i, s) && n.closePath()), + jn(t, e.options), + t.stroke(n); + })(t, e, i, s) + : (function(t, e, i, s) { + const { segments: n, options: o } = e, + a = qn(e); + for (const r of n) + jn(t, o, r.style), + t.beginPath(), + a(t, e, r, { start: i, end: i + s - 1 }) && t.closePath(), + t.stroke(); + })(t, e, i, s); + } + class Zn extends Ns { + static id = "line"; + static defaults = { + borderCapStyle: "butt", + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: "miter", + borderWidth: 3, + capBezierPoints: !0, + cubicInterpolationMode: "default", + fill: !1, + spanGaps: !1, + stepped: !1, + tension: 0 + }; + static defaultRoutes = { + backgroundColor: "backgroundColor", + borderColor: "borderColor" + }; + static descriptors = { + _scriptable: !0, + _indexable: t => "borderDash" !== t && "fill" !== t + }; + constructor(t) { + super(), + (this.animated = !0), + (this.options = void 0), + (this._chart = void 0), + (this._loop = void 0), + (this._fullLoop = void 0), + (this._path = void 0), + (this._points = void 0), + (this._segments = void 0), + (this._decimated = !1), + (this._pointsUpdated = !1), + (this._datasetIndex = void 0), + t && Object.assign(this, t); + } + updateControlPoints(t, e) { + const i = this.options; + if ( + (i.tension || "monotone" === i.cubicInterpolationMode) && + !i.stepped && + !this._pointsUpdated + ) { + const s = i.spanGaps ? this._loop : this._fullLoop; + li(this._points, i, t, s, e), (this._pointsUpdated = !0); + } + } + set points(t) { + (this._points = t), + delete this._segments, + delete this._path, + (this._pointsUpdated = !1); + } + get points() { + return this._points; + } + get segments() { + return ( + this._segments || (this._segments = Ii(this, this.options.segment)) + ); + } + first() { + const t = this.segments, + e = this.points; + return t.length && e[t[0].start]; + } + last() { + const t = this.segments, + e = this.points, + i = t.length; + return i && e[t[i - 1].end]; + } + interpolate(t, e) { + const i = this.options, + s = t[e], + n = this.points, + o = Ri(this, { property: e, start: s, end: s }); + if (!o.length) return; + const a = [], + r = (function(t) { + return t.stepped + ? pi + : t.tension || "monotone" === t.cubicInterpolationMode + ? mi + : gi; + })(i); + let l, h; + for (l = 0, h = o.length; l < h; ++l) { + const { start: h, end: c } = o[l], + d = n[h], + u = n[c]; + if (d === u) { + a.push(d); + continue; + } + const f = r(d, u, Math.abs((s - d[e]) / (u[e] - d[e])), i.stepped); + (f[e] = t[e]), a.push(f); + } + return 1 === a.length ? a[0] : a; + } + pathSegment(t, e, i) { + return qn(this)(t, this, e, i); + } + path(t, e, i) { + const s = this.segments, + n = qn(this); + let o = this._loop; + (e = e || 0), (i = i || this.points.length - e); + for (const a of s) o &= n(t, this, a, { start: e, end: e + i - 1 }); + return !!o; + } + draw(t, e, i, s) { + const n = this.options || {}; + (this.points || []).length && + n.borderWidth && + (t.save(), Gn(t, this, i, s), t.restore()), + this.animated && ((this._pointsUpdated = !1), (this._path = void 0)); + } + } + function Jn(t, e, i, s) { + const n = t.options, + { [i]: o } = t.getProps([i], s); + return Math.abs(e - o) < n.radius + n.hitRadius; + } + function Qn(t, e) { + const { x: i, y: s, base: n, width: o, height: a } = t.getProps( + ["x", "y", "base", "width", "height"], + e + ); + let r, l, h, c, d; + return ( + t.horizontal + ? ((d = a / 2), + (r = Math.min(i, n)), + (l = Math.max(i, n)), + (h = s - d), + (c = s + d)) + : ((d = o / 2), + (r = i - d), + (l = i + d), + (h = Math.min(s, n)), + (c = Math.max(s, n))), + { left: r, top: h, right: l, bottom: c } + ); + } + function to(t, e, i, s) { + return t ? 0 : Z(e, i, s); + } + function eo(t) { + const e = Qn(t), + i = e.right - e.left, + s = e.bottom - e.top, + o = (function(t, e, i) { + const s = t.options.borderWidth, + n = t.borderSkipped, + o = vi(s); + return { + t: to(n.top, o.top, 0, i), + r: to(n.right, o.right, 0, e), + b: to(n.bottom, o.bottom, 0, i), + l: to(n.left, o.left, 0, e) + }; + })(t, i / 2, s / 2), + a = (function(t, e, i) { + const { enableBorderRadius: s } = t.getProps(["enableBorderRadius"]), + o = t.options.borderRadius, + a = Mi(o), + r = Math.min(e, i), + l = t.borderSkipped, + h = s || n(o); + return { + topLeft: to(!h || l.top || l.left, a.topLeft, 0, r), + topRight: to(!h || l.top || l.right, a.topRight, 0, r), + bottomLeft: to(!h || l.bottom || l.left, a.bottomLeft, 0, r), + bottomRight: to(!h || l.bottom || l.right, a.bottomRight, 0, r) + }; + })(t, i / 2, s / 2); + return { + outer: { x: e.left, y: e.top, w: i, h: s, radius: a }, + inner: { + x: e.left + o.l, + y: e.top + o.t, + w: i - o.l - o.r, + h: s - o.t - o.b, + radius: { + topLeft: Math.max(0, a.topLeft - Math.max(o.t, o.l)), + topRight: Math.max(0, a.topRight - Math.max(o.t, o.r)), + bottomLeft: Math.max(0, a.bottomLeft - Math.max(o.b, o.l)), + bottomRight: Math.max(0, a.bottomRight - Math.max(o.b, o.r)) + } + } + }; + } + function io(t, e, i, s) { + const n = null === e, + o = null === i, + a = t && !(n && o) && Qn(t, s); + return a && (n || Q(e, a.left, a.right)) && (o || Q(i, a.top, a.bottom)); + } + function so(t, e) { + t.rect(e.x, e.y, e.w, e.h); + } + function no(t, e, i = {}) { + const s = t.x !== i.x ? -e : 0, + n = t.y !== i.y ? -e : 0, + o = (t.x + t.w !== i.x + i.w ? e : 0) - s, + a = (t.y + t.h !== i.y + i.h ? e : 0) - n; + return { x: t.x + s, y: t.y + n, w: t.w + o, h: t.h + a, radius: t.radius }; + } + var oo = Object.freeze({ + __proto__: null, + ArcElement: class extends Ns { + static id = "arc"; + static defaults = { + borderAlign: "center", + borderColor: "#fff", + borderJoinStyle: void 0, + borderRadius: 0, + borderWidth: 2, + offset: 0, + spacing: 0, + angle: void 0, + circular: !0 + }; + static defaultRoutes = { backgroundColor: "backgroundColor" }; + constructor(t) { + super(), + (this.options = void 0), + (this.circumference = void 0), + (this.startAngle = void 0), + (this.endAngle = void 0), + (this.innerRadius = void 0), + (this.outerRadius = void 0), + (this.pixelMargin = 0), + (this.fullCircles = 0), + t && Object.assign(this, t); + } + inRange(t, e, i) { + const s = this.getProps(["x", "y"], i), + { angle: n, distance: o } = U(s, { x: t, y: e }), + { + startAngle: a, + endAngle: l, + innerRadius: h, + outerRadius: c, + circumference: d + } = this.getProps( + [ + "startAngle", + "endAngle", + "innerRadius", + "outerRadius", + "circumference" + ], + i + ), + u = this.options.spacing / 2, + f = r(d, l - a) >= C || G(n, a, l), + g = Q(o, h + u, c + u); + return f && g; + } + getCenterPoint(t) { + const { + x: e, + y: i, + startAngle: s, + endAngle: n, + innerRadius: o, + outerRadius: a + } = this.getProps( + [ + "x", + "y", + "startAngle", + "endAngle", + "innerRadius", + "outerRadius", + "circumference" + ], + t + ), + { offset: r, spacing: l } = this.options, + h = (s + n) / 2, + c = (o + a + l + r) / 2; + return { x: e + Math.cos(h) * c, y: i + Math.sin(h) * c }; + } + tooltipPosition(t) { + return this.getCenterPoint(t); + } + draw(t) { + const { options: e, circumference: i } = this, + s = (e.offset || 0) / 4, + n = (e.spacing || 0) / 2, + o = e.circular; + if ( + ((this.pixelMargin = "inner" === e.borderAlign ? 0.33 : 0), + (this.fullCircles = i > C ? Math.floor(i / C) : 0), + 0 === i || this.innerRadius < 0 || this.outerRadius < 0) + ) + return; + t.save(); + const a = (this.startAngle + this.endAngle) / 2; + t.translate(Math.cos(a) * s, Math.sin(a) * s); + const r = s * (1 - Math.sin(Math.min(D, i || 0))); + (t.fillStyle = e.backgroundColor), + (t.strokeStyle = e.borderColor), + (function(t, e, i, s, n) { + const { fullCircles: o, startAngle: a, circumference: r } = e; + let l = e.endAngle; + if (o) { + Wn(t, e, i, s, l, n); + for (let e = 0; e < o; ++e) t.fill(); + isNaN(r) || (l = a + (r % C || C)); + } + Wn(t, e, i, s, l, n), t.fill(); + })(t, this, r, n, o), + Hn(t, this, r, n, o), + t.restore(); + } + }, + LineElement: Zn, + PointElement: class extends Ns { + static id = "point"; + static defaults = { + borderWidth: 1, + hitRadius: 1, + hoverBorderWidth: 1, + hoverRadius: 4, + pointStyle: "circle", + radius: 3, + rotation: 0 + }; + static defaultRoutes = { + backgroundColor: "backgroundColor", + borderColor: "borderColor" + }; + constructor(t) { + super(), + (this.options = void 0), + (this.parsed = void 0), + (this.skip = void 0), + (this.stop = void 0), + t && Object.assign(this, t); + } + inRange(t, e, i) { + const s = this.options, + { x: n, y: o } = this.getProps(["x", "y"], i); + return ( + Math.pow(t - n, 2) + Math.pow(e - o, 2) < + Math.pow(s.hitRadius + s.radius, 2) + ); + } + inXRange(t, e) { + return Jn(this, t, "x", e); + } + inYRange(t, e) { + return Jn(this, t, "y", e); + } + getCenterPoint(t) { + const { x: e, y: i } = this.getProps(["x", "y"], t); + return { x: e, y: i }; + } + size(t) { + let e = (t = t || this.options || {}).radius || 0; + e = Math.max(e, (e && t.hoverRadius) || 0); + return 2 * (e + ((e && t.borderWidth) || 0)); + } + draw(t, e) { + const i = this.options; + this.skip || + i.radius < 0.1 || + !Ee(this, e, this.size(i) / 2) || + ((t.strokeStyle = i.borderColor), + (t.lineWidth = i.borderWidth), + (t.fillStyle = i.backgroundColor), + Te(t, i, this.x, this.y)); + } + getRange() { + const t = this.options || {}; + return t.radius + t.hitRadius; + } + }, + BarElement: class extends Ns { + static id = "bar"; + static defaults = { + borderSkipped: "start", + borderWidth: 0, + borderRadius: 0, + inflateAmount: "auto", + pointStyle: void 0 + }; + static defaultRoutes = { + backgroundColor: "backgroundColor", + borderColor: "borderColor" + }; + constructor(t) { + super(), + (this.options = void 0), + (this.horizontal = void 0), + (this.base = void 0), + (this.width = void 0), + (this.height = void 0), + (this.inflateAmount = void 0), + t && Object.assign(this, t); + } + draw(t) { + const { + inflateAmount: e, + options: { borderColor: i, backgroundColor: s } + } = this, + { inner: n, outer: o } = eo(this), + a = + (r = o.radius).topLeft || + r.topRight || + r.bottomLeft || + r.bottomRight + ? We + : so; + var r; + t.save(), + (o.w === n.w && o.h === n.h) || + (t.beginPath(), + a(t, no(o, e, n)), + t.clip(), + a(t, no(n, -e, o)), + (t.fillStyle = i), + t.fill("evenodd")), + t.beginPath(), + a(t, no(n, e)), + (t.fillStyle = s), + t.fill(), + t.restore(); + } + inRange(t, e, i) { + return io(this, t, e, i); + } + inXRange(t, e) { + return io(this, t, null, e); + } + inYRange(t, e) { + return io(this, null, t, e); + } + getCenterPoint(t) { + const { x: e, y: i, base: s, horizontal: n } = this.getProps( + ["x", "y", "base", "horizontal"], + t + ); + return { x: n ? (e + s) / 2 : e, y: n ? i : (i + s) / 2 }; + } + getRange(t) { + return "x" === t ? this.width / 2 : this.height / 2; + } + } + }); + const ao = [ + "rgb(54, 162, 235)", + "rgb(255, 99, 132)", + "rgb(255, 159, 64)", + "rgb(255, 205, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)", + "rgb(201, 203, 207)" + ], + ro = ao.map(t => t.replace("rgb(", "rgba(").replace(")", ", 0.5)")); + function lo(t) { + return ao[t % ao.length]; + } + function ho(t) { + return ro[t % ro.length]; + } + function co(t) { + return "doughnut" === t || "pie" === t + ? (function() { + let t = 0; + return e => { + e.backgroundColor = e.data.map(() => lo(t++)); + }; + })() + : "polarArea" === t + ? (function() { + let t = 0; + return e => { + e.backgroundColor = e.data.map(() => ho(t++)); + }; + })() + : (t, e) => { + (t.borderColor = lo(e)), (t.backgroundColor = ho(e)); + }; + } + function uo(t) { + let e; + for (e in t) if (t[e].borderColor || t[e].backgroundColor) return !0; + return !1; + } + var fo = { + id: "colors", + defaults: { enabled: !0 }, + beforeLayout(t, e, i) { + if (!i.enabled) return; + const { + type: s, + options: { elements: n }, + data: { datasets: o } + } = t.config; + if (uo(o) || (n && uo(n))) return; + const a = co(s); + o.forEach(a); + } + }; + function go(t) { + if (t._decimated) { + const e = t._data; + delete t._decimated, + delete t._data, + Object.defineProperty(t, "data", { value: e }); + } + } + function po(t) { + t.data.datasets.forEach(t => { + go(t); + }); + } + var mo = { + id: "decimation", + defaults: { algorithm: "min-max", enabled: !1 }, + beforeElementsUpdate: (t, e, s) => { + if (!s.enabled) return void po(t); + const n = t.width; + t.data.datasets.forEach((e, o) => { + const { _data: a, indexAxis: r } = e, + l = t.getDatasetMeta(o), + h = a || e.data; + if ("y" === Si([r, t.options.indexAxis])) return; + if (!l.controller.supportsDecimation) return; + const c = t.scales[l.xAxisID]; + if ("linear" !== c.type && "time" !== c.type) return; + if (t.options.parsing) return; + let { start: d, count: u } = (function(t, e) { + const i = e.length; + let s, + n = 0; + const { iScale: o } = t, + { + min: a, + max: r, + minDefined: l, + maxDefined: h + } = o.getUserBounds(); + return ( + l && (n = Z(et(e, o.axis, a).lo, 0, i - 1)), + (s = h ? Z(et(e, o.axis, r).hi + 1, n, i) - n : i - n), + { start: n, count: s } + ); + })(l, h); + if (u <= (s.threshold || 4 * n)) return void go(e); + let f; + switch ( + (i(a) && + ((e._data = h), + delete e.data, + Object.defineProperty(e, "data", { + configurable: !0, + enumerable: !0, + get: function() { + return this._decimated; + }, + set: function(t) { + this._data = t; + } + })), + s.algorithm) + ) { + case "lttb": + f = (function(t, e, i, s, n) { + const o = n.samples || s; + if (o >= i) return t.slice(e, e + i); + const a = [], + r = (i - 2) / (o - 2); + let l = 0; + const h = e + i - 1; + let c, + d, + u, + f, + g, + p = e; + for (a[l++] = t[p], c = 0; c < o - 2; c++) { + let s, + n = 0, + o = 0; + const h = Math.floor((c + 1) * r) + 1 + e, + m = Math.min(Math.floor((c + 2) * r) + 1, i) + e, + b = m - h; + for (s = h; s < m; s++) (n += t[s].x), (o += t[s].y); + (n /= b), (o /= b); + const x = Math.floor(c * r) + 1 + e, + _ = Math.min(Math.floor((c + 1) * r) + 1, i) + e, + { x: y, y: v } = t[p]; + for (u = f = -1, s = x; s < _; s++) + (f = + 0.5 * + Math.abs((y - n) * (t[s].y - v) - (y - t[s].x) * (o - v))), + f > u && ((u = f), (d = t[s]), (g = s)); + (a[l++] = d), (p = g); + } + return (a[l++] = t[h]), a; + })(h, d, u, n, s); + break; + case "min-max": + f = (function(t, e, s, n) { + let o, + a, + r, + l, + h, + c, + d, + u, + f, + g, + p = 0, + m = 0; + const b = [], + x = e + s - 1, + _ = t[e].x, + y = t[x].x - _; + for (o = e; o < e + s; ++o) { + (a = t[o]), (r = ((a.x - _) / y) * n), (l = a.y); + const e = 0 | r; + if (e === h) + l < f ? ((f = l), (c = o)) : l > g && ((g = l), (d = o)), + (p = (m * p + a.x) / ++m); + else { + const s = o - 1; + if (!i(c) && !i(d)) { + const e = Math.min(c, d), + i = Math.max(c, d); + e !== u && e !== s && b.push({ ...t[e], x: p }), + i !== u && i !== s && b.push({ ...t[i], x: p }); + } + o > 0 && s !== u && b.push(t[s]), + b.push(a), + (h = e), + (m = 0), + (f = g = l), + (c = d = u = o); + } + } + return b; + })(h, d, u, n); + break; + default: + throw new Error( + `Unsupported decimation algorithm '${s.algorithm}'` + ); + } + e._decimated = f; + }); + }, + destroy(t) { + po(t); + } + }; + function bo(t, e, i, s) { + if (s) return; + let n = e[t], + o = i[t]; + return ( + "angle" === t && ((n = K(n)), (o = K(o))), + { property: t, start: n, end: o } + ); + } + function xo(t, e, i) { + for (; e > t; e--) { + const t = i[e]; + if (!isNaN(t.x) && !isNaN(t.y)) break; + } + return e; + } + function _o(t, e, i, s) { + return t && e ? s(t[i], e[i]) : t ? t[i] : e ? e[i] : 0; + } + function yo(t, e) { + let i = [], + n = !1; + return ( + s(t) + ? ((n = !0), (i = t)) + : (i = (function(t, e) { + const { x: i = null, y: s = null } = t || {}, + n = e.points, + o = []; + return ( + e.segments.forEach(({ start: t, end: e }) => { + e = xo(t, e, n); + const a = n[t], + r = n[e]; + null !== s + ? (o.push({ x: a.x, y: s }), o.push({ x: r.x, y: s })) + : null !== i && + (o.push({ x: i, y: a.y }), o.push({ x: i, y: r.y })); + }), + o + ); + })(t, e)), + i.length + ? new Zn({ points: i, options: { tension: 0 }, _loop: n, _fullLoop: n }) + : null + ); + } + function vo(t) { + return t && !1 !== t.fill; + } + function Mo(t, e, i) { + let s = t[e].fill; + const n = [e]; + let a; + if (!i) return s; + for (; !1 !== s && -1 === n.indexOf(s); ) { + if (!o(s)) return s; + if (((a = t[s]), !a)) return !1; + if (a.visible) return s; + n.push(s), (s = a.fill); + } + return !1; + } + function wo(t, e, i) { + const s = (function(t) { + const e = t.options, + i = e.fill; + let s = r(i && i.target, i); + void 0 === s && (s = !!e.backgroundColor); + if (!1 === s || null === s) return !1; + if (!0 === s) return "origin"; + return s; + })(t); + if (n(s)) return !isNaN(s.value) && s; + let a = parseFloat(s); + return o(a) && Math.floor(a) === a + ? (function(t, e, i, s) { + ("-" !== t && "+" !== t) || (i = e + i); + if (i === e || i < 0 || i >= s) return !1; + return i; + })(s[0], e, a, i) + : ["origin", "start", "end", "stack", "shape"].indexOf(s) >= 0 && s; + } + function ko(t, e, i) { + const s = []; + for (let n = 0; n < i.length; n++) { + const o = i[n], + { first: a, last: r, point: l } = So(o, e, "x"); + if (!(!l || (a && r))) + if (a) s.unshift(l); + else if ((t.push(l), !r)) break; + } + t.push(...s); + } + function So(t, e, i) { + const s = t.interpolate(e, i); + if (!s) return {}; + const n = s[i], + o = t.segments, + a = t.points; + let r = !1, + l = !1; + for (let t = 0; t < o.length; t++) { + const e = o[t], + s = a[e.start][i], + h = a[e.end][i]; + if (Q(n, s, h)) { + (r = n === s), (l = n === h); + break; + } + } + return { first: r, last: l, point: s }; + } + class Po { + constructor(t) { + (this.x = t.x), (this.y = t.y), (this.radius = t.radius); + } + pathSegment(t, e, i) { + const { x: s, y: n, radius: o } = this; + return ( + (e = e || { start: 0, end: C }), + t.arc(s, n, o, e.end, e.start, !0), + !i.bounds + ); + } + interpolate(t) { + const { x: e, y: i, radius: s } = this, + n = t.angle; + return { x: e + Math.cos(n) * s, y: i + Math.sin(n) * s, angle: n }; + } + } + function Do(t) { + const { chart: e, fill: i, line: s } = t; + if (o(i)) + return (function(t, e) { + const i = t.getDatasetMeta(e); + return i && t.isDatasetVisible(e) ? i.dataset : null; + })(e, i); + if ("stack" === i) + return (function(t) { + const { scale: e, index: i, line: s } = t, + n = [], + o = s.segments, + a = s.points, + r = (function(t, e) { + const i = [], + s = t.getMatchingVisibleMetas("line"); + for (let t = 0; t < s.length; t++) { + const n = s[t]; + if (n.index === e) break; + n.hidden || i.unshift(n.dataset); + } + return i; + })(e, i); + r.push(yo({ x: null, y: e.bottom }, s)); + for (let t = 0; t < o.length; t++) { + const e = o[t]; + for (let t = e.start; t <= e.end; t++) ko(n, a[t], r); + } + return new Zn({ points: n, options: {} }); + })(t); + if ("shape" === i) return !0; + const a = (function(t) { + if ((t.scale || {}).getPointPositionForValue) + return (function(t) { + const { scale: e, fill: i } = t, + s = e.options, + o = e.getLabels().length, + a = s.reverse ? e.max : e.min, + r = (function(t, e, i) { + let s; + return ( + (s = + "start" === t + ? i + : "end" === t + ? e.options.reverse + ? e.min + : e.max + : n(t) + ? t.value + : e.getBaseValue()), + s + ); + })(i, e, a), + l = []; + if (s.grid.circular) { + const t = e.getPointPositionForValue(0, a); + return new Po({ + x: t.x, + y: t.y, + radius: e.getDistanceFromCenterForValue(r) + }); + } + for (let t = 0; t < o; ++t) l.push(e.getPointPositionForValue(t, r)); + return l; + })(t); + return (function(t) { + const { scale: e = {}, fill: i } = t, + s = (function(t, e) { + let i = null; + return ( + "start" === t + ? (i = e.bottom) + : "end" === t + ? (i = e.top) + : n(t) + ? (i = e.getPixelForValue(t.value)) + : e.getBasePixel && (i = e.getBasePixel()), + i + ); + })(i, e); + if (o(s)) { + const t = e.isHorizontal(); + return { x: t ? s : null, y: t ? null : s }; + } + return null; + })(t); + })(t); + return a instanceof Po ? a : yo(a, s); + } + function Co(t, e, i) { + const s = Do(e), + { line: n, scale: o, axis: a } = e, + r = n.options, + l = r.fill, + h = r.backgroundColor, + { above: c = h, below: d = h } = l || {}; + s && + n.points.length && + (Re(t, i), + (function(t, e) { + const { line: i, target: s, above: n, below: o, area: a, scale: r } = e, + l = i._loop ? "angle" : e.axis; + t.save(), + "x" === l && + o !== n && + (Oo(t, s, a.top), + Ao(t, { line: i, target: s, color: n, scale: r, property: l }), + t.restore(), + t.save(), + Oo(t, s, a.bottom)); + Ao(t, { line: i, target: s, color: o, scale: r, property: l }), + t.restore(); + })(t, { + line: n, + target: s, + above: c, + below: d, + area: i, + scale: o, + axis: a + }), + Ie(t)); + } + function Oo(t, e, i) { + const { segments: s, points: n } = e; + let o = !0, + a = !1; + t.beginPath(); + for (const r of s) { + const { start: s, end: l } = r, + h = n[s], + c = n[xo(s, l, n)]; + o + ? (t.moveTo(h.x, h.y), (o = !1)) + : (t.lineTo(h.x, i), t.lineTo(h.x, h.y)), + (a = !!e.pathSegment(t, r, { move: a })), + a ? t.closePath() : t.lineTo(c.x, i); + } + t.lineTo(e.first().x, i), t.closePath(), t.clip(); + } + function Ao(t, e) { + const { line: i, target: s, property: n, color: o, scale: a } = e, + r = (function(t, e, i) { + const s = t.segments, + n = t.points, + o = e.points, + a = []; + for (const t of s) { + let { start: s, end: r } = t; + r = xo(s, r, n); + const l = bo(i, n[s], n[r], t.loop); + if (!e.segments) { + a.push({ source: t, target: l, start: n[s], end: n[r] }); + continue; + } + const h = Ri(e, l); + for (const e of h) { + const s = bo(i, o[e.start], o[e.end], e.loop), + r = Ei(t, n, s); + for (const t of r) + a.push({ + source: t, + target: e, + start: { [i]: _o(l, s, "start", Math.max) }, + end: { [i]: _o(l, s, "end", Math.min) } + }); + } + } + return a; + })(i, s, n); + for (const { source: e, target: l, start: h, end: c } of r) { + const { style: { backgroundColor: r = o } = {} } = e, + d = !0 !== s; + t.save(), (t.fillStyle = r), To(t, a, d && bo(n, h, c)), t.beginPath(); + const u = !!i.pathSegment(t, e); + let f; + if (d) { + u ? t.closePath() : Lo(t, s, c, n); + const e = !!s.pathSegment(t, l, { move: u, reverse: !0 }); + (f = u && e), f || Lo(t, s, h, n); + } + t.closePath(), t.fill(f ? "evenodd" : "nonzero"), t.restore(); + } + } + function To(t, e, i) { + const { top: s, bottom: n } = e.chart.chartArea, + { property: o, start: a, end: r } = i || {}; + "x" === o && (t.beginPath(), t.rect(a, s, r - a, n - s), t.clip()); + } + function Lo(t, e, i, s) { + const n = e.interpolate(i, s); + n && t.lineTo(n.x, n.y); + } + var Eo = { + id: "filler", + afterDatasetsUpdate(t, e, i) { + const s = (t.data.datasets || []).length, + n = []; + let o, a, r, l; + for (a = 0; a < s; ++a) + (o = t.getDatasetMeta(a)), + (r = o.dataset), + (l = null), + r && + r.options && + r instanceof Zn && + (l = { + visible: t.isDatasetVisible(a), + index: a, + fill: wo(r, a, s), + chart: t, + axis: o.controller.options.indexAxis, + scale: o.vScale, + line: r + }), + (o.$filler = l), + n.push(l); + for (a = 0; a < s; ++a) + (l = n[a]), l && !1 !== l.fill && (l.fill = Mo(n, a, i.propagate)); + }, + beforeDraw(t, e, i) { + const s = "beforeDraw" === i.drawTime, + n = t.getSortedVisibleDatasetMetas(), + o = t.chartArea; + for (let e = n.length - 1; e >= 0; --e) { + const i = n[e].$filler; + i && + (i.line.updateControlPoints(o, i.axis), + s && i.fill && Co(t.ctx, i, o)); + } + }, + beforeDatasetsDraw(t, e, i) { + if ("beforeDatasetsDraw" !== i.drawTime) return; + const s = t.getSortedVisibleDatasetMetas(); + for (let e = s.length - 1; e >= 0; --e) { + const i = s[e].$filler; + vo(i) && Co(t.ctx, i, t.chartArea); + } + }, + beforeDatasetDraw(t, e, i) { + const s = e.meta.$filler; + vo(s) && "beforeDatasetDraw" === i.drawTime && Co(t.ctx, s, t.chartArea); + }, + defaults: { propagate: !0, drawTime: "beforeDatasetDraw" } + }; + const Ro = (t, e) => { + let { boxHeight: i = e, boxWidth: s = e } = t; + return ( + t.usePointStyle && + ((i = Math.min(i, e)), (s = t.pointStyleWidth || Math.min(s, e))), + { boxWidth: s, boxHeight: i, itemHeight: Math.max(e, i) } + ); + }; + class Io extends Ns { + constructor(t) { + super(), + (this._added = !1), + (this.legendHitBoxes = []), + (this._hoveredItem = null), + (this.doughnutMode = !1), + (this.chart = t.chart), + (this.options = t.options), + (this.ctx = t.ctx), + (this.legendItems = void 0), + (this.columnSizes = void 0), + (this.lineWidths = void 0), + (this.maxHeight = void 0), + (this.maxWidth = void 0), + (this.top = void 0), + (this.bottom = void 0), + (this.left = void 0), + (this.right = void 0), + (this.height = void 0), + (this.width = void 0), + (this._margins = void 0), + (this.position = void 0), + (this.weight = void 0), + (this.fullSize = void 0); + } + update(t, e, i) { + (this.maxWidth = t), + (this.maxHeight = e), + (this._margins = i), + this.setDimensions(), + this.buildLabels(), + this.fit(); + } + setDimensions() { + this.isHorizontal() + ? ((this.width = this.maxWidth), + (this.left = this._margins.left), + (this.right = this.width)) + : ((this.height = this.maxHeight), + (this.top = this._margins.top), + (this.bottom = this.height)); + } + buildLabels() { + const t = this.options.labels || {}; + let e = c(t.generateLabels, [this.chart], this) || []; + t.filter && (e = e.filter(e => t.filter(e, this.chart.data))), + t.sort && (e = e.sort((e, i) => t.sort(e, i, this.chart.data))), + this.options.reverse && e.reverse(), + (this.legendItems = e); + } + fit() { + const { options: t, ctx: e } = this; + if (!t.display) return void (this.width = this.height = 0); + const i = t.labels, + s = ki(i.font), + n = s.size, + o = this._computeTitleHeight(), + { boxWidth: a, itemHeight: r } = Ro(i, n); + let l, h; + (e.font = s.string), + this.isHorizontal() + ? ((l = this.maxWidth), (h = this._fitRows(o, n, a, r) + 10)) + : ((h = this.maxHeight), (l = this._fitCols(o, s, a, r) + 10)), + (this.width = Math.min(l, t.maxWidth || this.maxWidth)), + (this.height = Math.min(h, t.maxHeight || this.maxHeight)); + } + _fitRows(t, e, i, s) { + const { + ctx: n, + maxWidth: o, + options: { + labels: { padding: a } + } + } = this, + r = (this.legendHitBoxes = []), + l = (this.lineWidths = [0]), + h = s + a; + let c = t; + (n.textAlign = "left"), (n.textBaseline = "middle"); + let d = -1, + u = -h; + return ( + this.legendItems.forEach((t, f) => { + const g = i + e / 2 + n.measureText(t.text).width; + (0 === f || l[l.length - 1] + g + 2 * a > o) && + ((c += h), (l[l.length - (f > 0 ? 0 : 1)] = 0), (u += h), d++), + (r[f] = { left: 0, top: u, row: d, width: g, height: s }), + (l[l.length - 1] += g + a); + }), + c + ); + } + _fitCols(t, e, i, s) { + const { + ctx: n, + maxHeight: o, + options: { + labels: { padding: a } + } + } = this, + r = (this.legendHitBoxes = []), + l = (this.columnSizes = []), + h = o - t; + let c = a, + d = 0, + u = 0, + f = 0, + g = 0; + return ( + this.legendItems.forEach((t, o) => { + const { itemWidth: p, itemHeight: m } = (function(t, e, i, s, n) { + const o = (function(t, e, i, s) { + let n = t.text; + n && + "string" != typeof n && + (n = n.reduce((t, e) => (t.length > e.length ? t : e))); + return e + i.size / 2 + s.measureText(n).width; + })(s, t, e, i), + a = (function(t, e, i) { + let s = t; + "string" != typeof e.text && (s = zo(e, i)); + return s; + })(n, s, e.lineHeight); + return { itemWidth: o, itemHeight: a }; + })(i, e, n, t, s); + o > 0 && + u + m + 2 * a > h && + ((c += d + a), + l.push({ width: d, height: u }), + (f += d + a), + g++, + (d = u = 0)), + (r[o] = { left: f, top: u, col: g, width: p, height: m }), + (d = Math.max(d, p)), + (u += m + a); + }), + (c += d), + l.push({ width: d, height: u }), + c + ); + } + adjustHitBoxes() { + if (!this.options.display) return; + const t = this._computeTitleHeight(), + { + legendHitBoxes: e, + options: { + align: i, + labels: { padding: s }, + rtl: n + } + } = this, + o = Ci(n, this.left, this.width); + if (this.isHorizontal()) { + let n = 0, + a = ut(i, this.left + s, this.right - this.lineWidths[n]); + for (const r of e) + n !== r.row && + ((n = r.row), + (a = ut(i, this.left + s, this.right - this.lineWidths[n]))), + (r.top += this.top + t + s), + (r.left = o.leftForLtr(o.x(a), r.width)), + (a += r.width + s); + } else { + let n = 0, + a = ut(i, this.top + t + s, this.bottom - this.columnSizes[n].height); + for (const r of e) + r.col !== n && + ((n = r.col), + (a = ut( + i, + this.top + t + s, + this.bottom - this.columnSizes[n].height + ))), + (r.top = a), + (r.left += this.left + s), + (r.left = o.leftForLtr(o.x(r.left), r.width)), + (a += r.height + s); + } + } + isHorizontal() { + return ( + "top" === this.options.position || "bottom" === this.options.position + ); + } + draw() { + if (this.options.display) { + const t = this.ctx; + Re(t, this), this._draw(), Ie(t); + } + } + _draw() { + const { options: t, columnSizes: e, lineWidths: i, ctx: s } = this, + { align: n, labels: o } = t, + a = ue.color, + l = Ci(t.rtl, this.left, this.width), + h = ki(o.font), + { padding: c } = o, + d = h.size, + u = d / 2; + let f; + this.drawTitle(), + (s.textAlign = l.textAlign("left")), + (s.textBaseline = "middle"), + (s.lineWidth = 0.5), + (s.font = h.string); + const { boxWidth: g, boxHeight: p, itemHeight: m } = Ro(o, d), + b = this.isHorizontal(), + x = this._computeTitleHeight(); + (f = b + ? { + x: ut(n, this.left + c, this.right - i[0]), + y: this.top + c + x, + line: 0 + } + : { + x: this.left + c, + y: ut(n, this.top + x + c, this.bottom - e[0].height), + line: 0 + }), + Oi(this.ctx, t.textDirection); + const _ = m + c; + this.legendItems.forEach((y, v) => { + (s.strokeStyle = y.fontColor), (s.fillStyle = y.fontColor); + const M = s.measureText(y.text).width, + w = l.textAlign(y.textAlign || (y.textAlign = o.textAlign)), + k = g + u + M; + let S = f.x, + P = f.y; + l.setWidth(this.width), + b + ? v > 0 && + S + k + c > this.right && + ((P = f.y += _), + f.line++, + (S = f.x = ut(n, this.left + c, this.right - i[f.line]))) + : v > 0 && + P + _ > this.bottom && + ((S = f.x = S + e[f.line].width + c), + f.line++, + (P = f.y = ut( + n, + this.top + x + c, + this.bottom - e[f.line].height + ))); + if ( + ((function(t, e, i) { + if (isNaN(g) || g <= 0 || isNaN(p) || p < 0) return; + s.save(); + const n = r(i.lineWidth, 1); + if ( + ((s.fillStyle = r(i.fillStyle, a)), + (s.lineCap = r(i.lineCap, "butt")), + (s.lineDashOffset = r(i.lineDashOffset, 0)), + (s.lineJoin = r(i.lineJoin, "miter")), + (s.lineWidth = n), + (s.strokeStyle = r(i.strokeStyle, a)), + s.setLineDash(r(i.lineDash, [])), + o.usePointStyle) + ) { + const a = { + radius: (p * Math.SQRT2) / 2, + pointStyle: i.pointStyle, + rotation: i.rotation, + borderWidth: n + }, + r = l.xPlus(t, g / 2); + Le(s, a, r, e + u, o.pointStyleWidth && g); + } else { + const o = e + Math.max((d - p) / 2, 0), + a = l.leftForLtr(t, g), + r = Mi(i.borderRadius); + s.beginPath(), + Object.values(r).some(t => 0 !== t) + ? We(s, { x: a, y: o, w: g, h: p, radius: r }) + : s.rect(a, o, g, p), + s.fill(), + 0 !== n && s.stroke(); + } + s.restore(); + })(l.x(S), P, y), + (S = ft(w, S + g + u, b ? S + k : this.right, t.rtl)), + (function(t, e, i) { + Ve(s, i.text, t, e + m / 2, h, { + strikethrough: i.hidden, + textAlign: l.textAlign(i.textAlign) + }); + })(l.x(S), P, y), + b) + ) + f.x += k + c; + else if ("string" != typeof y.text) { + const t = h.lineHeight; + f.y += zo(y, t); + } else f.y += _; + }), + Ai(this.ctx, t.textDirection); + } + drawTitle() { + const t = this.options, + e = t.title, + i = ki(e.font), + s = wi(e.padding); + if (!e.display) return; + const n = Ci(t.rtl, this.left, this.width), + o = this.ctx, + a = e.position, + r = i.size / 2, + l = s.top + r; + let h, + c = this.left, + d = this.width; + if (this.isHorizontal()) + (d = Math.max(...this.lineWidths)), + (h = this.top + l), + (c = ut(t.align, c, this.right - d)); + else { + const e = this.columnSizes.reduce((t, e) => Math.max(t, e.height), 0); + h = + l + + ut( + t.align, + this.top, + this.bottom - e - t.labels.padding - this._computeTitleHeight() + ); + } + const u = ut(a, c, c + d); + (o.textAlign = n.textAlign(dt(a))), + (o.textBaseline = "middle"), + (o.strokeStyle = e.color), + (o.fillStyle = e.color), + (o.font = i.string), + Ve(o, e.text, u, h, i); + } + _computeTitleHeight() { + const t = this.options.title, + e = ki(t.font), + i = wi(t.padding); + return t.display ? e.lineHeight + i.height : 0; + } + _getLegendItemAt(t, e) { + let i, s, n; + if (Q(t, this.left, this.right) && Q(e, this.top, this.bottom)) + for (n = this.legendHitBoxes, i = 0; i < n.length; ++i) + if ( + ((s = n[i]), + Q(t, s.left, s.left + s.width) && Q(e, s.top, s.top + s.height)) + ) + return this.legendItems[i]; + return null; + } + handleEvent(t) { + const e = this.options; + if ( + !(function(t, e) { + if ( + ("mousemove" === t || "mouseout" === t) && + (e.onHover || e.onLeave) + ) + return !0; + if (e.onClick && ("click" === t || "mouseup" === t)) return !0; + return !1; + })(t.type, e) + ) + return; + const i = this._getLegendItemAt(t.x, t.y); + if ("mousemove" === t.type || "mouseout" === t.type) { + const o = this._hoveredItem, + a = + ((n = i), + null !== (s = o) && + null !== n && + s.datasetIndex === n.datasetIndex && + s.index === n.index); + o && !a && c(e.onLeave, [t, o, this], this), + (this._hoveredItem = i), + i && !a && c(e.onHover, [t, i, this], this); + } else i && c(e.onClick, [t, i, this], this); + var s, n; + } + } + function zo(t, e) { + return e * (t.text ? t.text.length + 0.5 : 0); + } + var Fo = { + id: "legend", + _element: Io, + start(t, e, i) { + const s = (t.legend = new Io({ ctx: t.ctx, options: i, chart: t })); + os.configure(t, s, i), os.addBox(t, s); + }, + stop(t) { + os.removeBox(t, t.legend), delete t.legend; + }, + beforeUpdate(t, e, i) { + const s = t.legend; + os.configure(t, s, i), (s.options = i); + }, + afterUpdate(t) { + const e = t.legend; + e.buildLabels(), e.adjustHitBoxes(); + }, + afterEvent(t, e) { + e.replay || t.legend.handleEvent(e.event); + }, + defaults: { + display: !0, + position: "top", + align: "center", + fullSize: !0, + reverse: !1, + weight: 1e3, + onClick(t, e, i) { + const s = e.datasetIndex, + n = i.chart; + n.isDatasetVisible(s) + ? (n.hide(s), (e.hidden = !0)) + : (n.show(s), (e.hidden = !1)); + }, + onHover: null, + onLeave: null, + labels: { + color: t => t.chart.options.color, + boxWidth: 40, + padding: 10, + generateLabels(t) { + const e = t.data.datasets, + { + labels: { + usePointStyle: i, + pointStyle: s, + textAlign: n, + color: o, + useBorderRadius: a, + borderRadius: r + } + } = t.legend.options; + return t._getSortedDatasetMetas().map(t => { + const l = t.controller.getStyle(i ? 0 : void 0), + h = wi(l.borderWidth); + return { + text: e[t.index].label, + fillStyle: l.backgroundColor, + fontColor: o, + hidden: !t.visible, + lineCap: l.borderCapStyle, + lineDash: l.borderDash, + lineDashOffset: l.borderDashOffset, + lineJoin: l.borderJoinStyle, + lineWidth: (h.width + h.height) / 4, + strokeStyle: l.borderColor, + pointStyle: s || l.pointStyle, + rotation: l.rotation, + textAlign: n || l.textAlign, + borderRadius: a && (r || l.borderRadius), + datasetIndex: t.index + }; + }, this); + } + }, + title: { + color: t => t.chart.options.color, + display: !1, + position: "center", + text: "" + } + }, + descriptors: { + _scriptable: t => !t.startsWith("on"), + labels: { + _scriptable: t => !["generateLabels", "filter", "sort"].includes(t) + } + } + }; + class Vo extends Ns { + constructor(t) { + super(), + (this.chart = t.chart), + (this.options = t.options), + (this.ctx = t.ctx), + (this._padding = void 0), + (this.top = void 0), + (this.bottom = void 0), + (this.left = void 0), + (this.right = void 0), + (this.width = void 0), + (this.height = void 0), + (this.position = void 0), + (this.weight = void 0), + (this.fullSize = void 0); + } + update(t, e) { + const i = this.options; + if (((this.left = 0), (this.top = 0), !i.display)) + return void (this.width = this.height = this.right = this.bottom = 0); + (this.width = this.right = t), (this.height = this.bottom = e); + const n = s(i.text) ? i.text.length : 1; + this._padding = wi(i.padding); + const o = n * ki(i.font).lineHeight + this._padding.height; + this.isHorizontal() ? (this.height = o) : (this.width = o); + } + isHorizontal() { + const t = this.options.position; + return "top" === t || "bottom" === t; + } + _drawArgs(t) { + const { top: e, left: i, bottom: s, right: n, options: o } = this, + a = o.align; + let r, + l, + h, + c = 0; + return ( + this.isHorizontal() + ? ((l = ut(a, i, n)), (h = e + t), (r = n - i)) + : ("left" === o.position + ? ((l = i + t), (h = ut(a, s, e)), (c = -0.5 * D)) + : ((l = n - t), (h = ut(a, e, s)), (c = 0.5 * D)), + (r = s - e)), + { titleX: l, titleY: h, maxWidth: r, rotation: c } + ); + } + draw() { + const t = this.ctx, + e = this.options; + if (!e.display) return; + const i = ki(e.font), + s = i.lineHeight / 2 + this._padding.top, + { titleX: n, titleY: o, maxWidth: a, rotation: r } = this._drawArgs(s); + Ve(t, e.text, 0, 0, i, { + color: e.color, + maxWidth: a, + rotation: r, + textAlign: dt(e.align), + textBaseline: "middle", + translation: [n, o] + }); + } + } + var Bo = { + id: "title", + _element: Vo, + start(t, e, i) { + !(function(t, e) { + const i = new Vo({ ctx: t.ctx, options: e, chart: t }); + os.configure(t, i, e), os.addBox(t, i), (t.titleBlock = i); + })(t, i); + }, + stop(t) { + const e = t.titleBlock; + os.removeBox(t, e), delete t.titleBlock; + }, + beforeUpdate(t, e, i) { + const s = t.titleBlock; + os.configure(t, s, i), (s.options = i); + }, + defaults: { + align: "center", + display: !1, + font: { weight: "bold" }, + fullSize: !0, + padding: 10, + position: "top", + text: "", + weight: 2e3 + }, + defaultRoutes: { color: "color" }, + descriptors: { _scriptable: !0, _indexable: !1 } + }; + const No = new WeakMap(); + var Wo = { + id: "subtitle", + start(t, e, i) { + const s = new Vo({ ctx: t.ctx, options: i, chart: t }); + os.configure(t, s, i), os.addBox(t, s), No.set(t, s); + }, + stop(t) { + os.removeBox(t, No.get(t)), No.delete(t); + }, + beforeUpdate(t, e, i) { + const s = No.get(t); + os.configure(t, s, i), (s.options = i); + }, + defaults: { + align: "center", + display: !1, + font: { weight: "normal" }, + fullSize: !0, + padding: 0, + position: "top", + text: "", + weight: 1500 + }, + defaultRoutes: { color: "color" }, + descriptors: { _scriptable: !0, _indexable: !1 } + }; + const Ho = { + average(t) { + if (!t.length) return !1; + let e, + i, + s = 0, + n = 0, + o = 0; + for (e = 0, i = t.length; e < i; ++e) { + const i = t[e].element; + if (i && i.hasValue()) { + const t = i.tooltipPosition(); + (s += t.x), (n += t.y), ++o; + } + } + return { x: s / o, y: n / o }; + }, + nearest(t, e) { + if (!t.length) return !1; + let i, + s, + n, + o = e.x, + a = e.y, + r = Number.POSITIVE_INFINITY; + for (i = 0, s = t.length; i < s; ++i) { + const s = t[i].element; + if (s && s.hasValue()) { + const t = X(e, s.getCenterPoint()); + t < r && ((r = t), (n = s)); + } + } + if (n) { + const t = n.tooltipPosition(); + (o = t.x), (a = t.y); + } + return { x: o, y: a }; + } + }; + function jo(t, e) { + return e && (s(e) ? Array.prototype.push.apply(t, e) : t.push(e)), t; + } + function $o(t) { + return ("string" == typeof t || t instanceof String) && t.indexOf("\n") > -1 + ? t.split("\n") + : t; + } + function Yo(t, e) { + const { element: i, datasetIndex: s, index: n } = e, + o = t.getDatasetMeta(s).controller, + { label: a, value: r } = o.getLabelAndValue(n); + return { + chart: t, + label: a, + parsed: o.getParsed(n), + raw: t.data.datasets[s].data[n], + formattedValue: r, + dataset: o.getDataset(), + dataIndex: n, + datasetIndex: s, + element: i + }; + } + function Uo(t, e) { + const i = t.chart.ctx, + { body: s, footer: n, title: o } = t, + { boxWidth: a, boxHeight: r } = e, + l = ki(e.bodyFont), + h = ki(e.titleFont), + c = ki(e.footerFont), + u = o.length, + f = n.length, + g = s.length, + p = wi(e.padding); + let m = p.height, + b = 0, + x = s.reduce( + (t, e) => t + e.before.length + e.lines.length + e.after.length, + 0 + ); + if ( + ((x += t.beforeBody.length + t.afterBody.length), + u && + (m += + u * h.lineHeight + (u - 1) * e.titleSpacing + e.titleMarginBottom), + x) + ) { + m += + g * (e.displayColors ? Math.max(r, l.lineHeight) : l.lineHeight) + + (x - g) * l.lineHeight + + (x - 1) * e.bodySpacing; + } + f && + (m += e.footerMarginTop + f * c.lineHeight + (f - 1) * e.footerSpacing); + let _ = 0; + const y = function(t) { + b = Math.max(b, i.measureText(t).width + _); + }; + return ( + i.save(), + (i.font = h.string), + d(t.title, y), + (i.font = l.string), + d(t.beforeBody.concat(t.afterBody), y), + (_ = e.displayColors ? a + 2 + e.boxPadding : 0), + d(s, t => { + d(t.before, y), d(t.lines, y), d(t.after, y); + }), + (_ = 0), + (i.font = c.string), + d(t.footer, y), + i.restore(), + (b += p.width), + { width: b, height: m } + ); + } + function Xo(t, e, i, s) { + const { x: n, width: o } = i, + { + width: a, + chartArea: { left: r, right: l } + } = t; + let h = "center"; + return ( + "center" === s + ? (h = n <= (r + l) / 2 ? "left" : "right") + : n <= o / 2 + ? (h = "left") + : n >= a - o / 2 && (h = "right"), + (function(t, e, i, s) { + const { x: n, width: o } = s, + a = i.caretSize + i.caretPadding; + return ( + ("left" === t && n + o + a > e.width) || + ("right" === t && n - o - a < 0) || + void 0 + ); + })(h, t, e, i) && (h = "center"), + h + ); + } + function qo(t, e, i) { + const s = + i.yAlign || + e.yAlign || + (function(t, e) { + const { y: i, height: s } = e; + return i < s / 2 ? "top" : i > t.height - s / 2 ? "bottom" : "center"; + })(t, i); + return { xAlign: i.xAlign || e.xAlign || Xo(t, e, i, s), yAlign: s }; + } + function Ko(t, e, i, s) { + const { caretSize: n, caretPadding: o, cornerRadius: a } = t, + { xAlign: r, yAlign: l } = i, + h = n + o, + { topLeft: c, topRight: d, bottomLeft: u, bottomRight: f } = Mi(a); + let g = (function(t, e) { + let { x: i, width: s } = t; + return "right" === e ? (i -= s) : "center" === e && (i -= s / 2), i; + })(e, r); + const p = (function(t, e, i) { + let { y: s, height: n } = t; + return "top" === e ? (s += i) : (s -= "bottom" === e ? n + i : n / 2), s; + })(e, l, h); + return ( + "center" === l + ? "left" === r + ? (g += h) + : "right" === r && (g -= h) + : "left" === r + ? (g -= Math.max(c, u) + n) + : "right" === r && (g += Math.max(d, f) + n), + { x: Z(g, 0, s.width - e.width), y: Z(p, 0, s.height - e.height) } + ); + } + function Go(t, e, i) { + const s = wi(i.padding); + return "center" === e + ? t.x + t.width / 2 + : "right" === e + ? t.x + t.width - s.right + : t.x + s.left; + } + function Zo(t) { + return jo([], $o(t)); + } + function Jo(t, e) { + const i = + e && e.dataset && e.dataset.tooltip && e.dataset.tooltip.callbacks; + return i ? t.override(i) : t; + } + const Qo = { + beforeTitle: t, + title(t) { + if (t.length > 0) { + const e = t[0], + i = e.chart.data.labels, + s = i ? i.length : 0; + if (this && this.options && "dataset" === this.options.mode) + return e.dataset.label || ""; + if (e.label) return e.label; + if (s > 0 && e.dataIndex < s) return i[e.dataIndex]; + } + return ""; + }, + afterTitle: t, + beforeBody: t, + beforeLabel: t, + label(t) { + if (this && this.options && "dataset" === this.options.mode) + return t.label + ": " + t.formattedValue || t.formattedValue; + let e = t.dataset.label || ""; + e && (e += ": "); + const s = t.formattedValue; + return i(s) || (e += s), e; + }, + labelColor(t) { + const e = t.chart + .getDatasetMeta(t.datasetIndex) + .controller.getStyle(t.dataIndex); + return { + borderColor: e.borderColor, + backgroundColor: e.backgroundColor, + borderWidth: e.borderWidth, + borderDash: e.borderDash, + borderDashOffset: e.borderDashOffset, + borderRadius: 0 + }; + }, + labelTextColor() { + return this.options.bodyColor; + }, + labelPointStyle(t) { + const e = t.chart + .getDatasetMeta(t.datasetIndex) + .controller.getStyle(t.dataIndex); + return { pointStyle: e.pointStyle, rotation: e.rotation }; + }, + afterLabel: t, + afterBody: t, + beforeFooter: t, + footer: t, + afterFooter: t + }; + function ta(t, e, i, s) { + const n = t[e].call(i, s); + return void 0 === n ? Qo[e].call(i, s) : n; + } + class ea extends Ns { + static positioners = Ho; + constructor(t) { + super(), + (this.opacity = 0), + (this._active = []), + (this._eventPosition = void 0), + (this._size = void 0), + (this._cachedAnimations = void 0), + (this._tooltipItems = []), + (this.$animations = void 0), + (this.$context = void 0), + (this.chart = t.chart), + (this.options = t.options), + (this.dataPoints = void 0), + (this.title = void 0), + (this.beforeBody = void 0), + (this.body = void 0), + (this.afterBody = void 0), + (this.footer = void 0), + (this.xAlign = void 0), + (this.yAlign = void 0), + (this.x = void 0), + (this.y = void 0), + (this.height = void 0), + (this.width = void 0), + (this.caretX = void 0), + (this.caretY = void 0), + (this.labelColors = void 0), + (this.labelPointStyles = void 0), + (this.labelTextColors = void 0); + } + initialize(t) { + (this.options = t), + (this._cachedAnimations = void 0), + (this.$context = void 0); + } + _resolveAnimations() { + const t = this._cachedAnimations; + if (t) return t; + const e = this.chart, + i = this.options.setContext(this.getContext()), + s = i.enabled && e.options.animation && i.animations, + n = new Ds(this.chart, s); + return s._cacheable && (this._cachedAnimations = Object.freeze(n)), n; + } + getContext() { + return ( + this.$context || + (this.$context = + ((t = this.chart.getContext()), + (e = this), + (i = this._tooltipItems), + Di(t, { tooltip: e, tooltipItems: i, type: "tooltip" }))) + ); + var t, e, i; + } + getTitle(t, e) { + const { callbacks: i } = e, + s = ta(i, "beforeTitle", this, t), + n = ta(i, "title", this, t), + o = ta(i, "afterTitle", this, t); + let a = []; + return (a = jo(a, $o(s))), (a = jo(a, $o(n))), (a = jo(a, $o(o))), a; + } + getBeforeBody(t, e) { + return Zo(ta(e.callbacks, "beforeBody", this, t)); + } + getBody(t, e) { + const { callbacks: i } = e, + s = []; + return ( + d(t, t => { + const e = { before: [], lines: [], after: [] }, + n = Jo(i, t); + jo(e.before, $o(ta(n, "beforeLabel", this, t))), + jo(e.lines, ta(n, "label", this, t)), + jo(e.after, $o(ta(n, "afterLabel", this, t))), + s.push(e); + }), + s + ); + } + getAfterBody(t, e) { + return Zo(ta(e.callbacks, "afterBody", this, t)); + } + getFooter(t, e) { + const { callbacks: i } = e, + s = ta(i, "beforeFooter", this, t), + n = ta(i, "footer", this, t), + o = ta(i, "afterFooter", this, t); + let a = []; + return (a = jo(a, $o(s))), (a = jo(a, $o(n))), (a = jo(a, $o(o))), a; + } + _createItems(t) { + const e = this._active, + i = this.chart.data, + s = [], + n = [], + o = []; + let a, + r, + l = []; + for (a = 0, r = e.length; a < r; ++a) l.push(Yo(this.chart, e[a])); + return ( + t.filter && (l = l.filter((e, s, n) => t.filter(e, s, n, i))), + t.itemSort && (l = l.sort((e, s) => t.itemSort(e, s, i))), + d(l, e => { + const i = Jo(t.callbacks, e); + s.push(ta(i, "labelColor", this, e)), + n.push(ta(i, "labelPointStyle", this, e)), + o.push(ta(i, "labelTextColor", this, e)); + }), + (this.labelColors = s), + (this.labelPointStyles = n), + (this.labelTextColors = o), + (this.dataPoints = l), + l + ); + } + update(t, e) { + const i = this.options.setContext(this.getContext()), + s = this._active; + let n, + o = []; + if (s.length) { + const t = Ho[i.position].call(this, s, this._eventPosition); + (o = this._createItems(i)), + (this.title = this.getTitle(o, i)), + (this.beforeBody = this.getBeforeBody(o, i)), + (this.body = this.getBody(o, i)), + (this.afterBody = this.getAfterBody(o, i)), + (this.footer = this.getFooter(o, i)); + const e = (this._size = Uo(this, i)), + a = Object.assign({}, t, e), + r = qo(this.chart, i, a), + l = Ko(i, a, r, this.chart); + (this.xAlign = r.xAlign), + (this.yAlign = r.yAlign), + (n = { + opacity: 1, + x: l.x, + y: l.y, + width: e.width, + height: e.height, + caretX: t.x, + caretY: t.y + }); + } else 0 !== this.opacity && (n = { opacity: 0 }); + (this._tooltipItems = o), + (this.$context = void 0), + n && this._resolveAnimations().update(this, n), + t && + i.external && + i.external.call(this, { + chart: this.chart, + tooltip: this, + replay: e + }); + } + drawCaret(t, e, i, s) { + const n = this.getCaretPosition(t, i, s); + e.lineTo(n.x1, n.y1), e.lineTo(n.x2, n.y2), e.lineTo(n.x3, n.y3); + } + getCaretPosition(t, e, i) { + const { xAlign: s, yAlign: n } = this, + { caretSize: o, cornerRadius: a } = i, + { topLeft: r, topRight: l, bottomLeft: h, bottomRight: c } = Mi(a), + { x: d, y: u } = t, + { width: f, height: g } = e; + let p, m, b, x, _, y; + return ( + "center" === n + ? ((_ = u + g / 2), + "left" === s + ? ((p = d), (m = p - o), (x = _ + o), (y = _ - o)) + : ((p = d + f), (m = p + o), (x = _ - o), (y = _ + o)), + (b = p)) + : ((m = + "left" === s + ? d + Math.max(r, h) + o + : "right" === s + ? d + f - Math.max(l, c) - o + : this.caretX), + "top" === n + ? ((x = u), (_ = x - o), (p = m - o), (b = m + o)) + : ((x = u + g), (_ = x + o), (p = m + o), (b = m - o)), + (y = x)), + { x1: p, x2: m, x3: b, y1: x, y2: _, y3: y } + ); + } + drawTitle(t, e, i) { + const s = this.title, + n = s.length; + let o, a, r; + if (n) { + const l = Ci(i.rtl, this.x, this.width); + for ( + t.x = Go(this, i.titleAlign, i), + e.textAlign = l.textAlign(i.titleAlign), + e.textBaseline = "middle", + o = ki(i.titleFont), + a = i.titleSpacing, + e.fillStyle = i.titleColor, + e.font = o.string, + r = 0; + r < n; + ++r + ) + e.fillText(s[r], l.x(t.x), t.y + o.lineHeight / 2), + (t.y += o.lineHeight + a), + r + 1 === n && (t.y += i.titleMarginBottom - a); + } + } + _drawColorBox(t, e, i, s, o) { + const a = this.labelColors[i], + r = this.labelPointStyles[i], + { boxHeight: l, boxWidth: h, boxPadding: c } = o, + d = ki(o.bodyFont), + u = Go(this, "left", o), + f = s.x(u), + g = l < d.lineHeight ? (d.lineHeight - l) / 2 : 0, + p = e.y + g; + if (o.usePointStyle) { + const e = { + radius: Math.min(h, l) / 2, + pointStyle: r.pointStyle, + rotation: r.rotation, + borderWidth: 1 + }, + i = s.leftForLtr(f, h) + h / 2, + n = p + l / 2; + (t.strokeStyle = o.multiKeyBackground), + (t.fillStyle = o.multiKeyBackground), + Te(t, e, i, n), + (t.strokeStyle = a.borderColor), + (t.fillStyle = a.backgroundColor), + Te(t, e, i, n); + } else { + (t.lineWidth = n(a.borderWidth) + ? Math.max(...Object.values(a.borderWidth)) + : a.borderWidth || 1), + (t.strokeStyle = a.borderColor), + t.setLineDash(a.borderDash || []), + (t.lineDashOffset = a.borderDashOffset || 0); + const e = s.leftForLtr(f, h - c), + i = s.leftForLtr(s.xPlus(f, 1), h - c - 2), + r = Mi(a.borderRadius); + Object.values(r).some(t => 0 !== t) + ? (t.beginPath(), + (t.fillStyle = o.multiKeyBackground), + We(t, { x: e, y: p, w: h, h: l, radius: r }), + t.fill(), + t.stroke(), + (t.fillStyle = a.backgroundColor), + t.beginPath(), + We(t, { x: i, y: p + 1, w: h - 2, h: l - 2, radius: r }), + t.fill()) + : ((t.fillStyle = o.multiKeyBackground), + t.fillRect(e, p, h, l), + t.strokeRect(e, p, h, l), + (t.fillStyle = a.backgroundColor), + t.fillRect(i, p + 1, h - 2, l - 2)); + } + t.fillStyle = this.labelTextColors[i]; + } + drawBody(t, e, i) { + const { body: s } = this, + { + bodySpacing: n, + bodyAlign: o, + displayColors: a, + boxHeight: r, + boxWidth: l, + boxPadding: h + } = i, + c = ki(i.bodyFont); + let u = c.lineHeight, + f = 0; + const g = Ci(i.rtl, this.x, this.width), + p = function(i) { + e.fillText(i, g.x(t.x + f), t.y + u / 2), (t.y += u + n); + }, + m = g.textAlign(o); + let b, x, _, y, v, M, w; + for ( + e.textAlign = o, + e.textBaseline = "middle", + e.font = c.string, + t.x = Go(this, m, i), + e.fillStyle = i.bodyColor, + d(this.beforeBody, p), + f = a && "right" !== m ? ("center" === o ? l / 2 + h : l + 2 + h) : 0, + y = 0, + M = s.length; + y < M; + ++y + ) { + for ( + b = s[y], + x = this.labelTextColors[y], + e.fillStyle = x, + d(b.before, p), + _ = b.lines, + a && + _.length && + (this._drawColorBox(e, t, y, g, i), + (u = Math.max(c.lineHeight, r))), + v = 0, + w = _.length; + v < w; + ++v + ) + p(_[v]), (u = c.lineHeight); + d(b.after, p); + } + (f = 0), (u = c.lineHeight), d(this.afterBody, p), (t.y -= n); + } + drawFooter(t, e, i) { + const s = this.footer, + n = s.length; + let o, a; + if (n) { + const r = Ci(i.rtl, this.x, this.width); + for ( + t.x = Go(this, i.footerAlign, i), + t.y += i.footerMarginTop, + e.textAlign = r.textAlign(i.footerAlign), + e.textBaseline = "middle", + o = ki(i.footerFont), + e.fillStyle = i.footerColor, + e.font = o.string, + a = 0; + a < n; + ++a + ) + e.fillText(s[a], r.x(t.x), t.y + o.lineHeight / 2), + (t.y += o.lineHeight + i.footerSpacing); + } + } + drawBackground(t, e, i, s) { + const { xAlign: n, yAlign: o } = this, + { x: a, y: r } = t, + { width: l, height: h } = i, + { topLeft: c, topRight: d, bottomLeft: u, bottomRight: f } = Mi( + s.cornerRadius + ); + (e.fillStyle = s.backgroundColor), + (e.strokeStyle = s.borderColor), + (e.lineWidth = s.borderWidth), + e.beginPath(), + e.moveTo(a + c, r), + "top" === o && this.drawCaret(t, e, i, s), + e.lineTo(a + l - d, r), + e.quadraticCurveTo(a + l, r, a + l, r + d), + "center" === o && "right" === n && this.drawCaret(t, e, i, s), + e.lineTo(a + l, r + h - f), + e.quadraticCurveTo(a + l, r + h, a + l - f, r + h), + "bottom" === o && this.drawCaret(t, e, i, s), + e.lineTo(a + u, r + h), + e.quadraticCurveTo(a, r + h, a, r + h - u), + "center" === o && "left" === n && this.drawCaret(t, e, i, s), + e.lineTo(a, r + c), + e.quadraticCurveTo(a, r, a + c, r), + e.closePath(), + e.fill(), + s.borderWidth > 0 && e.stroke(); + } + _updateAnimationTarget(t) { + const e = this.chart, + i = this.$animations, + s = i && i.x, + n = i && i.y; + if (s || n) { + const i = Ho[t.position].call(this, this._active, this._eventPosition); + if (!i) return; + const o = (this._size = Uo(this, t)), + a = Object.assign({}, i, this._size), + r = qo(e, t, a), + l = Ko(t, a, r, e); + (s._to === l.x && n._to === l.y) || + ((this.xAlign = r.xAlign), + (this.yAlign = r.yAlign), + (this.width = o.width), + (this.height = o.height), + (this.caretX = i.x), + (this.caretY = i.y), + this._resolveAnimations().update(this, l)); + } + } + _willRender() { + return !!this.opacity; + } + draw(t) { + const e = this.options.setContext(this.getContext()); + let i = this.opacity; + if (!i) return; + this._updateAnimationTarget(e); + const s = { width: this.width, height: this.height }, + n = { x: this.x, y: this.y }; + i = Math.abs(i) < 0.001 ? 0 : i; + const o = wi(e.padding), + a = + this.title.length || + this.beforeBody.length || + this.body.length || + this.afterBody.length || + this.footer.length; + e.enabled && + a && + (t.save(), + (t.globalAlpha = i), + this.drawBackground(n, t, s, e), + Oi(t, e.textDirection), + (n.y += o.top), + this.drawTitle(n, t, e), + this.drawBody(n, t, e), + this.drawFooter(n, t, e), + Ai(t, e.textDirection), + t.restore()); + } + getActiveElements() { + return this._active || []; + } + setActiveElements(t, e) { + const i = this._active, + s = t.map(({ datasetIndex: t, index: e }) => { + const i = this.chart.getDatasetMeta(t); + if (!i) throw new Error("Cannot find a dataset at index " + t); + return { datasetIndex: t, element: i.data[e], index: e }; + }), + n = !u(i, s), + o = this._positionChanged(s, e); + (n || o) && + ((this._active = s), + (this._eventPosition = e), + (this._ignoreReplayEvents = !0), + this.update(!0)); + } + handleEvent(t, e, i = !0) { + if (e && this._ignoreReplayEvents) return !1; + this._ignoreReplayEvents = !1; + const s = this.options, + n = this._active || [], + o = this._getActiveElements(t, n, e, i), + a = this._positionChanged(o, t), + r = e || !u(o, n) || a; + return ( + r && + ((this._active = o), + (s.enabled || s.external) && + ((this._eventPosition = { x: t.x, y: t.y }), this.update(!0, e))), + r + ); + } + _getActiveElements(t, e, i, s) { + const n = this.options; + if ("mouseout" === t.type) return []; + if (!s) return e; + const o = this.chart.getElementsAtEventForMode(t, n.mode, n, i); + return n.reverse && o.reverse(), o; + } + _positionChanged(t, e) { + const { caretX: i, caretY: s, options: n } = this, + o = Ho[n.position].call(this, t, e); + return !1 !== o && (i !== o.x || s !== o.y); + } + } + var ia = { + id: "tooltip", + _element: ea, + positioners: Ho, + afterInit(t, e, i) { + i && (t.tooltip = new ea({ chart: t, options: i })); + }, + beforeUpdate(t, e, i) { + t.tooltip && t.tooltip.initialize(i); + }, + reset(t, e, i) { + t.tooltip && t.tooltip.initialize(i); + }, + afterDraw(t) { + const e = t.tooltip; + if (e && e._willRender()) { + const i = { tooltip: e }; + if ( + !1 === + t.notifyPlugins("beforeTooltipDraw", { ...i, cancelable: !0 }) + ) + return; + e.draw(t.ctx), t.notifyPlugins("afterTooltipDraw", i); + } + }, + afterEvent(t, e) { + if (t.tooltip) { + const i = e.replay; + t.tooltip.handleEvent(e.event, i, e.inChartArea) && (e.changed = !0); + } + }, + defaults: { + enabled: !0, + external: null, + position: "average", + backgroundColor: "rgba(0,0,0,0.8)", + titleColor: "#fff", + titleFont: { weight: "bold" }, + titleSpacing: 2, + titleMarginBottom: 6, + titleAlign: "left", + bodyColor: "#fff", + bodySpacing: 2, + bodyFont: {}, + bodyAlign: "left", + footerColor: "#fff", + footerSpacing: 2, + footerMarginTop: 6, + footerFont: { weight: "bold" }, + footerAlign: "left", + padding: 6, + caretPadding: 2, + caretSize: 5, + cornerRadius: 6, + boxHeight: (t, e) => e.bodyFont.size, + boxWidth: (t, e) => e.bodyFont.size, + multiKeyBackground: "#fff", + displayColors: !0, + boxPadding: 0, + borderColor: "rgba(0,0,0,0)", + borderWidth: 0, + animation: { duration: 400, easing: "easeOutQuart" }, + animations: { + numbers: { + type: "number", + properties: ["x", "y", "width", "height", "caretX", "caretY"] + }, + opacity: { easing: "linear", duration: 200 } + }, + callbacks: Qo + }, + defaultRoutes: { + bodyFont: "font", + footerFont: "font", + titleFont: "font" + }, + descriptors: { + _scriptable: t => + "filter" !== t && "itemSort" !== t && "external" !== t, + _indexable: !1, + callbacks: { _scriptable: !1, _indexable: !1 }, + animation: { _fallback: !1 }, + animations: { _fallback: "animation" } + }, + additionalOptionScopes: ["interaction"] + }, + sa = Object.freeze({ + __proto__: null, + Colors: fo, + Decimation: mo, + Filler: Eo, + Legend: Fo, + SubTitle: Wo, + Title: Bo, + Tooltip: ia + }); + function na(t, e, i, s) { + const n = t.indexOf(e); + if (-1 === n) + return ((t, e, i, s) => ( + "string" == typeof e + ? ((i = t.push(e) - 1), s.unshift({ index: i, label: e })) + : isNaN(e) && (i = null), + i + ))(t, e, i, s); + return n !== t.lastIndexOf(e) ? i : n; + } + function oa(t) { + const e = this.getLabels(); + return t >= 0 && t < e.length ? e[t] : t; + } + function aa(t, e, { horizontal: i, minRotation: s }) { + const n = j(s), + o = (i ? Math.sin(n) : Math.cos(n)) || 0.001, + a = 0.75 * e * ("" + t).length; + return Math.min(e / o, a); + } + class ra extends Ks { + constructor(t) { + super(t), + (this.start = void 0), + (this.end = void 0), + (this._startValue = void 0), + (this._endValue = void 0), + (this._valueRange = 0); + } + parse(t, e) { + return i(t) || + (("number" == typeof t || t instanceof Number) && !isFinite(+t)) + ? null + : +t; + } + handleTickRangeOptions() { + const { beginAtZero: t } = this.options, + { minDefined: e, maxDefined: i } = this.getUserBounds(); + let { min: s, max: n } = this; + const o = t => (s = e ? s : t), + a = t => (n = i ? n : t); + if (t) { + const t = z(s), + e = z(n); + t < 0 && e < 0 ? a(0) : t > 0 && e > 0 && o(0); + } + if (s === n) { + let e = 0 === n ? 1 : Math.abs(0.05 * n); + a(n + e), t || o(s - e); + } + (this.min = s), (this.max = n); + } + getTickLimit() { + const t = this.options.ticks; + let e, + { maxTicksLimit: i, stepSize: s } = t; + return ( + s + ? ((e = Math.ceil(this.max / s) - Math.floor(this.min / s) + 1), + e > 1e3 && + (console.warn( + `scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.` + ), + (e = 1e3))) + : ((e = this.computeTickLimit()), (i = i || 11)), + i && (e = Math.min(i, e)), + e + ); + } + computeTickLimit() { + return Number.POSITIVE_INFINITY; + } + buildTicks() { + const t = this.options, + e = t.ticks; + let s = this.getTickLimit(); + s = Math.max(2, s); + const n = (function(t, e) { + const s = [], + { + bounds: n, + step: o, + min: a, + max: r, + precision: l, + count: h, + maxTicks: c, + maxDigits: d, + includeBounds: u + } = t, + f = o || 1, + g = c - 1, + { min: p, max: m } = e, + b = !i(a), + x = !i(r), + _ = !i(h), + y = (m - p) / (d + 1); + let v, + M, + w, + k, + S = V((m - p) / g / f) * f; + if (S < 1e-14 && !b && !x) return [{ value: p }, { value: m }]; + (k = Math.ceil(m / S) - Math.floor(p / S)), + k > g && (S = V((k * S) / g / f) * f), + i(l) || ((v = Math.pow(10, l)), (S = Math.ceil(S * v) / v)), + "ticks" === n + ? ((M = Math.floor(p / S) * S), (w = Math.ceil(m / S) * S)) + : ((M = p), (w = m)), + b && x && o && W((r - a) / o, S / 1e3) + ? ((k = Math.round(Math.min((r - a) / S, c))), + (S = (r - a) / k), + (M = a), + (w = r)) + : _ + ? ((M = b ? a : M), (w = x ? r : w), (k = h - 1), (S = (w - M) / k)) + : ((k = (w - M) / S), + (k = F(k, Math.round(k), S / 1e3) + ? Math.round(k) + : Math.ceil(k))); + const P = Math.max(Y(S), Y(M)); + (v = Math.pow(10, i(l) ? P : l)), + (M = Math.round(M * v) / v), + (w = Math.round(w * v) / v); + let D = 0; + for ( + b && + (u && M !== a + ? (s.push({ value: a }), + M < a && D++, + F(Math.round((M + D * S) * v) / v, a, aa(a, y, t)) && D++) + : M < a && D++); + D < k; + ++D + ) + s.push({ value: Math.round((M + D * S) * v) / v }); + return ( + x && u && w !== r + ? s.length && F(s[s.length - 1].value, r, aa(r, y, t)) + ? (s[s.length - 1].value = r) + : s.push({ value: r }) + : (x && w !== r) || s.push({ value: w }), + s + ); + })( + { + maxTicks: s, + bounds: t.bounds, + min: t.min, + max: t.max, + precision: e.precision, + step: e.stepSize, + count: e.count, + maxDigits: this._maxDigits(), + horizontal: this.isHorizontal(), + minRotation: e.minRotation || 0, + includeBounds: !1 !== e.includeBounds + }, + this._range || this + ); + return ( + "ticks" === t.bounds && H(n, this, "value"), + t.reverse + ? (n.reverse(), (this.start = this.max), (this.end = this.min)) + : ((this.start = this.min), (this.end = this.max)), + n + ); + } + configure() { + const t = this.ticks; + let e = this.min, + i = this.max; + if ((super.configure(), this.options.offset && t.length)) { + const s = (i - e) / Math.max(t.length - 1, 1) / 2; + (e -= s), (i += s); + } + (this._startValue = e), (this._endValue = i), (this._valueRange = i - e); + } + getLabelForValue(t) { + return ne(t, this.chart.options.locale, this.options.ticks.format); + } + } + class la extends ra { + static id = "linear"; + static defaults = { ticks: { callback: ae.formatters.numeric } }; + determineDataLimits() { + const { min: t, max: e } = this.getMinMax(!0); + (this.min = o(t) ? t : 0), + (this.max = o(e) ? e : 1), + this.handleTickRangeOptions(); + } + computeTickLimit() { + const t = this.isHorizontal(), + e = t ? this.width : this.height, + i = j(this.options.ticks.minRotation), + s = (t ? Math.sin(i) : Math.cos(i)) || 0.001, + n = this._resolveTickFontOptions(0); + return Math.ceil(e / Math.min(40, n.lineHeight / s)); + } + getPixelForValue(t) { + return null === t + ? NaN + : this.getPixelForDecimal((t - this._startValue) / this._valueRange); + } + getValueForPixel(t) { + return this._startValue + this.getDecimalForPixel(t) * this._valueRange; + } + } + const ha = t => Math.floor(I(t)), + ca = (t, e) => Math.pow(10, ha(t) + e); + function da(t) { + return 1 === t / Math.pow(10, ha(t)); + } + function ua(t, e, i) { + const s = Math.pow(10, i), + n = Math.floor(t / s); + return Math.ceil(e / s) - n; + } + function fa(t, { min: e, max: i }) { + e = a(t.min, e); + const s = [], + n = ha(e); + let o = (function(t, e) { + let i = ha(e - t); + for (; ua(t, e, i) > 10; ) i++; + for (; ua(t, e, i) < 10; ) i--; + return Math.min(i, ha(t)); + })(e, i), + r = o < 0 ? Math.pow(10, Math.abs(o)) : 1; + const l = Math.pow(10, o), + h = n > o ? Math.pow(10, n) : 0, + c = Math.round((e - h) * r) / r, + d = Math.floor((e - h) / l / 10) * l * 10; + let u = Math.floor((c - d) / Math.pow(10, o)), + f = a(t.min, Math.round((h + d + u * Math.pow(10, o)) * r) / r); + for (; f < i; ) + s.push({ value: f, major: da(f), significand: u }), + u >= 10 ? (u = u < 15 ? 15 : 20) : u++, + u >= 20 && (o++, (u = 2), (r = o >= 0 ? 1 : r)), + (f = Math.round((h + d + u * Math.pow(10, o)) * r) / r); + const g = a(t.max, f); + return s.push({ value: g, major: da(g), significand: u }), s; + } + class ga extends Ks { + static id = "logarithmic"; + static defaults = { + ticks: { callback: ae.formatters.logarithmic, major: { enabled: !0 } } + }; + constructor(t) { + super(t), + (this.start = void 0), + (this.end = void 0), + (this._startValue = void 0), + (this._valueRange = 0); + } + parse(t, e) { + const i = ra.prototype.parse.apply(this, [t, e]); + if (0 !== i) return o(i) && i > 0 ? i : null; + this._zero = !0; + } + determineDataLimits() { + const { min: t, max: e } = this.getMinMax(!0); + (this.min = o(t) ? Math.max(0, t) : null), + (this.max = o(e) ? Math.max(0, e) : null), + this.options.beginAtZero && (this._zero = !0), + this._zero && + this.min !== this._suggestedMin && + !o(this._userMin) && + (this.min = + t === ca(this.min, 0) ? ca(this.min, -1) : ca(this.min, 0)), + this.handleTickRangeOptions(); + } + handleTickRangeOptions() { + const { minDefined: t, maxDefined: e } = this.getUserBounds(); + let i = this.min, + s = this.max; + const n = e => (i = t ? i : e), + o = t => (s = e ? s : t); + i === s && (i <= 0 ? (n(1), o(10)) : (n(ca(i, -1)), o(ca(s, 1)))), + i <= 0 && n(ca(s, -1)), + s <= 0 && o(ca(i, 1)), + (this.min = i), + (this.max = s); + } + buildTicks() { + const t = this.options, + e = fa({ min: this._userMin, max: this._userMax }, this); + return ( + "ticks" === t.bounds && H(e, this, "value"), + t.reverse + ? (e.reverse(), (this.start = this.max), (this.end = this.min)) + : ((this.start = this.min), (this.end = this.max)), + e + ); + } + getLabelForValue(t) { + return void 0 === t + ? "0" + : ne(t, this.chart.options.locale, this.options.ticks.format); + } + configure() { + const t = this.min; + super.configure(), + (this._startValue = I(t)), + (this._valueRange = I(this.max) - I(t)); + } + getPixelForValue(t) { + return ( + (void 0 !== t && 0 !== t) || (t = this.min), + null === t || isNaN(t) + ? NaN + : this.getPixelForDecimal( + t === this.min ? 0 : (I(t) - this._startValue) / this._valueRange + ) + ); + } + getValueForPixel(t) { + const e = this.getDecimalForPixel(t); + return Math.pow(10, this._startValue + e * this._valueRange); + } + } + function pa(t) { + const e = t.ticks; + if (e.display && t.display) { + const t = wi(e.backdropPadding); + return r(e.font && e.font.size, ue.font.size) + t.height; + } + return 0; + } + function ma(t, e, i, s, n) { + return t === s || t === n + ? { start: e - i / 2, end: e + i / 2 } + : t < s || t > n + ? { start: e - i, end: e } + : { start: e, end: e + i }; + } + function ba(t) { + const e = { + l: t.left + t._padding.left, + r: t.right - t._padding.right, + t: t.top + t._padding.top, + b: t.bottom - t._padding.bottom + }, + i = Object.assign({}, e), + n = [], + o = [], + a = t._pointLabels.length, + r = t.options.pointLabels, + l = r.centerPointLabels ? D / a : 0; + for (let u = 0; u < a; u++) { + const a = r.setContext(t.getPointLabelContext(u)); + o[u] = a.padding; + const f = t.getPointPosition(u, t.drawingArea + o[u], l), + g = ki(a.font), + p = + ((h = t.ctx), + (c = g), + (d = s((d = t._pointLabels[u])) ? d : [d]), + { w: Ce(h, c.string, d), h: d.length * c.lineHeight }); + n[u] = p; + const m = K(t.getIndexAngle(u) + l), + b = Math.round($(m)); + xa(i, e, m, ma(b, f.x, p.w, 0, 180), ma(b, f.y, p.h, 90, 270)); + } + var h, c, d; + t.setCenterPoint(e.l - i.l, i.r - e.r, e.t - i.t, i.b - e.b), + (t._pointLabelItems = (function(t, e, i) { + const s = [], + n = t._pointLabels.length, + o = t.options, + a = pa(o) / 2, + r = t.drawingArea, + l = o.pointLabels.centerPointLabels ? D / n : 0; + for (let o = 0; o < n; o++) { + const n = t.getPointPosition(o, r + a + i[o], l), + h = Math.round($(K(n.angle + L))), + c = e[o], + d = va(n.y, c.h, h), + u = _a(h), + f = ya(n.x, c.w, u); + s.push({ + x: n.x, + y: d, + textAlign: u, + left: f, + top: d, + right: f + c.w, + bottom: d + c.h + }); + } + return s; + })(t, n, o)); + } + function xa(t, e, i, s, n) { + const o = Math.abs(Math.sin(i)), + a = Math.abs(Math.cos(i)); + let r = 0, + l = 0; + s.start < e.l + ? ((r = (e.l - s.start) / o), (t.l = Math.min(t.l, e.l - r))) + : s.end > e.r && + ((r = (s.end - e.r) / o), (t.r = Math.max(t.r, e.r + r))), + n.start < e.t + ? ((l = (e.t - n.start) / a), (t.t = Math.min(t.t, e.t - l))) + : n.end > e.b && + ((l = (n.end - e.b) / a), (t.b = Math.max(t.b, e.b + l))); + } + function _a(t) { + return 0 === t || 180 === t ? "center" : t < 180 ? "left" : "right"; + } + function ya(t, e, i) { + return "right" === i ? (t -= e) : "center" === i && (t -= e / 2), t; + } + function va(t, e, i) { + return ( + 90 === i || 270 === i ? (t -= e / 2) : (i > 270 || i < 90) && (t -= e), t + ); + } + function Ma(t, e, i, s) { + const { ctx: n } = t; + if (i) n.arc(t.xCenter, t.yCenter, e, 0, C); + else { + let i = t.getPointPosition(0, e); + n.moveTo(i.x, i.y); + for (let o = 1; o < s; o++) + (i = t.getPointPosition(o, e)), n.lineTo(i.x, i.y); + } + } + class wa extends ra { + static id = "radialLinear"; + static defaults = { + display: !0, + animate: !0, + position: "chartArea", + angleLines: { + display: !0, + lineWidth: 1, + borderDash: [], + borderDashOffset: 0 + }, + grid: { circular: !1 }, + startAngle: 0, + ticks: { showLabelBackdrop: !0, callback: ae.formatters.numeric }, + pointLabels: { + backdropColor: void 0, + backdropPadding: 2, + display: !0, + font: { size: 10 }, + callback: t => t, + padding: 5, + centerPointLabels: !1 + } + }; + static defaultRoutes = { + "angleLines.color": "borderColor", + "pointLabels.color": "color", + "ticks.color": "color" + }; + static descriptors = { angleLines: { _fallback: "grid" } }; + constructor(t) { + super(t), + (this.xCenter = void 0), + (this.yCenter = void 0), + (this.drawingArea = void 0), + (this._pointLabels = []), + (this._pointLabelItems = []); + } + setDimensions() { + const t = (this._padding = wi(pa(this.options) / 2)), + e = (this.width = this.maxWidth - t.width), + i = (this.height = this.maxHeight - t.height); + (this.xCenter = Math.floor(this.left + e / 2 + t.left)), + (this.yCenter = Math.floor(this.top + i / 2 + t.top)), + (this.drawingArea = Math.floor(Math.min(e, i) / 2)); + } + determineDataLimits() { + const { min: t, max: e } = this.getMinMax(!1); + (this.min = o(t) && !isNaN(t) ? t : 0), + (this.max = o(e) && !isNaN(e) ? e : 0), + this.handleTickRangeOptions(); + } + computeTickLimit() { + return Math.ceil(this.drawingArea / pa(this.options)); + } + generateTickLabels(t) { + ra.prototype.generateTickLabels.call(this, t), + (this._pointLabels = this.getLabels() + .map((t, e) => { + const i = c(this.options.pointLabels.callback, [t, e], this); + return i || 0 === i ? i : ""; + }) + .filter((t, e) => this.chart.getDataVisibility(e))); + } + fit() { + const t = this.options; + t.display && t.pointLabels.display + ? ba(this) + : this.setCenterPoint(0, 0, 0, 0); + } + setCenterPoint(t, e, i, s) { + (this.xCenter += Math.floor((t - e) / 2)), + (this.yCenter += Math.floor((i - s) / 2)), + (this.drawingArea -= Math.min( + this.drawingArea / 2, + Math.max(t, e, i, s) + )); + } + getIndexAngle(t) { + return K( + t * (C / (this._pointLabels.length || 1)) + + j(this.options.startAngle || 0) + ); + } + getDistanceFromCenterForValue(t) { + if (i(t)) return NaN; + const e = this.drawingArea / (this.max - this.min); + return this.options.reverse ? (this.max - t) * e : (t - this.min) * e; + } + getValueForDistanceFromCenter(t) { + if (i(t)) return NaN; + const e = t / (this.drawingArea / (this.max - this.min)); + return this.options.reverse ? this.max - e : this.min + e; + } + getPointLabelContext(t) { + const e = this._pointLabels || []; + if (t >= 0 && t < e.length) { + const i = e[t]; + return (function(t, e, i) { + return Di(t, { label: i, index: e, type: "pointLabel" }); + })(this.getContext(), t, i); + } + } + getPointPosition(t, e, i = 0) { + const s = this.getIndexAngle(t) - L + i; + return { + x: Math.cos(s) * e + this.xCenter, + y: Math.sin(s) * e + this.yCenter, + angle: s + }; + } + getPointPositionForValue(t, e) { + return this.getPointPosition(t, this.getDistanceFromCenterForValue(e)); + } + getBasePosition(t) { + return this.getPointPositionForValue(t || 0, this.getBaseValue()); + } + getPointLabelPosition(t) { + const { left: e, top: i, right: s, bottom: n } = this._pointLabelItems[t]; + return { left: e, top: i, right: s, bottom: n }; + } + drawBackground() { + const { + backgroundColor: t, + grid: { circular: e } + } = this.options; + if (t) { + const i = this.ctx; + i.save(), + i.beginPath(), + Ma( + this, + this.getDistanceFromCenterForValue(this._endValue), + e, + this._pointLabels.length + ), + i.closePath(), + (i.fillStyle = t), + i.fill(), + i.restore(); + } + } + drawGrid() { + const t = this.ctx, + e = this.options, + { angleLines: s, grid: n, border: o } = e, + a = this._pointLabels.length; + let r, l, h; + if ( + (e.pointLabels.display && + (function(t, e) { + const { + ctx: s, + options: { pointLabels: n } + } = t; + for (let o = e - 1; o >= 0; o--) { + const e = n.setContext(t.getPointLabelContext(o)), + a = ki(e.font), + { + x: r, + y: l, + textAlign: h, + left: c, + top: d, + right: u, + bottom: f + } = t._pointLabelItems[o], + { backdropColor: g } = e; + if (!i(g)) { + const t = Mi(e.borderRadius), + i = wi(e.backdropPadding); + s.fillStyle = g; + const n = c - i.left, + o = d - i.top, + a = u - c + i.width, + r = f - d + i.height; + Object.values(t).some(t => 0 !== t) + ? (s.beginPath(), + We(s, { x: n, y: o, w: a, h: r, radius: t }), + s.fill()) + : s.fillRect(n, o, a, r); + } + Ve(s, t._pointLabels[o], r, l + a.lineHeight / 2, a, { + color: e.color, + textAlign: h, + textBaseline: "middle" + }); + } + })(this, a), + n.display && + this.ticks.forEach((t, e) => { + if (0 !== e) { + l = this.getDistanceFromCenterForValue(t.value); + const i = this.getContext(e), + s = n.setContext(i), + r = o.setContext(i); + !(function(t, e, i, s, n) { + const o = t.ctx, + a = e.circular, + { color: r, lineWidth: l } = e; + (!a && !s) || + !r || + !l || + i < 0 || + (o.save(), + (o.strokeStyle = r), + (o.lineWidth = l), + o.setLineDash(n.dash), + (o.lineDashOffset = n.dashOffset), + o.beginPath(), + Ma(t, i, a, s), + o.closePath(), + o.stroke(), + o.restore()); + })(this, s, l, a, r); + } + }), + s.display) + ) { + for (t.save(), r = a - 1; r >= 0; r--) { + const i = s.setContext(this.getPointLabelContext(r)), + { color: n, lineWidth: o } = i; + o && + n && + ((t.lineWidth = o), + (t.strokeStyle = n), + t.setLineDash(i.borderDash), + (t.lineDashOffset = i.borderDashOffset), + (l = this.getDistanceFromCenterForValue( + e.ticks.reverse ? this.min : this.max + )), + (h = this.getPointPosition(r, l)), + t.beginPath(), + t.moveTo(this.xCenter, this.yCenter), + t.lineTo(h.x, h.y), + t.stroke()); + } + t.restore(); + } + } + drawBorder() {} + drawLabels() { + const t = this.ctx, + e = this.options, + i = e.ticks; + if (!i.display) return; + const s = this.getIndexAngle(0); + let n, o; + t.save(), + t.translate(this.xCenter, this.yCenter), + t.rotate(s), + (t.textAlign = "center"), + (t.textBaseline = "middle"), + this.ticks.forEach((s, a) => { + if (0 === a && !e.reverse) return; + const r = i.setContext(this.getContext(a)), + l = ki(r.font); + if ( + ((n = this.getDistanceFromCenterForValue(this.ticks[a].value)), + r.showLabelBackdrop) + ) { + (t.font = l.string), + (o = t.measureText(s.label).width), + (t.fillStyle = r.backdropColor); + const e = wi(r.backdropPadding); + t.fillRect( + -o / 2 - e.left, + -n - l.size / 2 - e.top, + o + e.width, + l.size + e.height + ); + } + Ve(t, s.label, 0, -n, l, { color: r.color }); + }), + t.restore(); + } + drawTitle() {} + } + const ka = { + millisecond: { common: !0, size: 1, steps: 1e3 }, + second: { common: !0, size: 1e3, steps: 60 }, + minute: { common: !0, size: 6e4, steps: 60 }, + hour: { common: !0, size: 36e5, steps: 24 }, + day: { common: !0, size: 864e5, steps: 30 }, + week: { common: !1, size: 6048e5, steps: 4 }, + month: { common: !0, size: 2628e6, steps: 12 }, + quarter: { common: !1, size: 7884e6, steps: 4 }, + year: { common: !0, size: 3154e7 } + }, + Sa = Object.keys(ka); + function Pa(t, e) { + return t - e; + } + function Da(t, e) { + if (i(e)) return null; + const s = t._adapter, + { parser: n, round: a, isoWeekday: r } = t._parseOpts; + let l = e; + return ( + "function" == typeof n && (l = n(l)), + o(l) || (l = "string" == typeof n ? s.parse(l, n) : s.parse(l)), + null === l + ? null + : (a && + (l = + "week" !== a || (!N(r) && !0 !== r) + ? s.startOf(l, a) + : s.startOf(l, "isoWeek", r)), + +l) + ); + } + function Ca(t, e, i, s) { + const n = Sa.length; + for (let o = Sa.indexOf(t); o < n - 1; ++o) { + const t = ka[Sa[o]], + n = t.steps ? t.steps : Number.MAX_SAFE_INTEGER; + if (t.common && Math.ceil((i - e) / (n * t.size)) <= s) return Sa[o]; + } + return Sa[n - 1]; + } + function Oa(t, e, i) { + if (i) { + if (i.length) { + const { lo: s, hi: n } = tt(i, e); + t[i[s] >= e ? i[s] : i[n]] = !0; + } + } else t[e] = !0; + } + function Aa(t, e, i) { + const s = [], + n = {}, + o = e.length; + let a, r; + for (a = 0; a < o; ++a) + (r = e[a]), (n[r] = a), s.push({ value: r, major: !1 }); + return 0 !== o && i + ? (function(t, e, i, s) { + const n = t._adapter, + o = +n.startOf(e[0].value, s), + a = e[e.length - 1].value; + let r, l; + for (r = o; r <= a; r = +n.add(r, 1, s)) + (l = i[r]), l >= 0 && (e[l].major = !0); + return e; + })(t, s, n, i) + : s; + } + class Ta extends Ks { + static id = "time"; + static defaults = { + bounds: "data", + adapters: {}, + time: { + parser: !1, + unit: !1, + round: !1, + isoWeekday: !1, + minUnit: "millisecond", + displayFormats: {} + }, + ticks: { source: "auto", callback: !1, major: { enabled: !1 } } + }; + constructor(t) { + super(t), + (this._cache = { data: [], labels: [], all: [] }), + (this._unit = "day"), + (this._majorUnit = void 0), + (this._offsets = {}), + (this._normalized = !1), + (this._parseOpts = void 0); + } + init(t, e = {}) { + const i = t.time || (t.time = {}), + s = (this._adapter = new Cn._date(t.adapters.date)); + s.init(e), + b(i.displayFormats, s.formats()), + (this._parseOpts = { + parser: i.parser, + round: i.round, + isoWeekday: i.isoWeekday + }), + super.init(t), + (this._normalized = e.normalized); + } + parse(t, e) { + return void 0 === t ? null : Da(this, t); + } + beforeLayout() { + super.beforeLayout(), (this._cache = { data: [], labels: [], all: [] }); + } + determineDataLimits() { + const t = this.options, + e = this._adapter, + i = t.time.unit || "day"; + let { + min: s, + max: n, + minDefined: a, + maxDefined: r + } = this.getUserBounds(); + function l(t) { + a || isNaN(t.min) || (s = Math.min(s, t.min)), + r || isNaN(t.max) || (n = Math.max(n, t.max)); + } + (a && r) || + (l(this._getLabelBounds()), + ("ticks" === t.bounds && "labels" === t.ticks.source) || + l(this.getMinMax(!1))), + (s = o(s) && !isNaN(s) ? s : +e.startOf(Date.now(), i)), + (n = o(n) && !isNaN(n) ? n : +e.endOf(Date.now(), i) + 1), + (this.min = Math.min(s, n - 1)), + (this.max = Math.max(s + 1, n)); + } + _getLabelBounds() { + const t = this.getLabelTimestamps(); + let e = Number.POSITIVE_INFINITY, + i = Number.NEGATIVE_INFINITY; + return ( + t.length && ((e = t[0]), (i = t[t.length - 1])), { min: e, max: i } + ); + } + buildTicks() { + const t = this.options, + e = t.time, + i = t.ticks, + s = + "labels" === i.source ? this.getLabelTimestamps() : this._generate(); + "ticks" === t.bounds && + s.length && + ((this.min = this._userMin || s[0]), + (this.max = this._userMax || s[s.length - 1])); + const n = this.min, + o = st(s, n, this.max); + return ( + (this._unit = + e.unit || + (i.autoSkip + ? Ca(e.minUnit, this.min, this.max, this._getLabelCapacity(n)) + : (function(t, e, i, s, n) { + for (let o = Sa.length - 1; o >= Sa.indexOf(i); o--) { + const i = Sa[o]; + if (ka[i].common && t._adapter.diff(n, s, i) >= e - 1) + return i; + } + return Sa[i ? Sa.indexOf(i) : 0]; + })(this, o.length, e.minUnit, this.min, this.max))), + (this._majorUnit = + i.major.enabled && "year" !== this._unit + ? (function(t) { + for (let e = Sa.indexOf(t) + 1, i = Sa.length; e < i; ++e) + if (ka[Sa[e]].common) return Sa[e]; + })(this._unit) + : void 0), + this.initOffsets(s), + t.reverse && o.reverse(), + Aa(this, o, this._majorUnit) + ); + } + afterAutoSkip() { + this.options.offsetAfterAutoskip && + this.initOffsets(this.ticks.map(t => +t.value)); + } + initOffsets(t = []) { + let e, + i, + s = 0, + n = 0; + this.options.offset && + t.length && + ((e = this.getDecimalForValue(t[0])), + (s = 1 === t.length ? 1 - e : (this.getDecimalForValue(t[1]) - e) / 2), + (i = this.getDecimalForValue(t[t.length - 1])), + (n = + 1 === t.length + ? i + : (i - this.getDecimalForValue(t[t.length - 2])) / 2)); + const o = t.length < 3 ? 0.5 : 0.25; + (s = Z(s, 0, o)), + (n = Z(n, 0, o)), + (this._offsets = { start: s, end: n, factor: 1 / (s + 1 + n) }); + } + _generate() { + const t = this._adapter, + e = this.min, + i = this.max, + s = this.options, + n = s.time, + o = n.unit || Ca(n.minUnit, e, i, this._getLabelCapacity(e)), + a = r(s.ticks.stepSize, 1), + l = "week" === o && n.isoWeekday, + h = N(l) || !0 === l, + c = {}; + let d, + u, + f = e; + if ( + (h && (f = +t.startOf(f, "isoWeek", l)), + (f = +t.startOf(f, h ? "day" : o)), + t.diff(i, e, o) > 1e5 * a) + ) + throw new Error( + e + " and " + i + " are too far apart with stepSize of " + a + " " + o + ); + const g = "data" === s.ticks.source && this.getDataTimestamps(); + for (d = f, u = 0; d < i; d = +t.add(d, a, o), u++) Oa(c, d, g); + return ( + (d !== i && "ticks" !== s.bounds && 1 !== u) || Oa(c, d, g), + Object.keys(c) + .sort((t, e) => t - e) + .map(t => +t) + ); + } + getLabelForValue(t) { + const e = this._adapter, + i = this.options.time; + return i.tooltipFormat + ? e.format(t, i.tooltipFormat) + : e.format(t, i.displayFormats.datetime); + } + _tickFormatFunction(t, e, i, s) { + const n = this.options, + o = n.ticks.callback; + if (o) return c(o, [t, e, i], this); + const a = n.time.displayFormats, + r = this._unit, + l = this._majorUnit, + h = r && a[r], + d = l && a[l], + u = i[e], + f = l && d && u && u.major; + return this._adapter.format(t, s || (f ? d : h)); + } + generateTickLabels(t) { + let e, i, s; + for (e = 0, i = t.length; e < i; ++e) + (s = t[e]), (s.label = this._tickFormatFunction(s.value, e, t)); + } + getDecimalForValue(t) { + return null === t ? NaN : (t - this.min) / (this.max - this.min); + } + getPixelForValue(t) { + const e = this._offsets, + i = this.getDecimalForValue(t); + return this.getPixelForDecimal((e.start + i) * e.factor); + } + getValueForPixel(t) { + const e = this._offsets, + i = this.getDecimalForPixel(t) / e.factor - e.end; + return this.min + i * (this.max - this.min); + } + _getLabelSize(t) { + const e = this.options.ticks, + i = this.ctx.measureText(t).width, + s = j(this.isHorizontal() ? e.maxRotation : e.minRotation), + n = Math.cos(s), + o = Math.sin(s), + a = this._resolveTickFontOptions(0).size; + return { w: i * n + a * o, h: i * o + a * n }; + } + _getLabelCapacity(t) { + const e = this.options.time, + i = e.displayFormats, + s = i[e.unit] || i.millisecond, + n = this._tickFormatFunction(t, 0, Aa(this, [t], this._majorUnit), s), + o = this._getLabelSize(n), + a = + Math.floor( + this.isHorizontal() ? this.width / o.w : this.height / o.h + ) - 1; + return a > 0 ? a : 1; + } + getDataTimestamps() { + let t, + e, + i = this._cache.data || []; + if (i.length) return i; + const s = this.getMatchingVisibleMetas(); + if (this._normalized && s.length) + return (this._cache.data = s[0].controller.getAllParsedValues(this)); + for (t = 0, e = s.length; t < e; ++t) + i = i.concat(s[t].controller.getAllParsedValues(this)); + return (this._cache.data = this.normalize(i)); + } + getLabelTimestamps() { + const t = this._cache.labels || []; + let e, i; + if (t.length) return t; + const s = this.getLabels(); + for (e = 0, i = s.length; e < i; ++e) t.push(Da(this, s[e])); + return (this._cache.labels = this._normalized ? t : this.normalize(t)); + } + normalize(t) { + return rt(t.sort(Pa)); + } + } + function La(t, e, i) { + let s, + n, + o, + a, + r = 0, + l = t.length - 1; + i + ? (e >= t[r].pos && e <= t[l].pos && ({ lo: r, hi: l } = et(t, "pos", e)), + ({ pos: s, time: o } = t[r]), + ({ pos: n, time: a } = t[l])) + : (e >= t[r].time && + e <= t[l].time && + ({ lo: r, hi: l } = et(t, "time", e)), + ({ time: s, pos: o } = t[r]), + ({ time: n, pos: a } = t[l])); + const h = n - s; + return h ? o + ((a - o) * (e - s)) / h : o; + } + var Ea = class extends Ta { + static id = "timeseries"; + static defaults = Ta.defaults; + constructor(t) { + super(t), + (this._table = []), + (this._minPos = void 0), + (this._tableRange = void 0); + } + initOffsets() { + const t = this._getTimestampsForTable(), + e = (this._table = this.buildLookupTable(t)); + (this._minPos = La(e, this.min)), + (this._tableRange = La(e, this.max) - this._minPos), + super.initOffsets(t); + } + buildLookupTable(t) { + const { min: e, max: i } = this, + s = [], + n = []; + let o, a, r, l, h; + for (o = 0, a = t.length; o < a; ++o) + (l = t[o]), l >= e && l <= i && s.push(l); + if (s.length < 2) + return [ + { time: e, pos: 0 }, + { time: i, pos: 1 } + ]; + for (o = 0, a = s.length; o < a; ++o) + (h = s[o + 1]), + (r = s[o - 1]), + (l = s[o]), + Math.round((h + r) / 2) !== l && + n.push({ time: l, pos: o / (a - 1) }); + return n; + } + _getTimestampsForTable() { + let t = this._cache.all || []; + if (t.length) return t; + const e = this.getDataTimestamps(), + i = this.getLabelTimestamps(); + return ( + (t = + e.length && i.length + ? this.normalize(e.concat(i)) + : e.length + ? e + : i), + (t = this._cache.all = t), + t + ); + } + getDecimalForValue(t) { + return (La(this._table, t) - this._minPos) / this._tableRange; + } + getValueForPixel(t) { + const e = this._offsets, + i = this.getDecimalForPixel(t) / e.factor - e.end; + return La(this._table, i * this._tableRange + this._minPos, !0); + } + }, + Ra = Object.freeze({ + __proto__: null, + CategoryScale: class extends Ks { + static id = "category"; + static defaults = { ticks: { callback: oa } }; + constructor(t) { + super(t), + (this._startValue = void 0), + (this._valueRange = 0), + (this._addedLabels = []); + } + init(t) { + const e = this._addedLabels; + if (e.length) { + const t = this.getLabels(); + for (const { index: i, label: s } of e) + t[i] === s && t.splice(i, 1); + this._addedLabels = []; + } + super.init(t); + } + parse(t, e) { + if (i(t)) return null; + const s = this.getLabels(); + return ((t, e) => (null === t ? null : Z(Math.round(t), 0, e)))( + (e = + isFinite(e) && s[e] === t + ? e + : na(s, t, r(e, t), this._addedLabels)), + s.length - 1 + ); + } + determineDataLimits() { + const { minDefined: t, maxDefined: e } = this.getUserBounds(); + let { min: i, max: s } = this.getMinMax(!0); + "ticks" === this.options.bounds && + (t || (i = 0), e || (s = this.getLabels().length - 1)), + (this.min = i), + (this.max = s); + } + buildTicks() { + const t = this.min, + e = this.max, + i = this.options.offset, + s = []; + let n = this.getLabels(); + (n = 0 === t && e === n.length - 1 ? n : n.slice(t, e + 1)), + (this._valueRange = Math.max(n.length - (i ? 0 : 1), 1)), + (this._startValue = this.min - (i ? 0.5 : 0)); + for (let i = t; i <= e; i++) s.push({ value: i }); + return s; + } + getLabelForValue(t) { + return oa.call(this, t); + } + configure() { + super.configure(), + this.isHorizontal() || (this._reversePixels = !this._reversePixels); + } + getPixelForValue(t) { + return ( + "number" != typeof t && (t = this.parse(t)), + null === t + ? NaN + : this.getPixelForDecimal( + (t - this._startValue) / this._valueRange + ) + ); + } + getPixelForTick(t) { + const e = this.ticks; + return t < 0 || t > e.length - 1 + ? null + : this.getPixelForValue(e[t].value); + } + getValueForPixel(t) { + return Math.round( + this._startValue + this.getDecimalForPixel(t) * this._valueRange + ); + } + getBasePixel() { + return this.bottom; + } + }, + LinearScale: la, + LogarithmicScale: ga, + RadialLinearScale: wa, + TimeScale: Ta, + TimeSeriesScale: Ea + }); + return ( + Sn.register(Vn, Ra, oo, sa), + (Sn.helpers = { ...Bi }), + (Sn._adapters = Cn), + (Sn.Animation = Ps), + (Sn.Animations = Ds), + (Sn.animator = bt), + (Sn.controllers = Js.controllers.items), + (Sn.DatasetController = Bs), + (Sn.Element = Ns), + (Sn.elements = oo), + (Sn.Interaction = Ui), + (Sn.layouts = os), + (Sn.platforms = ws), + (Sn.Scale = Ks), + (Sn.Ticks = ae), + Object.assign(Sn, Vn, Ra, oo, sa, ws), + (Sn.Chart = Sn), + "undefined" != typeof window && (window.Chart = Sn), + Sn + ); +}); +//# sourceMappingURL=chart.umd.js.map diff --git a/pandora_console/include/graphs/chartjs/chartjs-plugin-datalabels.min.js b/pandora_console/include/graphs/chartjs/chartjs-plugin-datalabels.min.js new file mode 100644 index 0000000000..e84e6ff057 --- /dev/null +++ b/pandora_console/include/graphs/chartjs/chartjs-plugin-datalabels.min.js @@ -0,0 +1,7 @@ +/*! + * chartjs-plugin-datalabels v2.2.0 + * https://chartjs-plugin-datalabels.netlify.app + * (c) 2017-2022 chartjs-plugin-datalabels contributors + * Released under the MIT license + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("chart.js/helpers"),require("chart.js")):"function"==typeof define&&define.amd?define(["chart.js/helpers","chart.js"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).ChartDataLabels=e(t.Chart.helpers,t.Chart)}(this,(function(t,e){"use strict";var r=function(){if("undefined"!=typeof window){if(window.devicePixelRatio)return window.devicePixelRatio;var t=window.screen;if(t)return(t.deviceXDPI||1)/(t.logicalXDPI||1)}return 1}(),a=function(e){var r,a=[];for(e=[].concat(e);e.length;)"string"==typeof(r=e.pop())?a.unshift.apply(a,r.split("\n")):Array.isArray(r)?e.push.apply(e,r):t.isNullOrUndef(e)||a.unshift(""+r);return a},o=function(t,e,r){var a,o=[].concat(e),n=o.length,i=t.font,l=0;for(t.font=r.string,a=0;ar.right&&(a|=2),er.bottom&&(a|=4),a}function u(t,e){var r,a,o=e.anchor,n=t;return e.clamp&&(n=function(t,e){for(var r,a,o,n=t.x0,i=t.y0,l=t.x1,u=t.y1,d=s(n,i,e),c=s(l,u,e);d|c&&!(d&c);)8&(r=d||c)?(a=n+(l-n)*(e.top-i)/(u-i),o=e.top):4&r?(a=n+(l-n)*(e.bottom-i)/(u-i),o=e.bottom):2&r?(o=i+(u-i)*(e.right-n)/(l-n),a=e.right):1&r&&(o=i+(u-i)*(e.left-n)/(l-n),a=e.left),r===d?d=s(n=a,i=o,e):c=s(l=a,u=o,e);return{x0:n,x1:l,y0:i,y1:u}}(n,e.area)),"start"===o?(r=n.x0,a=n.y0):"end"===o?(r=n.x1,a=n.y1):(r=(n.x0+n.x1)/2,a=(n.y0+n.y1)/2),function(t,e,r,a,o){switch(o){case"center":r=a=0;break;case"bottom":r=0,a=1;break;case"right":r=1,a=0;break;case"left":r=-1,a=0;break;case"top":r=0,a=-1;break;case"start":r=-r,a=-a;break;case"end":break;default:o*=Math.PI/180,r=Math.cos(o),a=Math.sin(o)}return{x:t,y:e,vx:r,vy:a}}(r,a,t.vx,t.vy,e.align)}var d=function(t,e){var r=(t.startAngle+t.endAngle)/2,a=Math.cos(r),o=Math.sin(r),n=t.innerRadius,i=t.outerRadius;return u({x0:t.x+a*n,y0:t.y+o*n,x1:t.x+a*i,y1:t.y+o*i,vx:a,vy:o},e)},c=function(t,e){var r=l(t,e.origin),a=r.x*t.options.radius,o=r.y*t.options.radius;return u({x0:t.x-a,y0:t.y-o,x1:t.x+a,y1:t.y+o,vx:r.x,vy:r.y},e)},h=function(t,e){var r=l(t,e.origin),a=t.x,o=t.y,n=0,i=0;return t.horizontal?(a=Math.min(t.x,t.base),n=Math.abs(t.base-t.x)):(o=Math.min(t.y,t.base),i=Math.abs(t.base-t.y)),u({x0:a,y0:o+i,x1:a+n,y1:o,vx:r.x,vy:r.y},e)},f=function(t,e){var r=l(t,e.origin);return u({x0:t.x,y0:t.y,x1:t.x+(t.width||0),y1:t.y+(t.height||0),vx:r.x,vy:r.y},e)},x=function(t){return Math.round(t*r)/r};function y(t,e){var r=e.chart.getDatasetMeta(e.datasetIndex).vScale;if(!r)return null;if(void 0!==r.xCenter&&void 0!==r.yCenter)return{x:r.xCenter,y:r.yCenter};var a=r.getBasePixel();return t.horizontal?{x:a,y:null}:{x:null,y:a}}function v(t,e,r){var a=r.backgroundColor,o=r.borderColor,n=r.borderWidth;(a||o&&n)&&(t.beginPath(),function(t,e,r,a,o,n){var i=Math.PI/2;if(n){var l=Math.min(n,o/2,a/2),s=e+l,u=r+l,d=e+a-l,c=r+o-l;t.moveTo(e,u),sr.x+r.w+2||t.y>r.y+r.h+2)},intersects:function(t){var e,r,a,o=this._points(),n=t._points(),i=[M(o[0],o[1]),M(o[0],o[3])];for(this._rotation!==t._rotation&&i.push(M(n[0],n[1]),M(n[0],n[3])),e=0;et.getProps([e],!0)[e]}),n=a.geometry(),i=$(l,a.model(),n),o._box.update(i,n,a.rotation()));(function(t,e){var r,a,o,n;for(r=t.length-1;r>=0;--r)for(o=t[r].$layout,a=r-1;a>=0&&o._visible;--a)(n=t[a].$layout)._visible&&o._box.intersects(n._box)&&e(o,n)})(t,(function(t,e){var r=t._hidable,a=e._hidable;r&&a||a?e._visible=!1:r&&(t._visible=!1)}))}(t)},lookup:function(t,e){var r,a;for(r=t.length-1;r>=0;--r)if((a=t[r].$layout)&&a._visible&&a._box.contains(e))return t[r];return null},draw:function(t,e){var r,a,o,n,i,l;for(r=0,a=e.length;r [ - '#ffffff', - '#ffffff', - ], + if (isset($options['ttl']) === true && (int) $options['ttl'] === 2) { + $params = [ + 'chart_data' => $chart_data, + 'options' => $options, + 'return_img_base_64' => true, ]; - if ($config['style'] === 'pandora_black' && !is_metaconsole() && $ttl === 1) { - $options['grid']['backgroundColor'] = [ - 'colors' => [ - '#222', - '#222', - ], - ]; - } + + return generator_chart_to_pdf('vbar_graph', $params); } - if (isset($options['grid']['margin']) === false) { - $options['grid']['margin'] = 0; - } - - if (isset($options['grid']['labelMargin']) === false) { - $options['grid']['labelMargin'] = 5; - } - - if (isset($options['grid']['axisMargin']) === false) { - $options['grid']['axisMargin'] = 5; - } - - if (isset($options['grid']['markings']) === false) { - $options['grid']['markings'] = []; - } - - if (isset($options['grid']['borderWidth']) === false) { - $options['grid']['borderWidth'] = 0; - } - - if (isset($options['grid']['borderColor']) === false) { - $options['grid']['borderColor'] = '#ffffff'; - } - - if (isset($options['grid']['minBorderMargin']) === false) { - $options['grid']['minBorderMargin'] = 5; - } - - if (isset($options['grid']['clickable']) === false) { - $options['grid']['clickable'] = false; - } - - if (isset($options['grid']['hoverable']) === false) { - $options['grid']['hoverable'] = false; - } - - if (isset($options['grid']['autoHighlight']) === false) { - $options['grid']['autoHighlight'] = false; - } - - if (isset($options['grid']['mouseActiveRadius']) === false) { - $options['grid']['mouseActiveRadius'] = false; - } - - // Series bars. - if (isset($options['seriesBars']['show']) === false) { - $options['seriesBars']['show'] = true; - } - - if (isset($options['seriesBars']['lineWidth']) === false) { - $options['seriesBars']['lineWidth'] = 0.3; - } - - if (isset($options['seriesBars']['fill']) === false) { - $options['seriesBars']['fill'] = true; - } - - if (isset($options['seriesBars']['fillColor']) === false) { - $options['seriesBars']['fillColor'] = [ - 'colors' => [ - [ 'opacity' => 0.9 ], - [ 'opacity' => 0.9 ], - ], - ]; - }; - - // Generals options. - if (isset($options['generals']['unit']) === false) { - $options['generals']['unit'] = ''; - } - - if (isset($options['generals']['divisor']) === false) { - $options['generals']['divisor'] = 1000; - } - - if (isset($options['generals']['forceTicks']) === false) { - $options['generals']['forceTicks'] = false; - } - - if (isset($options['generals']['arrayColors']) === false) { - $options['generals']['arrayColors'] = false; - } - - if (isset($options['generals']['rotate']) === false) { - $options['generals']['rotate'] = false; - } - - if (isset($options['generals']['pdf']['width']) === false) { - $options['generals']['pdf']['width'] = false; - } - - if (isset($options['generals']['pdf']['height']) === false) { - $options['generals']['pdf']['height'] = false; - } - - $params = [ - 'data' => $data, - 'x' => [ - 'title' => [ - 'title' => $options['x']['title']['title'], - 'fontSize' => $options['x']['title']['fontSize'], - 'fontFamily' => $options['x']['title']['fontFamily'], - 'padding' => $options['x']['title']['padding'], - ], - 'font' => [ - 'size' => $options['x']['font']['size'], - 'lineHeight' => $options['x']['font']['lineHeight'], - 'style' => $options['x']['font']['style'], - 'weight' => $options['x']['font']['weight'], - 'family' => $options['x']['font']['family'], - 'variant' => $options['x']['font']['variant'], - 'color' => ($options['agent_view'] === true) ? 'black' : $options['x']['font']['color'], - ], - 'show' => $options['x']['show'], - 'position' => $options['x']['position'], - 'color' => $options['x']['color'], - 'labelWidth' => $options['x']['labelWidth'], - 'labelHeight' => $options['x']['labelHeight'], - ], - 'y' => [ - 'title' => [ - 'title' => $options['y']['title']['title'], - 'fontSize' => $options['y']['title']['fontSize'], - 'fontFamily' => $options['y']['title']['fontFamily'], - 'padding' => $options['y']['title']['padding'], - ], - 'font' => [ - 'size' => $options['y']['font']['size'], - 'lineHeight' => $options['y']['font']['lineHeight'], - 'style' => $options['y']['font']['style'], - 'weight' => $options['y']['font']['weight'], - 'family' => $options['y']['font']['family'], - 'variant' => $options['y']['font']['variant'], - 'color' => ($options['agent_view'] === true) ? 'black' : $options['y']['font']['color'], - ], - 'show' => $options['y']['show'], - 'position' => $options['y']['position'], - 'color' => $options['y']['color'], - 'labelWidth' => $options['y']['labelWidth'], - 'labelHeight' => $options['y']['labelHeight'], - ], - 'bars' => [ - 'align' => $options['bars']['align'], - 'barWidth' => $options['bars']['barWidth'], - 'horizontal' => $options['bars']['horizontal'], - ], - 'grid' => [ - 'show' => $options['grid']['show'], - 'aboveData' => $options['grid']['aboveData'], - 'color' => $options['grid']['color'], - 'backgroundColor' => $options['grid']['backgroundColor'], - 'margin' => ($options['agent_view'] === true) ? 6 : $options['grid']['margin'], - 'labelMargin' => ($options['agent_view'] === true) ? 12 : $options['grid']['labelMargin'], - 'axisMargin' => $options['grid']['axisMargin'], - 'markings' => $options['grid']['markings'], - 'borderWidth' => $options['grid']['borderWidth'], - 'borderColor' => $options['grid']['borderColor'], - 'minBorderMargin' => $options['grid']['minBorderMargin'], - 'clickable' => $options['grid']['clickable'], - 'hoverable' => $options['grid']['hoverable'], - 'autoHighlight' => $options['grid']['autoHighlight'], - 'mouseActiveRadius' => $options['grid']['mouseActiveRadius'], - ], - 'seriesBars' => [ - 'show' => $options['seriesBars']['show'], - 'lineWidth' => $options['seriesBars']['lineWidth'], - 'fill' => $options['seriesBars']['fill'], - 'fillColor' => $options['seriesBars']['fillColor'], - ], - 'generals' => [ - 'unit' => $options['generals']['unit'], - 'divisor' => $options['generals']['divisor'], - 'forceTicks' => $options['generals']['forceTicks'], - 'arrayColors' => $options['generals']['arrayColors'], - 'rotate' => $options['generals']['rotate'], - ], - ]; - - if ($options['agent_view'] === true) { - $params['agent_view'] = true; - } - - if (empty($params['data']) === true) { - return graph_nodata_image( - 0, - 0, - 'vbar', - '', - true, - ($ttl === 2) ? true : false - ); - } - - if ((int) $ttl === 2) { - $params['backgroundColor'] = $options['grid']['backgroundColor']; - $params['return_img_base_64'] = true; - $params['generals']['pdf']['width'] = $options['generals']['pdf']['width']; - $params['generals']['pdf']['height'] = $options['generals']['pdf']['height']; - return generator_chart_to_pdf('vbar', $params); - } - - return flot_vcolumn_chart($params); + $chart = get_build_setup_charts('BAR', $options, $chart_data); + $output = $chart->render(true); + return $output; } @@ -730,7 +332,7 @@ function hbar_graph( } if ($chart_data === false || empty($chart_data) === true) { - return graph_nodata_image($width, $height, 'hbar'); + return graph_nodata_image($options); } if ($ttl == 2) { @@ -774,158 +376,738 @@ function hbar_graph( } +/** + * Pie graph PIE. + * + * @param array $chart_data Data. + * @param array $options Options. + * + * @return string Output html charts + */ function pie_graph( $chart_data, - $width, - $height, - $others_str='other', - $homedir='', - $water_mark='', - $font='', - $font_size=8, - $ttl=1, - $legend_position=false, - $colors='', - $hide_labels=false, - $max_values=9 + $options ) { if (empty($chart_data) === true) { - return graph_nodata_image($width, $height, 'pie'); + if (isset($options['ttl']) === true + && (int) $options['ttl'] === 2 + ) { + $options['base64'] = true; + } + + return graph_nodata_image($options); } - if ($water_mark !== false) { - setup_watermark($water_mark, $water_mark_file, $water_mark_url); - } - - // This library allows only 8 colors. - // $max_values = 9; - // Remove the html_entities. - $temp = []; - foreach ($chart_data as $key => $value) { - $temp[io_safe_output($key)] = $value; - } - - $chart_data = $temp; - + // Number max elements. + $max_values = (isset($options['maxValues']) === true) ? $options['maxValues'] : 15; if (count($chart_data) > $max_values) { + $others_str = (isset($options['otherStr']) === true) ? $options['otherStr'] : __('Others'); $chart_data_trunc = []; $n = 1; foreach ($chart_data as $key => $value) { - if ($n < $max_values) { + if ($n <= $max_values) { $chart_data_trunc[$key] = $value; } else { - if (!isset($chart_data_trunc[$others_str])) { + if (isset($options['labels'][$key]) === true) { + unset($options['labels'][$key]); + } + + if (isset($chart_data_trunc[$others_str]) === false) { $chart_data_trunc[$others_str] = 0; } - $chart_data_trunc[$others_str] += $value; + if (empty($value) === false) { + $chart_data_trunc[$others_str] += (float) $value; + } } $n++; } + $options['labels'][$max_values] = $others_str; $chart_data = $chart_data_trunc; } - if ($ttl == 2) { + if (isset($options['ttl']) === true + && (int) $options['ttl'] === 2 + ) { $params = [ - 'values' => array_values($chart_data), - 'keys' => array_keys($chart_data), - 'width' => $width, - 'height' => $height, - 'water_mark_url' => $water_mark_url, - 'font' => $font, - 'font_size' => $font_size, - 'legend_position' => $legend_position, - 'colors' => $colors, - 'hide_labels' => $hide_labels, + 'chart_data' => $chart_data, + 'options' => $options, + 'return_img_base_64' => true, ]; - return generator_chart_to_pdf('pie_chart', $params); + return generator_chart_to_pdf('pie_graph', $params); } - return flot_pie_chart( - array_values($chart_data), - array_keys($chart_data), - $width, - $height, - $water_mark_url, - $font, - $font_size, - $legend_position, - $colors, - $hide_labels - ); + $chart = get_build_setup_charts('PIE', $options, $chart_data); + $output = $chart->render(true, true); + return $output; } +/** + * Rin graph DOUGHNUT. + * + * @param array $chart_data Data. + * @param array $options Options. + * + * @return string Output html charts + */ function ring_graph( $chart_data, - $width, - $height, - $others_str='other', - $homedir='', - $water_mark='', - $font='', - $font_size='', - $ttl=1, - $legend_position=false, - $colors='', - $hide_labels=false, - $background_color='white', - $pdf=false + $options ) { - if (empty($chart_data)) { - return graph_nodata_image($width, $height, 'pie'); + global $config; + + if (empty($chart_data) === true) { + if (isset($options['ttl']) === true + && (int) $options['ttl'] === 2 + ) { + $options['base64'] = true; + } + + return graph_nodata_image($options); } - setup_watermark($water_mark, $water_mark_file, $water_mark_url); - - // This library allows only 8 colors - $max_values = 18; - - if ($ttl == 2) { + if (isset($options['ttl']) === true && (int) $options['ttl'] === 2) { $params = [ - 'chart_data' => $chart_data, - 'width' => $width, - 'height' => $height, - 'colors' => $colors, - 'module_name_list' => $module_name_list, - 'long_index' => $long_index, - 'no_data' => $no_data, - 'water_mark' => $water_mark, - 'font' => $font, - 'font_size' => $font_size, - 'unit' => $unit, - 'ttl' => $ttl, - 'homeurl' => $homeurl, - 'background_color' => $background_color, - 'legend_position' => $legend_position, - 'pdf' => $pdf, + 'chart_data' => $chart_data, + 'options' => $options, + 'return_img_base_64' => true, ]; return generator_chart_to_pdf('ring_graph', $params); } - return flot_custom_pie_chart( - $chart_data, - $width, - $height, - $colors, - $module_name_list, - $long_index, - $no_data, - false, - '', - $water_mark, - $font, - $font_size, - $unit, - $ttl, - $homeurl, - $background_color, - $legend_position, - $background_color, - $pdf - ); + $chart = get_build_setup_charts('DOUGHNUT', $options, $chart_data); + $output = $chart->render(true, true); + return $output; +} + + +function get_build_setup_charts($type, $options, $data) +{ + global $config; + + $factory = new Factory(); + + switch ($type) { + case 'DOUGHNUT': + $chart = $factory->create($factory::DOUGHNUT); + break; + + case 'PIE': + $chart = $factory->create($factory::PIE); + break; + + case 'BAR': + $chart = $factory->create($factory::BAR); + break; + + default: + // code... + break; + } + + $example = [ + 'id' => null, + 'width' => null, + 'height' => null, + 'maintainAspectRatio' => false, + 'responsive' => true, + 'radius' => null, + 'rotation' => null, + 'circumference' => null, + 'axis' => 'y', + 'legend' => [ + 'display' => true, + 'position' => 'top', + 'align' => 'center', + 'font' => [ + 'family' => '', + 'size' => 12, + 'style' => 'normal', + 'weight' => null, + 'lineHeight' => 1.2, + ], + ], + 'title' => [ + 'display' => true, + 'position' => 'top', + 'color' => '', + 'align' => 'center', + 'text' => '', + 'font' => [ + 'family' => '', + 'size' => 12, + 'style' => 'normal', + 'weight' => null, + 'lineHeight' => 1.2, + ], + ], + 'dataLabel' => [ + 'display' => true, + 'color' => '', + 'clip' => true, + 'clamp' => true, + 'anchor' => 'center', + 'formatter' => 'namefunction', + 'fonts' => [ + 'family' => '', + 'size' => 12, + 'style' => 'normal', + 'weight' => null, + 'lineHeight' => 1.2, + ], + ], + 'scales' => [ + 'x' => [ + 'grid' => [ + 'display' => false, + 'color' => 'orange', + ], + 'ticks' => [ + 'fonts' => [ + 'family' => '', + 'size' => 12, + 'style' => 'normal', + 'weight' => null, + 'lineHeight' => 1.2, + ], + ], + ], + 'y' => [ + 'grid' => [ + 'display' => false, + 'color' => 'orange', + ], + 'ticks' => [ + 'fonts' => [ + 'family' => '', + 'size' => 12, + 'style' => 'normal', + 'weight' => null, + 'lineHeight' => 1.2, + ], + ], + ], + ], + ]; + + // Set Id. + $id = uniqid('graph_'); + if (isset($options['id']) === true && empty($options['id']) === false) { + $id = $options['id']; + } + + $chart->setId($id); + + // Height is null maximum possible. + if (isset($options['height']) === true + && empty($options['height']) === false + ) { + $chart->setHeight($options['height']); + } + + // Width is null maximum possible. + if (isset($options['width']) === true + && empty($options['width']) === false + ) { + $chart->setWidth($options['width']); + } + + // Fonts defaults. + $chart->defaults()->getFonts()->setFamily((empty($config['fontpath']) === true) ? 'Lato' : $config['fontpath']); + $chart->defaults()->getFonts()->setStyle('normal'); + $chart->defaults()->getFonts()->setWeight(600); + $chart->defaults()->getFonts()->setSize(((int) $config['font_size'] + 2)); + + if (isset($options['waterMark']) === true + && empty($options['waterMark']) === false + ) { + // WaterMark. + $chart->defaults()->getWaterMark()->setWidth(88); + $chart->defaults()->getWaterMark()->setHeight(16); + $chart->defaults()->getWaterMark()->setSrc($options['waterMark']['url']); + $chart->defaults()->getWaterMark()->setPosition('end'); + $chart->defaults()->getWaterMark()->setAlign('top'); + } + + if ((isset($options['pdf']) === true && $options['pdf'] === true) + || (isset($options['ttl']) === true && (int) $options['ttl'] === 2) + ) { + $chart->options()->disableAnimation(false); + } + + // Set Maintain Aspect Ratio for responsive charts. + $maintainAspectRatio = false; + if (isset($options['maintainAspectRatio']) === true + && empty($options['maintainAspectRatio']) === false + ) { + $maintainAspectRatio = $options['maintainAspectRatio']; + } + + $chart->options()->setMaintainAspectRatio($maintainAspectRatio); + + // Set Responsive for responsive charts. + $responsive = true; + if (isset($options['responsive']) === true + && empty($options['responsive']) === false + ) { + $responsive = $options['responsive']; + } + + $chart->options()->setResponsive($responsive); + + // LEGEND. + if (isset($options['legend']) === true + && empty($options['legend']) === false + && is_array($options['legend']) === true + ) { + $legend = $chart->options()->getPlugins()->getLegend(); + + // Set Display legends. + $legendDisplay = true; + if (isset($options['legend']['display']) === true) { + $legendDisplay = $options['legend']['display']; + } + + $legend->setDisplay($legendDisplay); + + // Set Position legends. + $legendPosition = 'top'; + if (isset($options['legend']['position']) === true + && empty($options['legend']['position']) === false + ) { + $legendPosition = $options['legend']['position']; + } + + $legend->setPosition($legendPosition); + + // Set Align legends. + $legendAlign = 'center'; + if (isset($options['legend']['align']) === true + && empty($options['legend']['align']) === false + ) { + $legendAlign = $options['legend']['align']; + } + + $legend->setAlign($legendAlign); + + // Defaults fonts legends. + $legend->labels()->getFonts()->setFamily((empty($config['fontpath']) === true) ? 'Lato' : $config['fontpath']); + $legend->labels()->getFonts()->setStyle('normal'); + $legend->labels()->getFonts()->setWeight(600); + $legend->labels()->getFonts()->setSize(((int) $config['font_size'] + 2)); + if (isset($options['legend']['fonts']) === true + && empty($options['legend']['fonts']) === false + && is_array($options['legend']['fonts']) === true + ) { + if (isset($options['legend']['fonts']['size']) === true) { + $legend->labels()->getFonts()->setSize($options['legend']['fonts']['size']); + } + + if (isset($options['legend']['fonts']['style']) === true) { + $legend->labels()->getFonts()->setStyle($options['legend']['fonts']['style']); + } + + if (isset($options['legend']['fonts']['weight']) === true) { + $legend->labels()->getFonts()->setWeight($options['legend']['fonts']['weight']); + } + + if (isset($options['legend']['fonts']['family']) === true) { + $legend->labels()->getFonts()->setFamily($options['legend']['fonts']['family']); + } + } + } + + if (isset($options['layout']) === true + && empty($options['layout']) === false + && is_array($options['layout']) === true + ) { + $layout = $chart->options()->getLayout(); + if (isset($options['layout']['padding']) === true + && empty($options['layout']['padding']) === false + && is_array($options['layout']['padding']) === true + ) { + if (isset($options['layout']['padding']['top']) === true) { + $layout->padding()->setTop($options['layout']['padding']['top']); + } + + if (isset($options['layout']['padding']['bottom']) === true) { + $layout->padding()->setBottom($options['layout']['padding']['bottom']); + } + + if (isset($options['layout']['padding']['left']) === true) { + $layout->padding()->setLeft($options['layout']['padding']['left']); + } + + if (isset($options['layout']['padding']['right']) === true) { + $layout->padding()->setRight($options['layout']['padding']['right']); + } + } + } + + // Display labels. + if (isset($options['dataLabel']) === true + && empty($options['dataLabel']) === false + && is_array($options['dataLabel']) === true + ) { + $dataLabel = $chart->options()->getPlugins()->getDataLabel(); + + $chart->addPlugin('ChartDataLabels'); + + $dataLabelDisplay = 'auto'; + if (isset($options['dataLabel']['display']) === true) { + $dataLabelDisplay = $options['dataLabel']['display']; + } + + $dataLabel->setDisplay($dataLabelDisplay); + + $dataLabelColor = '#343434'; + if (isset($options['dataLabel']['color']) === true) { + $dataLabelColor = $options['dataLabel']['color']; + } + + $dataLabel->setColor($dataLabelColor); + + $dataLabelClip = false; + if (isset($options['dataLabel']['clip']) === true) { + $dataLabelClip = $options['dataLabel']['clip']; + } + + $dataLabel->setClip($dataLabelClip); + + $dataLabelClamp = true; + if (isset($options['dataLabel']['clamp']) === true) { + $dataLabelClamp = $options['dataLabel']['clamp']; + } + + $dataLabel->setClamp($dataLabelClamp); + + $dataLabelAnchor = 'end'; + if (isset($options['dataLabel']['anchor']) === true) { + $dataLabelAnchor = $options['dataLabel']['anchor']; + } + + $dataLabel->setAnchor($dataLabelAnchor); + + $dataLabelAlign = 'end'; + if (isset($options['dataLabel']['align']) === true) { + $dataLabelAlign = $options['dataLabel']['align']; + } + + $dataLabel->setAlign($dataLabelAlign); + + $dataLabelOffset = 0; + if (isset($options['dataLabel']['offset']) === true) { + $dataLabelOffset = $options['dataLabel']['offset']; + } + + $dataLabel->setOffset($dataLabelOffset); + + switch ($type) { + case 'DOUGHNUT': + case 'PIE': + $dataLabelFormatter = 'formatterDataLabelPie'; + break; + + case 'BAR': + if (isset($options['axis']) === true + && empty($options['axis']) === false + ) { + $dataLabelFormatter = 'formatterDataHorizontalBar'; + } else { + $dataLabelFormatter = 'formatterDataVerticalBar'; + } + break; + + default: + // Not possible. + break; + } + + if (isset($options['dataLabel']['formatter']) === true) { + $dataLabelFormatter = $options['dataLabel']['formatter']; + } + + $dataLabel->setFormatter($dataLabelFormatter); + + // Defaults fonts datalabel. + $dataLabel->getFonts()->setFamily((empty($config['fontpath']) === true) ? 'Lato' : $config['fontpath']); + $dataLabel->getFonts()->setStyle('normal'); + $dataLabel->getFonts()->setWeight(600); + $dataLabel->getFonts()->setSize(((int) $config['font_size'] + 2)); + + if (isset($options['dataLabel']['fonts']) === true + && empty($options['dataLabel']['fonts']) === false + && is_array($options['dataLabel']['fonts']) === true + ) { + if (isset($options['dataLabel']['fonts']['size']) === true) { + $dataLabel->getFonts()->setSize($options['dataLabel']['fonts']['size']); + } + + if (isset($options['dataLabel']['fonts']['style']) === true) { + $dataLabel->getFonts()->setStyle($options['dataLabel']['fonts']['style']); + } + + if (isset($options['dataLabel']['fonts']['weight']) === true) { + $dataLabel->getFonts()->setWeight($options['dataLabel']['fonts']['weight']); + } + + if (isset($options['dataLabel']['fonts']['family']) === true) { + $dataLabel->getFonts()->setFamily($options['dataLabel']['fonts']['family']); + } + } + } + + // Title. + if (isset($options['title']) === true + && empty($options['title']) === false + && is_array($options['title']) === true + ) { + $chartTitle = $chart->options()->getPlugins()->getTitle(); + + $display = false; + if (isset($options['title']['display']) === true) { + $display = $options['title']['display']; + } + + $chartTitle->setDisplay($display); + + $text = __('Title'); + if (isset($options['title']['text']) === true) { + $text = $options['title']['text']; + } + + $chartTitle->setText($text); + + $position = 'top'; + if (isset($options['title']['position']) === true) { + $position = $options['title']['position']; + } + + $chartTitle->setPosition($position); + + $color = 'top'; + if (isset($options['title']['color']) === true) { + $color = $options['title']['color']; + } + + $chartTitle->setColor($color); + + if (isset($options['title']['fonts']) === true + && empty($options['title']['fonts']) === false + && is_array($options['title']['fonts']) === true + ) { + if (isset($options['title']['fonts']['size']) === true) { + $chartTitle->getFonts()->setSize($options['title']['fonts']['size']); + } + + if (isset($options['title']['fonts']['style']) === true) { + $chartTitle->getFonts()->setStyle($options['title']['fonts']['style']); + } + + if (isset($options['title']['fonts']['family']) === true) { + $chartTitle->getFonts()->setFamily($options['title']['fonts']['family']); + } + } + } + + // Radius is null maximum possible. + if (isset($options['radius']) === true + && empty($options['radius']) === false + ) { + $chart->setRadius($options['radius']); + } + + // Rotation is null 0º. + if (isset($options['rotation']) === true + && empty($options['rotation']) === false + ) { + $chart->setRotation($options['rotation']); + } + + // Circumferende is null 360º. + if (isset($options['circumference']) === true + && empty($options['circumference']) === false + ) { + $chart->setCircumference($options['circumference']); + } + + if (isset($options['scales']) === true + && empty($options['scales']) === false + && is_array($options['scales']) === true + ) { + $scales = $chart->options()->getScales(); + + // Defaults scalesFont X. + $scalesXFonts = $scales->getX()->ticks()->getFonts(); + $scalesXFonts->setFamily((empty($config['fontpath']) === true) ? 'Lato' : $config['fontpath']); + $scalesXFonts->setStyle('normal'); + $scalesXFonts->setWeight(600); + $scalesXFonts->setSize(((int) $config['font_size'] + 2)); + + // Defaults scalesFont Y. + $scalesYFonts = $scales->getY()->ticks()->getFonts(); + $scalesYFonts->setFamily((empty($config['fontpath']) === true) ? 'Lato' : $config['fontpath']); + $scalesYFonts->setStyle('normal'); + $scalesYFonts->setWeight(600); + $scalesYFonts->setSize(((int) $config['font_size'] + 2)); + + if (isset($options['scales']['x']) === true + && empty($options['scales']['x']) === false + && is_array($options['scales']['x']) === true + ) { + if (isset($options['scales']['x']['bounds']) === true) { + $scales->getX()->setBounds($options['scales']['x']['bounds']); + } + + if (isset($options['scales']['x']['grid']) === true + && empty($options['scales']['x']['grid']) === false + && is_array($options['scales']['x']['grid']) === true + ) { + if (isset($options['scales']['x']['grid']['display']) === true) { + $scales->getX()->grid()->setDrawOnChartArea($options['scales']['x']['grid']['display']); + } + + if (isset($options['scales']['x']['grid']['color']) === true) { + $scales->getX()->grid()->setColor($options['scales']['x']['grid']['color']); + } + } + + if (isset($options['scales']['x']['ticks']) === true + && empty($options['scales']['x']['ticks']) === false + && is_array($options['scales']['x']['ticks']) === true + ) { + if (isset($options['scales']['x']['ticks']['fonts']) === true + && empty($options['scales']['x']['ticks']['fonts']) === false + && is_array($options['scales']['x']['ticks']['fonts']) === true + ) { + $scaleXTicksFonts = $scales->getX()->ticks()->getFonts(); + if (isset($options['scales']['x']['ticks']['fonts']['size']) === true) { + $scaleXTicksFonts->setSize($options['scales']['x']['ticks']['fonts']['size']); + } + + if (isset($options['scales']['x']['ticks']['fonts']['style']) === true) { + $scaleXTicksFonts->setStyle($options['scales']['x']['ticks']['fonts']['style']); + } + + if (isset($options['scales']['x']['ticks']['fonts']['family']) === true) { + $scaleXTicksFonts->setFamily($options['scales']['x']['ticks']['fonts']['family']); + } + } + } + } + + if (isset($options['scales']['y']) === true + && empty($options['scales']['y']) === false + && is_array($options['scales']['y']) === true + ) { + if (isset($options['scales']['y']['bounds']) === true) { + $scales->getY()->setBounds($options['scales']['y']['bounds']); + } + + if (isset($options['scales']['y']['grid']) === true + && empty($options['scales']['y']['grid']) === false + && is_array($options['scales']['y']['grid']) === true + ) { + if (isset($options['scales']['y']['grid']['display']) === true) { + $scales->getY()->grid()->setDrawOnChartArea($options['scales']['y']['grid']['display']); + } + + if (isset($options['scales']['y']['grid']['color']) === true) { + $scales->getY()->grid()->setColor($options['scales']['y']['grid']['color']); + } + } + + if (isset($options['scales']['y']['ticks']) === true + && empty($options['scales']['y']['ticks']) === false + && is_array($options['scales']['y']['ticks']) === true + ) { + if (isset($options['scales']['y']['ticks']['fonts']) === true + && empty($options['scales']['y']['ticks']['fonts']) === false + && is_array($options['scales']['y']['ticks']['fonts']) === true + ) { + $scaleYTicksFonts = $scales->getY()->ticks()->getFonts(); + if (isset($options['scales']['y']['ticks']['fonts']['size']) === true) { + $scaleYTicksFonts->setSize($options['scales']['y']['ticks']['fonts']['size']); + } + + if (isset($options['scales']['y']['ticks']['fonts']['style']) === true) { + $scaleYTicksFonts->setStyle($options['scales']['y']['ticks']['fonts']['style']); + } + + if (isset($options['scales']['y']['ticks']['fonts']['family']) === true) { + $scaleYTicksFonts->setFamily($options['scales']['y']['ticks']['fonts']['family']); + } + } + } + } + } + + // Color. + if (isset($options['colors']) === true + && empty($options['colors']) === false + && is_array($options['colors']) === true + ) { + $colors = $options['colors']; + $borders = $options['colors']; + } else { + // Colors. + $defaultColor = []; + $defaultBorder = []; + $defaultColorArray = color_graph_array(); + foreach ($defaultColorArray as $key => $value) { + list($r, $g, $b) = sscanf($value['color'], '#%02x%02x%02x'); + $defaultColor[$key] = 'rgba('.$r.', '.$g.', '.$b.', 0.6)'; + $defaultBorder[$key] = $value['color']; + } + + $colors = array_values($defaultColor); + $borders = array_values($defaultBorder); + } + + // Set labels. + if (isset($options['labels']) === true + && empty($options['labels']) === false + && is_array($options['labels']) === true + ) { + $chart->labels()->exchangeArray($options['labels']); + } + + // Add Datasets. + $setData = $chart->createDataSet(); + switch ($type) { + case 'DOUGHNUT': + case 'PIE': + $setData->setLabel('data')->setBackgroundColor($borders); + $setData->setLabel('data')->data()->exchangeArray(array_values($data)); + break; + + case 'BAR': + $setData->setLabel('data')->setBackgroundColor($colors); + $setData->setLabel('data')->setBorderColor($borders); + $setData->setLabel('data')->setBorderWidth(2); + + $setData->setLabel('data')->data()->exchangeArray(array_values($data)); + + // Para las horizontales. + if (isset($options['axis']) === true + && empty($options['axis']) === false + ) { + $chart->options()->setIndexAxis($options['axis']); + $setData->setAxis($options['axis']); + } + break; + + default: + // Not possible. + break; + } + + $chart->addDataSet($setData); + + return $chart; } diff --git a/pandora_console/include/graphs/functions_d3.php b/pandora_console/include/graphs/functions_d3.php index e48babd58d..acb675423a 100644 --- a/pandora_console/include/graphs/functions_d3.php +++ b/pandora_console/include/graphs/functions_d3.php @@ -756,71 +756,3 @@ 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 -) { - global $config; - - 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_'); - - // This is for "Style template" in visual styles. - switch ($config['style']) { - case 'pandora': - $textColor = '#000'; - $strokeColor = '#fff'; - break; - - case 'pandora_black': - $textColor = '#fff'; - $strokeColor = '#222'; - break; - - default: - $textColor = '#000'; - $strokeColor = '#fff'; - break; - } - - $textColor = json_encode($textColor); - $strokeColor = json_encode($strokeColor); - - $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 821b85c4d6..b76abb634e 100644 --- a/pandora_console/include/graphs/functions_flot.php +++ b/pandora_console/include/graphs/functions_flot.php @@ -52,6 +52,11 @@ function include_javascript_dependencies_flot_graph($return=false, $mobile=false '; + + // Chartjs. + $output .= ''; + $output .= ''; + $output .= " '; + if (strlen($home_url) !== 0) { + echo ''; + } else { + $_GET['sec2'] = 'general/logon_ok'; + } break; } diff --git a/pandora_console/install.php b/pandora_console/install.php index e34c26b115..f472cfd1ee 100644 --- a/pandora_console/install.php +++ b/pandora_console/install.php @@ -129,7 +129,7 @@
'; if ($agent['id_os'] == CLUSTER_OS_ID) { - if (enterprise_installed()) { - $cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId( - $agent['id_agente'] - ); - $url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR; - $url .= '/operation/cluster/cluster'; - $url = ui_get_full_url( - $url.'&op=view&id='.$cluster->id() - ); - $data[0] .= ''.__('View').''; - } + $cluster = PandoraFMS\Cluster::loadFromAgentId( + $agent['id_agente'] + ); + $url = 'index.php?sec=reporting&sec2='; + $url .= 'operation/cluster/cluster'; + $url = ui_get_full_url( + $url.'&op=view&id='.$cluster->id() + ); + $data[0] .= ''.__('View').''; } else { $data[0] .= ''.__('View').''; } @@ -865,17 +863,15 @@ foreach ($agents as $agent) { $data[0] .= ' | '; if ($agent['id_os'] == CLUSTER_OS_ID) { - if (enterprise_installed()) { - $cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId( - $agent['id_agente'] - ); - $url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR; - $url .= '/operation/cluster/cluster'; - $url = ui_get_full_url( - $url.'&op=update&id='.$cluster->id() - ); - $data[0] .= ''.__('Edit').''; - } + $cluster = PandoraFMS\Cluster::loadFromAgentId( + $agent['id_agente'] + ); + $url = 'index.php?sec=reporting&sec2='; + $url .= 'operation/cluster/cluster'; + $url = ui_get_full_url( + $url.'&op=update&id='.$cluster->id() + ); + $data[0] .= ''.__('Edit').''; } else { $data[0] .= ''.__('Edit').''; } diff --git a/pandora_console/operation/agentes/tactical.php b/pandora_console/operation/agentes/tactical.php index ce334d873e..d2dabfb167 100755 --- a/pandora_console/operation/agentes/tactical.php +++ b/pandora_console/operation/agentes/tactical.php @@ -55,6 +55,7 @@ if ($force_refresh == 1) { db_process_sql('UPDATE tgroup_stat SET utimestamp = 0'); } +$updated_time = ''; if ($config['realtimestats'] == 0) { $updated_time = ""; $updated_time .= __('Last update').' : '.ui_print_timestamp(db_get_sql('SELECT min(utimestamp) FROM tgroup_stat'), true); @@ -240,13 +241,15 @@ if ($is_admin) { include $config['homedir'].'/godmode/servers/servers.build_table.php'; } -$out = '
'; - $out .= '
- '.__('Event graph').''.html_print_image('images/spinner.gif', true, ['id' => 'spinner_total_event_graph']).'
'; - $out .= '
'; - $out .= '
- '.__('Event graph by agent').''.html_print_image('images/spinner.gif', true, ['id' => 'spinner_graphic_event_group']).'
'; - $out .= '
'; +$out = '
'; +$out .= '
'; +$out .= ''.__('Event graph').''; +$out .= html_print_image('images/spinner.gif', true, ['id' => 'spinner_total_event_graph']); +$out .= '
'; +$out .= '
'; +$out .= '
+ '.__('Event graph by agent').''.html_print_image('images/spinner.gif', true, ['id' => 'spinner_graphic_event_group']).'
'; +$out .= '
'; ui_toggle( diff --git a/pandora_console/operation/agentes/ver_agente.php b/pandora_console/operation/agentes/ver_agente.php index e2616b8d08..2ee6781f00 100644 --- a/pandora_console/operation/agentes/ver_agente.php +++ b/pandora_console/operation/agentes/ver_agente.php @@ -66,6 +66,7 @@ if (is_ajax()) { $agent_alias = get_parameter('alias', ''); $agents_inserted = get_parameter('agents_inserted', []); $id_group = (int) get_parameter('id_group'); + $pendingdelete = (bool) get_parameter('pendingdelete'); $refresh_contact = get_parameter('refresh_contact', 0); @@ -735,6 +736,11 @@ if (is_ajax()) { ($sql_tags_join === '') ? 'INNER JOIN tagente ON tagente.id_agente = t1.id_agente' : '', (empty($where_tags)) ? '' : " WHERE 1=1 $where_tags" ); + if ($pendingdelete == true) { + $sql .= sprintf( + ' AND delete_pending=0' + ); + } } } else { $sql = sprintf( @@ -2063,6 +2069,10 @@ switch ($tab) { // I do not understand, nor do I agree with this operation, but it is what it is. $(document).ready (function () { $('.ehorus_tab').hover(ehorus_tab_show, ehorus_tab_hide); + // #9587 Doble find on agent view List modules and list of alerts. + $('#button-filter').on('click',function(){ + search_alerts_status(); + }); }); function ehorus_tab_show() { @@ -2074,4 +2084,9 @@ switch ($tab) { $('.subsubmenu').hide("fast"); },15000); } + + function search_alerts_status(){ + $('#text-free_search_alert').val($('#text-status_text_monitor').val()); + $('#form_alerts_status_datatable_search_bt').trigger('click'); + } diff --git a/pandora_console/operation/cluster/cluster.php b/pandora_console/operation/cluster/cluster.php new file mode 100755 index 0000000000..2cc078ef4b --- /dev/null +++ b/pandora_console/operation/cluster/cluster.php @@ -0,0 +1,70 @@ + '[ClusterManager]'.$e->getMessage() ]); + exit; + } else { + echo '[ClusterManager]'.$e->getMessage(); + } + + // Stop this execution, but continue 'globally'. + return; +} + +// AJAX controller. +if (is_ajax()) { + $method = get_parameter('method'); + + if (method_exists($obj, $method) === true) { + $obj->{$method}(); + } else { + $obj->error('Method not found. [ClusterManager::'.$method.']'); + } + + // Stop any execution. + exit; +} else { + // Run. + $obj->run(); +} diff --git a/pandora_console/operation/events/events.php b/pandora_console/operation/events/events.php index 695b99b888..648581a3a6 100644 --- a/pandora_console/operation/events/events.php +++ b/pandora_console/operation/events/events.php @@ -102,10 +102,16 @@ if (isset($fb64) === true) { ); } +$id_group_filter = get_parameter( + 'filter[id_group_filter]', + ($filter['id_group_filter'] ?? '') +); + $id_group = get_parameter( 'filter[id_group]', - ($filter['id_group'] ?? '') + ($filter['id_group'] ?? $id_group_filter) ); + $event_type = get_parameter( 'filter[event_type]', ($filter['event_type'] ?? '') @@ -183,7 +189,7 @@ $search_secondary_groups = get_parameter( ); $search_recursive_groups = get_parameter( 'filter[search_recursive_groups]', - 0 + ($filter['search_recursive_groups'] ?? '') ); $id_group_filter = get_parameter( 'filter[id_group_filter]', diff --git a/pandora_console/operation/incidents/incident_statistics.php b/pandora_console/operation/incidents/incident_statistics.php index b4966c1b96..333c0206db 100755 --- a/pandora_console/operation/incidents/incident_statistics.php +++ b/pandora_console/operation/incidents/incident_statistics.php @@ -26,16 +26,16 @@ if (!$config['integria_enabled']) { } echo ' -

'.__('Incidents by status').'

'; +

'.__('Incidents by status').'

'; echo graph_incidents_status(); -echo '

'.__('Incidents by priority').'

'; +echo '

'.__('Incidents by priority').'

'; echo grafico_incidente_prioridad(); -echo '

'.__('Incidents by group').'

'; +echo '

'.__('Incidents by group').'

'; echo graphic_incident_group(); -echo '

'.__('Incidents by user').'

'; +echo '

'.__('Incidents by user').'

'; echo graphic_incident_user(); echo '
'; diff --git a/pandora_console/operation/menu.php b/pandora_console/operation/menu.php index 8db384eda6..334a6aa065 100644 --- a/pandora_console/operation/menu.php +++ b/pandora_console/operation/menu.php @@ -147,7 +147,12 @@ if ($access_console_node === true) { $sub['snmpconsole']['subtype'] = 'nolink'; } - enterprise_hook('cluster_menu'); + if (check_acl($config['id_user'], 0, 'AR')) { + $sub['operation/cluster/cluster']['text'] = __('Cluster View'); + $sub['operation/cluster/cluster']['id'] = 'cluster'; + $sub['operation/cluster/cluster']['refr'] = 0; + } + enterprise_hook('aws_menu'); enterprise_hook('SAP_view'); diff --git a/pandora_console/operation/network/network_report.php b/pandora_console/operation/network/network_report.php index be18d814bb..c91e1b1ee6 100644 --- a/pandora_console/operation/network/network_report.php +++ b/pandora_console/operation/network/network_report.php @@ -269,6 +269,7 @@ if (!empty($main_value)) { // Print the data and build the chart. $table->data = []; $chart_data = []; +$labels = []; $hide_filter = !empty($main_value) && ($action === 'udp' || $action === 'tcp'); foreach ($data as $item) { $row = []; @@ -294,19 +295,20 @@ foreach ($data as $item) { $table->data[] = $row; + $labels[] = io_safe_output($item['host']); // Build the pie graph data structure. switch ($order_by) { case 'pkts': - $chart_data[$item['host']] = $item['sum_bytes']; + $chart_data[] = $item['sum_bytes']; break; case 'flows': - $chart_data[$item['host']] = $item['sum_flows']; + $chart_data[] = $item['sum_flows']; break; case 'bytes': default: - $chart_data[$item['host']] = $item['sum_bytes']; + $chart_data[] = $item['sum_bytes']; break; } } @@ -317,13 +319,21 @@ if (empty($data)) { echo '
'; html_print_table($table); + $options = [ + 'height' => 230, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + // Print the graph. echo '
'; echo pie_graph( $chart_data, - 320, - 200, - __('Others') + $options ); echo '
'; echo '
'; diff --git a/pandora_console/operation/reporting/graph_viewer.php b/pandora_console/operation/reporting/graph_viewer.php index 60d1146b67..c77d02245e 100644 --- a/pandora_console/operation/reporting/graph_viewer.php +++ b/pandora_console/operation/reporting/graph_viewer.php @@ -307,34 +307,6 @@ if ($view_graph) { return; } - if ($graph_return) { - echo ""; - echo '
'; - if (!is_ajax()) { - echo ''; - } - - if ($stacked == CUSTOM_GRAPH_VBARS) { - echo '
'; - echo '
'; - } else { - echo '
'; - } - - echo $graph_return; - echo '
'; - if ($stacked == CUSTOM_GRAPH_VBARS) { - echo '
'; - } - - echo '
'; - } else { - ui_print_info_message([ 'no_close' => true, 'message' => __('No data.') ]); - } - if ($stacked == CUSTOM_GRAPH_BULLET_CHART_THRESHOLD) { $stacked = 4; } @@ -403,6 +375,31 @@ if ($view_graph) { echo ''; echo ''; + if ($graph_return) { + echo ""; + echo '
'; + if (!is_ajax()) { + echo ''; + } + + echo '
'; + + echo '
'; + echo '
'; + echo $graph_return; + echo '
'; + echo '
'; + + echo '
'; + + echo '
'; + } else { + ui_print_info_message([ 'no_close' => true, 'message' => __('No data.') ]); + } + /* We must add javascript here. Otherwise, the date picker won't work if the date is not correct because php is returning. diff --git a/pandora_console/operation/snmpconsole/snmp_browser.php b/pandora_console/operation/snmpconsole/snmp_browser.php index d8e8b9e497..d7b710c7c5 100644 --- a/pandora_console/operation/snmpconsole/snmp_browser.php +++ b/pandora_console/operation/snmpconsole/snmp_browser.php @@ -252,9 +252,7 @@ function snmp_show_result_message(data) { // Stop waiting modal. waiting_modal(stop); - var dato = data.replace(/[^]+(?=\[)/, ""); - - dato = JSON.parse(dato); + var dato = JSON.parse(data); if (dato.length !== 0) { $("#error_text").text(""); @@ -678,13 +676,16 @@ function show_add_module() { //Submit form to agent module url. $("#snmp_create_module").attr( "action", - "index.php?sec=gagente&sec2=godmode/agentes/configurar_agente&id_agente="+id_agent+"&tab=module&edit_module=1"); + "index.php?sec=gagente&sec2=godmode/agentes/configurar_agente&id_agente=" + +id_agent+ + "&tab=module&edit_module=1" + ); $('#snmp_create_module').submit(); - - - //Close dialog. - $('#dialog_create_module').dialog("close"); + }, + onDeny: function () { + $("#dialog_create_module").dialog("close"); + return false; } }); } diff --git a/pandora_console/operation/snmpconsole/snmp_statistics.php b/pandora_console/operation/snmpconsole/snmp_statistics.php index ff041c3d5d..56109aa04c 100755 --- a/pandora_console/operation/snmpconsole/snmp_statistics.php +++ b/pandora_console/operation/snmpconsole/snmp_statistics.php @@ -184,7 +184,7 @@ $table_source_data->head['num'] = __('Number'); $table_source_data->data = []; $table_source_graph_data = []; - +$labels = []; foreach ($traps_generated_by_source as $trap) { $row = []; @@ -202,25 +202,30 @@ foreach ($traps_generated_by_source as $trap) { $table_source_data->data[] = $row; - $table_source_graph_data[$trap['source']] = (int) $trap['num']; + $labels[] = io_safe_output($trap['source']); + $table_source_graph_data[] = (int) $trap['num']; } $table_source_row['table'] = html_print_table($table_source_data, true); unset($table_source_data); if (empty($table_source_graph_data)) { - $table_source_graph = graph_nodata_image(); + $table_source_graph = graph_nodata_image([]); } else { + $options = [ + 'height' => 200, + 'waterMark' => $water_mark, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + $table_source_graph = pie_graph( $table_source_graph_data, - 400, - 550, - __('Other'), - '', - $water_mark, - $config['fontpath'], - $config['font_size'], - 1 + $options ); } @@ -252,32 +257,36 @@ $table_oid_data->head['num'] = __('Number'); $table_oid_data->data = []; $table_oid_graph_data = []; - +$labels = []; foreach ($traps_generated_by_oid as $trap) { $table_oid_data->data[] = [ 'oid' => $trap['oid'], 'num' => (int) $trap['num'], ]; - - $table_oid_graph_data[$trap['oid']] = (int) $trap['num']; + $labels[] = io_safe_output($trap['oid']); + $table_oid_graph_data[] = (int) $trap['num']; } $table_oid_row['table'] = html_print_table($table_oid_data, true); unset($table_oid_data); if (empty($table_oid_graph_data)) { - $table_oid_graph = graph_nodata_image(); + $table_oid_graph = graph_nodata_image([]); } else { + $options = [ + 'height' => 200, + 'waterMark' => $water_mark, + 'legend' => [ + 'display' => true, + 'position' => 'right', + 'align' => 'center', + ], + 'labels' => $labels, + ]; + $table_oid_graph = pie_graph( $table_oid_graph_data, - 400, - 550, - __('Other'), - '', - $water_mark, - $config['fontpath'], - $config['font_size'], - 1 + $options ); } diff --git a/pandora_console/operation/snmpconsole/snmp_view.php b/pandora_console/operation/snmpconsole/snmp_view.php index e3880e2593..9f0f9e44f8 100755 --- a/pandora_console/operation/snmpconsole/snmp_view.php +++ b/pandora_console/operation/snmpconsole/snmp_view.php @@ -14,7 +14,7 @@ * |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______| * * ============================================================================ - * Copyright (c) 2005-2021 Artica Soluciones Tecnologicas + * Copyright (c) 2005-2022 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 @@ -26,1340 +26,65 @@ * ============================================================================ */ +// Begin. global $config; -enterprise_include('operation/snmpconsole/snmp_view.php'); -enterprise_include('include/functions_snmp.php'); -require_once 'include/functions_agents.php'; -require_once 'include/functions_snmp.php'; -ui_require_css_file('snmp_view'); +require_once $config['homedir'].'/include/class/SnmpConsole.class.php'; -check_login(); -$agent_a = check_acl($config['id_user'], 0, 'AR'); -$agent_w = check_acl($config['id_user'], 0, 'AW'); -$access = ($agent_a == true) ? 'AR' : (($agent_w == true) ? 'AW' : 'AR'); -if (!$agent_a && !$agent_w) { - db_pandora_audit( - AUDIT_LOG_ACL_VIOLATION, - 'Trying to access SNMP Console' +$ajaxPage = $config['homedir'].'/operation/snmpconsole/snmp_view'; + +$filter_alert = get_parameter('filter_alert', -1); +$filter_severity = get_parameter('filter_severity', -1); +$filter_free_search = get_parameter('filter_free_search', ''); +$filter_status = get_parameter('filter_status', 0); +$filter_group_by = get_parameter('filter_group_by', 0); +$filter_hours_ago = get_parameter('filter_hours_ago', 8); +$filter_trap_type = get_parameter('filter_trap_type', -1); +$refr = get_parameter('refr', 300); + +// Control call flow. +try { + // User access and validation is being processed on class constructor. + $controller = new SnmpConsole( + $ajaxPage, + $filter_alert, + $filter_severity, + $filter_free_search, + $filter_status, + $filter_group_by, + $filter_hours_ago, + $filter_trap_type, + $refr ); - include 'general/noaccess.php'; - exit; -} - -// Read parameters. -$filter_severity = (int) get_parameter('filter_severity', -1); -$filter_fired = (int) get_parameter('filter_fired', -1); -$filter_status = (int) get_parameter('filter_status', 0); -$free_search_string = (string) get_parameter('free_search_string', ''); -$pagination = (int) get_parameter('pagination', $config['block_size']); -$offset = (int) get_parameter('offset', 0); -$pure = (int) get_parameter('pure', 0); -$trap_type = (int) get_parameter('trap_type', -1); -$group_by = (int) get_parameter('group_by', 0); -$refr = (int) get_parameter('refresh'); -$default_refr = !empty($refr) ? $refr : $config['vc_refr']; -$hours_ago = get_parameter('hours_ago', 8); - -// Build ranges. -$now = new DateTime(); -$ago = new DateTime(); -$interval = new DateInterval(sprintf('PT%dH', $hours_ago)); -$ago->sub($interval); - -$date_from_trap = $ago->format('Y/m/d'); -$date_to_trap = $now->format('Y/m/d'); -$time_from_trap = $ago->format('H:i:s'); -$time_to_trap = $now->format('H:i:s'); - -$user_groups = users_get_groups($config['id_user'], $access, false); - -$str_user_groups = ''; -$i = 0; -foreach ($user_groups as $id => $name) { - if ($i == 0) { - $str_user_groups .= $id; +} catch (Exception $e) { + if ((bool) is_ajax() === true) { + echo json_encode(['error' => '[SnmpConsole]'.$e->getMessage() ]); + exit; } else { - $str_user_groups .= ','.$id; - } - - $i++; -} - -$url = 'index.php?sec=estado&sec2=operation/snmpconsole/snmp_view'; -$url .= '&filter_severity='.$filter_severity.'&filter_fired='.$filter_fired; -$url .= '&free_search_string='.$free_search_string.'&pagination='.$pagination; -$url .= '&offset='.$offset.'&trap_type='.$trap_type.'&group_by='.$group_by; -$url .= '&hours_ago='.$hours_ago.'&pure='.$pure; - -$statistics['text'] = '
'.html_print_image( - 'images/op_reporting.png', - true, - [ - 'title' => __('Statistics'), - 'class' => 'invert_filter', - ] -).''; -$list['text'] = ''.html_print_image( - 'images/op_snmp.png', - true, - [ - 'title' => __('List'), - 'class' => 'invert_filter', - ] -).''; -$list['active'] = true; - -if ($config['pure']) { - $fullscreen['text'] = ''.html_print_image( - 'images/normal_screen.png', - true, - [ - 'title' => __('Normal screen'), - 'class' => 'invert_filter', - ] - ).''; -} else { - // Fullscreen. - $fullscreen['text'] = ''.html_print_image( - 'images/full_screen.png', - true, - [ - 'title' => __('Full screen'), - 'class' => 'invert_filter', - ] - ).''; -} - - -// OPERATIONS -// Delete SNMP Trap entry Event. -if (isset($_GET['delete'])) { - $id_trap = (int) get_parameter_get('delete', 0); - if ($id_trap > 0) { - if ($group_by) { - $sql_ids_traps = 'SELECT id_trap, source FROM ttrap WHERE oid IN (SELECT oid FROM ttrap WHERE id_trap = '.$id_trap.') - AND source IN (SELECT source FROM ttrap WHERE id_trap = '.$id_trap.')'; - $ids_traps = db_get_all_rows_sql($sql_ids_traps); - - foreach ($ids_traps as $key => $value) { - $result = db_process_sql_delete('ttrap', ['id_trap' => $value['id_trap']]); - enterprise_hook('snmp_update_forwarded_modules', [$value]); - } - } else { - $forward_info = db_get_row('ttrap', 'id_trap', $id_trap); - $result = db_process_sql_delete('ttrap', ['id_trap' => $id_trap]); - enterprise_hook('snmp_update_forwarded_modules', [$forward_info]); - ui_print_result_message( - $result, - __('Successfully deleted'), - __('Could not be deleted') - ); - } - } -} - -// Check Event. -if (isset($_GET['check'])) { - $id_trap = (int) get_parameter_get('check', 0); - $values = [ - 'status' => 1, - 'id_usuario' => $config['id_user'], - ]; - $result = db_process_sql_update('ttrap', $values, ['id_trap' => $id_trap]); - enterprise_hook('snmp_update_forwarded_modules', [$id_trap]); - - ui_print_result_message( - $result, - __('Successfully updated'), - __('Could not be updated') - ); -} - -// Mass-process DELETE. -if (isset($_POST['deletebt'])) { - $trap_ids = get_parameter_post('snmptrapid', []); - if (is_array($trap_ids)) { - if ($group_by) { - foreach ($trap_ids as $key => $value) { - $sql_ids_traps = 'SELECT id_trap, source FROM ttrap WHERE oid IN (SELECT oid FROM ttrap WHERE id_trap = '.$value.') - AND source IN (SELECT source FROM ttrap WHERE id_trap = '.$value.')'; - $ids_traps = db_get_all_rows_sql($sql_ids_traps); - - foreach ($ids_traps as $key2 => $value2) { - $result = db_process_sql_delete('ttrap', ['id_trap' => $value2['id_trap']]); - enterprise_hook('snmp_update_forwarded_modules', [$value2]); - } - } - } else { - foreach ($trap_ids as $id_trap) { - $forward_info = db_get_row('ttrap', 'id_trap', $id_trap); - db_process_sql_delete('ttrap', ['id_trap' => $id_trap]); - enterprise_hook('snmp_update_forwarded_modules', [$forward_info]); - } - } - } -} - -// Mass-process UPDATE. -if (isset($_POST['updatebt'])) { - $trap_ids = get_parameter_post('snmptrapid', []); - if (is_array($trap_ids)) { - foreach ($trap_ids as $id_trap) { - $sql = sprintf("UPDATE ttrap SET status = 1, id_usuario = '%s' WHERE id_trap = %d", $config['id_user'], $id_trap); - db_process_sql($sql); - enterprise_hook('snmp_update_forwarded_modules', [$id_trap]); - } - } -} - -// All traps. -$all_traps = db_get_all_rows_sql('SELECT DISTINCT source FROM ttrap'); - -if (empty($all_traps)) { - $all_traps = []; -} - -// Set filters. -$agents = []; -$oids = []; -$severities = get_priorities(); -$alerted = [ - __('Not fired'), - __('Fired'), -]; -foreach ($all_traps as $trap) { - $agent = agents_get_agent_with_ip($trap['source']); - $agents[$trap['source']] = $agent !== false ? ($agent['alias'] ? $agent['alias'] : $agent['nombre']) : $trap['source']; - $oid = enterprise_hook('get_oid', [$trap]); - if ($oid === ENTERPRISE_NOT_HOOK) { - $oid = $trap['oid']; - } - - $oids[$oid] = $oid; -} - -$prea = array_keys($user_groups); -$ids = join(',', $prea); -// Cuantos usuarios hay operadores con un grupo que exista y no lo tenga ningun usuario. -$user_in_group_wo_agents = db_get_value_sql('select count(DISTINCT(id_usuario)) from tusuario_perfil where id_usuario ="'.$config['id_user'].'" and id_perfil = 1 and id_grupo in (select id_grupo from tgrupo where id_grupo in ('.$ids.') and id_grupo not in (select id_grupo from tagente))'); - -switch ($config['dbtype']) { - case 'mysql': - case 'postgresql': - if ($user_in_group_wo_agents == 0) { - $rows = db_get_all_rows_filter( - 'tagente', - ['id_grupo' => array_keys($user_groups)], - ['id_agente'] - ); - $id_agents = []; - foreach ($rows as $row) { - $id_agents[] = $row['id_agente']; - } - - if (!empty($id_agents)) { - $address_by_user_groups = agents_get_addresses($id_agents); - foreach ($address_by_user_groups as $i => $a) { - $address_by_user_groups[$i] = '"'.$a.'"'; - } - } - } else { - $rows = db_get_all_rows_filter( - 'tagente', - [], - ['id_agente'] - ); - $id_agents = []; - foreach ($rows as $row) { - $id_agents[] = $row['id_agente']; - } - - $all_address_agents = agents_get_addresses($id_agents); - foreach ($all_address_agents as $i => $a) { - $all_address_agents[$i] = '"'.$a.'"'; - } - } - break; - - default: - // Default. - break; -} - -if (empty($address_by_user_groups)) { - $address_by_user_groups = []; - array_unshift($address_by_user_groups, '""'); -} - -if (empty($all_address_agents)) { - $all_address_agents = []; - array_unshift($all_address_agents, '""'); -} - - -// Make query to extract traps of DB. -switch ($config['dbtype']) { - case 'mysql': - $sql = 'SELECT * - FROM ttrap - WHERE ( - `source` IN ('.implode(',', $address_by_user_groups).") OR - `source`='' OR - `source` NOT IN (".implode(',', $all_address_agents).') - ) - %s - ORDER BY timestamp DESC - LIMIT %d,%d'; - break; - - case 'postgresql': - $sql = 'SELECT * - FROM ttrap - WHERE ( - source IN ('.implode(',', $address_by_user_groups).") OR - source='' OR - source NOT IN (".implode(',', $all_address_agents).') - ) - %s - ORDER BY timestamp DESC - LIMIT %d OFFSET %d'; - break; - - case 'oracle': - $sql = "SELECT * - FROM ttrap - WHERE (source IN ( - SELECT direccion FROM tagente - WHERE id_grupo IN ($str_user_groups) - ) OR source='' OR source NOT IN (SELECT direccion FROM tagente WHERE direccion IS NOT NULL)) %s - ORDER BY timestamp DESC"; - break; - - default: - // Default. - break; -} - -switch ($config['dbtype']) { - case 'mysql': - case 'postgresql': - $sql_all = 'SELECT * - FROM ttrap - WHERE ( - source IN ('.implode(',', $address_by_user_groups).") OR - source='' OR - source NOT IN (".implode(',', $all_address_agents).') - ) - %s - ORDER BY timestamp DESC'; - $sql_count = 'SELECT COUNT(id_trap) - FROM ttrap - WHERE ( - source IN ('.implode(',', $address_by_user_groups).") OR - source='' OR - source NOT IN (".implode(',', $all_address_agents).') - ) - %s'; - break; - - case 'oracle': - $sql_all = "SELECT * - FROM ttrap - WHERE (source IN ( - SELECT direccion FROM tagente - WHERE id_grupo IN ($str_user_groups) - ) OR source='' OR source NOT IN (SELECT direccion FROM tagente WHERE direccion IS NOT NULL)) - %s - ORDER BY timestamp DESC"; - $sql_count = "SELECT COUNT(id_trap) - FROM ttrap - WHERE ( - source IN ( - SELECT direccion FROM tagente - WHERE id_grupo IN ($str_user_groups) - ) OR source='' OR source NOT IN (SELECT direccion FROM tagente WHERE direccion IS NOT NULL)) - %s"; - break; - - default: - // Default. - break; -} - -// $whereSubquery = 'WHERE 1=1'; -$whereSubquery = ''; - -if ($filter_fired != -1) { - $whereSubquery .= ' AND alerted = '.$filter_fired; -} - -if ($free_search_string != '') { - switch ($config['dbtype']) { - case 'mysql': - $whereSubquery .= ' - AND (source LIKE "%'.$free_search_string.'%" OR - oid LIKE "%'.$free_search_string.'%" OR - oid_custom LIKE "%'.$free_search_string.'%" OR - type_custom LIKE "%'.$free_search_string.'%" OR - value LIKE "%'.$free_search_string.'%" OR - value_custom LIKE "%'.$free_search_string.'%" OR - id_usuario LIKE "%'.$free_search_string.'%" OR - text LIKE "%'.$free_search_string.'%" OR - description LIKE "%'.$free_search_string.'%")'; - break; - - case 'postgresql': - case 'oracle': - $whereSubquery .= ' - AND (source LIKE \'%'.$free_search_string.'%\' OR - oid LIKE \'%'.$free_search_string.'%\' OR - oid_custom LIKE \'%'.$free_search_string.'%\' OR - type_custom LIKE \'%'.$free_search_string.'%\' OR - value LIKE \'%'.$free_search_string.'%\' OR - value_custom LIKE \'%'.$free_search_string.'%\' OR - id_usuario LIKE \'%'.$free_search_string.'%\' OR - text LIKE \'%'.$free_search_string.'%\' OR - description LIKE \'%'.$free_search_string.'%\')'; - break; - - default: - // Default. - break; - } -} - -if ($date_from_trap != '') { - if ($time_from_trap != '') { - $whereSubquery .= ' - AND (UNIX_TIMESTAMP(timestamp) > UNIX_TIMESTAMP("'.$date_from_trap.' '.$time_from_trap.'")) - '; - } else { - $whereSubquery .= ' - AND (UNIX_TIMESTAMP(timestamp) > UNIX_TIMESTAMP("'.$date_from_trap.' 23:59:59")) - '; - } -} - -if ($date_to_trap != '') { - if ($time_to_trap) { - $whereSubquery .= ' - AND (UNIX_TIMESTAMP(timestamp) < UNIX_TIMESTAMP("'.$date_to_trap.' '.$time_to_trap.'")) - '; - } else { - $whereSubquery .= ' - AND (UNIX_TIMESTAMP(timestamp) < UNIX_TIMESTAMP("'.$date_to_trap.' 23:59:59")) - '; - } -} - -if ($filter_severity != -1) { - // There are two special severity values aimed to match two different trap standard severities in database: warning/critical and critical/normal. - if ($filter_severity != EVENT_CRIT_OR_NORMAL && $filter_severity != EVENT_CRIT_WARNING_OR_CRITICAL) { - // Test if enterprise is installed to search oid in text or oid field in ttrap. - if ($config['enterprise_installed']) { - $whereSubquery .= ' AND ( - (alerted = 0 AND severity = '.$filter_severity.') OR - (alerted = 1 AND priority = '.$filter_severity.'))'; - } else { - $whereSubquery .= ' AND ( - (alerted = 0 AND 1 = '.$filter_severity.') OR - (alerted = 1 AND priority = '.$filter_severity.'))'; - } - } else if ($filter_severity === EVENT_CRIT_WARNING_OR_CRITICAL) { - // Test if enterprise is installed to search oid in text or oid field in ttrap. - if ($config['enterprise_installed']) { - $whereSubquery .= ' AND ( - (alerted = 0 AND (severity = '.EVENT_CRIT_WARNING.' OR severity = '.EVENT_CRIT_CRITICAL.')) OR - (alerted = 1 AND (priority = '.EVENT_CRIT_WARNING.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; - } else { - $whereSubquery .= ' AND ( - (alerted = 1 AND (priority = '.EVENT_CRIT_WARNING.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; - } - } else if ($filter_severity === EVENT_CRIT_OR_NORMAL) { - // Test if enterprise is installed to search oid in text or oid field in ttrap. - if ($config['enterprise_installed']) { - $whereSubquery .= ' AND ( - (alerted = 0 AND (severity = '.EVENT_CRIT_NORMAL.' OR severity = '.EVENT_CRIT_CRITICAL.')) OR - (alerted = 1 AND (priority = '.EVENT_CRIT_NORMAL.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; - } else { - $whereSubquery .= ' AND ( - (alerted = 1 AND (priority = '.EVENT_CRIT_NORMAL.' OR priority = '.EVENT_CRIT_CRITICAL.')))'; - } - } -} - -if ($filter_status != -1) { - $whereSubquery .= ' AND status = '.$filter_status; -} - -if ($trap_type == 5) { - $whereSubquery .= ' AND type NOT IN (0, 1, 2, 3, 4)'; -} else if ($trap_type != -1) { - $whereSubquery .= ' AND type = '.$trap_type; -} - -// Disable this feature (time will decide if temporarily) in Oracle cause the group by is very confictive. -if ($group_by && $config['dbtype'] != 'oracle') { - $where_without_group = $whereSubquery; - $whereSubquery .= ' GROUP BY source,oid'; -} - -switch ($config['dbtype']) { - case 'mysql': - $sql = sprintf($sql, $whereSubquery, $offset, $pagination); - break; - - case 'postgresql': - $sql = sprintf($sql, $whereSubquery, $pagination, $offset); - break; - - case 'oracle': - $set = []; - $set['limit'] = $pagination; - $set['offset'] = $offset; - $sql = sprintf($sql, $whereSubquery); - $sql = oracle_recode_query($sql, $set); - break; - - default: - // Default. - break; -} - -$sql_all = sprintf($sql_all, $whereSubquery); -$sql_count = sprintf($sql_count, $whereSubquery); - -$table = new stdClass(); -$table->width = '100%'; -$table->cellpadding = 0; -$table->cellspacing = 0; -$table->class = 'databox filters'; -$table->size = []; -$table->size[0] = '120px'; -$table->data = []; - -// Alert status select. -$table->data[1][0] = ''.__('Alert').''; -$table->data[1][1] = html_print_select( - $alerted, - 'filter_fired', - $filter_fired, - '', - __('All'), - '-1', - true -); - -// Block size for pagination select. -$table->data[2][0] = ''.__('Block size for pagination').''; -$paginations[25] = 25; -$paginations[50] = 50; -$paginations[100] = 100; -$paginations[200] = 200; -$paginations[500] = 500; -$table->data[2][1] = html_print_select( - $paginations, - 'pagination', - $pagination, - '', - __('Default'), - $config['block_size'], - true -); - -// Severity select. -$table->data[1][2] = ''.__('Severity').''; -$table->data[1][3] = html_print_select( - $severities, - 'filter_severity', - $filter_severity, - '', - __('All'), - -1, - true -); - -// Status. -$table->data[3][0] = ''.__('Status').''; - -$status_array[-1] = __('All'); -$status_array[0] = __('Not validated'); -$status_array[1] = __('Validated'); -$table->data[3][1] = html_print_select( - $status_array, - 'filter_status', - $filter_status, - '', - '', - '', - true -); - -// Free search (search by all alphanumeric fields). -$table->data[2][3] = ''.__('Free search').''.ui_print_help_tip( - __( - 'Search by any alphanumeric field in the trap. - REMEMBER trap sources need to be searched by IP Address' - ), - true -); -$table->data[2][4] = html_print_input_text( - 'free_search_string', - $free_search_string, - '', - 40, - 0, - true -); - -$table->data[4][0] = ''.__('Max. hours old').''; -$table->data[4][1] = html_print_input( - [ - 'type' => 'number', - 'name' => 'hours_ago', - 'value' => $hours_ago, - 'step' => 1, - 'return' => true, - ] -); - -// Type filter (ColdStart, WarmStart, LinkDown, LinkUp, authenticationFailure, Other). -$table->data[4][3] = ''.__('Trap type').''.ui_print_help_tip(__('Search by trap type'), true); -$trap_types = [ - -1 => __('None'), - 0 => __('Cold start (0)'), - 1 => __('Warm start (1)'), - 2 => __('Link down (2)'), - 3 => __('Link up (3)'), - 4 => __('Authentication failure (4)'), - 5 => __('Other'), -]; -$table->data[4][4] = html_print_select( - $trap_types, - 'trap_type', - $trap_type, - '', - '', - '', - true, - false, - false -); - -// Disable this feature (time will decide if temporarily) in Oracle cause the group by is very confictive. -if ($config['dbtype'] != 'oracle') { - $table->data[3][3] = ''.__('Group by Enterprise String/IP').''; - $table->data[3][4] = __('Yes').' '.html_print_radio_button('group_by', 1, '', $group_by, true).'  '; - $table->data[3][4] .= __('No').' '.html_print_radio_button('group_by', 0, '', $group_by, true); -} - -$filter = '
'; -$filter .= html_print_table($table, true); -$filter .= '
'; -$filter .= html_print_submit_button(__('Update'), 'search', false, 'class="sub upd"', true); -$filter .= '
'; -$filter .= '
'; - -$filter_resume = []; -$filter_resume['filter_fired'] = $alerted[$filter_fired]; -$filter_resume['filter_severity'] = $severities[$filter_severity]; -$filter_resume['pagination'] = $paginations[$pagination]; -$filter_resume['free_search_string'] = $free_search_string; -$filter_resume['filter_status'] = $status_array[$filter_status]; -$filter_resume['group_by'] = $group_by; -$filter_resume['hours_ago'] = $hours_ago; -$filter_resume['trap_type'] = $trap_types[$trap_type]; - -$traps = db_get_all_rows_sql($sql, true); -$trapcount = (int) db_get_value_sql($sql_count, false, true); - -// Re-sort traps by timestamp if history db is enabled. -if ($config['history_db_enabled'] == 1) { - usort( - $traps, - function ($a, $b) { - return strtotime($a['timestamp']) < strtotime($b['timestamp']); - } - ); -} - -// No traps. -if (empty($traps)) { - // Header - ui_print_standard_header( - __('SNMP Console'), - 'images/op_snmp.png', - false, - 'snmp_console', - false, - [ - $list, - $statistics, - ], - [ - [ - 'link' => '', - 'label' => __('Monitoring'), - ], - [ - 'link' => '', - 'label' => __('SNMP'), - ], - ] - ); - - $sql2 = 'SELECT * - FROM ttrap - WHERE ( - `source` IN ('.implode(',', $address_by_user_groups).") OR - `source`='' OR - `source` NOT IN (".implode(',', $all_address_agents).') - ) - AND status = 0 - ORDER BY timestamp DESC'; - $traps2 = db_get_all_rows_sql($sql2); - - if (!empty($traps2)) { - ui_toggle($filter, __('Toggle filter(s)')); - - print_snmp_tags_active_filters($filter_resume); - - ui_print_info_message(['no_close' => true, 'message' => __('There are no SNMP traps in database that contains this filter') ]); - } else { - ui_print_info_message(['no_close' => true, 'message' => __('There are no SNMP traps in database') ]); + echo '[SnmpConsole]'.$e->getMessage(); } + // Stop this execution, but continue 'globally'. return; -} else { - if ($config['pure']) { - echo '
'; +} - echo ''; - - echo '
'; - - ui_require_css_file('pandora_enterprise', ENTERPRISE_DIR.'/include/styles/'); - ui_require_css_file('pandora_dashboard', ENTERPRISE_DIR.'/include/styles/'); - ui_require_css_file('cluetip', 'include/styles/js/'); - - ui_require_jquery_file('countdown'); - ui_require_javascript_file('pandora_dashboard', ENTERPRISE_DIR.'/include/javascript/'); - ui_require_javascript_file('wz_jsgraphics'); - ui_require_javascript_file('pandora_visual_console'); + if (method_exists($controller, $method) === true) { + if ($controller->ajaxMethod($method) === true) { + $controller->{$method}(); + } else { + $controller->error('Unavailable method.'); + } } else { - // Header - ui_print_standard_header( - __('SNMP Console'), - 'images/op_snmp.png', - false, - '', - false, - [ - $fullscreen, - $list, - $statistics, - ], - [ - [ - 'link' => '', - 'label' => __('Monitoring'), - ], - [ - 'link' => '', - 'label' => __('SNMP'), - ], - ] - ); + $controller->error('Method not found. ['.$method.']'); } -} -ui_toggle($filter, __('Toggle filter(s)')); -unset($table); - -print_snmp_tags_active_filters($filter_resume); - -if (($config['dbtype'] == 'oracle') && ($traps !== false)) { - $traps_size = count($traps); - for ($i = 0; $i < $traps_size; $i++) { - unset($traps[$i]['rnum']); - } -} - -$url_snmp = 'index.php?sec=snmpconsole&sec2=operation/snmpconsole/snmp_view'; -$url_snmp .= '&filter_severity='.$filter_severity.'&filter_fired='.$filter_fired; -$url_snmp .= '&filter_status='.$filter_status.'&refresh='.((int) get_parameter('refresh', 0)); -$url_snmp .= '&pure='.$config['pure'].'&trap_type='.$trap_type; -$url_snmp .= '&group_by='.$group_by.'&free_search_string='.$free_search_string; -$url_snmp .= '&hours_ago='.$hours_ago; - -$urlPagination = $url_snmp.'&pagination='.$pagination.'&offset='.$offset; - -ui_pagination($trapcount, $urlPagination, $offset, $pagination); - -echo '
'; - -$table = new StdClass(); -$table->cellpadding = 0; -$table->cellspacing = 0; -$table->width = '100%'; -$table->class = 'databox data'; -$table->head = []; -$table->size = []; -$table->data = []; -$table->align = []; -$table->headstyle = []; - -$table->head[0] = __('Status'); -$table->align[0] = 'center'; -$table->size[0] = '5%'; -$table->headstyle[0] = 'text-align: center'; - -$table->head[1] = __('SNMP Agent'); -$table->align[1] = 'center'; -$table->size[1] = '15%'; -$table->headstyle[1] = 'text-align: center'; - -$table->head[2] = __('Enterprise String'); -$table->align[2] = 'center'; -$table->size[2] = '18%'; -$table->headstyle[2] = 'text-align: center'; - -if ($group_by) { - $table->head[3] = __('Count'); - $table->align[3] = 'center'; - $table->size[3] = '5%'; - $table->headstyle[3] = 'text-align: center'; -} - -$table->head[4] = __('Trap subtype'); -$table->align[4] = 'center'; -$table->size[4] = '10%'; -$table->headstyle[4] = 'text-align: center'; - -$table->head[5] = __('User ID'); -$table->align[5] = 'center'; -$table->size[5] = '10%'; -$table->headstyle[5] = 'text-align: center'; - -$table->head[6] = __('Timestamp'); -$table->align[6] = 'center'; -$table->size[6] = '10%'; -$table->headstyle[6] = 'text-align: center'; - -$table->head[7] = __('Alert'); -$table->align[7] = 'center'; -$table->size[7] = '5%'; -$table->headstyle[7] = 'text-align: center'; - -$table->head[8] = __('Action'); -$table->align[8] = 'center'; -$table->size[8] = '10%'; -$table->headstyle[8] = 'min-width: 125px;text-align: center'; - -$table->head[9] = html_print_checkbox_extended( - 'allbox', - 1, - false, - false, - 'javascript:CheckAll();', - 'class="chk" title="'.__('All').'"', - true -); -$table->align[9] = 'center'; -$table->size[9] = '5%'; -$table->headstyle[9] = 'text-align: center'; - -$table->style[8] = 'background: #F3F3F3; color: #111 !important;'; - -// Skip offset records. -$idx = 0; -if ($traps !== false) { - foreach ($traps as $trap) { - $data = []; - if (empty($trap['description'])) { - $trap['description'] = ''; - } - - $severity = enterprise_hook('get_severity', [$trap]); - if ($severity === ENTERPRISE_NOT_HOOK) { - $severity = $trap['alerted'] == 1 ? $trap['priority'] : 1; - } - - // Status. - if ($trap['status'] == 0) { - $data[0] = html_print_image( - 'images/pixel_red.png', - true, - [ - 'title' => __('Not validated'), - 'width' => '20', - 'height' => '20', - ] - ); - } else { - $data[0] = html_print_image( - 'images/pixel_green.png', - true, - [ - 'title' => __('Validated'), - 'width' => '20', - 'height' => '20', - ] - ); - } - - // Agent matching source address. - $table->cellclass[$idx][1] = get_priority_class($severity); - $agent = agents_get_agent_with_ip($trap['source']); - if ($agent === false) { - if (! check_acl($config['id_user'], 0, 'AR')) { - continue; - } - - $data[1] = ''.$trap['source'].''; - } else { - if (! check_acl($config['id_user'], $agent['id_grupo'], 'AR')) { - continue; - } - - $data[1] = ''; - $data[1] .= ''.$agent['alias'].ui_print_help_tip($trap['source'], true, 'images/tip-blanco.png'); - ''; - } - - // OID. - $table->cellclass[$idx][2] = get_priority_class($severity); - if (! empty($trap['text'])) { - $enterprise_string = $trap['text']; - } else if (! empty($trap['oid'])) { - $enterprise_string = $trap['oid']; - } else { - $enterprise_string = __('N/A'); - } - - $data[2] = ''.$enterprise_string.''; - - // Count. - if ($group_by) { - $sql = "SELECT * FROM ttrap WHERE 1=1 - $where_without_group - AND oid='".$trap['oid']."' - AND source='".$trap['source']."'"; - $group_traps = db_get_all_rows_sql($sql); - $count_group_traps = count($group_traps); - $table->cellclass[$idx][3] = get_priority_class($severity); - $data[3] = ''.$count_group_traps.''; - } - - // Value. - $table->cellclass[$idx][4] = get_priority_class($severity); - if (empty($trap['value'])) { - $data[4] = __('N/A'); - } else { - $data[4] = ui_print_truncate_text($trap['value'], GENERIC_SIZE_TEXT, false); - } - - // User. - $table->cellclass[$idx][5] = get_priority_class($severity); - if (!empty($trap['status'])) { - $data[5] = ''.substr($trap['id_usuario'], 0, 8).''; - if (!empty($trap['id_usuario'])) { - $data[5] .= ui_print_help_tip(get_user_fullname($trap['id_usuario']), true); - } - } else { - $data[5] = '--'; - } - - // Timestamp. - $table->cellclass[$idx][6] = get_priority_class($severity); - $data[6] = ''; - $data[6] .= ui_print_timestamp($trap['timestamp'], true); - $data[6] .= ''; - - // Use alert severity if fired. - if (!empty($trap['alerted'])) { - $data[7] = html_print_image('images/pixel_yellow.png', true, ['width' => '20', 'height' => '20', 'border' => '0', 'title' => __('Alert fired')]); - } else { - $data[7] = html_print_image('images/pixel_gray.png', true, ['width' => '20', 'height' => '20', 'border' => '0', 'title' => __('Alert not fired')]); - } - - // Actions. - $data[8] = ''; - - if (empty($trap['status'])) { - $data[8] .= ''.html_print_image('images/ok.png', true, ['border' => '0', 'title' => __('Validate')]).' '; - } - - if ($trap['source'] == '') { - $is_admin = db_get_value('is_admin', 'tusuario', 'id_user', $config['id_user']); - if ($is_admin) { - $data[8] .= ''.html_print_image( - 'images/cross.png', - true, - [ - 'border' => '0', - 'title' => __('Delete'), - 'class' => 'invert_filter', - ] - ).' '; - } - } else { - $agent_trap_group = db_get_value('id_grupo', 'tagente', 'nombre', $trap['source']); - - $data[8] .= ''.html_print_image( - 'images/cross.png', - true, - [ - 'border' => '0', - 'title' => __('Delete'), - 'class' => 'invert_filter', - ] - ).' '; - } - - $data[8] .= ''.html_print_image( - 'images/eye.png', - true, - [ - 'alt' => __('Show more'), - 'title' => __('Show more'), - 'class' => 'invert_filter', - ] - ).''; - $data[8] .= enterprise_hook('editor_link', [$trap]); - - - $data[9] = html_print_checkbox_extended('snmptrapid[]', $trap['id_trap'], false, false, '', 'class="chk"', true); - - array_push($table->data, $data); - - // Hiden file for description. - $string = ' - - - - - - - - '; - - if ($trap['description'] != '') { - $string .= ' - - - '; - } - - if ($trap['type'] != '') { - $trap_types = [ - -1 => __('None'), - 0 => __('Cold start (0)'), - 1 => __('Warm start (1)'), - 2 => __('Link down (2)'), - 3 => __('Link up (3)'), - 4 => __('Authentication failure (4)'), - 5 => __('Other'), - ]; - - switch ($trap['type']) { - case -1: - $desc_trap_type = __('None'); - break; - - case 0: - $desc_trap_type = __('Cold start (0)'); - break; - - case 1: - $desc_trap_type = __('Warm start (1)'); - break; - - case 2: - $desc_trap_type = __('Link down (2)'); - break; - - case 3: - $desc_trap_type = __('Link up (3)'); - break; - - case 4: - $desc_trap_type = __('Authentication failure (4)'); - break; - - default: - $desc_trap_type = __('Other'); - break; - } - - $string .= ''; - } - - if ($group_by) { - $sql = "SELECT * FROM ttrap WHERE 1=1 - $where_without_group - AND oid='".$trap['oid']."' - AND source='".$trap['source']."'"; - $group_traps = db_get_all_rows_sql($sql); - $count_group_traps = count($group_traps); - - $sql = "SELECT timestamp FROM ttrap WHERE 1=1 - $where_without_group - AND oid='".$trap['oid']."' - AND source='".$trap['source']."' - ORDER BY `timestamp` DESC"; - $last_trap = db_get_value_sql($sql); - - $sql = "SELECT timestamp FROM ttrap WHERE 1=1 - $where_without_group - AND oid='".$trap['oid']."' - AND source='".$trap['source']."' - ORDER BY `timestamp` ASC"; - $first_trap = db_get_value_sql($sql); - - $string .= ' - - - '; - $string .= ' - - - '; - $string .= ' - - - '; - } - - $string .= '
'.''.__('Variable bindings:').''; - - if ($group_by) { - $new_url = 'index.php?sec=snmpconsole&sec2=operation/snmpconsole/snmp_view'; - $new_url .= '&filter_severity='.$filter_severity; - $new_url .= '&filter_fired='.$filter_fired; - $new_url .= '&filter_status='.$filter_status; - $new_url .= '&refresh='.((int) get_parameter('refresh', 0)); - $new_url .= '&pure='.$config['pure']; - $new_url .= '&group_by=0&free_search_string='.$free_search_string; - $new_url .= '&hours_ago='.$hours_ago; - - $string .= ''.__('See more details').''; - } else { - // Print binding vars separately. - $binding_vars = explode("\t", $trap['oid_custom']); - foreach ($binding_vars as $var) { - $string .= $var.'
'; - } - } - - $string .= '
'.''.__('Enterprise String:').' '.$trap['oid'].'
'.''.__('Description:').''.$trap['description'].'
'.__('Trap type:').''.$desc_trap_type.'
'.''.__('Count:').''.$count_group_traps.'
'.''.__('First trap:').''.$first_trap.'
'.''.__('Last trap:').''.$last_trap.'
'; - - $data = [$string]; - // $data = array($trap['description']); - $idx++; - $table->rowclass[$idx] = 'trap_info_'.$trap['id_trap']; - $table->colspan[$idx][0] = 10; - $table->rowstyle[$idx] = 'display: none;'; - array_push($table->data, $data); - - $idx++; - } -} - -// No matching traps. -if ($idx == 0) { - echo '
'.__('No matching traps found').'
'; + // Stop any execution. + exit; } else { - html_print_table($table); + // Run. + $controller->run(); } - -unset($table); - -echo '
'; - -html_print_submit_button(__('Validate'), 'updatebt', false, 'class="sub ok"'); - -echo ' '; -html_print_submit_button(__('Delete'), 'deletebt', false, 'class="sub delete" onClick="javascript:return confirm(\''.__('Are you sure?').'\')"'); - -echo '
'; - - -echo '
'; -echo '

'.__('Status').'

'; -echo html_print_image( - 'images/pixel_green.png', - true, - [ - 'width' => '20', - 'height' => '20', - ] -).' - '.__('Validated'); -echo '
'; -echo html_print_image( - 'images/pixel_red.png', - true, - [ - 'width' => '20', - 'height' => '20', - ] -).' - '.__('Not validated'); -echo '
'; -echo '
'; -echo '

'.__('Alert').'

'; -echo html_print_image( - 'images/pixel_yellow.png', - true, - [ - 'width' => '20', - 'height' => '20', - ] -).' - '.__('Fired'); -echo '
'; -echo html_print_image( - 'images/pixel_gray.png', - true, - [ - 'width' => '20', - 'height' => '20', - ] -).' - '.__('Not fired'); -echo '
'; -echo '
'; -echo '

'.__('Action').'

'; -echo html_print_image('images/ok.png', true).' - '.__('Validate'); -echo '
'; -echo html_print_image('images/cross.png', true, ['class' => 'invert_filter']).' - '.__('Delete'); -echo '
'; -echo '
'; -echo '

'.__('Legend').'

'; -foreach (get_priorities() as $num => $name) { - echo ''.$name.''; - echo '
'; -} - -echo '
'; -echo '
 
'; - -ui_include_time_picker(); -?> - - \ No newline at end of file diff --git a/pandora_console/operation/users/user_edit.php b/pandora_console/operation/users/user_edit.php index 264b892d2c..c1f2e1fee2 100644 --- a/pandora_console/operation/users/user_edit.php +++ b/pandora_console/operation/users/user_edit.php @@ -401,7 +401,7 @@ if (!$meta) { // User only can change skins if has more than one group. if (function_exists('skins_print_select')) { if (count($usr_groups) > 1) { - $skin = '

'.__('Skin').':

'; + $skin = '

'.__('Theme').':

'; $skin .= skins_print_select($id_usr, 'skin', $user_info['id_skin'], '', __('None'), 0, true).'
'; } } @@ -410,7 +410,7 @@ if (!$meta) { // User only can change skins if has more than one group. if (function_exists('skins_print_select')) { if (count($usr_groups) > 1) { - $skin = '

'.__('Skin').ui_print_help_tip( + $skin = '

'.__('Theme').ui_print_help_tip( __('This change will only apply to nodes'), true ).'

'; diff --git a/pandora_console/pandora_console.redhat.spec b/pandora_console/pandora_console.redhat.spec index 14cfc5c399..2f5db066ae 100644 --- a/pandora_console/pandora_console.redhat.spec +++ b/pandora_console/pandora_console.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_console %define version 7.0NG.767 -%define release 221216 +%define release 230102 # 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 index 5ac0296d06..6ae68d552b 100644 --- a/pandora_console/pandora_console.rhel7.spec +++ b/pandora_console/pandora_console.rhel7.spec @@ -3,7 +3,7 @@ # %define name pandorafms_console %define version 7.0NG.767 -%define release 221216 +%define release 230102 # User and Group under which Apache is running %define httpd_name httpd diff --git a/pandora_console/pandora_console.spec b/pandora_console/pandora_console.spec index 2fbe1c7a85..6dd06231dc 100644 --- a/pandora_console/pandora_console.spec +++ b/pandora_console/pandora_console.spec @@ -3,7 +3,7 @@ # %define name pandorafms_console %define version 7.0NG.767 -%define release 221216 +%define release 230102 %define httpd_name httpd # User and Group under which Apache is running %define httpd_name apache2 diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql index fd51f8f529..cc7cea5dff 100644 --- a/pandora_console/pandoradb.sql +++ b/pandora_console/pandoradb.sql @@ -1275,7 +1275,7 @@ CREATE TABLE IF NOT EXISTS `tusuario` ( `firstname` VARCHAR(255) NOT NULL, `lastname` VARCHAR(255) NOT NULL, `middlename` VARCHAR(255) NOT NULL, - `password` VARCHAR(45) DEFAULT NULL, + `password` VARCHAR(60) DEFAULT NULL, `comments` VARCHAR(200) DEFAULT NULL, `last_connect` BIGINT NOT NULL DEFAULT 0, `registered` BIGINT NOT NULL DEFAULT 0, diff --git a/pandora_console/pandoradb_data.sql b/pandora_console/pandoradb_data.sql index 8e00936d8a..c962b6f3ff 100644 --- a/pandora_console/pandoradb_data.sql +++ b/pandora_console/pandoradb_data.sql @@ -112,10 +112,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', 59), +('MR', 60), ('identification_reminder', 1), ('identification_reminder_timestamp', 0), -('current_package', 767), +('current_package', 768), ('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.00097656250000":"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'), @@ -146,7 +146,7 @@ INSERT INTO `tconfig` (`token`, `value`) VALUES ('gotty', '/usr/bin/gotty'), ('custom_module_units', '{"bytes":"bytes","entries":"entries","files":"files","hits":"hits","sessions":"sessions","users":"users","ºC":"ºC","ºF":"ºF"}'), ('server_unique_identifier', replace(uuid(),'-','')), -('lts_name', 'Hope'); +('lts_name', ''); UNLOCK TABLES; -- @@ -301,28 +301,28 @@ INSERT INTO `ttipo_modulo` VALUES -- INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (1,1,'CPU','CPU','/usr/bin/perl','Model;Company;Speed','IyEvdXNyL2Jpbi9wZXJsDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBwYW5kb3JhX2NwdS5wbA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgQ29weXJpZ2h0IChjKSAyMDA4IFJhbW9uIE5vdm9hLCBybm92b2FAYXJ0aWNhLmVzDQojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MDQojDQojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3INCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4NCiMNCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsDQojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mDQojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUNCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4NCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUNCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KdXNlIHN0cmljdDsNCnVzZSB3YXJuaW5nczsNCg0KIyBDaGVjayBmb3Igc3NoDQpteSAkc3NoX2NsaWVudCA9ICJzc2giOw0KaWYgKHN5c3RlbSgiJHNzaF9jbGllbnQgLXYgPiAvZGV2L251bGwgMj4mMSIpID4+IDggIT0gMjU1KSB7DQoJcHJpbnQgIltlcnJvcl0gJHNzaF9jbGllbnQgbm90IGZvdW5kLlxuIjsNCglleGl0IDE7DQp9DQoNCmlmICgkI0FSR1YgPCAxKSB7DQoJcHJpbnQgIlVzYWdlOiAkMCA8dGFyZ2V0IGlwPiA8dXNlcm5hbWU+XG4iOw0KCWV4aXQgMTsNCn0NCg0KbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOw0KbXkgJHVzZXJuYW1lID0gJEFSR1ZbMV07DQoNCiMgUmV0cmlldmUgQ1BVIGRhdGENCm15ICgkbW9kZWwsICR2ZW5kb3IsICRzcGVlZCk7DQpteSBAY3B1X2luZm8gPSBgJHNzaF9jbGllbnQgJHVzZXJuYW1lXEAkdGFyZ2V0X2lwICJjYXQgL3Byb2MvY3B1aW5mbyJgOw0KZm9yZWFjaCBteSAkbGluZSAoQGNwdV9pbmZvKSB7DQoJaWYgKCRsaW5lID1+IC9tb2RlbCBuYW1lXHMrOlxzKyguKikvKSB7DQoJCSRtb2RlbCA9ICQxOw0KCX0gZWxzaWYgKCRsaW5lID1+IC92ZW5kb3JfaWRccys6XHMrKC4qKS8pIHsNCgkJJHZlbmRvciA9ICQxOw0KCX0gZWxzaWYgKCRsaW5lID1+IC9jcHUgTUh6XHMrOlxzKyguKikvKSB7DQoJCSRzcGVlZCA9ICQxOw0KCX0NCn0NCiANCnJldHVybiAxIGlmICgkbW9kZWwgZXEgJycgfHwgJHZlbmRvciBlcSAnJyB8fCAkc3BlZWQgZXEgJycpOw0KcHJpbnQgIiRtb2RlbDskdmVuZG9yOyIgLiBzcHJpbnRmICgiJS4wZiIsICRzcGVlZCkgLiAiIE1IelxuIjsNCg0KZXhpdCAwOw0K',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (2,9,'CPU','CPU','/usr/bin/perl','Name;Speed;Description','IyEvdXNyL2Jpbi9wZXJsDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBwYW5kb3JhX2NwdS5wbA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgQ29weXJpZ2h0IChjKSAyMDA4IFJhbW9uIE5vdm9hLCBybm92b2FAYXJ0aWNhLmVzDQojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MDQojDQojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3INCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4NCiMNCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsDQojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mDQojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUNCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4NCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUNCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KdXNlIHN0cmljdDsNCnVzZSB3YXJuaW5nczsNCg0KdXNlIEZpbGU6OkJhc2VuYW1lOw0KdXNlIEhUTUw6OkVudGl0aWVzICgpOw0KDQojIENoZWNrIGZvciB3bWljDQpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsNCmlmIChzeXN0ZW0oIiR3bWlfY2xpZW50ID4gL2Rldi9udWxsIDI+JjEiKSAhPSAyNTYpIHsNCglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOw0KCWV4aXQgMTsNCn0NCg0KaWYgKCQjQVJHViAhPSAyKSB7DQoJcHJpbnQgIlVzYWdlOiAkMCA8dGFyZ2V0IGlwPiA8dXNlcm5hbWU+IDxwYXNzd29yZD5cbiI7DQoJZXhpdCAxOw0KfQ0KDQpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07DQpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsNCm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOw0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQ0KIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0Kc3ViIHJ1bl9xdWVyeSB7DQoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOw0KCW15IEByZXN1bHQgPSB7fTsNCg0KCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsNCg0KCSMgUnVuIHRoZSBEQ09NL1dNSSBjbGllbnQNCglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7DQoNCglteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsNCgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMNCglpZiAoJCNsaW5lcyA8IDIpIHsNCgkJZXhpdCAxOw0KCX0NCg0KCSMgRHJvcCB0aGUgaGVhZGVyDQoJc2hpZnQgKEBsaW5lcyk7DQoJDQoJIyBHZXQgY29sdW1uIG5hbWVzDQoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsNCg0KCSMgR2V0IHJvdyBkYXRhDQoJbXkgJGlkeCA9IDA7DQoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsNCg0KCQkjIENoZWNrIGZvciBlcnJvcnMNCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7DQoJCQlleGl0IDE7DQoJCX0NCg0KCQkjIEJsYWNrIGxpc3QNCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7DQoJCQluZXh0Ow0KCQl9DQoNCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOw0KCQlmb3IgKG15ICRqID0gMDsgJGogPD0gJCNjb2x1bW5fbmFtZXM7ICRqKyspIHsNCgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7DQoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOw0KCQkJfQ0KCQkJZWxzZSB7DQoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOw0KCQkJfQ0KCQl9DQoJCQ0KCQkkaWR4Kys7DQoJfQ0KCQ0KCXJldHVybiBAcmVzdWx0Ow0KfQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIA0KIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkNCiMgUHJpbnRzIGEgbW9kdWxlZGF0YSBYTUwgdGFnLiAkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSBhbmQNCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24NCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5Lg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCnN1YiBwcmludF9tb2R1bGVkYXRhIHsNCglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsNCglteSAkbW9kdWxlX2RhdGEgPSAkX1sxXTsNCglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07DQoJbXkgQGRhdGEgPSBAeyRfWzNdfTsNCglteSAkcmVzdWx0Ow0KDQoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsNCgkJDQoJCSRyZXN1bHQgPSAnJzsNCg0KCQkjIEl0ZW0NCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgew0KCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAiOyI7DQoJCX0NCg0KCQkjIERhdGENCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9KSkgew0KCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAiIE1oejsiOw0KCQl9DQoNCgkJIyBEZXNjcmlwdGlvbg0KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgew0KCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259IC4gIjsiOw0KCQl9DQoNCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQ0KCX0NCn0NCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgTWFpbg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE5hbWUsIE1heENsb2NrU3BlZWQsIERlc2NyaXB0aW9uIEZST00gV2luMzJfUHJvY2Vzc29yIik7DQpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJNYXhDbG9ja1NwZWVkIiwgIkRlc2NyaXB0aW9uIiwgXEByZXN1bHQpOw0KZXhpdCAwOw0K',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (2,9,'CPU','CPU','/usr/bin/perl','Name;Speed;Description','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9jcHUucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAiOyI7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gIiBNaHo7IjsKCQl9CgoJCSMgRGVzY3JpcHRpb24KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0gLiAiOyI7CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE5hbWUsIE1heENsb2NrU3BlZWQsIERlc2NyaXB0aW9uIEZST00gV2luMzJfUHJvY2Vzc29yIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIk1heENsb2NrU3BlZWQiLCAiRGVzY3JpcHRpb24iLCBcQHJlc3VsdCk7CmV4aXQgMDsK',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (3,1,'RAM','Memory modules','','Model;Size','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (4,9,'RAM','Memory modules','/usr/bin/perl','Slot;Size;Speed','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9yYW0ucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDggQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnVzZSBzdHJpY3Q7CnVzZSB3YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgpOwoKIyBDaGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgiJHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gc3ByaW50ZigiJS4xZiIsICRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSAvIDEwNDg1NzYpIC4gIiBNQnl0ZXM7IjsKCQl9CgoJCSMgRGVzY3JpcHRpb24KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn07CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIERldmljZUxvY2F0b3IsIENhcGFjaXR5LCBTcGVlZCBGUk9NIFdpbjMyX1BoeXNpY2FsTWVtb3J5Iik7CnByaW50X21vZHVsZWRhdGEgKCJEZXZpY2VMb2NhdG9yIiwgIkNhcGFjaXR5IiwgIlNwZWVkIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (4,9,'RAM','Memory modules','/usr/bin/perl','Slot;Size;Speed','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9yYW0ucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gc3ByaW50ZigiJS4xZiIsICRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSAvIDEwNDg1NzYpIC4gIiBNQnl0ZXM7IjsKCQl9CgoJCSMgRGVzY3JpcHRpb24KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn07CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIFRhZywgQ2FwYWNpdHksIE5hbWUgRlJPTSBXaW4zMl9QaHlzaWNhbE1lbW9yeSIpOwpwcmludF9tb2R1bGVkYXRhICgiVGFnIiwgIkNhcGFjaXR5IiwgIk5hbWUiLCBcQHJlc3VsdCk7CmV4aXQgMDsK',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (5,1,'Video','Video cards','','Controller;Model;Company','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (6,9,'Video','Video cards','/usr/bin/perl','Name;vRAM;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV92aWRlby5wbAojIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFy dGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2lj YXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3Ry aWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdl bmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUg Rm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGlu IHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJB TlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJ VFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdl bmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUg cmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25n IHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMg Rm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9u LCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNl IHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7Cgoj IENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21p X2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21p X2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXBy aW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhp dCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFd OwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9x dWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMg dGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBy dW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3 cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAk b3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdl dF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xu LywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVz IDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7 CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNo aWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRp ID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAo JGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0 CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29s dW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQj Y29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJ CQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7 CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJ CX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEs IAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1M IHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlv biBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwg dGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRh IHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07Cglt eSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkg JHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAn JzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJ CQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0 YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAu PSAnICcgLiBzcHJpbnRmKCIlLjFmIiwgJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC8gMTA0ODU3 NikgLiAiIE1CeXRlczsiOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVt ZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gJGVsZW1l bnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkK CX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVz dWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgQWRhcHRlclJBTSwgVmlkZW9Qcm9jZXNz b3IgRlJPTSBXaW4zMl9WaWRlb0NvbnRyb2xsZXIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIkNhcHRp b24iLCAiQWRhcHRlclJBTSIsICJWaWRlb1Byb2Nlc3NvciIsIFxAcmVzdWx0KTsKZXhpdCAwOwo=',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (6,9,'Video','Video cards','/usr/bin/perl','Name;vRAM;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV92aWRlby5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAnICcgLiBzcHJpbnRmKCIlLjFmIiwgJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC8gMTA0ODU3NikgLiAiIE1CeXRlczsiOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJyAnIC4gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgQWRhcHRlclJBTSwgVmlkZW9Qcm9jZXNzb3IgRlJPTSBXaW4zMl9WaWRlb0NvbnRyb2xsZXIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIkNhcHRpb24iLCAiQWRhcHRlclJBTSIsICJWaWRlb1Byb2Nlc3NvciIsIFxAcmVzdWx0KTsKZXhpdCAwOwo=',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (7,1,'NIC','Network Interface Cards','','Device;Model;Company;MACAddress','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (8,9,'NIC','Network Interface Cards','/usr/bin/perl','Caption;MACAddress;IPAddress','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9uaWMucGwKIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRp Y2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDggQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2Fz IFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmli dXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5l cmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZv dW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0 aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5U WTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZ IG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5l cmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJl Y2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3 aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZv dW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3Rvbiwg TUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnVzZSBzdHJpY3Q7CnVzZSB3 YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgpOwoKIyBD aGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgiJHdtaV9j bGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9j bGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7Cglwcmlu dCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQg MTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsK bXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVl cnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRo ZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVu X3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3Fs X3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91 dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRf aXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8s ICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8 IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJ CgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlm dCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9 IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRs aW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJ CWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVt biA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2Nv bHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJ JHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJ CQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9 CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAK IyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0 YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24g YXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRh Z3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7 CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkg JG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRy ZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7 CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJ JHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEK CQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0g JGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlm IChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQg Lj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQg LiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj CgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgTUFDQWRkcmVzcywgSVBB ZGRyZXNzIEZST00gV2luMzJfTmV0d29ya0FkYXB0ZXJDb25maWd1cmF0aW9uIik7CnByaW50X21v ZHVsZWRhdGEgKCJDYXB0aW9uIiwgIk1BQ0FkZHJlc3MiLCAiSVBBZGRyZXNzIiwgXEByZXN1bHQp OwpleGl0IDA7Cg==',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (8,9,'NIC','Network Interface Cards','/usr/bin/perl','Caption;MACAddress;IPAddress','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9uaWMucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgQ2FwdGlvbiwgTUFDQWRkcmVzcywgSVBBZGRyZXNzIEZST00gV2luMzJfTmV0d29ya0FkYXB0ZXJDb25maWd1cmF0aW9uIik7CnByaW50X21vZHVsZWRhdGEgKCJDYXB0aW9uIiwgIk1BQ0FkZHJlc3MiLCAiSVBBZGRyZXNzIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (9,1,'HD','Hard drives','','Type;Model;Size','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (10,9,'HD','Hard drives','/usr/bin/perl','Model;Size;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9oZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CiAgICAgICAgcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKICAgICAgICBleGl0IDE7Cn0KCmlmICgkI0FSR1YgIT0gMikgewogICAgICAgIHByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwogICAgICAgIGV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKICAgICAgICBteSAkd3FsX3F1ZXJ5ID0gJF9bMF07CiAgICAgICAgbXkgQHJlc3VsdCA9IHt9OwogICAgICAgICR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCiAgICAgICAgIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAogICAgICAgIG15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsKCiAgICAgICAgbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CiAgICAgICAgIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCiAgICAgICAgaWYgKCQjbGluZXMgPCAyKSB7CiAgICAgICAgICAgICAgICBleGl0IDE7CiAgICAgICAgfQoKICAgICAgICAjIERyb3AgdGhlIGhlYWRlcgogICAgICAgIHNoaWZ0IChAbGluZXMpOwoKICAgICAgICAjIEdldCBjb2x1bW4gbmFtZXMKICAgICAgICBteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOwoKICAgICAgICAjIEdldCByb3cgZGF0YQogICAgICAgIG15ICRpZHggPSAwOwogICAgICAgIGZvciAobXkgJGkgPSAwOyAkaSA8PSAkI2xpbmVzOyAkaSsrKSB7CgogICAgICAgICAgICAgICAgIyBDaGVjayBmb3IgZXJyb3JzCiAgICAgICAgICAgICAgICBpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKICAgICAgICAgICAgICAgICAgICAgICAgZXhpdCAxOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICMgQmxhY2sgbGlzdAogICAgICAgICAgICAgICAgaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIG5leHQ7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwogICAgICAgICAgICAgICAgZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRyZXN1bHRbJGlkeF0tPnskY29sdW1uX25hbWVzWyRqXX0gPSAkY29sdW1uWyRqXTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICRpZHgrKzsKICAgICAgICB9CgogICAgICAgIHJldHVybiBAcmVzdWx0Owp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBwcmludF9tb2R1bGVkYXRhICgkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSwKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CiAgICAgICAgbXkgJG1vZHVsZV9pdGVtID0gJF9bMF07CiAgICAgICAgbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CiAgICAgICAgbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwogICAgICAgIG15ICRtb2R1bGVfZXh0cmFfZGVzY3JpcHRpb24gPSAkX1szXTsKICAgICAgICBteSBAZGF0YSA9IEB7JF9bNF19OwogICAgICAgIG15ICRyZXN1bHQ7CgogICAgICAgIGZvcmVhY2ggbXkgJGVsZW1lbnQgKEBkYXRhKSB7CiAgICAgICAgICAgICAgICAkcmVzdWx0ID0gJyc7CgogICAgICAgICAgICAgICAgIyBJdGVtCiAgICAgICAgICAgICAgICBpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19IC4gJzsnOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICMgRGF0YQogICAgICAgICAgICAgICAgaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9KSkgewogICAgICAgICAgICAgICAgICAgICAgICAkcmVzdWx0IC49ICcgJyAuIHNwcmludGYoIiUuMWYiLCAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLyAxMDczNzQxODI0KSAuICIgR0J5dGVzOyI7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgIyBEZXNjcmlwdGlvbgogICAgICAgICAgICAgICAgaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAgJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGVsc2lmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9leHRyYV9kZXNjcmlwdGlvbn0pKXsKICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAgJGVsZW1lbnQtPnskbW9kdWxlX2V4dHJhX2Rlc2NyaXB0aW9ufTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBwcmludCAkcmVzdWx0IC4gIlxuIjsKICAgICAgICB9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUICogRlJPTSBXaW4zMl9EaXNrRHJpdmUiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIkNhcHRpb24iLCAiU2l6ZSIsICJTZXJpYWxOdW1iZXIiLCAiU2lnbmF0dXJlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (10,9,'HD','Hard drives','/usr/bin/perl','Model;Size;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9oZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAnICcgLiBzcHJpbnRmKCIlLjFmIiwgJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC8gMTA3Mzc0MTgyNCkgLiAiIEdCeXRlczsiOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJyAoJyAuICRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0gLiAiKSI7CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE1vZGVsLCBTaXplLCBTeXN0ZW1OYW1lIEZST00gV2luMzJfRGlza0RyaXZlIik7CnByaW50X21vZHVsZWRhdGEgKCJNb2RlbCIsICJTaXplIiwgIlN5c3RlbU5hbWUiLCBcQHJlc3VsdCk7CQpleGl0IDA7Cg==',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (11,1,'CDROM','CD-ROM drives','','Type;Model;Features','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (12,9,'CDROM','CD-ROM drives','/usr/bin/perl','Name;Description;Drive;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9jZHJvbS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKI3VzZSBzdHJpY3Q7CnVzZSB3YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgpOwoKIyBDaGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgiJHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewogICAgICAgIHByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7CiAgICAgICAgZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKICAgICAgICBwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKICAgICAgICBleGl0IDE7Cn0KCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsKbXkgJHVzZXJuYW1lID0gJEFSR1ZbMV07Cm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHJ1bl9xdWVyeSB7CiAgICAgICAgbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwogICAgICAgIG15IEByZXN1bHQgPSB7fTsKCiAgICAgICAgJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOwoKICAgICAgICAjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CiAgICAgICAgbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKICAgICAgICBteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsKICAgICAgICAjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKICAgICAgICBpZiAoJCNsaW5lcyA8IDIpIHsKICAgICAgICAgICAgICAgIGV4aXQgMTsKICAgICAgICB9CgogICAgICAgICMgRHJvcCB0aGUgaGVhZGVyCiAgICAgICAgc2hpZnQgKEBsaW5lcyk7CgogICAgICAgICMgR2V0IGNvbHVtbiBuYW1lcwogICAgICAgIG15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgogICAgICAgICMgR2V0IHJvdyBkYXRhCiAgICAgICAgbXkgJGlkeCA9IDA7CiAgICAgICAgZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCiAgICAgICAgICAgICAgICAjIENoZWNrIGZvciBlcnJvcnMKICAgICAgICAgICAgICAgIGlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewogICAgICAgICAgICAgICAgICAgICAgICBleGl0IDE7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgIyBCbGFjayBsaXN0CiAgICAgICAgICAgICAgICBpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKICAgICAgICAgICAgICAgICAgICAgICAgbmV4dDsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CiAgICAgICAgICAgICAgICBmb3IgKG15ICRqID0gMDsgJGogPD0gJCNjb2x1bW5fbmFtZXM7ICRqKyspIHsKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCEgZGVmaW5lZCgkY29sdW1uWyRqXSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgJGlkeCsrOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKICAgICAgICBteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKICAgICAgICBteSBAZGF0YSA9IEB7JF9bMV19OwogICAgICAgIG15ICRyZXN1bHQ7CgogICAgICAgICRyZXN1bHQgPSAnJzsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiwgQGRhdGEpCiMgUHJpbnRzIGEgbW9kdWxlZGF0YSBYTUwgdGFnLiAkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSBhbmQKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uIGFyZSB1c2VkIHRvIGluZGV4IHRoZSBpdGVtLCBkYXRhIGFuZCBkZXNjcmlwdGlvbgojIFhNTCB0YWdzIHJlc3BlY3RpdmVseS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHByaW50X21vZHVsZWRhdGEgewogICAgICAgIG15IEBtb2R1bGVfaGVhZGVycyA9IEB7JF9bMF19OwogICAgICAgIG15IEBkYXRhID0gQHskX1sxXX07CiAgICAgICAgbXkgJHJlc3VsdDsKCiAgICAgICAgZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKICAgICAgICAgICAgICAgICRyZXN1bHQgPSAnJzsKICAgICAgICAgICAgICAgIGZvcmVhY2ggbXkgJG1oZWFkZXIgKEBtb2R1bGVfaGVhZGVycykgewogICAgICAgICAgICAgICAgICAgICAgICBpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbWhlYWRlcn0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtaGVhZGVyfSAuICc7JzsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkcmVzdWx0IC49ICc7JzsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgY2hvcCgkcmVzdWx0KTsKICAgICAgICAgICAgICAgIHByaW50ICRyZXN1bHQgLiAiXG4iOwogICAgICAgIH0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ICA9IHJ1bl9xdWVyeSgiU0VMRUNUICogRlJPTSBXaW4zMl9DRFJPTURyaXZlIik7Cm15IEBoZWFkZXJzID0gKCJOYW1lIiwgIkRlc2NyaXB0aW9uIiwgIkRyaXZlIiwgIkRldmljZUlkIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycywgXEByZXN1bHQpOwpleGl0IDA7Cgo=',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (12,9,'CDROM','CD-ROM drives','/usr/bin/perl','Name;Description;Drive;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9jZHJvbS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBEZXNjcmlwdGlvbiwgRHJpdmUgRlJPTSBXaW4zMl9DRFJPTURyaXZlIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIkRlc2NyaXB0aW9uIiwgIkRyaXZlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (13,1,'Software','Installed software packages','','Name;Version;Description','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (14,9,'Software','Installed software packages','/usr/bin/perl','Name;Version','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zb2Z0d2FyZS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKICAgICAgICBteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKICAgICAgICBteSAkbW9kdWxlX2RhdGEgPSAkX1sxXTsKICAgICAgICBteSBAZGF0YSA9IEB7JF9bMl19OwogICAgICAgIG15ICRyZXN1bHQ7CgogICAgICAgIGZvcmVhY2ggbXkgJGVsZW1lbnQgKEBkYXRhKSB7CiAgICAgICAgICAgICAgICAkcmVzdWx0ID0gJyc7CiAgICAgICAgICAgICAgICAjIEl0ZW0KICAgICAgICAgICAgICAgIGlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgIyBEYXRhCiAgICAgICAgICAgICAgICBpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9OwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHByaW50ICRyZXN1bHQgLiAiXG4iOwogICAgICAgIH0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgVmVyc2lvbiBGUk9NIFdpbjMyX1Byb2R1Y3QiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIk5hbWUiLCAiVmVyc2lvbiIsIFxAcmVzdWx0KTsKZXhpdCAwOwoK',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (15,9,'Patches','Installed patches','/usr/bin/perl','HotFixID;Description;FixComments;ServicePackInEffect','IyEvdXNyL2Jpbi9wZXJsDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBwYW5kb3JhX3BhdGNoLnBsDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMNCiMgICAgICAgICAgIChjKSAyMDA4IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwNCiMNCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcg0KIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLg0KIw0KIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwNCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YNCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQ0KIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLg0KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQ0KIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQp1c2Ugc3RyaWN0Ow0KdXNlIHdhcm5pbmdzOw0KDQp1c2UgRmlsZTo6QmFzZW5hbWU7DQp1c2UgSFRNTDo6RW50aXRpZXMgKCk7DQoNCiMgQ2hlY2sgZm9yIHdtaWMNCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOw0KaWYgKHN5c3RlbSgiJHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1Nikgew0KCXByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7DQoJZXhpdCAxOw0KfQ0KDQppZiAoJCNBUkdWICE9IDIpIHsNCglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsNCglleGl0IDE7DQp9DQoNCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsNCm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOw0KbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07DQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpDQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpzdWIgcnVuX3F1ZXJ5IHsNCglteSAkd3FsX3F1ZXJ5ID0gJF9bMF07DQoJbXkgQHJlc3VsdCA9IHt9Ow0KDQoJJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOw0KDQoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudA0KCW15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsNCg0KCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOw0KCSMgSGVhZGVyLCBkZXNjcmlwdGlvbiwgcmVzdWx0cw0KCWlmICgkI2xpbmVzIDwgMikgew0KCQlleGl0IDE7DQoJfQ0KDQoJIyBEcm9wIHRoZSBoZWFkZXINCglzaGlmdCAoQGxpbmVzKTsNCgkNCgkjIEdldCBjb2x1bW4gbmFtZXMNCglteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOw0KDQoJIyBHZXQgcm93IGRhdGENCglteSAkaWR4ID0gMDsNCglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgew0KDQoJCSMgQ2hlY2sgZm9yIGVycm9ycw0KCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsNCgkJCWV4aXQgMTsNCgkJfQ0KDQoJCSMgQmxhY2sgbGlzdA0KCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsNCgkJCW5leHQ7DQoJCX0NCg0KCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7DQoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgew0KCQkJaWYgKCEgZGVmaW5lZCgkY29sdW1uWyRqXSkpIHsNCgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7DQoJCQl9DQoJCQllbHNlIHsNCgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07DQoJCQl9DQoJCX0NCgkJDQoJCSRpZHgrKzsNCgl9DQoJDQoJcmV0dXJuIEByZXN1bHQ7DQp9DQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIFNVQiBwcmludF9tb2R1bGVkYXRhICgkbW9kdWxlX2l0ZW0sICRtb2R1bGVfZGF0YSwgDQojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQ0KIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCANCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhbmQgJG1vZHVsZV9zZXJ2aWNlcGFjayBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSwgZGVzY3JpcHRpb24gYW5kIHNlcnZpY2VwYWNrDQojIFhNTCB0YWdzIHJlc3BlY3RpdmVseS4NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7DQoJbXkgJG1vZHVsZV9pdGVtID0gJF9bMF07DQoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07DQoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOw0KCW15ICRtb2R1bGVfc2VydmljZXBhY2sgPSAkX1szXTsNCglteSBAZGF0YSA9IEB7JF9bNF19Ow0KCW15ICRyZXN1bHQ7DQoNCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgew0KCQkNCgkJJHJlc3VsdCA9ICcnOw0KDQoJCSMgSXRlbQ0KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0pKSB7DQoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsNCgkJfQ0KDQoJCSMgRGF0YQ0KCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7DQoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSAuICc7JzsNCgkJfQ0KDQoJCSMgRGVzY3JpcHRpb24NCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsNCgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSAuICc7JzsNCgkJfQ0KDQoJCSMgU2VydmljZSBwYWNrDQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9zZXJ2aWNlcGFja30pKSB7DQoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9zZXJ2aWNlcGFja307DQoJCX0NCg0KCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJDQoJfQ0KfQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBNYWluDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgSG90Rml4SUQsIERlc2NyaXB0aW9uLCBGaXhDb21tZW50cywgU2VydmljZVBhY2tJbkVmZmVjdCBGUk9NIFdpbjMyX1F1aWNrRml4RW5naW5lZXJpbmciKTsNCnByaW50X21vZHVsZWRhdGEgKCJIb3RGaXhJRCIsICJEZXNjcmlwdGlvbiIsICJGaXhDb21tZW50cyIsICJTZXJ2aWNlUGFja0luRWZmZWN0IiwgXEByZXN1bHQpOw0KZXhpdCAwOw0K',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (14,9,'Software','Installed software packages','/usr/bin/perl','Name;Version','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zb2Z0d2FyZS5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX07CgkJfQoKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCA9IHJ1bl9xdWVyeSgiU0VMRUNUIE5hbWUsIFZlcnNpb24gRlJPTSBXaW4zMl9Qcm9kdWN0Iik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIlZlcnNpb24iLCAiIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (15,9,'Patches','Installed patches','/usr/bin/perl','HotFixID;Description;FixComments;ServicePackInEffect','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wYXRjaC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBIb3RGaXhJRCwgRGVzY3JpcHRpb24sIEZpeENvbW1lbnRzIEZST00gV2luMzJfUXVpY2tGaXhFbmdpbmVlcmluZyIpOwpwcmludF9tb2R1bGVkYXRhICgiSG90Rml4SUQiLCAiRGVzY3JpcHRpb24iLCAiRml4Q29tbWVudHMiLCBcQHJlc3VsdCk7CmV4aXQgMDsK',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (16,1,'Init services','Services programmed to lauch in Unix','','Service','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (17,9,'Init services','Windows services','/usr/bin/perl','Name;PathName;State','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zZXJ2aWNlcy5wbAoj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9h QGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xv Z2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRp c3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05V IEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdh cmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVk IGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdB UlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFC SUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05V IEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhh dmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFs b25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJl CiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9z dG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsK dXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7 CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIk d21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAk d21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsK CXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJ ZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdW WzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1 bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVy bnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1 YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoK CSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50Cglt eSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRh cmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQo L1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xp bmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5l cyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8s IHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15 ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlp ZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBs aXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBA Y29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9 ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7 CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxz ZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07 CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2Rh dGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEg WE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlw dGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBY TUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVk YXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07 CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJ bXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQg PSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkg ewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMg RGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3Vs dCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9u CgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJl c3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJl c3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBQYXRoTmFtZSwgU3Rh dGUgRlJPTSBXaW4zMl9TZXJ2aWNlIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIlBhdGhO YW1lIiwgIlN0YXRlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (17,9,'Init services','Windows services','/usr/bin/perl','Name;PathName;State','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9zZXJ2aWNlcy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOC0yMDIxIEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgp1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwoKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBQYXRoTmFtZSwgU3RhdGUgRlJPTSBXaW4zMl9TZXJ2aWNlIik7CnByaW50X21vZHVsZWRhdGEgKCJOYW1lIiwgIlBhdGhOYW1lIiwgIlN0YXRlIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (18,1,'File system','UNIX filesystem mounted on system','','Device;Free size;Total size;Mount point','',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (19,9,'File system','Disk drives','','Device;Total size;Free size;Mount point','',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (20,1,'Process','Process running on system','','Process output from ps','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (21,9,'Process','Process running on system','/usr/bin/perl','Name;PathName;State','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9jZXNzZXMucGwK IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3Zv YUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDggQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9s b2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVk aXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdO VSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3 YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRl ZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBX QVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRB QklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdO VSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBo YXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBh bG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2Fy ZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJv c3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnVzZSBzdHJpY3Q7 CnVzZSB3YXJuaW5nczsKCnVzZSBGaWxlOjpCYXNlbmFtZTsKdXNlIEhUTUw6OkVudGl0aWVzICgp OwoKIyBDaGVjayBmb3Igd21pYwpteSAkd21pX2NsaWVudCA9ICJ3bWljIjsKaWYgKHN5c3RlbSgi JHdtaV9jbGllbnQgPiAvZGV2L251bGwgMj4mMSIpICE9IDI1NikgewoJcHJpbnQgIltlcnJvcl0g JHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7 CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsK CWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJH VlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBy dW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1 cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpz dWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsK Cgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJ bXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0 YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0 KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNs aW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGlu ZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwv LCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yICht eSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJ aWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sg bGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkg QGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8 PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkg ewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVs c2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpd OwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9k YXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRh IFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3Jp cHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMg WE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxl ZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFd OwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsK CW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0 ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkp IHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkj IERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1 bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlv bgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRy ZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRy ZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj IyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgUGF0aE5hbWUsIFN0 YXRlIEZST00gV2luMzJfU2VydmljZSIpOwpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJQYXRo TmFtZSIsICJTdGF0ZSIsIFxAcmVzdWx0KTsJCmV4aXQgMDsK',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (21,9,'Process','Process running on system','/usr/bin/perl','Name;PathName;State','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9jZXNzZXMucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgUGF0aE5hbWUsIFN0YXRlIEZST00gV2luMzJfU2VydmljZSIpOwpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJQYXRoTmFtZSIsICJTdGF0ZSIsIFxAcmVzdWx0KTsJCmV4aXQgMDsK',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (22,1,'Users','User list','','Username','',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (23,9,'Users','User list','/usr/bin/perl','Domain;User','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV91c2Vycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAwOCBSYW1vbiBOb3ZvYSwgcm5vdm9hQGFydGljYS5lcwojICAgICAgICAgICAoYykgMjAwOCBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMgICAgICAgICAgIChjKSAyMDE1IEJvcmphIFNhbmNoZXogPGZib3JqYS5zYW5jaGV6QGFydGljYS5lcz4KIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCgkjIERyb3AgdGhlIGhlYWRlcgoJc2hpZnQgKEBsaW5lcyk7CgkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoKCQkjIENoZWNrIGZvciBlcnJvcnMKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL15FUlJPUi8pIHsKCQkJZXhpdCAxOwoJCX0KCgkJIyBCbGFjayBsaXN0CgkJaWYgKCRsaW5lc1skaV0gPX4gbS9GaWxlIDEvKSB7CgkJCW5leHQ7CgkJfQoKCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15ICRtb2R1bGVfaXRlbSA9ICRfWzBdOwoJbXkgJG1vZHVsZV9kYXRhID0gJF9bMV07CglteSAkbW9kdWxlX2Rlc2NyaXB0aW9uID0gJF9bMl07CglteSBAZGF0YSA9IEB7JF9bM119OwoJbXkgJHJlc3VsdDsKCglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCQoJCSRyZXN1bHQgPSAnJzsKCgkJIyBJdGVtCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2l0ZW19KSkgewoJCQkkcmVzdWx0IC49ICRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSAuICc7JzsKCQl9CgoJCSMgRGF0YQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kYXRhfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0gLiAnOyc7CgkJfQoKCQkjIERlc2NyaXB0aW9uCgkJaWYgKGRlZmluZWQoJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfZGVzY3JpcHRpb259OwoJCX0KCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7CQoJfQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIE1haW4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm15IEByZXN1bHQgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLCBGdWxsTmFtZSwgU3RhdHVzIEZST00gV2luMzJfVXNlckFjY291bnQiKTsKcHJpbnRfbW9kdWxlZGF0YSAoIk5hbWUiLCAiRnVsbE5hbWUiLCAiU3RhdHVzIiwgXEByZXN1bHQpOwpleGl0IDA7Cg==',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (23,9,'Users','User list','/usr/bin/perl','Domain;User','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9jZXNzZXMucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMDggUmFtb24gTm92b2EsIHJub3ZvYUBhcnRpY2EuZXMKIyAgICAgICAgICAgKGMpIDIwMDgtMjAyMSBBcnRpY2EgU29sdWNpb25lcyBUZWNub2xvZ2ljYXMgUy5MCiMKIyBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yCiMgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgdmVyc2lvbiAyLgojCiMgVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCiMgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKIyBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiMgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KIyBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFsb25nIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNvZnR3YXJlCiMgRm91bmRhdGlvbiwgSW5jLiwgNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgMDIxMTAtMTMwMSwgVVNBLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKCnVubGVzcygtZSAkd21pX2NsaWVudCkgewoJcHJpbnQgIltlcnJvcl0gJHdtaV9jbGllbnQgbm90IGZvdW5kLlxuIjsKCWV4aXQgMTsKfQoKaWYgKCQjQVJHViAhPSAyKSB7CglwcmludCAiVXNhZ2U6ICQwIDx0YXJnZXQgaXA+IDx1c2VybmFtZT4gPHBhc3N3b3JkPlxuIjsKCWV4aXQgMTsKfQoKbXkgJHRhcmdldF9pcCA9ICRBUkdWWzBdOwpteSAkdXNlcm5hbWUgPSAkQVJHVlsxXTsKbXkgJHBhc3N3b3JkID0gJEFSR1ZbMl07CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNVQiBydW5fcXVlcnkgKCR3cWxfcXVlcnkpCiMgUnVucyB0aGUgZ2l2ZW4gV1FMIHF1ZXJ5IGFuZCByZXR1cm5zIHRoZSByZXN1bHQgYXMgYW4gYXJyYXkgb2YgaGFzaGVzLgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcnVuX3F1ZXJ5IHsKCW15ICR3cWxfcXVlcnkgPSAkX1swXTsKCW15IEByZXN1bHQgPSB7fTsKCgkkd3FsX3F1ZXJ5ID1+ICdzLyIvXCcvZyc7CgoJIyBSdW4gdGhlIERDT00vV01JIGNsaWVudAoJbXkgJG91dHB1dCA9IGAkd21pX2NsaWVudCAtVSAnJHVzZXJuYW1lJyUnJHBhc3N3b3JkJyAvLyR0YXJnZXRfaXAgXCIkd3FsX3F1ZXJ5XCIgMj4vZGV2L251bGxgOwoKCW15IEBsaW5lcyA9IHNwbGl0KC9cbi8sICRvdXRwdXQpOwoJIyBIZWFkZXIsIGRlc2NyaXB0aW9uLCByZXN1bHRzCglpZiAoJCNsaW5lcyA8IDIpIHsKCQlleGl0IDE7Cgl9CgoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCgkjIEdldCBjb2x1bW4gbmFtZXMKCW15IEBjb2x1bW5fbmFtZXMgPSBzcGxpdCgvXHwvLCBzaGlmdCAoQGxpbmVzKSk7CgoJIyBHZXQgcm93IGRhdGEKCW15ICRpZHggPSAwOwoJZm9yIChteSAkaSA9IDA7ICRpIDw9ICQjbGluZXM7ICRpKyspIHsKCgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSAkbW9kdWxlX2l0ZW0gPSAkX1swXTsKCW15ICRtb2R1bGVfZGF0YSA9ICRfWzFdOwoJbXkgJG1vZHVsZV9kZXNjcmlwdGlvbiA9ICRfWzJdOwoJbXkgQGRhdGEgPSBAeyRfWzNdfTsKCW15ICRyZXN1bHQ7CgoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkKCQkkcmVzdWx0ID0gJyc7CgoJCSMgSXRlbQoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9pdGVtfSkpIHsKCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaXRlbX0gLiAnOyc7CgkJfQoKCQkjIERhdGEKCQlpZiAoZGVmaW5lZCgkZWxlbWVudC0+eyRtb2R1bGVfZGF0YX0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2RhdGF9IC4gJzsnOwoJCX0KCgkJIyBEZXNjcmlwdGlvbgoJCWlmIChkZWZpbmVkKCRlbGVtZW50LT57JG1vZHVsZV9kZXNjcmlwdGlvbn0pKSB7CgkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2Rlc2NyaXB0aW9ufTsKCQl9CgoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ID0gcnVuX3F1ZXJ5KCJTRUxFQ1QgTmFtZSwgUGF0aE5hbWUsIFN0YXRlIEZST00gV2luMzJfU2VydmljZSIpOwpwcmludF9tb2R1bGVkYXRhICgiTmFtZSIsICJQYXRoTmFtZSIsICJTdGF0ZSIsIFxAcmVzdWx0KTsJCmV4aXQgMDsK',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (24,9,'Services','Services installed','','Name;Command;Status','',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (25,15,'Software','Installed software applications','','Name;Version;Description','',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (26,7,'Cisco Interface Remote Inventory','Remote inventory module to get all cards in a Cisco','/usr/bin/perl','Name;Card Name;ID/Serial','IyEvdXNyL2Jpbi9wZXJsDQojSW52ZW50YXJpbyBkZSBUYXJqZXRhcw0KI1Nsb3Q7UGFydE51bWJlcjtTZXJpYWxOdW1iZXINCg0KIyEvdXNyL2Jpbi9wZXJsIC13DQoNCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsNCm15ICRjb21tdW5pdHkgPSAkQVJHVlsxXTsNCg0KI34gRXhlY3V0ZSBjb21tYW5kDQokY29tbWFuZCA9IGBzbm1wd2FsayAtdjJjIC1jICRjb21tdW5pdHkgJHRhcmdldF9pcCBtaWItMi40Ny4xLjEuMS4xLjJgOw0KDQojfiBTcGxpdCB0aGUgb3V0cHV0IGluIGxpbmVzDQpAdGVtcCA9IHNwbGl0ICgiXG4iLCAkY29tbWFuZCk7DQpteSBAdmFsdWVzOw0KDQojfiBFYWNoIGxpbmUNCmZvcmVhY2ggKEB0ZW1wKSB7DQoJI34gc3dhcCBkb3RzIGJ5IHNwYWNlcw0KCSRfID1+IHMvXC4vIC9nOw0KCSN+IHNwbGl0IHRoZSBsaW5lIGJ5IHNwYWNlcw0KCUBsaW5lID0gc3BsaXQgKC8gLywgJF8pOw0KCSN+IHNhdmUgdGhlIDd0aCB2YWx1ZQ0KCXB1c2ggKEB2YWx1ZXMsICRsaW5lWzddKTsNCn0NCg0KZm9yZWFjaCAoQHZhbHVlcykgew0KCSN+IHByaW50ICIkXyI7DQoJJFBJRCA9ICcnOw0KCSRQSUQgLj0gYHNubXBnZXQgLXYyYyAtYyAkY29tbXVuaXR5IC1PdnEgJHRhcmdldF9pcCBtaWItMi40Ny4xLjEuMS4xLjEzLiRfYDsNCgkkUElEIC49IGBzbm1wZ2V0IC12MmMgLWMgJGNvbW11bml0eSAtT3ZxICR0YXJnZXRfaXAgbWliLTIuNDcuMS4xLjEuMS4xMS4kX2A7DQoJJFBJRCA9fiBzL1wifFxufFw8fFw+fFwmfFxbfFxdLy9nOw0KDQoJaWYgKCRQSUQgbmUgIiIgKSB7DQoJCSRyZXN1bHQgPSAnJzsNCgkJIyRyZXN1bHQgLj0gYHNubXBnZXQgLXYyYyAtYyAkY29tbXVuaXR5IC1PdnEgJHRhcmdldF9pcCBzeXNOYW1lLjBgIC4gJzsnOw0KCQkkcmVzdWx0IC49IGBzbm1wZ2V0IC12MmMgLWMgJGNvbW11bml0eSAtT3ZxICR0YXJnZXRfaXAgbWliLTIuNDcuMS4xLjEuMS43LiRfYCAuICc7JzsNCgkJIyRyZXN1bHQgLj0gYHNubXBnZXQgLXYyYyAtYyAkY29tbXVuaXR5IC1PdnEgJHRhcmdldF9pcCBtaWItMi40Ny4xLjEuMS4xLjIuJF9gIC4gJzsnOw0KCQkkcmVzdWx0IC49IGBzbm1wZ2V0IC12MmMgLWMgJGNvbW11bml0eSAtT3ZxICR0YXJnZXRfaXAgbWliLTIuNDcuMS4xLjEuMS4xMy4kX2AgLiAnOyc7DQoJCSRyZXN1bHQgLj0gYHNubXBnZXQgLXYyYyAtYyAkY29tbXVuaXR5IC1PdnEgJHRhcmdldF9pcCBtaWItMi40Ny4xLjEuMS4xLjExLiRfYDsNCgkJDQoJCSRyZXN1bHQgPX4gcy9cInxcbnxcPHxcPnxcJnxcW3xcXS8vZzsNCgkJcHJpbnQgJHJlc3VsdCAuICJcbiI7DQoJfQ0KfQ0KZXhpdCAwOw0K',0,2); @@ -331,11 +331,11 @@ INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `descri INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (29,1,'IP','IP address','','Interface;Address','',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (30,1,'Routes','Routes table','','Destination;Gateway;Mask;Flags;Metric;Use;Interface','',0,2); INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (31,7,'Cisco Configuration','This script will collect a remote CISCO configuration','/usr/bin/perl','Configuration dump','IyEvdXNyL2Jpbi9wZXJsIAojCiMgKGMpIEFydGljYSAyMDE0CiMgVGhpcyBpcyBhbiBFbnRlcnByaXNlIGludmVudG9yeSBzY3JpcHQgZm9yIFBhbmRvcmEgRk1TIDUKIyBUaGlzIHNjcmlwdCB3aWxsIGNvbGxlY3QgYSByZW1vdGUgQ0lTQ08gY29uZmlndXJhdGlvbgojIFJFTUVNQkVSIFRPIEFDVElWQVRFIElUIEFTIEJMT0NLIE1PREUgISEhCgp1c2Ugd2FybmluZ3M7CnVzZSBzdHJpY3Q7CnVzZSBOZXQ6OlRlbG5ldDsKCm15ICR0YXJnZXQgPSAiIjsKbXkgJGxvZ2luX3Bhc3MgPSAiIjsKbXkgJGVuYWJsZV9wYXNzID0gIiI7Cm15ICRvdXRwdXRfZmlsZSA9ICIvdG1wL2Npc2NvIi4kJDsKbXkgJFRJTUVPVVQ9NTsKCiMgTG9hZCBjb25maWcgZmlsZSBmcm9tIGNvbW1hbmQgbGluZQppZiAoJCNBUkdWIDwgMiAgKXsKICAgICAgICBwcmludCAiSSBuZWVkIHNldmVyYWwgcGFyYW1ldGVycyBpbiB0aGlzIGZvcm1hdDogXG5cbiI7CglwcmludCAiICAgICAgZ2V0X2NvbmZpZy5wbCA8aXBfYWRkcmVzcz4gPGxvZ2luX3Bhc3M+IDxlbmFibGVfcGFzcz4gXG5cbiI7CiAgICAgICAgZXhpdDsKfQoKJHRhcmdldCA9ICRBUkdWWzBdOwokbG9naW5fcGFzcyA9ICRBUkdWWzFdOwokZW5hYmxlX3Bhc3MgPSAkQVJHVlsyXTsKCm15ICR0ZWxuZXQgPSBuZXcgTmV0OjpUZWxuZXQgKCBUaW1lb3V0PT4kVElNRU9VVCwgRXJybW9kZT0+J3JldHVybicpOyAKJHRlbG5ldC0+b3BlbigkdGFyZ2V0KTsgCiR0ZWxuZXQtPndhaXRmb3IoJy9QYXNzd29yZDovaScpOyAKJHRlbG5ldC0+cHJpbnQoJGxvZ2luX3Bhc3MpOyAKCiR0ZWxuZXQtPndhaXRmb3IoJy9cPiQvaScpOyAKJHRlbG5ldC0+cHJpbnQoJ2VuJyk7CiR0ZWxuZXQtPndhaXRmb3IoJy9QYXNzd29yZDovaScpOwokdGVsbmV0LT5wcmludCgkZW5hYmxlX3Bhc3MpOwokdGVsbmV0LT53YWl0Zm9yKCcvXCMvJyk7CiR0ZWxuZXQtPmNtZF9yZW1vdmVfbW9kZSgxKTsKCiR0ZWxuZXQtPnByaW50KCdzaG93IHJ1bm5pbmctY29uZmlnJyk7CgpteSAkZmggPSAkdGVsbmV0LT5pbnB1dF9sb2coJG91dHB1dF9maWxlKTsKbXkgJG91dHB1dCA9IDE7Cgp3aGlsZSAoZGVmaW5lZCgkb3V0cHV0KSl7Cgkkb3V0cHV0ID0gJHRlbG5ldC0+d2FpdGZvcignL01vcmUvaScpOyAKCSR0ZWxuZXQtPnByaW50KCIiKTsgCn0KCm15ICRidWZmZXIgPSAiIjsKCm9wZW4gT1VURiwgJG91dHB1dF9maWxlOwp3aGlsZSAoPE9VVEY+KSB7CgkkXyA9fiBzL1x4MDgvL2c7CgkkXyA9fiBzL1wtXC1Nb3JlXC1cLVtcdFxzXSovL2c7CgkkXyA9fiBzL1w7Ly9nOwoJJGJ1ZmZlciA9ICRidWZmZXIgLiAkXzsKfQpjbG9zZSAoT1VURik7Cgp1bmxpbmsgKCRvdXRwdXRfZmlsZSk7CnByaW50ICRidWZmZXI7Cg==',1,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (32,9,'Motherboard','This script retrieves the manufacturer,model and ID from the motherboard of a Windows system','/usr/bin/perl','Manufacturer;Model;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb3RoZXJib2FyZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCAqIEZST00gV2luMzJfY29tcHV0ZXJzeXN0ZW0iKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJNYW51ZmFjdHVyZXIiLCJNb2RlbCIsIk9FTVN0cmluZ0FycmF5Iik7Cm15IEB1bml0cyAgID0gKCIiLCIiLCIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoXEBoZWFkZXJzLFxAdW5pdHMsIFxAcmVzdWx0KTsKZXhpdCAwOwo=',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (33,9,'Monitors','Retrieve all monitor info from a Windows system.','/usr/bin/perl','Model;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb25pdG9ycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLFBOUERldmljZUlEIEZST00gV2luMzJfRGVza3RvcE1vbml0b3IiKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJOYW1lIiwiUE5QRGV2aWNlSUQiKTsKbXkgQHVuaXRzICAgPSAoIiIsIiIpOwpwcmludF9tb2R1bGVkYXRhIChcQGhlYWRlcnMsXEB1bml0cywgXEByZXN1bHQpOwpleGl0IDA7Cgo=',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (34,9,'Printers','Retrieve all printers of a Windows system','/usr/bin/perl','Name;Driver;Port','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcmludGVycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDA4IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBDYXB0aW9uLERyaXZlck5hbWUsUG9ydE5hbWUgRlJPTSBXaW4zMl9wcmludGVyIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiQ2FwdGlvbiIsIkRyaXZlck5hbWUiLCJQb3J0TmFtZSIpOwpteSBAdW5pdHMgICA9ICgiIiwiIiwiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (35,9,'product_key','Retrieve the Windows product key','/usr/bin/perl','Key','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0a2V5LnBsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ29weXJpZ2h0IChjKSAyMDE1IEJvcmphIFNhbmNoZXosIDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIndtaWMiOwppZiAoc3lzdGVtKCIkd21pX2NsaWVudCA+IC9kZXYvbnVsbCAyPiYxIikgIT0gMjU2KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNlbGVjdCBPQTN4T3JpZ2luYWxQcm9kdWN0S2V5IGZyb20gU29mdHdhcmVMaWNlbnNpbmdTZXJ2aWNlIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiT0EzeE9yaWdpbmFsUHJvZHVjdEtleSIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK',0,2); -INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (36,9,'product_ID','Retrieve the Windows product ID','/usr/bin/perl','Key','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0SUQucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMTUgQm9yamEgU2FuY2hleiwgPGZib3JqYS5zYW5jaGV6QGFydGljYS5lcz4KIyAgICAgICAgICAgKGMpIDIwMTUgQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiN1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAid21pYyI7CmlmIChzeXN0ZW0oIiR3bWlfY2xpZW50ID4gL2Rldi9udWxsIDI+JjEiKSAhPSAyNTYpIHsKCXByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7CglleGl0IDE7Cn0KCmlmICgkI0FSR1YgIT0gMikgewoJcHJpbnQgIlVzYWdlOiAkMCA8dGFyZ2V0IGlwPiA8dXNlcm5hbWU+IDxwYXNzd29yZD5cbiI7CglleGl0IDE7Cn0KCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsKbXkgJHVzZXJuYW1lID0gJEFSR1ZbMV07Cm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHJ1bl9xdWVyeSB7CglteSAkd3FsX3F1ZXJ5ID0gJF9bMF07CglteSBAcmVzdWx0ID0ge307CgoJJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOwoKCSMgUnVuIHRoZSBEQ09NL1dNSSBjbGllbnQKCW15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsKCglteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsKCSMgSGVhZGVyLCBkZXNjcmlwdGlvbiwgcmVzdWx0cwoJaWYgKCQjbGluZXMgPCAyKSB7CgkJZXhpdCAxOwoJfQoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCQoJIyBHZXQgY29sdW1uIG5hbWVzCglteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOwoKCSMgR2V0IHJvdyBkYXRhCglteSAkaWR4ID0gMDsKCWZvciAobXkgJGkgPSAwOyAkaSA8PSAkI2xpbmVzOyAkaSsrKSB7CgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15IEBtb2R1bGVfaGVhZGVycyA9IEB7JF9bMF19OwoJbXkgQG1vZHVsZV91bml0cyAgID0gQHskX1sxXX07CglteSBAZGF0YSAgICAgICAgICAgPSBAeyRfWzJdfTsKCW15ICRyZXN1bHQ7Cglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCSRyZXN1bHQgPSAnJzsKCQlmb3IgKCRpPTA7ICRpPEBtb2R1bGVfaGVhZGVyczskaSsrKSB7CgkJCWlmIChkZWZpbmVkICgkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19KSkgewoJCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19IC4gJG1vZHVsZV91bml0c1skaV0gLiAnOyc7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0IC49ICc7JzsKCQkJfQoJCX0KCQljaG9wKCRyZXN1bHQpOwoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ICA9IHJ1bl9xdWVyeSgiU2VsZWN0IFNlcmlhbE51bWJlciBmcm9tIFdpbjMyX09wZXJhdGluZ1N5c3RlbSIpOwojIFJlbWVtYmVyIHRvIG1hdGNoIGxlbmdodCBvZiBib3RoIGFycmF5cyBoZWFkZXJzICYgdW5pdHMKbXkgQGhlYWRlcnMgPSAoIlNlcmlhbE51bWJlciIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsKCg==',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (32,9,'Motherboard','This script retrieves the manufacturer,model and ID from the motherboard of a Windows system','/usr/bin/perl','Manufacturer;Model;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb3RoZXJib2FyZC5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCAqIEZST00gV2luMzJfY29tcHV0ZXJzeXN0ZW0iKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJNYW51ZmFjdHVyZXIiLCJNb2RlbCIsIk9FTVN0cmluZ0FycmF5Iik7Cm15IEB1bml0cyAgID0gKCIiLCIiLCIiKTsKcHJpbnRfbW9kdWxlZGF0YSAoXEBoZWFkZXJzLFxAdW5pdHMsIFxAcmVzdWx0KTsKZXhpdCAwOwo=',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (33,9,'Monitors','Retrieve all monitor info from a Windows system.','/usr/bin/perl','Model;ID','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9tb25pdG9ycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBOYW1lLFBOUERldmljZUlEIEZST00gV2luMzJfRGVza3RvcE1vbml0b3IiKTsKIyBSZW1lbWJlciB0byBtYXRjaCBsZW5naHQgb2YgYm90aCBhcnJheXMgaGVhZGVycyAmIHVuaXRzCm15IEBoZWFkZXJzID0gKCJOYW1lIiwiUE5QRGV2aWNlSUQiKTsKbXkgQHVuaXRzICAgPSAoIiIsIiIpOwpwcmludF9tb2R1bGVkYXRhIChcQGhlYWRlcnMsXEB1bml0cywgXEByZXN1bHQpOwpleGl0IDA7',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (34,9,'Printers','Retrieve all printers of a Windows system','/usr/bin/perl','Name;Driver;Port','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcmludGVycy5wbAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvcHlyaWdodCAoYykgMjAxNSBCb3JqYSBTYW5jaGV6IDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDA4IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNFTEVDVCBDYXB0aW9uLERyaXZlck5hbWUsUG9ydE5hbWUgRlJPTSBXaW4zMl9wcmludGVyIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiQ2FwdGlvbiIsIkRyaXZlck5hbWUiLCJQb3J0TmFtZSIpOwpteSBAdW5pdHMgICA9ICgiIiwiIiwiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (35,9,'product_key','Retrieve the Windows product key','/usr/bin/perl','Key','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0a2V5LnBsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ29weXJpZ2h0IChjKSAyMDE1IEJvcmphIFNhbmNoZXosIDxmYm9yamEuc2FuY2hlekBhcnRpY2EuZXM+CiMgICAgICAgICAgIChjKSAyMDE1IEFydGljYSBTb2x1Y2lvbmVzIFRlY25vbG9naWNhcyBTLkwKIwojIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IKIyBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQojIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDIuCiMKIyBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKIyBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgojIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUKIyBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgojIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5vdCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKIyBGb3VuZGF0aW9uLCBJbmMuLCA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BICAwMjExMC0xMzAxLCBVU0EuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojdXNlIHN0cmljdDsKdXNlIHdhcm5pbmdzOwoKdXNlIEZpbGU6OkJhc2VuYW1lOwp1c2UgSFRNTDo6RW50aXRpZXMgKCk7CgojIENoZWNrIGZvciB3bWljCm15ICR3bWlfY2xpZW50ID0gIi91c3IvYmluL3BhbmRvcmF3bWljIjsKdW5sZXNzKC1lICR3bWlfY2xpZW50KSB7CglwcmludCAiW2Vycm9yXSAkd21pX2NsaWVudCBub3QgZm91bmQuXG4iOwoJZXhpdCAxOwp9CgppZiAoJCNBUkdWICE9IDIpIHsKCXByaW50ICJVc2FnZTogJDAgPHRhcmdldCBpcD4gPHVzZXJuYW1lPiA8cGFzc3dvcmQ+XG4iOwoJZXhpdCAxOwp9CgpteSAkdGFyZ2V0X2lwID0gJEFSR1ZbMF07Cm15ICR1c2VybmFtZSA9ICRBUkdWWzFdOwpteSAkcGFzc3dvcmQgPSAkQVJHVlsyXTsKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHJ1bl9xdWVyeSAoJHdxbF9xdWVyeSkKIyBSdW5zIHRoZSBnaXZlbiBXUUwgcXVlcnkgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBhcyBhbiBhcnJheSBvZiBoYXNoZXMuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBydW5fcXVlcnkgewoJbXkgJHdxbF9xdWVyeSA9ICRfWzBdOwoJbXkgQHJlc3VsdCA9IHt9OwoKCSR3cWxfcXVlcnkgPX4gJ3MvIi9cJy9nJzsKCgkjIFJ1biB0aGUgRENPTS9XTUkgY2xpZW50CglteSAkb3V0cHV0ID0gYCR3bWlfY2xpZW50IC1VICckdXNlcm5hbWUnJSckcGFzc3dvcmQnIC8vJHRhcmdldF9pcCBcIiR3cWxfcXVlcnlcIiAyPi9kZXYvbnVsbGA7CgoJbXkgQGxpbmVzID0gc3BsaXQoL1xuLywgJG91dHB1dCk7CgkjIEhlYWRlciwgZGVzY3JpcHRpb24sIHJlc3VsdHMKCWlmICgkI2xpbmVzIDwgMikgewoJCWV4aXQgMTsKCX0KCSMgRHJvcCB0aGUgaGVhZGVyCglzaGlmdCAoQGxpbmVzKTsKCQkKCSMgR2V0IGNvbHVtbiBuYW1lcwoJbXkgQGNvbHVtbl9uYW1lcyA9IHNwbGl0KC9cfC8sIHNoaWZ0IChAbGluZXMpKTsKCgkjIEdldCByb3cgZGF0YQoJbXkgJGlkeCA9IDA7Cglmb3IgKG15ICRpID0gMDsgJGkgPD0gJCNsaW5lczsgJGkrKykgewoJCSMgQ2hlY2sgZm9yIGVycm9ycwoJCWlmICgkbGluZXNbJGldID1+IG0vXkVSUk9SLykgewoJCQlleGl0IDE7CgkJfQoKCQkjIEJsYWNrIGxpc3QKCQlpZiAoJGxpbmVzWyRpXSA9fiBtL0ZpbGUgMS8pIHsKCQkJbmV4dDsKCQl9CgkJbXkgQGNvbHVtbiA9IHNwbGl0KC9cfC8sICRsaW5lc1skaV0pOwoJCWZvciAobXkgJGogPSAwOyAkaiA8PSAkI2NvbHVtbl9uYW1lczsgJGorKykgewoJCQlpZiAoISBkZWZpbmVkKCRjb2x1bW5bJGpdKSkgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICIiOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdFskaWR4XS0+eyRjb2x1bW5fbmFtZXNbJGpdfSA9ICRjb2x1bW5bJGpdOwoJCQl9CgkJfQoJCQoJCSRpZHgrKzsKCX0KCQoJcmV0dXJuIEByZXN1bHQ7Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1VCIHByaW50X21vZHVsZWRhdGEgKCRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhLCAKIyAkbW9kdWxlX2Rlc2NyaXB0aW9uLCBAZGF0YSkKIyBQcmludHMgYSBtb2R1bGVkYXRhIFhNTCB0YWcuICRtb2R1bGVfaXRlbSwgJG1vZHVsZV9kYXRhIGFuZAojICRtb2R1bGVfZGVzY3JpcHRpb24gYXJlIHVzZWQgdG8gaW5kZXggdGhlIGl0ZW0sIGRhdGEgYW5kIGRlc2NyaXB0aW9uCiMgWE1MIHRhZ3MgcmVzcGVjdGl2ZWx5LgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdWIgcHJpbnRfbW9kdWxlZGF0YSB7CglteSBAbW9kdWxlX2hlYWRlcnMgPSBAeyRfWzBdfTsKCW15IEBtb2R1bGVfdW5pdHMgICA9IEB7JF9bMV19OwoJbXkgQGRhdGEgICAgICAgICAgID0gQHskX1syXX07CglteSAkcmVzdWx0OwoJZm9yZWFjaCBteSAkZWxlbWVudCAoQGRhdGEpIHsKCQkkcmVzdWx0ID0gJyc7CgkJZm9yICgkaT0wOyAkaTxAbW9kdWxlX2hlYWRlcnM7JGkrKykgewoJCQlpZiAoZGVmaW5lZCAoJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSkpIHsKCQkJCSRyZXN1bHQgLj0gJGVsZW1lbnQtPnskbW9kdWxlX2hlYWRlcnNbJGldfSAuICRtb2R1bGVfdW5pdHNbJGldIC4gJzsnOwoJCQl9CgkJCWVsc2UgewoJCQkJJHJlc3VsdCAuPSAnOyc7CgkJCX0KCQl9CgkJY2hvcCgkcmVzdWx0KTsKCQlwcmludCAkcmVzdWx0IC4gIlxuIjsJCgl9Cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbXkgQHJlc3VsdCAgPSBydW5fcXVlcnkoIlNlbGVjdCBPQTN4T3JpZ2luYWxQcm9kdWN0S2V5IGZyb20gU29mdHdhcmVMaWNlbnNpbmdTZXJ2aWNlIik7CiMgUmVtZW1iZXIgdG8gbWF0Y2ggbGVuZ2h0IG9mIGJvdGggYXJyYXlzIGhlYWRlcnMgJiB1bml0cwpteSBAaGVhZGVycyA9ICgiT0EzeE9yaWdpbmFsUHJvZHVjdEtleSIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsK',0,2); +INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `description`, `interpreter`, `data_format`, `code`, `block_mode`,`script_mode`) VALUES (36,9,'product_ID','Retrieve the Windows product ID','/usr/bin/perl','Key','IyEvdXNyL2Jpbi9wZXJsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgcGFuZG9yYV9wcm9kdWN0SUQucGwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDb3B5cmlnaHQgKGMpIDIwMTUgQm9yamEgU2FuY2hleiwgPGZib3JqYS5zYW5jaGV6QGFydGljYS5lcz4KIyAgICAgICAgICAgKGMpIDIwMTUgQXJ0aWNhIFNvbHVjaW9uZXMgVGVjbm9sb2dpY2FzIFMuTAojCiMgVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgojIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlCiMgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHZlcnNpb24gMi4KIwojIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLAojIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiMgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQojIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCiMgWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKIyBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZQojIEZvdW5kYXRpb24sIEluYy4sIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgIDAyMTEwLTEzMDEsIFVTQS4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiN1c2Ugc3RyaWN0Owp1c2Ugd2FybmluZ3M7Cgp1c2UgRmlsZTo6QmFzZW5hbWU7CnVzZSBIVE1MOjpFbnRpdGllcyAoKTsKCiMgQ2hlY2sgZm9yIHdtaWMKbXkgJHdtaV9jbGllbnQgPSAiL3Vzci9iaW4vcGFuZG9yYXdtaWMiOwp1bmxlc3MoLWUgJHdtaV9jbGllbnQpIHsKCXByaW50ICJbZXJyb3JdICR3bWlfY2xpZW50IG5vdCBmb3VuZC5cbiI7CglleGl0IDE7Cn0KCmlmICgkI0FSR1YgIT0gMikgewoJcHJpbnQgIlVzYWdlOiAkMCA8dGFyZ2V0IGlwPiA8dXNlcm5hbWU+IDxwYXNzd29yZD5cbiI7CglleGl0IDE7Cn0KCm15ICR0YXJnZXRfaXAgPSAkQVJHVlswXTsKbXkgJHVzZXJuYW1lID0gJEFSR1ZbMV07Cm15ICRwYXNzd29yZCA9ICRBUkdWWzJdOwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcnVuX3F1ZXJ5ICgkd3FsX3F1ZXJ5KQojIFJ1bnMgdGhlIGdpdmVuIFdRTCBxdWVyeSBhbmQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGFuIGFycmF5IG9mIGhhc2hlcy4KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3ViIHJ1bl9xdWVyeSB7CglteSAkd3FsX3F1ZXJ5ID0gJF9bMF07CglteSBAcmVzdWx0ID0ge307CgoJJHdxbF9xdWVyeSA9fiAncy8iL1wnL2cnOwoKCSMgUnVuIHRoZSBEQ09NL1dNSSBjbGllbnQKCW15ICRvdXRwdXQgPSBgJHdtaV9jbGllbnQgLVUgJyR1c2VybmFtZSclJyRwYXNzd29yZCcgLy8kdGFyZ2V0X2lwIFwiJHdxbF9xdWVyeVwiIDI+L2Rldi9udWxsYDsKCglteSBAbGluZXMgPSBzcGxpdCgvXG4vLCAkb3V0cHV0KTsKCSMgSGVhZGVyLCBkZXNjcmlwdGlvbiwgcmVzdWx0cwoJaWYgKCQjbGluZXMgPCAyKSB7CgkJZXhpdCAxOwoJfQoJIyBEcm9wIHRoZSBoZWFkZXIKCXNoaWZ0IChAbGluZXMpOwoJCQoJIyBHZXQgY29sdW1uIG5hbWVzCglteSBAY29sdW1uX25hbWVzID0gc3BsaXQoL1x8Lywgc2hpZnQgKEBsaW5lcykpOwoKCSMgR2V0IHJvdyBkYXRhCglteSAkaWR4ID0gMDsKCWZvciAobXkgJGkgPSAwOyAkaSA8PSAkI2xpbmVzOyAkaSsrKSB7CgkJIyBDaGVjayBmb3IgZXJyb3JzCgkJaWYgKCRsaW5lc1skaV0gPX4gbS9eRVJST1IvKSB7CgkJCWV4aXQgMTsKCQl9CgoJCSMgQmxhY2sgbGlzdAoJCWlmICgkbGluZXNbJGldID1+IG0vRmlsZSAxLykgewoJCQluZXh0OwoJCX0KCQlteSBAY29sdW1uID0gc3BsaXQoL1x8LywgJGxpbmVzWyRpXSk7CgkJZm9yIChteSAkaiA9IDA7ICRqIDw9ICQjY29sdW1uX25hbWVzOyAkaisrKSB7CgkJCWlmICghIGRlZmluZWQoJGNvbHVtblskal0pKSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gIiI7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0WyRpZHhdLT57JGNvbHVtbl9uYW1lc1skal19ID0gJGNvbHVtblskal07CgkJCX0KCQl9CgkJCgkJJGlkeCsrOwoJfQoJCglyZXR1cm4gQHJlc3VsdDsKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUIgcHJpbnRfbW9kdWxlZGF0YSAoJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEsIAojICRtb2R1bGVfZGVzY3JpcHRpb24sIEBkYXRhKQojIFByaW50cyBhIG1vZHVsZWRhdGEgWE1MIHRhZy4gJG1vZHVsZV9pdGVtLCAkbW9kdWxlX2RhdGEgYW5kCiMgJG1vZHVsZV9kZXNjcmlwdGlvbiBhcmUgdXNlZCB0byBpbmRleCB0aGUgaXRlbSwgZGF0YSBhbmQgZGVzY3JpcHRpb24KIyBYTUwgdGFncyByZXNwZWN0aXZlbHkuCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnN1YiBwcmludF9tb2R1bGVkYXRhIHsKCW15IEBtb2R1bGVfaGVhZGVycyA9IEB7JF9bMF19OwoJbXkgQG1vZHVsZV91bml0cyAgID0gQHskX1sxXX07CglteSBAZGF0YSAgICAgICAgICAgPSBAeyRfWzJdfTsKCW15ICRyZXN1bHQ7Cglmb3JlYWNoIG15ICRlbGVtZW50IChAZGF0YSkgewoJCSRyZXN1bHQgPSAnJzsKCQlmb3IgKCRpPTA7ICRpPEBtb2R1bGVfaGVhZGVyczskaSsrKSB7CgkJCWlmIChkZWZpbmVkICgkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19KSkgewoJCQkJJHJlc3VsdCAuPSAkZWxlbWVudC0+eyRtb2R1bGVfaGVhZGVyc1skaV19IC4gJG1vZHVsZV91bml0c1skaV0gLiAnOyc7CgkJCX0KCQkJZWxzZSB7CgkJCQkkcmVzdWx0IC49ICc7JzsKCQkJfQoJCX0KCQljaG9wKCRyZXN1bHQpOwoJCXByaW50ICRyZXN1bHQgLiAiXG4iOwkKCX0KfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNYWluCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpteSBAcmVzdWx0ICA9IHJ1bl9xdWVyeSgiU2VsZWN0IFNlcmlhbE51bWJlciBmcm9tIFdpbjMyX09wZXJhdGluZ1N5c3RlbSIpOwojIFJlbWVtYmVyIHRvIG1hdGNoIGxlbmdodCBvZiBib3RoIGFycmF5cyBoZWFkZXJzICYgdW5pdHMKbXkgQGhlYWRlcnMgPSAoIlNlcmlhbE51bWJlciIpOwpteSBAdW5pdHMgICA9ICgiIik7CnByaW50X21vZHVsZWRhdGEgKFxAaGVhZGVycyxcQHVuaXRzLCBcQHJlc3VsdCk7CmV4aXQgMDsKCg==',0,2); -- Categoria field is used to segregate several types -- (plugin, agents, network) on their data @@ -345,7 +345,7 @@ INSERT INTO `tmodule_inventory` (`id_module_inventory`, `id_os`, `name`, `descri -- Dumping data for table `tusuario` -- INSERT INTO `tusuario` (`id_user`, `fullname`, `firstname`, `lastname`, `middlename`, `password`, `comments`, `last_connect`, `registered`, `email`, `phone`, `is_admin`, `language`, `block_size`, `section`, `data_section`, `metaconsole_access`, `local_user`) VALUES -('admin', 'Pandora', 'Pandora', 'Admin', '', '1da7ee7d45b96d0e1f45ee4ee23da560', 'Admin Pandora', 1232642121, 0, 'admin@example.com', '555-555-5555', 1, 'default', 0, 'Default', '', 'advanced', 1); +('admin', 'Pandora', 'Pandora', 'Admin', '', '$2y$10$Wv/xoxjI2VAkthJhk/PzeeGIhBKYU/K.TMgUdmW7fEP2NQkdWlB9K', 'Admin Pandora', 1232642121, 0, 'admin@example.com', '555-555-5555', 1, 'default', 0, 'Default', '', 'advanced', 1); -- -- Dumping data for table `tusuario_perfil` diff --git a/pandora_console/vendor/artica/phpchartjs/.codeclimate.yml b/pandora_console/vendor/artica/phpchartjs/.codeclimate.yml new file mode 100644 index 0000000000..0a1763e5fa --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/.codeclimate.yml @@ -0,0 +1,30 @@ +--- +engines: + duplication: + enabled: true + config: + languages: + - javascript + - php + fixme: + enabled: true + phpmd: + enabled: true + checks: + Design/TooManyFields: + enabled: false + CleanCode/StaticAccess: + enabled: false + Naming/LongVariable: + enabled: false +ratings: + paths: + - "**.inc" + - "**.js" + - "**.jsx" + - "**.module" + - "**.php" + - "**.py" + - "**.rb" +exclude_paths: +- test/ diff --git a/pandora_console/vendor/artica/phpchartjs/.gitignore b/pandora_console/vendor/artica/phpchartjs/.gitignore new file mode 100644 index 0000000000..c6101292c5 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/.gitignore @@ -0,0 +1,9 @@ +# Created by .ignore support plugin (hsz.mobi) +/.idea/ +/composer.phar +composer.phar +/vendor/ +codeCoverage +build +*DS_Store +.phpunit.result.cache diff --git a/pandora_console/vendor/artica/phpchartjs/.travis.yml b/pandora_console/vendor/artica/phpchartjs/.travis.yml new file mode 100644 index 0000000000..d8bbb86d46 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/.travis.yml @@ -0,0 +1,31 @@ +language: php +php: + - '5.6' + - '7.0' + - '7.1' + - '7.2' + - '7.3' + - '7.4' + - nightly + +before_script: + - composer self-update + - composer install + - mkdir build/logs -p + +script: + - composer test + - composer cs-check + - php vendor/bin/security-checker security:check + +after_script: + - rm -rf build/logs + +matrix: + allow_failures: + - php: nightly + +branches: + only: + - master + - dev diff --git a/pandora_console/vendor/artica/phpchartjs/LICENSE.md b/pandora_console/vendor/artica/phpchartjs/LICENSE.md new file mode 100644 index 0000000000..dbbe355815 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/LICENSE.md @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/pandora_console/vendor/artica/phpchartjs/README.md b/pandora_console/vendor/artica/phpchartjs/README.md new file mode 100644 index 0000000000..7e7399642c --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/README.md @@ -0,0 +1,177 @@ +### Stable +[![Latest Stable Version](https://poser.pugx.org/Articaam/phpchartjs/v/stable)](https://packagist.org/packages/Articaam/phpchartjs) +[![Total Downloads](https://poser.pugx.org/Articaam/phpchartjs/downloads)](https://packagist.org/packages/Articaam/phpchartjs) +[![License](https://poser.pugx.org/Articaam/phpchartjs/license)](https://packagist.org/packages/Articaam/phpchartjs) +[![composer.lock](https://poser.pugx.org/Articaam/phpchartjs/composerlock)](https://packagist.org/packages/Articaam/phpchartjs) +[![Build Status](https://travis-ci.org/Articaam/PHPChartJS.svg?branch=master)](https://travis-ci.org/Articaam/PHPChartJS) +[![Code Climate](https://codeclimate.com/github/Articaam/PHPChartJS/badges/gpa.svg)](https://codeclimate.com/github/Articaam/PHPChartJS) +[![Test Coverage](https://codeclimate.com/github/Articaam/PHPChartJS/badges/coverage.svg)](https://codeclimate.com/github/Articaam/PHPChartJS/coverage) +[![Quality](https://scrutinizer-ci.com/g/Articaam/PHPChartJS/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Articaam/PHPChartJS/) + +### Development +[![Latest Unstable Version](https://poser.pugx.org/Articaam/phpchartjs/v/unstable)](https://packagist.org/packages/Articaam/phpchartjs) +[![Build Status](https://travis-ci.org/Articaam/PHPChartJS.svg?branch=dev)](https://travis-ci.org/Articaam/PHPChartJS) + +# PHPChartJS +PHP OOP library for [ChartJS](http://www.chartjs.org/) + +PHPChartJS acts as an interface between the ChartJS library and the server side code. Set up a chart in no time and have every aspect of the graph managable from your PHP code. This interface is set up to provide code completion in every scenario so you never have to guess or lookup what options are available for the chosen chart. The library is entirely object oriented. + +This library is still in active development and aims to implement all options ChartJS has to offer. Check the [Configuration milestone](https://github.com/Articaam/PHPChartJS/milestone/1) to view the progress of implementing all existing options. + +## Example use +````php +setId("myBar"); + +// Set labels +$bar->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add apples +$apples = $bar->createDataSet(); +$apples->setLabel("apples") + ->setBackgroundColor("rgba( 0, 150, 0, .5 )") + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$bar->addDataSet($apples); + +// Add oranges as well +$oranges = $bar->createDataSet(); +$oranges->setLabel("oranges") + ->setBackgroundColor("rgba( 255, 153, 0, .5 )") + ->data()->exchangeArray([ 30, 29, 5, 5, 20, 3 ]); + +// Add some extra data +$oranges->data()->append(10); + +$bar->addDataSet($oranges); + +// Render the chart +echo $bar->render(); +```` +The result generated by rendering the chart will look something like this: + +````html + + +```` + +### Callbacks +You can provide javascript callbacks with ease: + +````php +$myCallback = "function(item){ console.log(item); }"; +$bar->options()->getTooltips()->callbacks()->setAfterBody($myCallback); +```` + +### Rendering + +Rendering the chart creates some HTML and some JavaScript. The JavaScript contains a JSON object providing the necessary +configuration for ChartJS. Every part of the configuration can be cast to an array or a JSON object. + +Render isolated part of the configuration: + +````php +$json = $myChart->options()->getScales()->jsonSerialize(); +```` + +Or return an array containing the set configuration: + +````php +$options = $myChart->options()->getScales()->getArrayCopy(); +```` + + +### Pretty mode +If you're not a fan of the long lines of code that are being generated you can force the rendering to be done in *pretty mode*, see the following example. + +````php +// Render in pretty mode +$bar->render(true); +```` + +Want to see more? Fork this project and take a look at the examples in the test folder to explore the different options. + +## Configuration options +Every option that is supported by ChartJS will be made available in this library. + +### Layers +ChartJS requires you to build a JSON object containing the configuration options you want to set for the current chart. +These options are spread over multiple layers inside the configuration object. Accessing these layers with PHPChartJS is + super easy. + +Let's say we wanted to access the chart's getAnimation subtree within the options subtree: +````php +$getAnimation = $myChart->options()->getAnimation(); +```` +You can now adjust any of ChartJS's getAnimation settings by using the getters and setters provided in that particular class. + +## Collections +If ChartJS requires an array with certain items as subsets in a configuration option that array will be represented by a +collection in PHPChartJS. The collection can always by accessed directly to add, remove and replace values. + +In some cases a specific object with a predetermined list of options is required in a collection. In these cases methods +will be provided to create a new instance of said object and adding it to the collection. + +Datasets are stored in a collection: + +````php +// Create new dataset +$dataset = $myChart->createDataSet(); +... (add data to the dataset) +$myChart->addDataSet($dataset); +```` + +But the actual data in a dataset is also stored inside a collection: + +````php +// Create new dataset +$dataset = $myChart->createDataSet(); + +// Add some data +$dataset->data()->append(1)->append(2); + +// Prepend some data +$dataset->data()->prepend(0); + +// Replace data at certain position +$dataset->data()->offsetSet(1, 3); + +// Retrieve data from certain position +$value = $dataset->data()->offsetGet(1); // 3 + +// Add a lot of data at once whilst returning the old values +$oldData = $dataset->data()->exchangeArray([1, 2, 3]); // array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } + +$myChart->addDataSet($dataset); +```` +For more information about the collections visit the [Articaam/collection](https://github.com/Articaam/collection) project. + +## Installation + +### Using composer + $ composer require Articaam/phpchartjs + +### Development +This project uses composer, which should be installed on your system. Most +Linux systems have composer available in their PHP packages. +Alternatively you can download composer from [getcomposer.org](http://getcomposer.org). + +If you use the PhpStorm IDE then you can simply init composer through the IDE. However, +full use requires the commandline. See PhpStorm help, search for composer. + +To start development, do `composer install` from the project directory. + +**Remark** Do not use `composer update` unless you changed the dependency requirements in composer.json. +The difference is that `composer install` will use composer.lock read-only, +while `composer update` will update your composer.lock file regardless of any change. +As the composer.lock file is committed to the repo, other developers might conclude +dependencies have changed, while they have not. diff --git a/pandora_console/vendor/artica/phpchartjs/_config.yml b/pandora_console/vendor/artica/phpchartjs/_config.yml new file mode 100644 index 0000000000..c4192631f2 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/pandora_console/vendor/artica/phpchartjs/composer.json b/pandora_console/vendor/artica/phpchartjs/composer.json new file mode 100644 index 0000000000..81cd50ecf5 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/composer.json @@ -0,0 +1,47 @@ +{ + "name": "artica/phpchartjs", + "description": "PHP library for ChartJS", + "keywords": [ + "chartjs", + "graph", + "php" + ], + "readme": "README.md", + "homepage": "https://artica.es/", + "type": "package", + "version": "v1.0.2", + "license": "AGPL-3.0-or-later", + "authors": [ + { + "name": "Bob Kruithof" + }, + { + "name": "Daniel Barbero" + } + ], + "require": { + "php": ">=7.2", + "ext-dom": "*", + "ext-json": "*", + "laminas/laminas-json": ">3.1.2", + "halfpastfouram/collection": "1.0.0", + "symfony/var-dumper": "^3.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "3.5.3", + "friendsofphp/php-cs-fixer": "*", + "sensiolabs/security-checker": "^5.0" + }, + "autoload": { + "psr-4": { + "Artica\\PHPChartJS\\": "src/", + "Test\\": "test/" + } + }, + "scripts": { + "test": "./vendor/bin/phpunit", + "cs-check": "./vendor/bin/phpcs", + "cs-fix": "./vendor/bin/phpcbf" + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/composer.lock b/pandora_console/vendor/artica/phpchartjs/composer.lock new file mode 100644 index 0000000000..72cd9f603a --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/composer.lock @@ -0,0 +1,3910 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "7b4943f1b24f426de0dd965e616bf88f", + "packages": [ + { + "name": "halfpastfouram/collection", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/halfpastfouram/collection.git", + "reference": "0862d0b431fef9dc2245518dc06b86ff00dcd102" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/halfpastfouram/collection/zipball/0862d0b431fef9dc2245518dc06b86ff00dcd102", + "reference": "0862d0b431fef9dc2245518dc06b86ff00dcd102", + "shasum": "" + }, + "require": { + "php": ">=5.6.0 || ^7.0" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "phpunit/phpunit": "5.2.*" + }, + "type": "package", + "autoload": { + "psr-4": { + "Test\\": "test/", + "Halfpastfour\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL 3.0" + ], + "authors": [ + { + "name": "Bob Kruithof" + } + ], + "description": "A flexible PHP Collection complete with custom Iterator.", + "homepage": "http://github.com/halfpastfouram/collection", + "keywords": [ + "collection", + "php" + ], + "support": { + "issues": "https://github.com/halfpastfouram/collection/issues", + "source": "https://github.com/halfpastfouram/collection/tree/master" + }, + "time": "2016-12-18T13:04:48+00:00" + }, + { + "name": "laminas/laminas-json", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-json.git", + "reference": "7a8a1d7bf2d05dd6c1fbd7c0868d3848cf2b57ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/7a8a1d7bf2d05dd6c1fbd7c0868d3848cf2b57ec", + "reference": "7a8a1d7bf2d05dd6c1fbd7c0868d3848cf2b57ec", + "shasum": "" + }, + "require": { + "php": "~8.0.0 || ~8.1.0 || ~8.2.0" + }, + "conflict": { + "zendframework/zend-json": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.4.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.1", + "phpunit/phpunit": "^9.5.25" + }, + "suggest": { + "laminas/laminas-json-server": "For implementing JSON-RPC servers", + "laminas/laminas-xml2json": "For converting XML documents to JSON" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Json\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://laminas.dev", + "keywords": [ + "json", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-json/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-json/issues", + "rss": "https://github.com/laminas/laminas-json/releases.atom", + "source": "https://github.com/laminas/laminas-json" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2022-10-17T04:06:45+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "0719f6cf4633a38b2c1585140998579ce23b4b7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0719f6cf4633a38b2c1585140998579ce23b4b7d", + "reference": "0719f6cf4633a38b2c1585140998579ce23b4b7d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + }, + "require-dev": { + "ext-iconv": "*", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "ext-symfony_debug": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + } + ], + "packages-dev": [ + { + "name": "composer/ca-bundle", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "69098eca243998b53eed7a48d82dedd28b447cd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/69098eca243998b53eed7a48d82dedd28b447cd5", + "reference": "69098eca243998b53eed7a48d82dedd28b447cd5", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "psr/log": "^1.0", + "symfony/phpunit-bridge": "^4.2 || ^5", + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.3.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-10-12T12:08:29+00:00" + }, + { + "name": "composer/pcre", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/1.0.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-21T20:24:37+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/9e36aeed4616366d2b690bdce11f71e9178c579a", + "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a", + "shasum": "" + }, + "require": { + "composer/pcre": "^1", + "php": "^5.3.2 || ^7.0 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/2.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-24T20:20:32+00:00" + }, + { + "name": "doctrine/annotations", + "version": "1.13.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "648b0343343565c4a056bfc8392201385e8d89f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/648b0343343565c4a056bfc8392201385e8d89f0", + "reference": "648b0343343565c4a056bfc8392201385e8d89f0", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2", + "vimeo/psalm": "^4.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.13.3" + }, + "time": "2022-07-02T10:48:51+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-03-03T08:28:38+00:00" + }, + { + "name": "doctrine/lexer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-02-28T11:07:21+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "47177af1cfb9dab5d1cc4daf91b7179c2efe7fad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/47177af1cfb9dab5d1cc4daf91b7179c2efe7fad", + "reference": "47177af1cfb9dab5d1cc4daf91b7179c2efe7fad", + "shasum": "" + }, + "require": { + "composer/semver": "^3.2", + "composer/xdebug-handler": "^2.0", + "doctrine/annotations": "^1.12", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.2.5 || ^8.0", + "php-cs-fixer/diff": "^2.0", + "symfony/console": "^4.4.20 || ^5.1.3 || ^6.0", + "symfony/event-dispatcher": "^4.4.20 || ^5.0 || ^6.0", + "symfony/filesystem": "^4.4.20 || ^5.0 || ^6.0", + "symfony/finder": "^4.4.20 || ^5.0 || ^6.0", + "symfony/options-resolver": "^4.4.20 || ^5.0 || ^6.0", + "symfony/polyfill-mbstring": "^1.23", + "symfony/polyfill-php80": "^1.23", + "symfony/polyfill-php81": "^1.23", + "symfony/process": "^4.4.20 || ^5.0 || ^6.0", + "symfony/stopwatch": "^4.4.20 || ^5.0 || ^6.0" + }, + "require-dev": { + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^1.5", + "mikey179/vfsstream": "^1.6.8", + "php-coveralls/php-coveralls": "^2.5.2", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^1.1 || ^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5", + "phpunitgoodpractices/polyfill": "^1.5", + "phpunitgoodpractices/traits": "^1.9.1", + "symfony/phpunit-bridge": "^5.2.4 || ^6.0", + "symfony/yaml": "^4.4.20 || ^5.0 || ^6.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", + "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2021-12-11T16:25:08+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" + }, + "time": "2022-11-12T15:38:23+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "php-cs-fixer/diff", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "sebastian/diff v3 backport support for PHP 5.6+", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/diff/issues", + "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" + }, + "abandoned": true, + "time": "2020-10-14T08:32:19+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.19", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c77b56b63e3d2031bd8997fcec43c1925ae46559", + "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.14", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.19" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-11-18T07:47:47+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.26", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2022-10-28T06:00:21+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/log", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/2.0.0" + }, + "time": "2021-07-14T16:41:46+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-03T09:37:03+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-12T14:47:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "sensiolabs/security-checker", + "version": "v5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/security-checker.git", + "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/46be3f58adac13084497961e10eed9a7fb4d44d1", + "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0", + "php": ">=5.5.9", + "symfony/console": "~2.7|~3.0|~4.0" + }, + "bin": [ + "security-checker" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "SensioLabs\\Security\\": "SensioLabs/Security" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "description": "A security checker for your composer.lock", + "support": { + "issues": "https://github.com/sensiolabs/security-checker/issues", + "source": "https://github.com/sensiolabs/security-checker/tree/master" + }, + "abandoned": "https://github.com/fabpot/local-php-security-checker", + "time": "2018-12-19T17:14:59+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.5.3", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", + "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2019-12-04T04:46:47+00:00" + }, + { + "name": "symfony/console", + "version": "v4.4.48", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "8e70c1cab07ac641b885ce80385b9824a293c623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/8e70c1cab07ac641b885ce80385b9824a293c623", + "reference": "8e70c1cab07ac641b885ce80385b9824a293c623", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/lock": "<4.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v4.4.48" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-10-26T16:02:45+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v4.4.44", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "1e866e9e5c1b22168e0ce5f0b467f19bba61266a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1e866e9e5c1b22168e0ce5f0b467f19bba61266a", + "reference": "1e866e9e5c1b22168e0ce5f0b467f19bba61266a", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/event-dispatcher-contracts": "^1.1", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "1.1" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/error-handler": "~3.4|~4.4", + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/service-contracts": "^1.1|^2", + "symfony/stopwatch": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.44" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-20T09:59:04+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v1.1.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/1d5cd762abaa6b2a4169d3e77610193a7157129e", + "reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "suggest": { + "psr/event-dispatcher": "", + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:41:36+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "4d216a2beef096edf040a070117c39ca2abce307" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d216a2beef096edf040a070117c39ca2abce307", + "reference": "4d216a2beef096edf040a070117c39ca2abce307", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.1.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-09-21T20:29:40+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-29T07:42:06+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a3016f5442e28386ded73c43a32a5b68586dd1c4", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/process", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/a6506e99cfad7059b1ab5cab395854a0a0c21292", + "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T17:24:16+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-30T19:17:29+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/266636bb8f3fbdccc302491df7b3a1b9a8c238a7", + "reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^1|^2|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.1.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-09-28T16:00:52+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.2", + "ext-dom": "*", + "ext-json": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/pandora_console/vendor/artica/phpchartjs/phpcs.xml b/pandora_console/vendor/artica/phpchartjs/phpcs.xml new file mode 100644 index 0000000000..1f87d5d9ab --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/phpcs.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + 4 + + + 4 + + + + src + diff --git a/pandora_console/vendor/artica/phpchartjs/phpunit.xml b/pandora_console/vendor/artica/phpchartjs/phpunit.xml new file mode 100644 index 0000000000..f1cf17d5bb --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/phpunit.xml @@ -0,0 +1,21 @@ + + + + + test/unit + + + + + src + + test + + + + + + + + diff --git a/pandora_console/vendor/artica/phpchartjs/src/ArraySerializableInterface.php b/pandora_console/vendor/artica/phpchartjs/src/ArraySerializableInterface.php new file mode 100644 index 0000000000..206ed81bec --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/ArraySerializableInterface.php @@ -0,0 +1,17 @@ + DataSet::class, + 'options' => Options::class, + ]; + + /** + * @var string + */ + protected $id; + + /** + * @var integer + */ + protected $height; + + /** + * @var integer + */ + protected $width; + + /** + * @var string + */ + protected $title; + + /** + * @var LabelsCollection + */ + protected $labels; + + /** + * @var PluginsCollection + */ + protected $plugins; + + /** + * @var Options + */ + protected $options; + + /** + * @var Defaults + */ + protected $defaults; + + /** + * @var DataSetCollection + */ + protected $dataSets; + + + /** + * @return string + */ + public function getId() + { + if (is_null($this->id)) { + $this->id = uniqid('chart'); + } + + return $this->id; + } + + + /** + * @param string $id + * + * @return Chart + */ + public function setId($id) + { + $this->id = strval($id); + + return $this; + } + + + /** + * @return integer + */ + public function getHeight() + { + return $this->height; + } + + + /** + * @param integer $height + * + * @return Chart + */ + public function setHeight($height) + { + $this->height = intval($height); + + return $this; + } + + + /** + * @return integer + */ + public function getWidth() + { + return $this->width; + } + + + /** + * @param integer $width + * + * @return Chart + */ + public function setWidth($width) + { + $this->width = intval($width); + + return $this; + } + + + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } + + + /** + * @param string $title + * + * @return Chart + */ + public function setTitle($title) + { + $this->title = strval($title); + + return $this; + } + + + /** + * @return LabelsCollection + */ + public function labels() + { + if (is_null($this->labels)) { + $this->labels = new LabelsCollection(); + } + + return $this->labels; + } + + + /** + * @param string $label + * + * @return $this + */ + public function addLabel($label) + { + $this->labels()->append($label); + + return $this; + } + + + /** + * @param $offset + * + * @return string|boolean + */ + public function getLabel($offset) + { + return $this->labels()->offsetGet($offset); + } + + + /** + * @return PluginsCollection + */ + public function plugins() + { + if (is_null($this->plugins)) { + $this->plugins = new PluginsCollection(); + } + + return $this->plugins; + } + + + /** + * @param string $plugin + * + * @return $this + */ + public function addPlugin($plugin) + { + $this->plugins()->append(new Expr(strval($plugin))); + + return $this; + } + + + /** + * @param $offset + * + * @return string|boolean + */ + public function getPlugin($offset) + { + return $this->plugins()->offsetGet($offset); + } + + + /** + * @return DataSetCollection + */ + public function dataSets() + { + if (is_null($this->dataSets)) { + $this->dataSets = new DataSetCollection(); + } + + return $this->dataSets; + } + + + /** + * @param DataSet $dataSet + * + * @return $this + */ + public function addDataSet(DataSet $dataSet) + { + $this->dataSets()->append($dataSet->setOwner($this)); + + return $this; + } + + + /** + * @param $offset + * + * @return DataSet|boolean + */ + public function getDataSet($offset) + { + return $this->dataSets()->offsetGet($offset); + } + + + /** + * @param boolean $pretty + * + * @return string + */ + public function render($pretty=false) + { + $renderer = new Html($this); + + return $renderer->render($pretty ? $renderer::RENDER_PRETTY : null); + } + + + /** + * @return DataSet + */ + public function createDataSet() + { + $datasetClass = static::MODEL['dataset']; + /* + @var \Artica\PHPChartJS\DataSet $dataSet + */ + $dataSet = new $datasetClass(); + $dataSet->setOwner($this); + + return $dataSet; + } + + + /** + * @return Options + */ + public function options() + { + if (is_null($this->options)) { + $optionsClass = static::MODEL['options']; + $this->options = new $optionsClass($this); + $this->options->setOwner($this); + } + + return $this->options; + } + + + /** + * @return Defaults + */ + public function defaults() + { + if (is_null($this->defaults)) { + $this->defaults = new Defaults($this); + } + + return $this->defaults; + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Chart/Bar.php b/pandora_console/vendor/artica/phpchartjs/src/Chart/Bar.php new file mode 100644 index 0000000000..a44cbe6126 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Chart/Bar.php @@ -0,0 +1,30 @@ + BarDataSet::class, + 'options' => BarOptions::class, + ]; +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Chart/Bubble.php b/pandora_console/vendor/artica/phpchartjs/src/Chart/Bubble.php new file mode 100644 index 0000000000..bd4fda09b9 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Chart/Bubble.php @@ -0,0 +1,30 @@ + BubbleDataSet::class, + 'options' => BubbleOptions::class, + ]; +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Chart/Doughnut.php b/pandora_console/vendor/artica/phpchartjs/src/Chart/Doughnut.php new file mode 100644 index 0000000000..58ff7d1380 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Chart/Doughnut.php @@ -0,0 +1,15 @@ + LineDataSet::class, + 'options' => LineOptions::class, + ]; +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Chart/Pie.php b/pandora_console/vendor/artica/phpchartjs/src/Chart/Pie.php new file mode 100644 index 0000000000..e593a95e15 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Chart/Pie.php @@ -0,0 +1,31 @@ + PieDataSet::class, + 'options' => PieOptions::class, + ]; +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Chart/PolarArea.php b/pandora_console/vendor/artica/phpchartjs/src/Chart/PolarArea.php new file mode 100644 index 0000000000..29a722a71c --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Chart/PolarArea.php @@ -0,0 +1,31 @@ + PolarAreaDataSet::class, + 'options' => PolarAreaOptions::class, + ]; +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Chart/Radar.php b/pandora_console/vendor/artica/phpchartjs/src/Chart/Radar.php new file mode 100644 index 0000000000..893c049e3b --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Chart/Radar.php @@ -0,0 +1,31 @@ + RadarDataSet::class, + 'options' => RadarOptions::class, + ]; +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Chart/Scatter.php b/pandora_console/vendor/artica/phpchartjs/src/Chart/Scatter.php new file mode 100644 index 0000000000..395325e508 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Chart/Scatter.php @@ -0,0 +1,31 @@ + ScatterDataSet::class, + 'options' => ScatterOptions::class, + ]; +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/ChartInterface.php b/pandora_console/vendor/artica/phpchartjs/src/ChartInterface.php new file mode 100644 index 0000000000..94c18c3b56 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/ChartInterface.php @@ -0,0 +1,51 @@ +owner = $chart; + + return $this; + } + + /** + * @return ChartInterface + */ + public function owner() + { + return $this->owner; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/ChartOwnedInterface.php b/pandora_console/vendor/artica/phpchartjs/src/ChartOwnedInterface.php new file mode 100644 index 0000000000..2dd25efc9c --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/ChartOwnedInterface.php @@ -0,0 +1,22 @@ +defaultFontColor; + } + + /** + * @param string $defaultFontColor + * + * @return $this + */ + public function setDefaultFontColor($defaultFontColor) + { + $this->defaultFontColor = strval($defaultFontColor); + + return $this; + } + + /** + * @return string + */ + public function getDefaultFontFamily() + { + return $this->defaultFontFamily; + } + + /** + * @param string $defaultFontFamily + * + * @return $this + */ + public function setDefaultFontFamily($defaultFontFamily) + { + $this->defaultFontFamily = strval($defaultFontFamily); + + return $this; + } + + /** + * @return int + */ + public function getDefaultFontSize() + { + return $this->defaultFontSize; + } + + /** + * @param int $defaultFontSize + * + * @return $this + */ + public function setDefaultFontSize($defaultFontSize) + { + $this->defaultFontSize = intval($defaultFontSize); + + return $this; + } + + /** + * @return string + */ + public function getDefaultFontStyle() + { + return $this->defaultFontStyle; + } + + /** + * @param string $defaultFontStyle + * + * @return $this + */ + public function setDefaultFontStyle($defaultFontStyle) + { + $this->defaultFontStyle = $defaultFontStyle; + + return $this; + } + + /** + * @return LayoutConfig + */ + public function layout() + { + if (is_null($this->layout)) { + $this->layout = new LayoutConfig(); + } + + return $this->layout; + } + + /** + * @return TooltipsConfig + */ + public function tooltips() + { + if (is_null($this->tooltips)) { + $this->tooltips = new TooltipsConfig(); + } + + return $this->tooltips; + } + + /** + * @return HoverConfig + */ + public function hover() + { + if (is_null($this->hover)) { + $this->hover = new HoverConfig(); + } + + return $this->hover; + } + + /** + * @return AnimationConfig + */ + public function animation() + { + if (is_null($this->animation)) { + $this->animation = new AnimationConfig(); + } + + return $this->animation; + } + + /** + * @return ElementsConfig + */ + public function elements() + { + if (is_null($this->elements)) { + $this->elements = new ElementsConfig(); + } + + return $this->elements; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/ConfigDefaults/HoverConfig.php b/pandora_console/vendor/artica/phpchartjs/src/ConfigDefaults/HoverConfig.php new file mode 100644 index 0000000000..3fadca4007 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/ConfigDefaults/HoverConfig.php @@ -0,0 +1,8 @@ +type; + } + + /** + * @param string $type + * + * @return $this + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * @return Data + */ + public function data() + { + if (is_null($this->data)) { + $this->data = new Data(); + } + + return $this->data; + } + + /** + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * @param string $label + * + * @return $this + */ + public function setLabel($label) + { + $this->label = strval($label); + + return $this; + } + + /** + * @return string + */ + public function getAxis() + { + return $this->axis; + } + + /** + * @param string $axis + * + * @return $this + */ + public function setAxis($axis) + { + $this->axis = $axis; + + return $this; + } + + /** + * @return string + */ + public function getXAxisID() + { + return $this->xAxisID; + } + + /** + * @param string $xAxisID + * + * @return $this + */ + public function setXAxisID($xAxisID) + { + $this->xAxisID = strval($xAxisID); + + return $this; + } + + /** + * @return string + */ + public function getYAxisID() + { + return $this->yAxisID; + } + + /** + * @param string $yAxisID + * + * @return $this + */ + public function setYAxisID($yAxisID) + { + $this->yAxisID = strval($yAxisID); + + return $this; + } + + /** + * @return string|string[] + */ + public function getBackgroundColor() + { + return $this->backgroundColor; + } + + /** + * @param string|string[] $backgroundColor + * + * @return $this + */ + public function setBackgroundColor($backgroundColor) + { + if (is_array($backgroundColor)) { + $backgroundColor = array_map('strval', $backgroundColor); + } + if (! is_array($backgroundColor)) { + $backgroundColor = strval($backgroundColor); + } + + $this->backgroundColor = $backgroundColor; + + return $this; + } + + /** + * @return string|string[] + */ + public function getBorderColor() + { + return $this->borderColor; + } + + /** + * @param string|string[] $borderColor + * + * @return $this + */ + public function setBorderColor($borderColor) + { + if (is_array($borderColor)) { + $borderColor = array_map('strval', $borderColor); + } + if (! is_array($borderColor)) { + $borderColor = strval($borderColor); + } + + $this->borderColor = $borderColor; + + return $this; + } + + /** + * @return int|int[] + */ + public function getBorderWidth() + { + return $this->borderWidth; + } + + /** + * @param int|int[] $borderWidth + * + * @return $this + */ + public function setBorderWidth($borderWidth) + { + if (is_array($borderWidth)) { + $borderWidth = array_map('intval', $borderWidth); + } + if (! is_array($borderWidth)) { + $borderWidth = intval($borderWidth); + } + + $this->borderWidth = $borderWidth; + + return $this; + } + + /** + * @return string + */ + public function getBorderSkipped() + { + return $this->borderSkipped; + } + + /** + * @param string $borderSkipped + * + * @return $this + */ + public function setBorderSkipped($borderSkipped) + { + $this->borderSkipped = strval($borderSkipped); + + return $this; + } + + /** + * @return string|string[] + */ + public function getHoverBackgroundColor() + { + return $this->hoverBackgroundColor; + } + + /** + * @param string|string[] $hoverBackgroundColor + * + * @return $this + */ + public function setHoverBackgroundColor($hoverBackgroundColor) + { + if (is_array($hoverBackgroundColor)) { + $hoverBackgroundColor = array_map('strval', $hoverBackgroundColor); + } + if (! is_array($hoverBackgroundColor)) { + $hoverBackgroundColor = strval($hoverBackgroundColor); + } + + $this->hoverBackgroundColor = $hoverBackgroundColor; + + return $this; + } + + /** + * @return string|string[] + */ + public function getHoverBorderColor() + { + return $this->hoverBorderColor; + } + + /** + * @param string|string[] $hoverBorderColor + * + * @return $this + */ + public function setHoverBorderColor($hoverBorderColor) + { + if (is_array($hoverBorderColor)) { + $hoverBorderColor = array_map('strval', $hoverBorderColor); + } + if (! is_array($hoverBorderColor)) { + $hoverBorderColor = strval($hoverBorderColor); + } + + $this->hoverBorderColor = $hoverBorderColor; + + return $this; + } + + /** + * @return int|int[] + */ + public function getHoverBorderWidth() + { + return $this->hoverBorderWidth; + } + + /** + * @param int|int[] $hoverBorderWidth + * + * @return $this + */ + public function setHoverBorderWidth($hoverBorderWidth) + { + if (is_array($hoverBorderWidth)) { + $hoverBorderWidth = array_map('intval', $hoverBorderWidth); + } + if (! is_array($hoverBorderWidth)) { + $hoverBorderWidth = intval($hoverBorderWidth); + } + + $this->hoverBorderWidth = $hoverBorderWidth; + + return $this; + } + + /** + * @return bool|null + */ + public function isHidden() + { + if (is_null($this->hidden)) { + $this->hidden = false; + } + + return $this->hidden; + } + + /** + * @param bool|null $hidden + * + * @return $this + */ + public function setHidden($hidden) + { + $this->hidden = is_null($hidden) ? null : boolval($hidden); + + return $this; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/DataSet/BarDataSet.php b/pandora_console/vendor/artica/phpchartjs/src/DataSet/BarDataSet.php new file mode 100644 index 0000000000..18c13b87a0 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/DataSet/BarDataSet.php @@ -0,0 +1,13 @@ +pointStyle; + } + + /** + * @param string $pointStyle + * + * @return \Artica\PHPChartJS\DataSet\BubbleDataSet + */ + public function setPointStyle($pointStyle) + { + $this->pointStyle = $pointStyle; + + return $this; + } + + /** + * @return int|null + */ + public function getRadius() + { + return $this->radius; + } + + /** + * @param int $radius + * + * @return \Artica\PHPChartJS\DataSet\BubbleDataSet + */ + public function setRadius($radius) + { + $this->radius = $radius; + + return $this; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/DataSet/LineDataSet.php b/pandora_console/vendor/artica/phpchartjs/src/DataSet/LineDataSet.php new file mode 100644 index 0000000000..014f528422 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/DataSet/LineDataSet.php @@ -0,0 +1,512 @@ +fill; + } + + /** + * @param bool|string $fill + * + * @return $this + */ + public function setFill($fill) + { + $this->fill = $fill; + + return $this; + } + + /** + * @return string + */ + public function getCubicInterpolationMode() + { + return $this->cubicInterpolationMode; + } + + /** + * @param string $cubicInterpolationMode + * + * @return $this + */ + public function setCubicInterpolationMode($cubicInterpolationMode) + { + $this->cubicInterpolationMode = $cubicInterpolationMode; + + return $this; + } + + /** + * @return int + */ + public function getLineTension() + { + return $this->lineTension; + } + + /** + * @param float $lineTension + * + * @return $this + */ + public function setLineTension($lineTension) + { + $this->lineTension = floatval($lineTension); + + return $this; + } + + /** + * @return string + */ + public function getBorderCapStyle() + { + return $this->borderCapStyle; + } + + /** + * @param string $borderCapStyle + * + * @return $this + */ + public function setBorderCapStyle($borderCapStyle) + { + $this->borderCapStyle = $borderCapStyle; + + return $this; + } + + /** + * @return array + */ + public function getBorderDash() + { + return $this->borderDash; + } + + /** + * @param array $borderDash + * + * @return $this + */ + public function setBorderDash($borderDash) + { + $this->borderDash = $borderDash; + + return $this; + } + + /** + * @return int + */ + public function getBorderDashOffset() + { + return $this->borderDashOffset; + } + + /** + * @param float $borderDashOffset + * + * @return $this + */ + public function setBorderDashOffset($borderDashOffset) + { + $this->borderDashOffset = floatval($borderDashOffset); + + return $this; + } + + /** + * @return string + */ + public function getBorderJoinStyle() + { + return $this->borderJoinStyle; + } + + /** + * @param string $borderJoinStyle + * + * @return $this + */ + public function setBorderJoinStyle($borderJoinStyle) + { + $this->borderJoinStyle = $borderJoinStyle; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointBorderColor() + { + return $this->pointBorderColor; + } + + /** + * @param string|string[] $pointBorderColor + * + * @return $this + */ + public function setPointBorderColor($pointBorderColor) + { + $this->pointBorderColor = $pointBorderColor; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointBackgroundColor() + { + return $this->pointBackgroundColor; + } + + /** + * @param string|string[] $pointBackgroundColor + * + * @return $this + */ + public function setPointBackgroundColor($pointBackgroundColor) + { + $this->pointBackgroundColor = $pointBackgroundColor; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointBorderWidth() + { + return $this->pointBorderWidth; + } + + /** + * @param int|int[] $pointBorderWidth + * + * @return $this + */ + public function setPointBorderWidth($pointBorderWidth) + { + $this->pointBorderWidth = $pointBorderWidth; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointRadius() + { + return $this->pointRadius; + } + + /** + * @param int|int[] $pointRadius + * + * @return $this + */ + public function setPointRadius($pointRadius) + { + $this->pointRadius = $pointRadius; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointHoverRadius() + { + return $this->pointHoverRadius; + } + + /** + * @param int|int[] $pointHoverRadius + * + * @return $this + */ + public function setPointHoverRadius($pointHoverRadius) + { + $this->pointHoverRadius = $pointHoverRadius; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointHitRadius() + { + return $this->pointHitRadius; + } + + /** + * @param int|int[] $pointHitRadius + * + * @return $this + */ + public function setPointHitRadius($pointHitRadius) + { + $this->pointHitRadius = $pointHitRadius; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointHoverBackgroundColor() + { + return $this->pointHoverBackgroundColor; + } + + /** + * @param string|string[] $pointHoverBackgroundColor + * + * @return $this + */ + public function setPointHoverBackgroundColor($pointHoverBackgroundColor) + { + $this->pointHoverBackgroundColor = $pointHoverBackgroundColor; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointHoverBorderColor() + { + return $this->pointHoverBorderColor; + } + + /** + * @param string|string[] $pointHoverBorderColor + * + * @return $this + */ + public function setPointHoverBorderColor($pointHoverBorderColor) + { + $this->pointHoverBorderColor = $pointHoverBorderColor; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointHoverBorderWidth() + { + return $this->pointHoverBorderWidth; + } + + /** + * @param int|int[] $pointHoverBorderWidth + * + * @return $this + */ + public function setPointHoverBorderWidth($pointHoverBorderWidth) + { + $this->pointHoverBorderWidth = $pointHoverBorderWidth; + + return $this; + } + + /** + * @return mixed + */ + public function getPointStyle() + { + return $this->pointStyle; + } + + /** + * @param mixed $pointStyle + * + * @return $this + */ + public function setPointStyle($pointStyle) + { + $this->pointStyle = $pointStyle; + + return $this; + } + + /** + * @return bool + */ + public function isShowLine() + { + return $this->showLine; + } + + /** + * @param bool $showLine + * + * @return $this + */ + public function setShowLine($showLine) + { + $this->showLine = $showLine; + + return $this; + } + + /** + * @return bool + */ + public function isSpanGaps() + { + return $this->spanGaps; + } + + /** + * @param bool $spanGaps + * + * @return $this + */ + public function setSpanGaps($spanGaps) + { + $this->spanGaps = $spanGaps; + + return $this; + } + + /** + * @return bool + */ + public function isSteppedLine() + { + return $this->steppedLine; + } + + /** + * @param bool $steppedLine + * + * @return $this + */ + public function setSteppedLine($steppedLine) + { + $this->steppedLine = $steppedLine; + + return $this; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/DataSet/PieDataSet.php b/pandora_console/vendor/artica/phpchartjs/src/DataSet/PieDataSet.php new file mode 100644 index 0000000000..bacd5594cd --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/DataSet/PieDataSet.php @@ -0,0 +1,13 @@ +fill; + } + + /** + * @param bool $fill + * + * @return $this + */ + public function setFill($fill) + { + $this->fill = $fill; + + return $this; + } + + /** + * @return int + */ + public function getLineTension() + { + return $this->lineTension; + } + + /** + * @param float $lineTension + * + * @return $this + */ + public function setLineTension($lineTension) + { + $this->lineTension = $lineTension; + + return $this; + } + + /** + * @return string + */ + public function getBorderCapStyle() + { + return $this->borderCapStyle; + } + + /** + * @param string $borderCapStyle + * + * @return $this + */ + public function setBorderCapStyle($borderCapStyle) + { + $this->borderCapStyle = $borderCapStyle; + + return $this; + } + + /** + * @return int[] + */ + public function getBorderDash() + { + return $this->borderDash; + } + + /** + * @param int[] $borderDash + * + * @return $this + */ + public function setBorderDash($borderDash) + { + $this->borderDash = $borderDash; + + return $this; + } + + /** + * @return int + */ + public function getBorderDashOffset() + { + return $this->borderDashOffset; + } + + /** + * @param int $borderDashOffset + * + * @return $this + */ + public function setBorderDashOffset($borderDashOffset) + { + $this->borderDashOffset = $borderDashOffset; + + return $this; + } + + /** + * @return string + */ + public function getBorderJoinStyle() + { + return $this->borderJoinStyle; + } + + /** + * @param string $borderJoinStyle + * + * @return $this + */ + public function setBorderJoinStyle($borderJoinStyle) + { + $this->borderJoinStyle = $borderJoinStyle; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointBorderColor() + { + return $this->pointBorderColor; + } + + /** + * @param string|string[] $pointBorderColor + * + * @return $this + */ + public function setPointBorderColor($pointBorderColor) + { + $this->pointBorderColor = $pointBorderColor; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointBackgroundColor() + { + return $this->pointBackgroundColor; + } + + /** + * @param string|string[] $pointBackgroundColor + * + * @return $this + */ + public function setPointBackgroundColor($pointBackgroundColor) + { + $this->pointBackgroundColor = $pointBackgroundColor; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointBorderWidth() + { + return $this->pointBorderWidth; + } + + /** + * @param int|int[] $pointBorderWidth + * + * @return $this + */ + public function setPointBorderWidth($pointBorderWidth) + { + $this->pointBorderWidth = $pointBorderWidth; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointRadius() + { + return $this->pointRadius; + } + + /** + * @param int|int[] $pointRadius + * + * @return $this + */ + public function setPointRadius($pointRadius) + { + $this->pointRadius = $pointRadius; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointHoverRadius() + { + return $this->pointHoverRadius; + } + + /** + * @param int|int[] $pointHoverRadius + * + * @return $this + */ + public function setPointHoverRadius($pointHoverRadius) + { + $this->pointHoverRadius = $pointHoverRadius; + + return $this; + } + + /** + * @return int|int[] + */ + public function getHitRadius() + { + return $this->hitRadius; + } + + /** + * @param int|int[] $hitRadius + * + * @return $this + */ + public function setHitRadius($hitRadius) + { + $this->hitRadius = $hitRadius; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointHoverBackgroundColor() + { + return $this->pointHoverBackgroundColor; + } + + /** + * @param string|string[] $pointHoverBackgroundColor + * + * @return $this + */ + public function setPointHoverBackgroundColor($pointHoverBackgroundColor) + { + $this->pointHoverBackgroundColor = $pointHoverBackgroundColor; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointHoverBorderColor() + { + return $this->pointHoverBorderColor; + } + + /** + * @param string|string[] $pointHoverBorderColor + * + * @return $this + */ + public function setPointHoverBorderColor($pointHoverBorderColor) + { + $this->pointHoverBorderColor = $pointHoverBorderColor; + + return $this; + } + + /** + * @return int|int[] + */ + public function getPointHoverBorderWidth() + { + return $this->pointHoverBorderWidth; + } + + /** + * @param int|int[] $pointHoverBorderWidth + * + * @return $this + */ + public function setPointHoverBorderWidth($pointHoverBorderWidth) + { + $this->pointHoverBorderWidth = $pointHoverBorderWidth; + + return $this; + } + + /** + * @return string|string[] + */ + public function getPointStyle() + { + return $this->pointStyle; + } + + /** + * @param string|string[] $pointStyle + * + * @return $this + */ + public function setPointStyle($pointStyle) + { + $this->pointStyle = $pointStyle; + + return $this; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/DataSet/ScatterDataSet.php b/pandora_console/vendor/artica/phpchartjs/src/DataSet/ScatterDataSet.php new file mode 100644 index 0000000000..0301875e5e --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/DataSet/ScatterDataSet.php @@ -0,0 +1,11 @@ +data as $row) { + /** @var DataSet $row */ + $rows[] = $row->getArrayCopy(); + } + + return $rows; + } + + /** + * @return array + */ + public function jsonSerialize() + { + $rows = []; + foreach ($this->data as $row) { + /** @var DataSet $row */ + $rows[] = $row->jsonSerialize(); + } + + return $rows; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Defaults.php b/pandora_console/vendor/artica/phpchartjs/src/Defaults.php new file mode 100644 index 0000000000..972b1bfb8c --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Defaults.php @@ -0,0 +1,66 @@ +font) === false) { + $this->font = new Fonts(); + } + + return $this->font; + } + + + /** + * Return Watermark. + * + * @return Watermark + */ + public function getWatermark() + { + if (isset($this->watermark) === false) { + $this->watermark = new WaterMark(); + } + + return $this->watermark; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Delegate/ArraySerializable.php b/pandora_console/vendor/artica/phpchartjs/src/Delegate/ArraySerializable.php new file mode 100644 index 0000000000..e8ed48fd75 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Delegate/ArraySerializable.php @@ -0,0 +1,36 @@ +getArrayCopy(); + } + } + + return $value; + }, get_object_vars($this)); + + // Filter out null values and return the remaining. + return array_filter($currentValues, function ($value, $key) { + return ! is_null($value) && $key !== 'owner'; + }, ARRAY_FILTER_USE_BOTH); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Delegate/JsonSerializable.php b/pandora_console/vendor/artica/phpchartjs/src/Delegate/JsonSerializable.php new file mode 100644 index 0000000000..abf59aa9f6 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Delegate/JsonSerializable.php @@ -0,0 +1,35 @@ +jsonSerialize(); + } elseif ($value instanceof ArraySerializableInterface) { + return $value->getArrayCopy(); + } + + return $value; + }, $this->getArrayCopy()); + } + + /** + * @return array + */ + abstract public function getArrayCopy(); +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Delegate/NumberUtils.php b/pandora_console/vendor/artica/phpchartjs/src/Delegate/NumberUtils.php new file mode 100644 index 0000000000..027b004dba --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Delegate/NumberUtils.php @@ -0,0 +1,40 @@ +data; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options.php b/pandora_console/vendor/artica/phpchartjs/src/Options.php new file mode 100644 index 0000000000..c8c6c8037b --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options.php @@ -0,0 +1,335 @@ +layout)) { + $this->layout = new Layout(); + } + + return $this->layout; + } + + + /** + * @return Elements + */ + public function getElements() + { + if (is_null($this->elements)) { + $this->elements = new Elements(); + } + + return $this->elements; + } + + + /** + * @return Title + */ + public function getTitle() + { + if (is_null($this->title)) { + $this->title = new Title(); + } + + return $this->title; + } + + + /** + * @return Hover + */ + public function getHover() + { + if (is_null($this->hover)) { + $this->hover = new Hover(); + } + + return $this->hover; + } + + + /** + * @return \Laminas\Json\Expr + */ + public function getOnClick() + { + return $this->onClick; + } + + + /** + * @param string $onClick + * + * @return $this + */ + public function setOnClick($onClick) + { + $this->onClick = new Expr(strval($onClick)); + + return $this; + } + + + /** + * @return Scales + */ + public function getScales() + { + if (is_null($this->scales)) { + $this->scales = new Scales(); + } + + return $this->scales; + } + + + /** + * @return Animation + */ + public function getAnimation() + { + if (is_null($this->animation)) { + $this->animation = new Animation(); + } + + return $this->animation; + } + + + /** + * @return bool + */ + public function disableAnimation() + { + $this->animation = false; + + return $this->animation; + } + + + /** + * @return Legend + */ + public function getLegend() + { + if (is_null($this->legend)) { + $this->legend = new Legend(); + } + + return $this->legend; + } + + + /** + * Get plugin. + * + * @return Plugin + */ + public function getPlugins() + { + if ($this->plugins === null) { + $this->plugins = new Plugins(); + } + + return $this->plugins; + } + + + /** + * @return Tooltips + */ + public function getTooltips() + { + if (is_null($this->tooltips)) { + $this->tooltips = new Tooltips(); + } + + return $this->tooltips; + } + + + /** + * @return boolean + */ + public function isResponsive() + { + if (is_null($this->responsive)) { + $this->responsive = true; + } + + return $this->responsive; + } + + + /** + * @param boolean $flag + * + * @return $this + */ + public function setResponsive($flag) + { + $this->responsive = boolval($flag); + + return $this; + } + + + /** + * @return boolean + */ + public function isMaintainAspectRatio() + { + if (is_null($this->maintainAspectRatio)) { + $this->maintainAspectRatio = true; + } + + return $this->maintainAspectRatio; + } + + + /** + * @param boolean $flag + * + * @return $this + */ + public function setMaintainAspectRatio($flag) + { + $this->maintainAspectRatio = boolval($flag); + + return $this; + } + + + /** + * Get Index axis. + * + * @return string + */ + public function getIndexAxis() + { + return $this->indexAxis; + } + + + /** + * Set Index Axis. + * + * @param string $indexAxis Index Axis. + * + * @return $this + */ + public function setIndexAxis($indexAxis) + { + $this->indexAxis = $indexAxis; + + return $this; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Animation.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Animation.php new file mode 100644 index 0000000000..8293ecd1b3 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Animation.php @@ -0,0 +1,126 @@ +duration; + } + + /** + * @param int $duration + * + * @return $this + */ + public function setDuration($duration) + { + $this->duration = $duration; + + return $this; + } + + /** + * @return string + */ + public function getEasing() + { + return $this->easing; + } + + /** + * @param string $easing + * + * @return $this + */ + public function setEasing($easing) + { + $this->easing = $easing; + + return $this; + } + + /** + * @return Expr + */ + public function getOnProgress() + { + return $this->onProgress; + } + + /** + * @param string $onProgress + * + * @return $this + */ + public function setOnProgress($onProgress) + { + $this->onProgress = new Expr(strval($onProgress)); + + return $this; + } + + /** + * @return Expr + */ + public function getOnComplete() + { + return $this->onComplete; + } + + /** + * @param string $onComplete + * + * @return $this + */ + public function setOnComplete($onComplete) + { + $this->onComplete = new Expr(strval($onComplete)); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Animation/PieAnimation.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Animation/PieAnimation.php new file mode 100644 index 0000000000..fd20911ed2 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Animation/PieAnimation.php @@ -0,0 +1,62 @@ +animateRotate; + } + + /** + * @param bool $animateRotate + * + * @return PieAnimation + */ + public function setAnimateRotate($animateRotate) + { + $this->animateRotate = $animateRotate; + + return $this; + } + + /** + * @return bool + */ + public function isAnimateScale() + { + return $this->animateScale; + } + + /** + * @param bool $animateScale + * + * @return PieAnimation + */ + public function setAnimateScale($animateScale) + { + $this->animateScale = $animateScale; + + return $this; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/BarOptions.php b/pandora_console/vendor/artica/phpchartjs/src/Options/BarOptions.php new file mode 100644 index 0000000000..6d3d42cd97 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/BarOptions.php @@ -0,0 +1,14 @@ +display; + } + + + /** + * @param string $display + * + * @return $this + */ + public function setDisplay($display) + { + $this->display = strval($display); + + return $this; + } + + + /** + * @return string + */ + public function getColor() + { + return $this->color; + } + + + /** + * @param string $color + * + * @return $this + */ + public function setColor($color) + { + $this->color = strval($color); + + return $this; + } + + + /** + * @return string + */ + public function getAnchor() + { + return $this->anchor; + } + + + /** + * @param string $anchor + * + * @return $this + */ + public function setAnchor($anchor) + { + $this->anchor = strval($anchor); + + return $this; + } + + + /** + * @return integer + */ + public function getOffset() + { + return $this->offset; + } + + + /** + * @param integer $offset + * + * @return $this + */ + public function setOffset($offset) + { + $this->offset = intval($offset); + + return $this; + } + + + /** + * @return string + */ + public function getAlign() + { + return $this->align; + } + + + /** + * @param string $align + * + * @return $this + */ + public function setAlign($align) + { + $this->align = strval($align); + + return $this; + } + + + /** + * @return Expr + */ + public function getFormatter() + { + return $this->formatter; + } + + + /** + * @param string $formatter + * + * @return $this + */ + public function setFormatter($formatter) + { + $this->formatter = new Expr(strval($formatter)); + + return $this; + } + + + /** + * @return boolean + */ + public function getClamp() + { + return $this->clamp; + } + + + /** + * @param boolean $clamp + * + * @return $this + */ + public function setClamp($clamp) + { + $this->clamp = boolval($clamp); + + return $this; + } + + + /** + * @return boolean + */ + public function getClip() + { + return $this->clip; + } + + + /** + * @param boolean $clip + * + * @return $this + */ + public function setClip($clip) + { + $this->clip = boolval($clip); + + return $this; + } + + /** + * Return Font. + * + * @return Font + */ + public function getFonts() + { + if (isset($this->font) === false) { + $this->font = new Fonts(); + } + + return $this->font; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Elements.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements.php new file mode 100644 index 0000000000..116fc46758 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements.php @@ -0,0 +1,129 @@ +rectangle; + } + + /** + * @return Rectangle + */ + public function rectangle() + { + if (is_null($this->rectangle)) { + $this->rectangle = new Rectangle(); + } + + return $this->rectangle; + } + + /** + * @return Line + */ + public function getLine() + { + return $this->line; + } + + /** + * @return Line + */ + public function line() + { + if (is_null($this->line)) { + $this->line = new Line(); + } + + return $this->line; + } + + /** + * @return Point + */ + public function getPoint() + { + return $this->point; + } + + /** + * @return Point + */ + public function point() + { + if (is_null($this->point)) { + $this->point = new Point(); + } + + return $this->point; + } + + /** + * @return Arc + */ + public function getArc() + { + return $this->arc; + } + + /** + * @return Arc + */ + public function arc() + { + if (is_null($this->arc)) { + $this->arc = new Arc(); + } + + return $this->arc; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Arc.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Arc.php new file mode 100644 index 0000000000..a44cb9e295 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Arc.php @@ -0,0 +1,109 @@ +backgroundColor; + } + + /** + * @param string $backgroundColor + * + * @return Arc + */ + public function setBackgroundColor($backgroundColor) + { + $this->backgroundColor = is_null($backgroundColor) ? null : strval($backgroundColor); + + return $this; + } + + /** + * @return string + */ + public function getBorderColor() + { + return $this->borderColor; + } + + /** + * @param string $borderColor + * + * @return Arc + */ + public function setBorderColor($borderColor) + { + $this->borderColor = is_null($borderColor) ? null : strval($borderColor); + + return $this; + } + + /** + * @return int + */ + public function getBorderWidth() + { + return $this->borderWidth; + } + + /** + * @param int $borderWidth + * + * @return Arc + */ + public function setBorderWidth($borderWidth) + { + $this->borderWidth = intval($borderWidth); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Line.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Line.php new file mode 100644 index 0000000000..66f9bf3cd8 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Line.php @@ -0,0 +1,323 @@ +tension; + } + + /** + * @param float $tension + * @return Line + */ + public function setTension($tension) + { + $this->tension = floatval($tension); + return $this; + } + + /** + * @return string + */ + public function getBackgroundColor() + { + return $this->backgroundColor; + } + + /** + * @param string $backgroundColor + * @return Line + */ + public function setBackgroundColor($backgroundColor) + { + $this->backgroundColor = is_null($backgroundColor) ? null : strval($backgroundColor); + return $this; + } + + /** + * @return int + */ + public function getBorderWidth() + { + return $this->borderWidth; + } + + /** + * @param int $borderWidth + * @return Line + */ + public function setBorderWidth($borderWidth) + { + $this->borderWidth = intval($borderWidth); + return $this; + } + + /** + * @return string + */ + public function getBorderColor() + { + return $this->borderColor; + } + + /** + * @param string $borderColor + * @return Line + */ + public function setBorderColor($borderColor) + { + $this->borderColor = is_null($borderColor) ? null : strval($borderColor); + return $this; + } + + /** + * @return string + */ + public function getBorderCapStyle() + { + return $this->borderCapStyle; + } + + /** + * @param string $borderCapStyle + * @return Line + */ + public function setBorderCapStyle($borderCapStyle) + { + $this->borderCapStyle = is_null($borderCapStyle) ? null : strval($borderCapStyle); + return $this; + } + + /** + * @return int[] + */ + public function getBorderDash() + { + return $this->borderDash; + } + + /** + * @param int[] $borderDash + * @return Line + */ + public function setBorderDash($borderDash) + { + if (is_array($borderDash)) { + array_walk_recursive( + $borderDash, + function (&$value) { + $value = intval($value); + } + ); + $this->borderDash = $borderDash; + } + return $this; + } + + /** + * @return float + */ + public function getBorderDashOffset() + { + return $this->borderDashOffset; + } + + /** + * @param float $borderDashOffset + * @return Line + */ + public function setBorderDashOffset($borderDashOffset) + { + $this->borderDashOffset = floatval($borderDashOffset); + return $this; + } + + /** + * @return string + */ + public function getBorderJoinStyle() + { + return $this->borderJoinStyle; + } + + /** + * @param string $borderJoinStyle + * @return Line + */ + public function setBorderJoinStyle($borderJoinStyle) + { + $this->borderJoinStyle = is_null($borderJoinStyle) ? null : strval($borderJoinStyle); + return $this; + } + + /** + * @return bool + */ + public function isCapBezierPoints() + { + return $this->capBezierPoints; + } + + /** + * @param bool $capBezierPoints + * @return Line + */ + public function setCapBezierPoints($capBezierPoints) + { + $this->capBezierPoints = boolval($capBezierPoints); + return $this; + } + + /** + * @return bool|string + */ + public function getFill() + { + return $this->fill; + } + + /** + * @param bool|string $fill + * @return Line + */ + public function setFill($fill) + { + $this->fill = is_null($fill) ? null : (is_bool($fill) ? $fill : strval($fill)); + return $this; + } + + /** + * @return bool + */ + public function isStepped() + { + return $this->stepped; + } + + /** + * @param bool $stepped + * @return Line + */ + public function setStepped($stepped) + { + $this->stepped = boolval($stepped); + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Point.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Point.php new file mode 100644 index 0000000000..3873656687 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Point.php @@ -0,0 +1,288 @@ +radius; + } + + /** + * @param int $radius + * + * @return Point + */ + public function setRadius($radius) + { + $this->radius = intval($radius); + + return $this; + } + + /** + * @return string + */ + public function getPointStyle() + { + return $this->pointStyle; + } + + /** + * @param string $pointStyle + * + * @return Point + */ + public function setPointStyle($pointStyle) + { + $this->pointStyle = is_null($pointStyle) ? null : strval($pointStyle); + + return $this; + } + + /** + * @return int + */ + public function getRotation() + { + return $this->rotation; + } + + /** + * @param int $rotation + * + * @return Point + */ + public function setRotation($rotation) + { + $this->rotation = intval($rotation); + + return $this; + } + + /** + * @return string + */ + public function getBackgroundColor() + { + return $this->backgroundColor; + } + + /** + * @param string $backgroundColor + * + * @return Point + */ + public function setBackgroundColor($backgroundColor) + { + $this->backgroundColor = is_null($backgroundColor) ? null : strval($backgroundColor); + + return $this; + } + + /** + * @return int + */ + public function getBorderWidth() + { + return $this->borderWidth; + } + + /** + * @param int $borderWidth + * + * @return Point + */ + public function setBorderWidth($borderWidth) + { + $this->borderWidth = intval($borderWidth); + + return $this; + } + + /** + * @return string + */ + public function getBorderColor() + { + return $this->borderColor; + } + + /** + * @param string $borderColor + * + * @return Point + */ + public function setBorderColor($borderColor) + { + $this->borderColor = is_null($borderColor) ? null : strval($borderColor); + + return $this; + } + + /** + * @return int + */ + public function getHitRadius() + { + return $this->hitRadius; + } + + /** + * @param int $hitRadius + * + * @return Point + */ + public function setHitRadius($hitRadius) + { + $this->hitRadius = intval($hitRadius); + + return $this; + } + + /** + * @return int + */ + public function getHoverRadius() + { + return $this->hoverRadius; + } + + /** + * @param int $hoverRadius + * + * @return Point + */ + public function setHoverRadius($hoverRadius) + { + $this->hoverRadius = intval($hoverRadius); + + return $this; + } + + /** + * @return int + */ + public function getHoverBorderWidth() + { + return $this->hoverBorderWidth; + } + + /** + * @param int $hoverBorderWidth + * + * @return Point + */ + public function setHoverBorderWidth($hoverBorderWidth) + { + $this->hoverBorderWidth = intval($hoverBorderWidth); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Rectangle.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Rectangle.php new file mode 100644 index 0000000000..ccd35f73a0 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Elements/Rectangle.php @@ -0,0 +1,142 @@ +backgroundColor; + } + + /** + * @param string $backgroundColor + * + * @return Rectangle + */ + public function setBackgroundColor($backgroundColor) + { + $this->backgroundColor = is_null($backgroundColor) ? null : strval($backgroundColor); + + return $this; + } + + /** + * @return int + */ + public function getBorderWidth() + { + return $this->borderWidth; + } + + /** + * @param int $borderWidth + * + * @return Rectangle + */ + public function setBorderWidth($borderWidth) + { + $this->borderWidth = intval($borderWidth); + + return $this; + } + + /** + * @return string + */ + public function getBorderColor() + { + return $this->borderColor; + } + + /** + * @param string $borderColor + * + * @return Rectangle + */ + public function setBorderColor($borderColor) + { + $this->borderColor = is_null($borderColor) ? null : strval($borderColor); + + return $this; + } + + /** + * @return string + */ + public function getBorderSkipped() + { + return $this->borderSkipped; + } + + /** + * @param string $borderSkipped + * + * @return Rectangle + */ + public function setBorderSkipped($borderSkipped) + { + $this->borderSkipped = is_null($borderSkipped) ? null : strval($borderSkipped); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Fonts.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Fonts.php new file mode 100644 index 0000000000..5f5a7c7746 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Fonts.php @@ -0,0 +1,163 @@ +size; + } + + + /** + * @param integer $size + * + * @return $this + */ + public function setSize($size) + { + $this->size = intval($size); + + return $this; + } + + + /** + * @return string + */ + public function getFamily() + { + return $this->family; + } + + + /** + * @param string $family + * + * @return $this + */ + public function setFamily($family) + { + $this->family = strval($family); + + return $this; + } + + + /** + * @return string + */ + public function getColor() + { + return $this->color; + } + + + /** + * @param string $color + * + * @return $this + */ + public function setColor($color) + { + $this->color = strval($color); + + return $this; + } + + + /** + * @return string + */ + public function getStyle() + { + return $this->style; + } + + + /** + * @param string $style + * + * @return $this + */ + public function setStyle($style) + { + $this->style = strval($style); + + return $this; + } + + + /** + * @return string + */ + public function getWeight() + { + return $this->weight; + } + + + /** + * @param string $weight + * + * @return $this + */ + public function setWeight($weight) + { + $this->weight = strval($weight); + + return $this; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Hover.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Hover.php new file mode 100644 index 0000000000..36cf1474f1 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Hover.php @@ -0,0 +1,134 @@ +mode; + } + + /** + * @param string $mode + * + * @return $this + */ + public function setMode($mode) + { + $this->mode = strval($mode); + + return $this; + } + + /** + * @return bool + */ + public function isIntersect() + { + return $this->intersect; + } + + /** + * @return bool + */ + public function getIntersect() + { + return $this->intersect; + } + + /** + * @param bool $intersect + * + * @return $this + */ + public function setIntersect($intersect) + { + $this->intersect = ! ! $intersect; + + return $this; + } + + /** + * @return int + */ + public function getAnimationDuration() + { + return $this->animationDuration; + } + + /** + * @param int $animationDuration + * + * @return $this + */ + public function setAnimationDuration($animationDuration) + { + $this->animationDuration = intval($animationDuration); + + return $this; + } + + /** + * @return \Laminas\Json\Expr + */ + public function getOnHover() + { + return $this->onHover; + } + + /** + * @param Expr $onHover + * + * @return $this + */ + public function setOnHover($onHover) + { + $this->onHover = new Expr(strval($onHover)); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Layout.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Layout.php new file mode 100644 index 0000000000..b41a20cc37 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Layout.php @@ -0,0 +1,59 @@ +padding = intval($padding); + } + + /** + * @return int|Padding + */ + public function getPadding() + { + return $this->padding; + } + + /** + * @return Padding + */ + public function padding() + { + if (is_null($this->padding)) { + $this->padding = new Padding(); + } + + return $this->padding; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Layout/Padding.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Layout/Padding.php new file mode 100644 index 0000000000..0b62c162e4 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Layout/Padding.php @@ -0,0 +1,124 @@ +bottom; + } + + /** + * @param int $bottom + * + * @return $this + */ + public function setBottom($bottom) + { + $this->bottom = intval($bottom); + + return $this; + } + + /** + * @return int + */ + public function getLeft() + { + return $this->left; + } + + /** + * @param int $left + * + * @return $this + */ + public function setLeft($left) + { + $this->left = intval($left); + + return $this; + } + + /** + * @return int + */ + public function getRight() + { + return $this->right; + } + + /** + * @param int $right + * + * @return $this + */ + public function setRight($right) + { + $this->right = intval($right); + + return $this; + } + + /** + * @return int + */ + public function getTop() + { + return $this->top; + } + + /** + * @param int $top + * + * @return $this + */ + public function setTop($top) + { + $this->top = intval($top); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Legend.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Legend.php new file mode 100644 index 0000000000..0cb0ff3352 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Legend.php @@ -0,0 +1,244 @@ +display; + } + + + /** + * @param boolean $display + * + * @return $this + */ + public function setDisplay($display) + { + $this->display = boolval($display); + + return $this; + } + + + /** + * @return string + */ + public function getPosition() + { + return $this->position; + } + + + /** + * @param string $position + * + * @return $this + */ + public function setPosition($position) + { + $this->position = strval($position); + + return $this; + } + + + /** + * @return boolean + */ + public function isFullWidth() + { + return $this->fullWidth; + } + + + /** + * @param boolean $fullWidth + * + * @return $this + */ + public function setFullWidth($fullWidth) + { + $this->fullWidth = boolval($fullWidth); + + return $this; + } + + + /** + * @return Expr + */ + public function getOnClick() + { + return $this->onClick; + } + + + /** + * @param string $onClick + * + * @return $this + */ + public function setOnClick($onClick) + { + $this->onClick = new Expr(strval($onClick)); + + return $this; + } + + + /** + * @return Expr + */ + public function getOnHover() + { + return $this->onHover; + } + + + /** + * @param string $onHover + * + * @return $this + */ + public function setOnHover($onHover) + { + $this->onHover = new Expr(strval($onHover)); + + return $this; + } + + + /** + * @return Labels + */ + public function labels() + { + if (is_null($this->labels)) { + $this->labels = new LegendLabels(); + } + + return $this->labels; + } + + + /** + * @return boolean + */ + public function isReverse() + { + return $this->reverse; + } + + + /** + * @param boolean $reverse + * + * @return $this + */ + public function setReverse($reverse) + { + $this->reverse = boolval($reverse); + + return $this; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + + /** + * Get align. + * + * @return string + */ + public function getAlign() + { + return $this->align; + } + + + /** + * Set align. + * + * @param string $align Align. + * + * @return $this + */ + public function setAlign(string $align) + { + $this->align = $align; + + return $this; + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Legend/Labels.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Legend/Labels.php new file mode 100644 index 0000000000..960de236ef --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Legend/Labels.php @@ -0,0 +1,158 @@ +boxWidth; + } + + + /** + * @param integer $boxWidth + * + * @return Labels + */ + public function setBoxWidth($boxWidth) + { + $this->boxWidth = intval($boxWidth); + + return $this; + } + + + /** + * Return Font. + * + * @return Font + */ + public function getFonts() + { + if (isset($this->font) === false) { + $this->font = new Fonts(); + } + + return $this->font; + } + + + /** + * @return integer + */ + public function getPadding() + { + return $this->padding; + } + + + /** + * @param integer $padding + * + * @return Labels + */ + public function setPadding($padding) + { + $this->padding = intval($padding); + + return $this; + } + + + /** + * @return Expr + */ + public function getGenerateLabels() + { + return $this->generateLabels; + } + + + /** + * @param string $generateLabels + * + * @return Labels + */ + public function setGenerateLabels($generateLabels) + { + $this->generateLabels = new Expr(strval($generateLabels)); + + return $this; + } + + + /** + * @return boolean + */ + public function isUsePointStyle() + { + return $this->usePointStyle; + } + + + /** + * @param boolean $usePointStyle + * + * @return Labels + */ + public function setUsePointStyle($usePointStyle) + { + $this->usePointStyle = !!$usePointStyle; + + return $this; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Legend/PieLegend.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Legend/PieLegend.php new file mode 100644 index 0000000000..bbec2be898 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Legend/PieLegend.php @@ -0,0 +1,13 @@ +animation)) { + $this->animation = new PieAnimation(); + } + + return $this->animation; + } + + /** + * @return int + */ + public function getCutoutPercentage() + { + return $this->cutoutPercentage; + } + + /** + * @param int $cutoutPercentage + * + * @return $this + */ + public function setCutoutPercentage($cutoutPercentage) + { + $this->cutoutPercentage = $cutoutPercentage; + + return $this; + } + + /** + * @return float + */ + public function getRotation() + { + return $this->rotation; + } + + /** + * @param float $rotation + * + * @return $this + */ + public function setRotation($rotation) + { + $this->rotation = $rotation; + + return $this; + } + + /** + * @return float + */ + public function getCircumference() + { + return $this->circumference; + } + + /** + * @param float $circumference + * + * @return $this + */ + public function setCircumference($circumference) + { + $this->circumference = $circumference; + + return $this; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Plugins.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Plugins.php new file mode 100644 index 0000000000..f1dc674a2a --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Plugins.php @@ -0,0 +1,73 @@ +legend) === false) { + $this->legend = new Legend(); + } + + return $this->legend; + } + + /** + * Return Title. + * + * @return Title + */ + public function getTitle() + { + if (isset($this->title) === false) { + $this->title = new Title(); + } + + return $this->title; + } + + + /** + * Return Data label. + * + * @return DataLabel + */ + public function getDataLabel() + { + if (isset($this->datalabels) === false) { + $this->datalabels = new DataLabel(); + } + + return $this->datalabels; + } + + + /** + * Serialize. + * + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/PolarAreaOptions.php b/pandora_console/vendor/artica/phpchartjs/src/Options/PolarAreaOptions.php new file mode 100644 index 0000000000..21b2f1bb35 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/PolarAreaOptions.php @@ -0,0 +1,14 @@ +type; + } + + /** + * @param string $type + * + * @return $this + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + + /** + * @return string + */ + public function getBounds() + { + return $this->bounds; + } + + /** + * @param string $bounds + * + * @return $this + */ + public function setBounds($bounds) + { + $this->bounds = $bounds; + + return $this; + } + + /** + * @return bool + */ + public function isDisplay() + { + return $this->display; + } + + /** + * @param bool $display + * + * @return $this + */ + public function setDisplay($display) + { + $this->display = $display; + + return $this; + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @param string $id + * + * @return $this + */ + public function setId($id) + { + $this->id = strval($id); + + return $this; + } + + /** + * @return bool + */ + public function isStacked() + { + return $this->stacked; + } + + /** + * @param bool $stacked + * + * @return $this + */ + public function setStacked($stacked) + { + $this->stacked = ! ! $stacked; + + return $this; + } + + /** + * @return int + */ + public function getBarThickness() + { + return $this->barThickness; + } + + /** + * @param int $barThickness + * + * @return $this + */ + public function setBarThickness($barThickness) + { + $this->barThickness = intval($barThickness); + + return $this; + } + + /** + * @return string + */ + public function getPosition() + { + return $this->position; + } + + /** + * @param string $position + * + * @return $this + */ + public function setPosition($position) + { + $this->position = strval($position); + + return $this; + } + + /** + * @return string + */ + public function getBeforeUpdate() + { + return $this->beforeUpdate; + } + + /** + * @param string $beforeUpdate + * + * @return $this + */ + public function setBeforeUpdate($beforeUpdate) + { + $this->beforeUpdate = strval($beforeUpdate); + + return $this; + } + + /** + * @return string + */ + public function getBeforeSetDimensions() + { + return $this->beforeSetDimensions; + } + + /** + * @param string $beforeSetDimensions + * + * @return $this + */ + public function setBeforeSetDimensions($beforeSetDimensions) + { + $this->beforeSetDimensions = strval($beforeSetDimensions); + + return $this; + } + + /** + * @return string + */ + public function getAfterSetDimensions() + { + return $this->afterSetDimensions; + } + + /** + * @param string $afterSetDimensions + * + * @return $this + */ + public function setAfterSetDimensions($afterSetDimensions) + { + $this->afterSetDimensions = strval($afterSetDimensions); + + return $this; + } + + /** + * @return string + */ + public function getBeforeDataLimits() + { + return $this->beforeDataLimits; + } + + /** + * @param string $beforeDataLimits + * + * @return $this + */ + public function setBeforeDataLimits($beforeDataLimits) + { + $this->beforeDataLimits = strval($beforeDataLimits); + + return $this; + } + + /** + * @return string + */ + public function getAfterDataLimits() + { + return $this->afterDataLimits; + } + + /** + * @param string $afterDataLimits + * + * @return $this + */ + public function setAfterDataLimits($afterDataLimits) + { + $this->afterDataLimits = strval($afterDataLimits); + + return $this; + } + + /** + * @return string + */ + public function getBeforeBuildTicks() + { + return $this->beforeBuildTicks; + } + + /** + * @param string $beforeBuildTicks + * + * @return $this + */ + public function setBeforeBuildTicks($beforeBuildTicks) + { + $this->beforeBuildTicks = strval($beforeBuildTicks); + + return $this; + } + + /** + * @return string + */ + public function getAfterBuildTicks() + { + return $this->afterBuildTicks; + } + + /** + * @param string $afterBuildTicks + * + * @return $this + */ + public function setAfterBuildTicks($afterBuildTicks) + { + $this->afterBuildTicks = strval($afterBuildTicks); + + return $this; + } + + /** + * @return string + */ + public function getBeforeTickToLabelConversion() + { + return $this->beforeTickToLabelConversion; + } + + /** + * @param string $beforeTickToLabelConversion + * + * @return $this + */ + public function setBeforeTickToLabelConversion($beforeTickToLabelConversion) + { + $this->beforeTickToLabelConversion = strval($beforeTickToLabelConversion); + + return $this; + } + + /** + * @return string + */ + public function getAfterTickToLabelConversion() + { + return $this->afterTickToLabelConversion; + } + + /** + * @param string $afterTickToLabelConversion + * + * @return $this + */ + public function setAfterTickToLabelConversion($afterTickToLabelConversion) + { + $this->afterTickToLabelConversion = strval($afterTickToLabelConversion); + + return $this; + } + + /** + * @return string + */ + public function getBeforeCalculateTickRotation() + { + return $this->beforeCalculateTickRotation; + } + + /** + * @param string $beforeCalculateTickRotation + * + * @return $this + */ + public function setBeforeCalculateTickRotation($beforeCalculateTickRotation) + { + $this->beforeCalculateTickRotation = strval($beforeCalculateTickRotation); + + return $this; + } + + /** + * @return string + */ + public function getAfterCalculateTickRotation() + { + return $this->afterCalculateTickRotation; + } + + /** + * @param string $afterCalculateTickRotation + * + * @return $this + */ + public function setAfterCalculateTickRotation($afterCalculateTickRotation) + { + $this->afterCalculateTickRotation = strval($afterCalculateTickRotation); + + return $this; + } + + /** + * @return string + */ + public function getBeforeFit() + { + return $this->beforeFit; + } + + /** + * @param string $beforeFit + * + * @return $this + */ + public function setBeforeFit($beforeFit) + { + $this->beforeFit = strval($beforeFit); + + return $this; + } + + /** + * @return string + */ + public function getAfterFit() + { + return $this->afterFit; + } + + /** + * @param string $afterFit + * + * @return $this + */ + public function setAfterFit($afterFit) + { + $this->afterFit = strval($afterFit); + + return $this; + } + + /** + * @return string + */ + public function getAfterUpdate() + { + return $this->afterUpdate; + } + + /** + * @param string $afterUpdate + * + * @return $this + */ + public function setAfterUpdate($afterUpdate) + { + $this->afterUpdate = strval($afterUpdate); + + return $this; + } + + /** + * @return GridLines + */ + public function getGrid() + { + return $this->grid; + } + + /** + * @return GridLines + */ + public function grid() + { + if (is_null($this->grid)) { + $this->grid = new GridLines(); + } + + return $this->grid; + } + + /** + * @return ScaleLabel + */ + public function getScaleLabel() + { + return $this->scaleLabel; + } + + /** + * @return ScaleLabel + */ + public function scaleLabel() + { + if (is_null($this->scaleLabel)) { + $this->scaleLabel = new ScaleLabel(); + } + + return $this->scaleLabel; + } + + /** + * @return Ticks + */ + public function getTicks() + { + return $this->ticks; + } + + /** + * @return Ticks + */ + public function ticks() + { + if (is_null($this->ticks)) { + $this->ticks = new Ticks(); + } + + return $this->ticks; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Scales.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales.php new file mode 100644 index 0000000000..167b43e111 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales.php @@ -0,0 +1,78 @@ +x)) { + $this->x = new XAxis(); + } + + return $this->x; + } + + /** + * @return YAxis + */ + public function getY() + { + if (is_null($this->y)) { + $this->y = new YAxis(); + } + + return $this->y; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/GridLines.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/GridLines.php new file mode 100644 index 0000000000..5af702fe8d --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/GridLines.php @@ -0,0 +1,339 @@ +display; + } + + /** + * @param bool $display + * + * @return $this + */ + public function setDisplay($display) + { + $this->display = $display; + + return $this; + } + + /** + * @return string|string[] + */ + public function getColor() + { + return $this->color; + } + + /** + * @param string|string[] $color + * + * @return $this + */ + public function setColor($color) + { + if (is_array($color)) { + $this->color = $this->recursiveToString($color); + } else { + $this->color = is_null($color) ? null : strval($color); + } + + return $this; + } + + /** + * @return float[]|null + */ + public function getBorderDash() + { + return $this->borderDash; + } + + /** + * @param float[] $borderDash + * + * @return $this + */ + public function setBorderDash($borderDash) + { + if (is_array($borderDash)) { + $this->borderDash = $this->recursiveToFloat($borderDash); + } + + return $this; + } + + /** + * @return float + */ + public function getBorderDashOffset() + { + return $this->borderDashOffset; + } + + /** + * @param float $borderDashOffset + * + * @return $this + */ + public function setBorderDashOffset($borderDashOffset) + { + $this->borderDashOffset = floatval($borderDashOffset); + + return $this; + } + + /** + * @return int|int[] + */ + public function getLineWidth() + { + return $this->lineWidth; + } + + /** + * @param int|int[] $lineWidth + * + * @return $this + */ + public function setLineWidth($lineWidth) + { + if (is_array($lineWidth)) { + $this->lineWidth = $this->recursiveToInt($lineWidth); + } else { + $this->lineWidth = is_null($lineWidth) ? null : intval($lineWidth); + } + + return $this; + } + + /** + * @return bool + */ + public function isDrawBorder() + { + return $this->drawBorder; + } + + /** + * @param bool $drawBorder + * + * @return $this + */ + public function setDrawBorder($drawBorder) + { + $this->drawBorder = boolval($drawBorder); + + return $this; + } + + /** + * @return bool + */ + public function isDrawOnChartArea() + { + return $this->drawOnChartArea; + } + + /** + * @param bool $drawOnChartArea + * + * @return $this + */ + public function setDrawOnChartArea($drawOnChartArea) + { + $this->drawOnChartArea = boolval($drawOnChartArea); + + return $this; + } + + /** + * @return bool + */ + public function isDrawTicks() + { + return $this->drawTicks; + } + + /** + * @param bool $drawTicks + * + * @return $this + */ + public function setDrawTicks($drawTicks) + { + $this->drawTicks = boolval($drawTicks); + + return $this; + } + + /** + * @return int + */ + public function getTickMarkLength() + { + return $this->tickMarkLength; + } + + /** + * @param int $tickMarkLength + * + * @return $this + */ + public function setTickMarkLength($tickMarkLength) + { + $this->tickMarkLength = intval($tickMarkLength); + + return $this; + } + + /** + * @return int + */ + public function getZeroLineWidth() + { + return $this->zeroLineWidth; + } + + /** + * @param int $zeroLineWidth + * + * @return $this + */ + public function setZeroLineWidth($zeroLineWidth) + { + $this->zeroLineWidth = intval($zeroLineWidth); + + return $this; + } + + /** + * @return string + */ + public function getZeroLineColor() + { + return $this->zeroLineColor; + } + + /** + * @param string $zeroLineColor + * + * @return $this + */ + public function setZeroLineColor($zeroLineColor) + { + $this->zeroLineColor = is_null($zeroLineColor) ? null : strval($zeroLineColor); + + return $this; + } + + /** + * @return bool + */ + public function isOffsetGridLines() + { + return $this->offsetGridLines; + } + + /** + * @param bool $offsetGridLines + * + * @return $this + */ + public function setOffsetGridLines($offsetGridLines) + { + $this->offsetGridLines = boolval($offsetGridLines); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/ScaleLabel.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/ScaleLabel.php new file mode 100644 index 0000000000..b74a4a6220 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/ScaleLabel.php @@ -0,0 +1,175 @@ +display; + } + + /** + * @param bool $display + * + * @return $this + */ + public function setDisplay($display) + { + $this->display = ! ! $display; + + return $this; + } + + /** + * @return string + */ + public function getLabelString() + { + return $this->labelString; + } + + /** + * @param string $labelString + * + * @return $this + */ + public function setLabelString($labelString) + { + $this->labelString = strval($labelString); + + return $this; + } + + /** + * @return string + */ + public function getFontColor() + { + return $this->fontColor; + } + + /** + * @param string $fontColor + * + * @return $this + */ + public function setFontColor($fontColor) + { + $this->fontColor = strval($fontColor); + + return $this; + } + + /** + * @return string + */ + public function getFontFamily() + { + return $this->fontFamily; + } + + /** + * @param string $fontFamily + * + * @return $this + */ + public function setFontFamily($fontFamily) + { + $this->fontFamily = strval($fontFamily); + + return $this; + } + + /** + * @return int + */ + public function getFontSize() + { + return $this->fontSize; + } + + /** + * @param int $fontSize + * + * @return $this + */ + public function setFontSize($fontSize) + { + $this->fontSize = intval($fontSize); + + return $this; + } + + /** + * @return string + */ + public function getFontStyle() + { + return $this->fontStyle; + } + + /** + * @param string $fontStyle + * + * @return $this + */ + public function setFontStyle($fontStyle) + { + $this->fontStyle = strval($fontStyle); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/Ticks.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/Ticks.php new file mode 100644 index 0000000000..00991061b8 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/Ticks.php @@ -0,0 +1,396 @@ +suggestedMin; + } + + /** + * @param float $suggestedMin + * + * @return $this + */ + public function setSuggestedMin($suggestedMin) + { + $this->suggestedMin = floatval($suggestedMin); + + return $this; + } + + /** + * @return bool + */ + public function isBeginAtZero() + { + return $this->beginAtZero; + } + + /** + * @param bool $beginAtZero + * + * @return $this + */ + public function setBeginAtZero($beginAtZero) + { + $this->beginAtZero = boolval($beginAtZero); + + return $this; + } + + /** + * @return float + */ + public function getStepSize() + { + return $this->stepSize; + } + + /** + * @param float $stepSize + * + * @return $this + */ + public function setStepSize($stepSize) + { + $this->stepSize = floatval($stepSize); + + return $this; + } + + /** + * @return bool + */ + public function isAutoSkip() + { + return $this->autoSkip; + } + + /** + * @param bool $autoSkip + * + * @return $this + */ + public function setAutoSkip($autoSkip) + { + $this->autoSkip = boolval($autoSkip); + + return $this; + } + + /** + * @return int + */ + public function getAutoSkipPadding() + { + return $this->autoSkipPadding; + } + + /** + * @param int $autoSkipPadding + * + * @return $this + */ + public function setAutoSkipPadding($autoSkipPadding) + { + $this->autoSkipPadding = intval($autoSkipPadding); + + return $this; + } + + /** + * @return Expr + */ + public function getCallback() + { + return $this->callback; + } + + /** + * @param string $callback + * + * @return $this + */ + public function setCallback($callback) + { + $this->callback = new Expr(strval($callback)); + + return $this; + } + + /** + * @return bool + */ + public function isDisplay() + { + return $this->display; + } + + /** + * @param bool $display + * + * @return $this + */ + public function setDisplay($display) + { + $this->display = boolval($display); + + return $this; + } + + /** + * Return Font. + * + * @return Font + */ + public function getFonts() + { + if (isset($this->font) === false) { + $this->font = new Fonts(); + } + + return $this->font; + } + + /** + * @return int + */ + public function getLabelOffset() + { + return $this->labelOffset; + } + + /** + * @param int $labelOffset + * + * @return $this + */ + public function setLabelOffset($labelOffset) + { + $this->labelOffset = intval($labelOffset); + + return $this; + } + + /** + * @return int + */ + public function getMaxRotation() + { + return $this->maxRotation; + } + + /** + * @param int $maxRotation + * + * @return $this + */ + public function setMaxRotation($maxRotation) + { + $this->maxRotation = intval($maxRotation); + + return $this; + } + + /** + * @return int + */ + public function getMinRotation() + { + return $this->minRotation; + } + + /** + * @param int $minRotation + * + * @return $this + */ + public function setMinRotation($minRotation) + { + $this->minRotation = intval($minRotation); + + return $this; + } + + /** + * @return bool + */ + public function isMirror() + { + return $this->mirror; + } + + /** + * @param bool $mirror + * + * @return $this + */ + public function setMirror($mirror) + { + $this->mirror = boolval($mirror); + + return $this; + } + + /** + * @return int + */ + public function getPadding() + { + return $this->padding; + } + + /** + * @param int $padding + * + * @return $this + */ + public function setPadding($padding) + { + $this->padding = intval($padding); + + return $this; + } + + /** + * @return bool + */ + public function isReverse() + { + return $this->reverse; + } + + /** + * @param bool $reverse + * + * @return $this + */ + public function setReverse($reverse) + { + $this->reverse = boolval($reverse); + + return $this; + } + + /** + * @return int + */ + public function getMax() + { + return $this->max; + } + + /** + * @param int $max + * + * @return $this + */ + public function setMax($max) + { + $this->max = intval($max); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/XAxis.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/XAxis.php new file mode 100644 index 0000000000..cf1da92e46 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/XAxis.php @@ -0,0 +1,71 @@ +categoryPercentage; + } + + /** + * @param float $categoryPercentage + * + * @return $this + */ + public function setCategoryPercentage($categoryPercentage) + { + $this->categoryPercentage = floatval($categoryPercentage); + + return $this; + } + + /** + * @return float + */ + public function getBarPercentage() + { + return $this->barPercentage; + } + + /** + * @param float $barPercentage + * + * @return $this + */ + public function setBarPercentage($barPercentage) + { + $this->barPercentage = floatval($barPercentage); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/XAxisCollection.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/XAxisCollection.php new file mode 100644 index 0000000000..1033e574b3 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/XAxisCollection.php @@ -0,0 +1,37 @@ +data as $row) { + /** @var XAxis $row */ + $rows[] = $row->getArrayCopy(); + } + + return $rows; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/YAxis.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/YAxis.php new file mode 100644 index 0000000000..7aad0fbd4e --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Scales/YAxis.php @@ -0,0 +1,13 @@ +data as $row) { + /** @var YAxis $row */ + $rows[] = $row->getArrayCopy(); + } + + return $rows; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/ScatterOptions.php b/pandora_console/vendor/artica/phpchartjs/src/Options/ScatterOptions.php new file mode 100644 index 0000000000..08cf8672ac --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/ScatterOptions.php @@ -0,0 +1,13 @@ +display; + } + + + /** + * @param boolean $display + * + * @return $this + */ + public function setDisplay($display) + { + $this->display = boolval($display); + + return $this; + } + + + /** + * @return string + */ + public function getPosition() + { + return $this->position; + } + + + /** + * @param string $position + * + * @return $this + */ + public function setPosition($position) + { + $this->position = strval($position); + + return $this; + } + + + /** + * @return string + */ + public function getColor() + { + return $this->color; + } + + + /** + * @param string $color + * + * @return $this + */ + public function setColor($color) + { + $this->color = strval($color); + + return $this; + } + + + /** + * @return boolean + */ + public function isFullWidth() + { + return $this->fullWidth; + } + + + /** + * @param boolean $fullWidth + * + * @return $this + */ + public function setFullWidth($fullWidth) + { + $this->fullWidth = boolval($fullWidth); + + return $this; + } + + + /** + * @return integer + */ + public function getPadding() + { + return $this->padding; + } + + + /** + * @param integer $padding + * + * @return $this + */ + public function setPadding($padding) + { + $this->padding = intval($padding); + + return $this; + } + + + /** + * @return string + */ + public function getText() + { + return $this->text; + } + + + /** + * @param string $text + * + * @return $this + */ + public function setText($text) + { + $this->text = strval($text); + + return $this; + } + + + /** + * Return Font. + * + * @return Font + */ + public function getFonts() + { + if (isset($this->font) === false) { + $this->font = new Fonts(); + } + + return $this->font; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Tooltips.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Tooltips.php new file mode 100644 index 0000000000..fe76d8876c --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Tooltips.php @@ -0,0 +1,819 @@ +enabled; + } + + /** + * @param bool $enabled + * + * @return $this + */ + public function setEnabled($enabled) + { + $this->enabled = boolval($enabled); + + return $this; + } + + /** + * @return \Laminas\Json\Expr + */ + public function getCustom() + { + return $this->custom; + } + + /** + * @param \Laminas\Json\Expr $custom + * + * @return $this + */ + public function setCustom($custom) + { + $this->custom = $custom; + + return $this; + } + + /** + * @return string + */ + public function getMode() + { + return $this->mode; + } + + /** + * @param string $mode + * + * @return $this + */ + public function setMode($mode) + { + $this->mode = strval($mode); + + return $this; + } + + /** + * @return bool + */ + public function isIntersect() + { + return $this->intersect; + } + + /** + * @param bool $intersect + * + * @return $this + */ + public function setIntersect($intersect) + { + $this->intersect = boolval($intersect); + + return $this; + } + + /** + * @return string + */ + public function getPosition() + { + return $this->position; + } + + /** + * @param string $position + * + * @return $this + */ + public function setPosition($position) + { + $this->position = strval($position); + + return $this; + } + + /** + * @return Expr + */ + public function getItemSort() + { + return $this->itemSort; + } + + /** + * @param Expr $itemSort + * + * @return $this + */ + public function setItemSort($itemSort) + { + $this->itemSort = new Expr(strval($itemSort)); + + return $this; + } + + /** + * @return Expr + */ + public function getFilter() + { + return $this->filter; + } + + /** + * @param Expr $filter + * + * @return $this + */ + public function setFilter($filter) + { + $this->filter = new Expr(strval($filter)); + + return $this; + } + + /** + * @return string + */ + public function getBackgroundColor() + { + return $this->backgroundColor; + } + + /** + * @param string $backgroundColor + * + * @return $this + */ + public function setBackgroundColor($backgroundColor) + { + $this->backgroundColor = strval($backgroundColor); + + return $this; + } + + /** + * @return string + */ + public function getTitleFontFamily() + { + return $this->titleFontFamily; + } + + /** + * @param string $titleFontFamily + * + * @return $this + */ + public function setTitleFontFamily($titleFontFamily) + { + $this->titleFontFamily = strval($titleFontFamily); + + return $this; + } + + /** + * @return int + */ + public function getTitleFontSize() + { + return $this->titleFontSize; + } + + /** + * @param int $titleFontSize + * + * @return $this + */ + public function setTitleFontSize($titleFontSize) + { + $this->titleFontSize = intval($titleFontSize); + + return $this; + } + + /** + * @return string + */ + public function getTitleFontStyle() + { + return $this->titleFontStyle; + } + + /** + * @param string $titleFontStyle + * + * @return $this + */ + public function setTitleFontStyle($titleFontStyle) + { + $this->titleFontStyle = strval($titleFontStyle); + + return $this; + } + + /** + * @return string + */ + public function getTitleFontColor() + { + return $this->titleFontColor; + } + + /** + * @param string $titleFontColor + * + * @return $this + */ + public function setTitleFontColor($titleFontColor) + { + $this->titleFontColor = strval($titleFontColor); + + return $this; + } + + /** + * @return int + */ + public function getTitleSpacing() + { + return $this->titleSpacing; + } + + /** + * @param int $titleSpacing + * + * @return $this + */ + public function setTitleSpacing($titleSpacing) + { + $this->titleSpacing = intval($titleSpacing); + + return $this; + } + + /** + * @return int + */ + public function getTitleMarginBottom() + { + return $this->titleMarginBottom; + } + + /** + * @param int $titleMarginBottom + * + * @return $this + */ + public function setTitleMarginBottom($titleMarginBottom) + { + $this->titleMarginBottom = intval($titleMarginBottom); + + return $this; + } + + /** + * @return string + */ + public function getBodyFontFamily() + { + return $this->bodyFontFamily; + } + + /** + * @param string $bodyFontFamily + * + * @return $this + */ + public function setBodyFontFamily($bodyFontFamily) + { + $this->bodyFontFamily = strval($bodyFontFamily); + + return $this; + } + + /** + * @return int + */ + public function getBodyFontSize() + { + return $this->bodyFontSize; + } + + /** + * @param int $bodyFontSize + * + * @return $this + */ + public function setBodyFontSize($bodyFontSize) + { + $this->bodyFontSize = intval($bodyFontSize); + + return $this; + } + + /** + * @return string + */ + public function getBodyFontStyle() + { + return $this->bodyFontStyle; + } + + /** + * @param string $bodyFontStyle + * + * @return $this + */ + public function setBodyFontStyle($bodyFontStyle) + { + $this->bodyFontStyle = strval($bodyFontStyle); + + return $this; + } + + /** + * @return string + */ + public function getBodyFontColor() + { + return $this->bodyFontColor; + } + + /** + * @param string $bodyFontColor + * + * @return $this + */ + public function setBodyFontColor($bodyFontColor) + { + $this->bodyFontColor = strval($bodyFontColor); + + return $this; + } + + /** + * @return int + */ + public function getBodySpacing() + { + return $this->bodySpacing; + } + + /** + * @param int $bodySpacing + * + * @return $this + */ + public function setBodySpacing($bodySpacing) + { + $this->bodySpacing = intval($bodySpacing); + + return $this; + } + + /** + * @return string + */ + public function getFooterFontFamily() + { + return $this->footerFontFamily; + } + + /** + * @param string $footerFontFamily + * + * @return $this + */ + public function setFooterFontFamily($footerFontFamily) + { + $this->footerFontFamily = strval($footerFontFamily); + + return $this; + } + + /** + * @return int + */ + public function getFooterFontSize() + { + return $this->footerFontSize; + } + + /** + * @param int $footerFontSize + * + * @return $this + */ + public function setFooterFontSize($footerFontSize) + { + $this->footerFontSize = intval($footerFontSize); + + return $this; + } + + /** + * @return string + */ + public function getFooterFontStyle() + { + return $this->footerFontStyle; + } + + /** + * @param string $footerFontStyle + * + * @return $this + */ + public function setFooterFontStyle($footerFontStyle) + { + $this->footerFontStyle = strval($footerFontStyle); + + return $this; + } + + /** + * @return string + */ + public function getFooterFontColor() + { + return $this->footerFontColor; + } + + /** + * @param string $footerFontColor + * + * @return $this + */ + public function setFooterFontColor($footerFontColor) + { + $this->footerFontColor = strval($footerFontColor); + + return $this; + } + + /** + * @return int + */ + public function getFooterSpacing() + { + return $this->footerSpacing; + } + + /** + * @param int $footerSpacing + * + * @return $this + */ + public function setFooterSpacing($footerSpacing) + { + $this->footerSpacing = intval($footerSpacing); + + return $this; + } + + /** + * @return int + */ + public function getFooterMarginTop() + { + return $this->footerMarginTop; + } + + /** + * @param int $footerMarginTop + * + * @return $this + */ + public function setFooterMarginTop($footerMarginTop) + { + $this->footerMarginTop = intval($footerMarginTop); + + return $this; + } + + /** + * @return int + */ + public function getXPadding() + { + return $this->xPadding; + } + + /** + * @param int $xPadding + * + * @return $this + */ + public function setXPadding($xPadding) + { + $this->xPadding = intval($xPadding); + + return $this; + } + + /** + * @return int + */ + public function getYPadding() + { + return $this->yPadding; + } + + /** + * @param int $yPadding + * + * @return $this + */ + public function setYPadding($yPadding) + { + $this->yPadding = intval($yPadding); + + return $this; + } + + /** + * @return int + */ + public function getCaretSize() + { + return $this->caretSize; + } + + /** + * @param int $caretSize + * + * @return $this + */ + public function setCaretSize($caretSize) + { + $this->caretSize = intval($caretSize); + + return $this; + } + + /** + * @return int + */ + public function getCornerRadius() + { + return $this->cornerRadius; + } + + /** + * @param int $cornerRadius + * + * @return $this + */ + public function setCornerRadius($cornerRadius) + { + $this->cornerRadius = intval($cornerRadius); + + return $this; + } + + /** + * @return string + */ + public function getMultiKeyBackground() + { + return $this->multiKeyBackground; + } + + /** + * @param string $multiKeyBackground + * + * @return $this + */ + public function setMultiKeyBackground($multiKeyBackground) + { + $this->multiKeyBackground = strval($multiKeyBackground); + + return $this; + } + + /** + * @return bool + */ + public function isDisplayColors() + { + return $this->displayColors; + } + + /** + * @param bool $displayColors + * + * @return $this + */ + public function setDisplayColors($displayColors) + { + $this->displayColors = boolval($displayColors); + + return $this; + } + + /** + * @return Callbacks + */ + public function callbacks() + { + if (is_null($this->callbacks)) { + $this->callbacks = new Callbacks(); + } + + return $this->callbacks; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/Tooltips/Callbacks.php b/pandora_console/vendor/artica/phpchartjs/src/Options/Tooltips/Callbacks.php new file mode 100644 index 0000000000..f41ad08a63 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/Tooltips/Callbacks.php @@ -0,0 +1,326 @@ +beforeTitle; + } + + /** + * @param string $beforeTitle + * + * @return $this + */ + public function setBeforeTitle($beforeTitle) + { + $this->beforeTitle = new Expr(strval($beforeTitle)); + + return $this; + } + + /** + * @return Expr + */ + public function getTitle() + { + return $this->title; + } + + /** + * @param string $title + * + * @return $this + */ + public function setTitle($title) + { + $this->title = new Expr(strval($title)); + + return $this; + } + + /** + * @return Expr + */ + public function getAfterTitle() + { + return $this->afterTitle; + } + + /** + * @param string $afterTitle + * + * @return $this + */ + public function setAfterTitle($afterTitle) + { + $this->afterTitle = new Expr(strval($afterTitle)); + + return $this; + } + + /** + * @return Expr + */ + public function getBeforeLabel() + { + return $this->beforeLabel; + } + + /** + * @param string $beforeLabel + * + * @return $this + */ + public function setBeforeLabel($beforeLabel) + { + $this->beforeLabel = new Expr(strval($beforeLabel)); + + return $this; + } + + /** + * @return Expr + */ + public function getLabel() + { + return $this->label; + } + + /** + * @param string $label + * + * @return $this + */ + public function setLabel($label) + { + $this->label = new Expr(strval($label)); + + return $this; + } + + /** + * @return Expr + */ + public function getLabelColor() + { + return $this->labelColor; + } + + /** + * @param string $labelColor + * + * @return $this + */ + public function setLabelColor($labelColor) + { + $this->labelColor = new Expr(strval($labelColor)); + + return $this; + } + + /** + * @return Expr + */ + public function getAfterLabel() + { + return $this->afterLabel; + } + + /** + * @param string $afterLabel + * + * @return $this + */ + public function setAfterLabel($afterLabel) + { + $this->afterLabel = new Expr(strval($afterLabel)); + + return $this; + } + + /** + * @return Expr + */ + public function getAfterBody() + { + return $this->afterBody; + } + + /** + * @param string $afterBody + * + * @return $this + */ + public function setAfterBody($afterBody) + { + $this->afterBody = new Expr(strval($afterBody)); + + return $this; + } + + /** + * @return Expr + */ + public function getBeforeFooter() + { + return $this->beforeFooter; + } + + /** + * @param string $beforeFooter + * + * @return $this + */ + public function setBeforeFooter($beforeFooter) + { + $this->beforeFooter = new Expr(strval($beforeFooter)); + + return $this; + } + + /** + * @return Expr + */ + public function getFooter() + { + return $this->footer; + } + + /** + * @param string $footer + * + * @return $this + */ + public function setFooter($footer) + { + $this->footer = new Expr(strval($footer)); + + return $this; + } + + /** + * @return Expr + */ + public function getAfterFooter() + { + return $this->afterFooter; + } + + /** + * @param string $afterFooter + * + * @return $this + */ + public function setAfterFooter($afterFooter) + { + $this->afterFooter = new Expr(strval($afterFooter)); + + return $this; + } + + /** + * @return Expr + */ + public function getDataPoints() + { + return $this->dataPoints; + } + + /** + * @param string $dataPoints + * + * @return $this + */ + public function setDataPoints($dataPoints) + { + $this->dataPoints = new Expr(strval($dataPoints)); + + return $this; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Options/WaterMark.php b/pandora_console/vendor/artica/phpchartjs/src/Options/WaterMark.php new file mode 100644 index 0000000000..7587cf864f --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Options/WaterMark.php @@ -0,0 +1,162 @@ +width; + } + + + /** + * @param float $width + * + * @return $this + */ + public function setWidth($width) + { + $this->width = intval($width); + + return $this; + } + + /** + * @return float + */ + public function getHeight() + { + return $this->height; + } + + + /** + * @param float $height + * + * @return $this + */ + public function setHeight($height) + { + $this->height = intval($height); + + return $this; + } + + + /** + * @return string + */ + public function getSrc() + { + return $this->src; + } + + + /** + * @param string $src + * + * @return $this + */ + public function setSrc($src) + { + $this->src = strval($src); + + return $this; + } + + + /** + * @return string + */ + public function getPosition() + { + return $this->position; + } + + + /** + * @param string $position + * + * @return $this + */ + public function setPosition($position) + { + $this->position = strval($position); + + return $this; + } + + + /** + * @return string + */ + public function getAlign() + { + return $this->align; + } + + + /** + * @param string $align + * + * @return $this + */ + public function setAlign($align) + { + $this->align = strval($align); + + return $this; + } + + + /** + * @return array + */ + public function jsonSerialize() + { + return $this->getArrayCopy(); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/PluginsCollection.php b/pandora_console/vendor/artica/phpchartjs/src/PluginsCollection.php new file mode 100644 index 0000000000..3e25aeecbc --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/PluginsCollection.php @@ -0,0 +1,22 @@ +data; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Renderer/Html.php b/pandora_console/vendor/artica/phpchartjs/src/Renderer/Html.php new file mode 100644 index 0000000000..1acf3de67e --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Renderer/Html.php @@ -0,0 +1,49 @@ +createElement('canvas'); + $canvas->setAttribute('id', $this->chart->getId()); + + // Add title, height and width if applicable + if ($this->chart->getTitle()) { + $canvas->setAttribute('title', $this->chart->getTitle()); + } + if ($this->chart->getHeight()) { + $canvas->setAttribute('height', $this->chart->getHeight()); + } + if ($this->chart->getWidth()) { + $canvas->setAttribute('width', $this->chart->getWidth()); + } + + $dom->appendChild($canvas); + + // Render JavaScript + $scriptRenderer = new JavaScript($this->chart); + $script = $dom->createElement('script', $scriptRenderer->render($flags)); + $dom->appendChild($script); + + return $dom->saveHTML(); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Renderer/JavaScript.php b/pandora_console/vendor/artica/phpchartjs/src/Renderer/JavaScript.php new file mode 100644 index 0000000000..100276743f --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Renderer/JavaScript.php @@ -0,0 +1,125 @@ +chart->getId()}\" ).getContext( \"2d\" );"; + + // Now, setup the chart instance + $jsonRenderer = new Json($this->chart); + $json = $jsonRenderer->render($flags); + + // Watermark. + if (empty($this->chart->defaults()->getWatermark()) === false) { + $script[] = 'const chart_watermark_'.$this->chart->getId().' = { + id: "chart_watermark_'.$this->chart->getId().'", + afterDraw: (chart) => { + const image = new Image(); + image.src = "'.$this->chart->defaults()->getWatermark()->getSrc().'"; + if (image.complete) { + const image_height = '.($this->chart->defaults()->getWatermark()->getHeight() ?? 20).'; + const image_width = '.($this->chart->defaults()->getWatermark()->getWidth() ?? 100).'; + const ctx = chart.ctx; + let x = 0; + let y = 0; + + switch ("'.$this->chart->defaults()->getWatermark()->getPosition().'") { + case "start": + x = 0; + break; + + case "center": + x = (chart.width / 2) - image_width; + break; + + default: + case "end": + x = chart.width - image_width; + break; + } + + switch ("'.$this->chart->defaults()->getWatermark()->getAlign().'") { + default: + case "top": + y = 0; + break; + + case "center": + y = (chart.height / 2) + image_height; + break; + + case "bottom": + y = chart.height + image_height; + break; + } + + ctx.globalAlpha = 1; + ctx.drawImage(image, x, y, image_width, image_height); + ctx.globalAlpha = 1; + } else { + image.onload = () => chart.draw(); + } + } + };'; + $script[] = 'Chart.register(chart_watermark_'.$this->chart->getId().');'; + } + + + + // Create chart. + $script[] = 'try {'; + $script[] = " var chart = new Chart( ctx, {$json} );"; + + // Defaults values. + $script[] = ' Chart.defaults.font.size = '.($this->chart->defaults()->getFonts()->getSize() ?? 8).';'; + $script[] = ' Chart.defaults.font.family = "'.($this->chart->defaults()->getFonts()->getFamily() ?? 'Lato, sans-serif').'";'; + $script[] = ' Chart.defaults.font.style = "'.($this->chart->defaults()->getFonts()->getStyle() ?? 'normal').'";'; + $script[] = ' Chart.defaults.font.weight = "'.($this->chart->defaults()->getFonts()->getWeight() ?? '').'";'; + + $script[] = '} catch (error) {'; + $script[] = ' console.error(error);'; + $script[] = '}'; + + $scriptString = implode("\n", $script); + + return $scriptString; + + // Return the script + return <<chart->getId()}'] = chart; + }})(window.onload); + JS; + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Renderer/Json.php b/pandora_console/vendor/artica/phpchartjs/src/Renderer/Json.php new file mode 100644 index 0000000000..6e3843b0c0 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Renderer/Json.php @@ -0,0 +1,64 @@ + constant(get_class($this->chart).'::TYPE'), + 'data' => [], + ]; + + $labels = $this->chart->labels()->jsonSerialize(); + if ($labels) { + $config['data']['labels'] = $labels; + } + + $dataSets = $this->chart->dataSets()->jsonSerialize(); + if ($dataSets) { + $config['data']['datasets'] = $dataSets; + } + + $options = $this->chart->options()->jsonSerialize(); + if (! empty($options)) { + $config['options'] = $options; + } + + $defaults = $this->chart->defaults()->jsonSerialize(); + if (! empty($defaults)) { + $config['defaults'] = $defaults; + } + + $plugins = $this->chart->plugins()->jsonSerialize(); + if (! empty($plugins)) { + $config['plugins'] = $plugins; + } + + $output = JsonHelper::encode($config, false, ['enableJsonExprFinder' => true]); + if (($flags & Renderer::RENDER_PRETTY)) { + $output = JsonHelper::prettyPrint($output); + } + + return $output; + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Renderer/Renderer.php b/pandora_console/vendor/artica/phpchartjs/src/Renderer/Renderer.php new file mode 100644 index 0000000000..67131777cf --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Renderer/Renderer.php @@ -0,0 +1,32 @@ +chart = $chart; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/src/Renderer/RendererInterface.php b/pandora_console/vendor/artica/phpchartjs/src/Renderer/RendererInterface.php new file mode 100644 index 0000000000..2fe7659600 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/src/Renderer/RendererInterface.php @@ -0,0 +1,27 @@ + $value) { + $function = 'set' . ucfirst($key); + if (! is_null($value) && method_exists($obj, $function)) { + $obj->$function($value); + } + } + } + + /** + * this method reads all defined attributes from the input array + * $input_data and calls the getter. It returns the resulting array. + * + * @param $obj + * @param array $dataTypes is an associative array that refers fieldnames to values. + * The values could be any primitive type, including an array. + * + * @return array + */ + public static function getAttributes($obj, array $dataTypes) + { + + if (! is_object($obj)) { + throw new RuntimeException("First param should be an object. "); + } + + $array = []; + foreach ($dataTypes as $key => $value) { + $function = ( gettype($value) == "boolean" ? 'is' : 'get' ) . ucfirst($key); + if (method_exists($obj, $function)) { + $getResult = $obj->$function($value); + $getResult = $getResult instanceof Expr ? $getResult->__toString() : $getResult; + $array[ $key ] = $getResult; + } + } + + return $array; + } + + /** + * @param $input_array + * + * @return mixed + */ + public static function removeNullsFromArray($input_array) + { + $array = $input_array; + $keys = array_keys($array); + foreach ($keys as $key) { + if (is_null($array[ $key ])) { + unset($array[ $key ]); + } + } + + return $array; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/bootstrap.php b/pandora_console/vendor/artica/phpchartjs/test/bootstrap.php new file mode 100644 index 0000000000..6c8c4f51b9 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/bootstrap.php @@ -0,0 +1,3 @@ +setId('myChart'); + +// Set labels +$bar->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $bar->createDataSet(); + +$apples->setLabel("apples") + ->setBackgroundColor("rgba( 0, 150, 0, .5 )") + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$bar->addDataSet($apples); + +$oranges = $bar->createDataSet(); +$oranges->setLabel("oranges") + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10]); +$bar->addDataSet($oranges); + +?> + + + + Bar + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/barAndLine.php b/pandora_console/vendor/artica/phpchartjs/test/example/barAndLine.php new file mode 100644 index 0000000000..d000b7e58b --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/barAndLine.php @@ -0,0 +1,72 @@ +create($factory::BAR); + +// Set labels +$bar->labels()->exchangeArray(["January", "February", "March", "April", "May", "June", "July"]); + +// Add Datasets +$dataSet = new LineDataSet(); +$dataSet->setLabel('My First dataset') + ->setType('line') + ->setFill(false) + ->setLineTension(0.1) + ->setBackgroundColor('rgba(75,192,192,0.4)') + ->setBorderColor('rgba(75,192,192,1)') + ->setBorderCapStyle('butt') + ->setBorderDash([]) + ->setBorderDashOffset(0.0) + ->setBorderJoinStyle('miter') + ->setPointBorderColor('rgba(75,192,192,1)') + ->setPointBackgroundColor('#fff') + ->setPointBorderWidth(1) + ->setPointHoverRadius(5) + ->setPointHoverBackgroundColor('rgba(75,192,192,1)') + ->setPointHoverBorderColor('rgba(220,220,220,1)') + ->setPointHoverBorderWidth(2) + ->setPointRadius(1) + ->setPointHitRadius(10) + ->data()->exchangeArray([65, 59, 80, 81, 56, 55, 40]); +$bar->addDataSet($dataSet); + +// Set mode to stacked +$scales = $bar->options()->getScales(); +$scales->getYAxes()->append($scales->createYAxis()->setStacked(true)) + ->append($scales->createYAxis()->setPosition('right')->setId('y2')); + +// Add even more data +$apples = $bar->createDataSet(); +$apples->setLabel('apples') + ->setYAxisID('y2') + ->setBackgroundColor('rgba( 0, 150, 0, .5 )') + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$bar->addDataSet($apples); + +$oranges = $bar->createDataSet(); +$oranges->setLabel('oranges') + ->setYAxisID('y2') + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10]); +$bar->addDataSet($oranges); + +?> + + + + Bar & line + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/barWithoutScales.php b/pandora_console/vendor/artica/phpchartjs/test/example/barWithoutScales.php new file mode 100644 index 0000000000..3dfa2c468b --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/barWithoutScales.php @@ -0,0 +1,52 @@ +setId('myChart'); + +// Set labels +$bar->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $bar->createDataSet(); + +$apples->setLabel("apples") + ->setBackgroundColor("rgba( 0, 150, 0, .5 )") + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$bar->addDataSet($apples); + +$oranges = $bar->createDataSet(); +$oranges->setLabel("oranges") + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10]); +$bar->addDataSet($oranges); + +/** @var BarOptions $options */ +$options = $bar->options(); +$xAxis = $options->getScales()->createXAxis(); +$yAxis = $options->getScales()->createYAxis(); + +$xAxis->setDisplay(false); +$yAxis->setDisplay(false); + +$options->getScales()->getXAxes()->append($xAxis); +$options->getScales()->getYAxes()->append($yAxis); + +?> + + + + Bar without scales + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/bubble.php b/pandora_console/vendor/artica/phpchartjs/test/example/bubble.php new file mode 100644 index 0000000000..b67099d399 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/bubble.php @@ -0,0 +1,54 @@ +create($factory::BUBBLE); + +// Set labels +$bubble->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $bubble->createDataSet(); +$apples->setLabel('My first dataset') + ->setBackgroundColor('rgba( 0, 150, 0, .5 )') + ->data()->exchangeArray([ + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ]); +$bubble->addDataSet($apples); + +$oranges = $bubble->createDataSet(); +$oranges->setLabel('My second dataset') + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([ + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ]); +$bubble->addDataSet($oranges); + +?> + + + + Bubble + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/disableAspectRatio.php b/pandora_console/vendor/artica/phpchartjs/test/example/disableAspectRatio.php new file mode 100644 index 0000000000..14af2f0392 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/disableAspectRatio.php @@ -0,0 +1,57 @@ +create($factory::BUBBLE); + +// Set labels +$bubble->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $bubble->createDataSet(); +$apples->setLabel('My first dataset') + ->setBackgroundColor('rgba( 0, 150, 0, .5 )') + ->data()->exchangeArray([ + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ]); +$bubble->addDataSet($apples); + +$oranges = $bubble->createDataSet(); +$oranges->setLabel('My second dataset') + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([ + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ['x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50)], + ]); +$bubble->addDataSet($oranges); + +$bubble->options()->setMaintainAspectRatio(false); +$bubble->setHeight(250); + +?> + + + + Disable aspect ratio + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/doughnut.php b/pandora_console/vendor/artica/phpchartjs/test/example/doughnut.php new file mode 100644 index 0000000000..3c708eca58 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/doughnut.php @@ -0,0 +1,57 @@ +create($factory::DOUGHNUT); + +// Set labels +$doughnut->labels()->exchangeArray([ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", +]); + +// Add Datasets +$apples = $doughnut->createDataSet(); +$apples->setLabel('apples') + ->setBackgroundColor($colors) + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7,]); +$doughnut->addDataSet($apples); + +$oranges = $doughnut->createDataSet(); +$oranges->setLabel('oranges') + ->setBackgroundColor($colors) + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10,]); +$doughnut->addDataSet($oranges); + +?> + + + + Doughnut + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/halfDoughnut.php b/pandora_console/vendor/artica/phpchartjs/test/example/halfDoughnut.php new file mode 100644 index 0000000000..df33165511 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/halfDoughnut.php @@ -0,0 +1,63 @@ +create($factory::DOUGHNUT); +/** @var \Artica\PHPChartJS\Options\PieOptions $options */ +$options = $doughnut->options(); +$options->setCutoutPercentage(50) + ->setCircumference(pi()) + ->setRotation(pi()) + ->getAnimation()->setDuration(1); + +// Set labels +$doughnut->labels()->exchangeArray([ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", +]); + +// Add Datasets +$apples = $doughnut->createDataSet(); +$apples->setLabel('apples') + ->setBackgroundColor($colors) + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7,]); +$doughnut->addDataSet($apples); + +$oranges = $doughnut->createDataSet(); +$oranges->setLabel('oranges') + ->setBackgroundColor($colors) + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10,]); +$doughnut->addDataSet($oranges); + +?> + + + + Half doughnut + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/hidden.php b/pandora_console/vendor/artica/phpchartjs/test/example/hidden.php new file mode 100644 index 0000000000..f4ca8d436c --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/hidden.php @@ -0,0 +1,41 @@ +setId('myChart'); + +// Set labels +$bar->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $bar->createDataSet(); + +$apples->setLabel("apples") + ->setBackgroundColor("rgba( 0, 150, 0, .5 )") + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$bar->addDataSet($apples); + +$oranges = $bar->createDataSet(); +$oranges->setLabel("oranges") + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10]); +$oranges->setHidden(true); +$bar->addDataSet($oranges); + +?> + + + + Bar + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/horizontalBar.php b/pandora_console/vendor/artica/phpchartjs/test/example/horizontalBar.php new file mode 100644 index 0000000000..94afd655e7 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/horizontalBar.php @@ -0,0 +1,44 @@ +create($factory::HORIZONTAL_BAR); + +// Set labels +$bar->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $bar->createDataSet(); +$apples->setLabel('apples') + ->setBackgroundColor('rgba( 0, 150, 0, .5 )') + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$bar->addDataSet($apples); + +$oranges = $bar->createDataSet(); +$oranges->setLabel('oranges') + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10]); +$bar->addDataSet($oranges); + +$scales = $bar->options()->getScales(); +$scales->getXAxes()->append($scales->createXAxis()->setStacked(true)); +$scales->getYAxes()->append($scales->createYAxis()->setStacked(true)); + +?> + + + + Horizontal bar + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/index.html b/pandora_console/vendor/artica/phpchartjs/test/example/index.html new file mode 100644 index 0000000000..9b5a69d186 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/index.html @@ -0,0 +1,94 @@ + + + + + + + + + + + Articaam/phpchartjs - examples + + +
+
+
+

Articaam/phpchartjs - examples

+ +
+
+ + + + +
+ + + + + + + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/line.php b/pandora_console/vendor/artica/phpchartjs/test/example/line.php new file mode 100644 index 0000000000..7c8f0571b2 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/line.php @@ -0,0 +1,71 @@ +create($factory::LINE); + +// Set labels +$line->labels()->exchangeArray(["January", "February", "March", "April", "May", "June", "July"]); + +// Add Datasets +$dataSet = $line->createDataSet(); +$dataSet->setLabel('My First dataset') + ->setFill(false) + ->setLineTension(0.1) + ->setBackgroundColor('rgba(75,192,192,0.4)') + ->setBorderColor('rgba(75,192,192,1)') + ->setBorderCapStyle('butt') + ->setBorderDash([]) + ->setBorderDashOffset(0.0) + ->setBorderJoinStyle('miter') + ->setPointBorderColor('rgba(75,192,192,1)') + ->setPointBackgroundColor('#fff') + ->setPointBorderWidth(1) + ->setPointHoverRadius(5) + ->setPointHoverBackgroundColor('rgba(75,192,192,1)') + ->setPointHoverBorderColor('rgba(220,220,220,1)') + ->setPointHoverBorderWidth(2) + ->setPointRadius(1) + ->setPointHitRadius(10) + ->setSpanGaps(false) + ->data()->exchangeArray([65, 59, 80, 81, 56, 55, 40]); +$line->addDataSet($dataSet); + +// Set mode to stacked +$scales = $line->options()->getScales(); +$scales->getYAxes()->append($scales->createYAxis()->setStacked(true)) + ->append($scales->createYAxis()->setPosition('right')->setId('y2')); + +// Add even more data +$apples = $line->createDataSet(); +$apples->setLabel('apples') + ->setYAxisID('y2') + ->setBackgroundColor('rgba( 0, 150, 0, .5 )') + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$line->addDataSet($apples); + +$oranges = $line->createDataSet(); +$oranges->setLabel('oranges') + ->setYAxisID('y2') + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10]); +$line->addDataSet($oranges); + +?> + + + + Line + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/lineScatter.php b/pandora_console/vendor/artica/phpchartjs/test/example/lineScatter.php new file mode 100644 index 0000000000..ae9dfdebba --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/lineScatter.php @@ -0,0 +1,40 @@ +create($factory::LINE); + +// Add Datasets +$dataSet = $line->createDataSet(); +$dataSet->setLabel('Scatter Dataset')->data()->exchangeArray([ + ['x' => -10, 'y' => 0], + ['x' => 0, 'y' => 10], + ['x' => 10, 'y' => 5], +]); +$line->addDataSet($dataSet); + +$scales = $line->options()->getScales(); +$xAxis = $scales->createXAxis(); +$xAxis->setType('linear') + ->setPosition('bottom'); + +$scales->getXAxes()->append($xAxis); + +?> + + + + Line scatter + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/onClick.php b/pandora_console/vendor/artica/phpchartjs/test/example/onClick.php new file mode 100644 index 0000000000..060a0818d5 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/onClick.php @@ -0,0 +1,49 @@ +setId('myChart'); + +// Set labels +$bar->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $bar->createDataSet(); + +$apples->setLabel("apples") + ->setBackgroundColor("rgba( 0, 150, 0, .5 )") + ->data()->exchangeArray([12, 19, 3, 17, 28, 24, 7]); +$bar->addDataSet($apples); + +$oranges = $bar->createDataSet(); +$oranges->setLabel("oranges") + ->setBackgroundColor('rgba( 255, 153, 0, .5 )') + ->data()->exchangeArray([30, 29, 5, 5, 20, 3, 10]); +$bar->addDataSet($oranges); +$bar->options()->setOnClick('myClickEvent'); +?> + + + + onClick + + + +render(); +?> + + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/pie.php b/pandora_console/vendor/artica/phpchartjs/test/example/pie.php new file mode 100644 index 0000000000..4d69523455 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/pie.php @@ -0,0 +1,52 @@ +create($factory::PIE); + +// Set labels +$pie->labels()->exchangeArray([ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", +]); + +// Add Datasets +$apples = $pie->createDataSet(); +$apples->setLabel('My First dataset') + ->setBackgroundColor($colors) + ->data()->exchangeArray([165, 59, 80, 81, 56, 55, 40]); +$pie->addDataSet($apples); + +?> + + + + Pie + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/polarArea.php b/pandora_console/vendor/artica/phpchartjs/test/example/polarArea.php new file mode 100644 index 0000000000..5ff22379e5 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/polarArea.php @@ -0,0 +1,35 @@ +create($factory::POLAR_AREA); + +// Set labels +$polarArea->labels()->exchangeArray(["Red", "Green", "Yellow", "Grey", "Blue"]); + +// Add Datasets +$dataSet = $polarArea->createDataSet(); +$dataSet->setLabel('My dataset') + ->setBackgroundColor($colors) + ->data()->exchangeArray([11, 16, 7, 3, 14]); +$polarArea->addDataSet($dataSet); + +?> + + + + Polar area + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/radar.php b/pandora_console/vendor/artica/phpchartjs/test/example/radar.php new file mode 100644 index 0000000000..c6b27766de --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/radar.php @@ -0,0 +1,58 @@ +create($factory::RADAR); + +// Set labels +$radar->labels()->exchangeArray([ + "Eating", + "Drinking", + "Sleeping", + "Designing", + "Coding", + "Cycling", + "Running", +]); + +// Add Datasets +$dataSet1 = $radar->createDataSet(); +$dataSet1->setLabel('My first dataset') + ->setBackgroundColor('rgba(179,181,198,0.2)') + ->setBorderColor('rgba(179,181,198,1)') + ->setPointBackgroundColor('rgba(179,181,198,1)') + ->setPointBorderColor('#fff') + ->setPointHoverBackgroundColor('#fff') + ->setPointHoverBorderColor('rgba(179,181,198,1)') + ->data()->exchangeArray([65, 59, 90, 81, 56, 55, 40]); +$radar->addDataSet($dataSet1); + +$dataSet2 = $radar->createDataSet(); +$dataSet2->setLabel('My second dataset') + ->setBackgroundColor('rgba(255,99,132,0.2)') + ->setBorderColor('rgba(255,99,132,1)') + ->setPointBackgroundColor('rgba(255,99,132,1)') + ->setPointBorderColor('#fff') + ->setPointHoverBackgroundColor('#fff') + ->setPointHoverBorderColor('rgba(255,99,132,1)') + ->data()->exchangeArray([28, 48, 40, 19, 96, 27, 100]); +$radar->addDataSet($dataSet2); + +?> + + + + Radar + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/example/scatter.php b/pandora_console/vendor/artica/phpchartjs/test/example/scatter.php new file mode 100644 index 0000000000..6d1a9ff0e5 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/example/scatter.php @@ -0,0 +1,69 @@ +create($factory::SCATTER); +$scatter->setTitle('Scatter chart'); + +/** @var ScatterOptions $options */ +$options = $scatter->options(); +$xAxis = $options->getScales()->createXAxis(); +$xAxis->ticks()->setStepSize(1); +$yAxis = $options->getScales()->createYAxis(); + +$options->getScales()->getXAxes()->append($xAxis); +$options->getScales()->getYAxes()->append($yAxis); + +// Set labels +$scatter->labels()->exchangeArray(["M", "T", "W", "T", "F", "S", "S"]); + +// Add Datasets +$apples = $scatter->createDataSet(); +$apples->setLabel('My first dataset') + ->setBackgroundColor('rgba( 0, 150, 0, .5 )') + ->setPointStyle('rect') + ->setPointRadius(10); +$apples->data()->exchangeArray([ + ['x' => 0, 'y' => 0], + ['x' => 0, 'y' => 1], + ['x' => 0, 'y' => 2], + ['x' => 0, 'y' => 3], + ['x' => 0, 'y' => 4], + ['x' => 0, 'y' => 5], + ['x' => 0, 'y' => 6], +]); +$scatter->addDataSet($apples); + +$oranges = $scatter->createDataSet(); +$oranges->setLabel('My second dataset') + ->setBackgroundColor('rgba( 255, 153, 0, .5 )'); +$oranges->data()->exchangeArray([ + ['x' => 1, 'y' => 0], + ['x' => 1, 'y' => 1], + ['x' => 1, 'y' => 2], + ['x' => 1, 'y' => 3], + ['x' => 1, 'y' => 4], + ['x' => 1, 'y' => 5], + ['x' => 1, 'y' => 6], +]); +$scatter->addDataSet($oranges); + +?> + + + + Scatter + + + +render(); +?> + + diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/BarTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/BarTest.php new file mode 100644 index 0000000000..0c1382f2f5 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/BarTest.php @@ -0,0 +1,56 @@ +assertInstanceOf(ChartInterface::class, $bar, 'The correct interface has been implemented'); + $this->assertInstanceOf(Bar::class, $bar, 'The correct class has been created'); + } + + /** + * Test the DataSet created by the Bar chart + */ + public function testDataSet() + { + $bar = new Bar(); + $chartData = [ 0, 1, 4, 2, 3, 0, 5, 2, 6 ]; + + // DataSet + $dataSet = $bar->createDataSet(); + $this->assertInstanceOf(BarDataSet::class, $dataSet, 'The correct class has been created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $bar->addDataSet($dataSet); + $this->assertEquals($chartData, $bar->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $bar = new Bar(); + $this->assertInstanceOf(BarOptions::class, $bar->options(), 'The correct class should be created'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/BubbleTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/BubbleTest.php new file mode 100644 index 0000000000..40a27148ad --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/BubbleTest.php @@ -0,0 +1,63 @@ +assertInstanceOf(ChartInterface::class, $bubble, 'The correct interface should be implemented'); + $this->assertInstanceOf(Bubble::class, $bubble, 'The correct class should be created'); + } + + /** + * Test the DataSet created by the Bar chart + */ + public function testDataSet() + { + $bubble = new Bubble(); + $chartData = [ + [ 'x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50) ], + [ 'x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50) ], + [ 'x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50) ], + [ 'x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50) ], + [ 'x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50) ], + [ 'x' => rand(0, 40), 'y' => rand(0, 30), 'r' => rand(0, 50) ], + ]; + + // DataSet + $dataSet = $bubble->createDataSet(); + $this->assertInstanceOf(BubbleDataSet::class, $dataSet, 'The correct class should be created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $bubble->addDataSet($dataSet); + $this->assertEquals($chartData, $bubble->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $bar = new Bubble(); + $this->assertInstanceOf(BubbleOptions::class, $bar->options(), 'The correct class should be created'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/DoughnutTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/DoughnutTest.php new file mode 100644 index 0000000000..7d243e3e16 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/DoughnutTest.php @@ -0,0 +1,56 @@ +assertInstanceOf(ChartInterface::class, $doughnut, 'The correct interface has been implemented'); + $this->assertInstanceOf(Doughnut::class, $doughnut, 'The correct class has been created'); + } + + /** + * Test the DataSet created by the Doughnut chart + */ + public function testDataSet() + { + $Doughnut = new Doughnut(); + $chartData = [ 0, 1, 4, 2, 3, 0, 5, 2, 6 ]; + + // DataSet + $dataSet = $Doughnut->createDataSet(); + $this->assertInstanceOf(PieDataSet::class, $dataSet, 'The correct class has been created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $Doughnut->addDataSet($dataSet); + $this->assertEquals($chartData, $Doughnut->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $Doughnut = new Doughnut(); + $this->assertInstanceOf(PieOptions::class, $Doughnut->options(), 'The correct class should be created'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/LineTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/LineTest.php new file mode 100644 index 0000000000..e023e784d2 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/LineTest.php @@ -0,0 +1,56 @@ +assertInstanceOf(ChartInterface::class, $Line, 'The correct interface has been implemented'); + $this->assertInstanceOf(Line::class, $Line, 'The correct class has been created'); + } + + /** + * Test the DataSet created by the Line chart + */ + public function testDataSet() + { + $Line = new Line(); + $chartData = [ 0, 1, 4, 2, 3, 0, 5, 2, 6 ]; + + // DataSet + $dataSet = $Line->createDataSet(); + $this->assertInstanceOf(LineDataSet::class, $dataSet, 'The correct class has been created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $Line->addDataSet($dataSet); + $this->assertEquals($chartData, $Line->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $Line = new Line(); + $this->assertInstanceOf(LineOptions::class, $Line->options(), 'The correct class should be created'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/PieTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/PieTest.php new file mode 100644 index 0000000000..e6a7be9346 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/PieTest.php @@ -0,0 +1,56 @@ +assertInstanceOf(ChartInterface::class, $Pie, 'The correct interface has been implemented'); + $this->assertInstanceOf(Pie::class, $Pie, 'The correct class has been created'); + } + + /** + * Test the DataSet created by the Pie chart + */ + public function testDataSet() + { + $Pie = new Pie(); + $chartData = [ 0, 1, 4, 2, 3, 0, 5, 2, 6 ]; + + // DataSet + $dataSet = $Pie->createDataSet(); + $this->assertInstanceOf(PieDataSet::class, $dataSet, 'The correct class has been created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $Pie->addDataSet($dataSet); + $this->assertEquals($chartData, $Pie->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $Pie = new Pie(); + $this->assertInstanceOf(PieOptions::class, $Pie->options(), 'The correct class should be created'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/PolarAreaTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/PolarAreaTest.php new file mode 100644 index 0000000000..0b359babd2 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/PolarAreaTest.php @@ -0,0 +1,60 @@ +assertInstanceOf(ChartInterface::class, $PolarArea, 'The correct interface has been implemented'); + $this->assertInstanceOf(PolarArea::class, $PolarArea, 'The correct class has been created'); + } + + /** + * Test the DataSet created by the PolarArea chart + */ + public function testDataSet() + { + $PolarArea = new PolarArea(); + $chartData = [ 0, 1, 4, 2, 3, 0, 5, 2, 6 ]; + + // DataSet + $dataSet = $PolarArea->createDataSet(); + $this->assertInstanceOf(PolarAreaDataSet::class, $dataSet, 'The correct class has been created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $PolarArea->addDataSet($dataSet); + $this->assertEquals($chartData, $PolarArea->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $PolarArea = new PolarArea(); + $this->assertInstanceOf( + PolarAreaOptions::class, + $PolarArea->options(), + 'The correct class should be created' + ); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/RadarTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/RadarTest.php new file mode 100644 index 0000000000..691f43d1f7 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/RadarTest.php @@ -0,0 +1,56 @@ +assertInstanceOf(ChartInterface::class, $Radar, 'The correct interface has been implemented'); + $this->assertInstanceOf(Radar::class, $Radar, 'The correct class has been created'); + } + + /** + * Test the DataSet created by the Radar chart + */ + public function testDataSet() + { + $Radar = new Radar(); + $chartData = [ 0, 1, 4, 2, 3, 0, 5, 2, 6 ]; + + // DataSet + $dataSet = $Radar->createDataSet(); + $this->assertInstanceOf(RadarDataSet::class, $dataSet, 'The correct class has been created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $Radar->addDataSet($dataSet); + $this->assertEquals($chartData, $Radar->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $Radar = new Radar(); + $this->assertInstanceOf(RadarOptions::class, $Radar->options(), 'The correct class should be created'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/ScatterTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/ScatterTest.php new file mode 100644 index 0000000000..2c60fd5dfb --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Chart/ScatterTest.php @@ -0,0 +1,57 @@ +assertInstanceOf(ChartInterface::class, $scatter, 'The correct interface has been implemented'); + $this->assertInstanceOf(Scatter::class, $scatter, 'The correct class has been created'); + } + + /** + * Test the DataSet created by the Scatter chart + */ + public function testDataSet() + { + $scatter = new Scatter(); + $chartData = [0, 1, 4, 2, 3, 0, 5, 2, 6]; + + // DataSet + $dataSet = $scatter->createDataSet(); + $this->assertInstanceOf(ScatterDataSet::class, $dataSet, 'The correct class has been created by the chart'); + + // Populate the collection + $dataSet->data()->exchangeArray($chartData); + + // Check if data is still correct. + $scatter->addDataSet($dataSet); + $this->assertEquals($chartData, $scatter->dataSets()->offsetGet(0)->data()->getArrayCopy()); + } + + /** + * + */ + public function testOptions() + { + $scatter = new Scatter(); + $this->assertInstanceOf(ScatterOptions::class, $scatter->options(), 'The correct class should be created'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/ChartOwnedTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/ChartOwnedTest.php new file mode 100644 index 0000000000..39768def31 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/ChartOwnedTest.php @@ -0,0 +1,41 @@ +assertNull($options->owner(), 'Owner should be empty'); + + $this->assertSame($options, $options->setOwner($bar = new Bar())); + $this->assertSame($bar, $options->owner()); + } + + /** + * + */ + public function testOwnerFromChart() + { + $bar = new Bar(); + $this->assertInstanceOf(ChartOwnedInterface::class, $bar->options()); + $this->assertSame($bar, $bar->options()->owner()); + + $this->assertInstanceOf(ChartOwnedInterface::class, $dataSet = $bar->createDataSet()); + $this->assertSame($dataSet->owner(), $bar); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/ChartTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/ChartTest.php new file mode 100644 index 0000000000..acf6fbf0c5 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/ChartTest.php @@ -0,0 +1,318 @@ + 'height', + // int + 'width' => 'width', + // int + 'title' => 'title', + // string + 'labels' => 'labels', + // LabelsCollection + 'options' => 'options', + // Options + 'dataSets' => 'dataSets', + // DataSetCollection + ]; + + /** + * @var array + */ + private $empty_data = [ + 'height' => null, + // int + 'width' => null, + // int + 'title' => null, + // string + ]; + + + /** + * + */ + public function setUp(): void + { + $this->chart = new Bar(); + } + + + /** + * + */ + public function testGetId() + { + $result = $this->chart->getId(); + self::assertNotNull($result); + } + + + /** + * + */ + public function testSetId() + { + $expected = '1203'; + $this->chart->setId($expected); + self::assertSame($expected, $this->chart->getId()); + } + + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->chart, $this->data_types); + self::assertSame($expected, $result); + } + + + /** + * + */ + public function testHeight() + { + $expected = 15; + $this->chart->setHeight($expected); + $result = $this->chart->getHeight(); + self::assertSame($expected, $result); + } + + + /** + * + */ + public function testWidth() + { + $expected = 17; + $this->chart->setWidth($expected); + $result = $this->chart->getWidth(); + self::assertSame($expected, $result); + } + + + /** + * + */ + public function testTitle() + { + $expected = 'no title'; + $this->chart->setTitle($expected); + $result = $this->chart->getTitle(); + self::assertSame($expected, $result); + } + + + /** + * + */ + public function testLabels() + { + $result = $this->chart->labels(); + self::assertInstanceOf(LabelsCollection::class, $result); + } + + + /** + * + */ + public function testLabelsAdd() + { + $labels = new LabelsCollection(); + $label = 'no label at all'; + $labels->append($label); + $result = $this->chart->addLabel($label)->labels(); + self::assertNotSame($labels, $result); + self::assertEquals($labels, $result); + } + + + /** + * + */ + public function testGetLabelValid() + { + $labels = new LabelsCollection(); + $label = 'no label at all'; + $labels->append($label); + $this->chart->addLabel($label)->labels(); + $result = $this->chart->getLabel(0); + self::assertSame($label, $result); + $result1 = $this->chart->getLabel(1); + self::assertNull($result1); + } + + + /** + * + */ + public function testGetLabelInValid() + { + $labels = new LabelsCollection(); + $label = 'only 1 label'; + $labels->append($label); + $this->chart->addLabel($label)->labels(); + $result1 = $this->chart->getLabel(1); + self::assertNull($result1); + } + + + /** + * + */ + public function testDataSets() + { + $expected = new DataSetCollection(); + $result = $this->chart->dataSets(); + self::assertNotSame($expected, $result); + self::assertEquals($expected, $result); + } + + + /** + * + */ + public function testAddDataSet() + { + $expected = new DataSetCollection(); + $dataSet1 = new DataSet(); + $expected->append($dataSet1); + $dataSet1->setOwner($this->chart); + $dataSet2 = new DataSet(); + $this->chart->addDataSet($dataSet2); + self::assertEquals($dataSet1, $dataSet2); + } + + + /** + * + */ + public function testGetDataSetEmpty() + { + $result = $this->chart->getDataSet(0); + self::assertNull($result); + } + + + /** + * + */ + public function testGetDataSet() + { + $dataSet = new DataSet(); + $this->chart->addDataSet($dataSet); + $result = $this->chart->getDataSet(0); + self::assertSame($dataSet, $result); + } + + + /** + * + */ + public function testRenderCanvas() + { + $chartHtml = '
'.$this->chart->render().'
'; + $htmlDoc = new DOMDocument(); + $htmlDoc->loadXML($chartHtml); + $canvas = $htmlDoc->getElementsByTagName('canvas')->item(0); + $result = $canvas->getAttribute('id'); + self::assertStringStartsWith('chart', $result); + } + + + /** + * + */ + public function testRenderHeight() + { + $expected = '500'; + $this->chart->setHeight($expected); + $chartHtml = '
'.$this->chart->render(true).'
'; + $htmlDoc = new DOMDocument(); + $htmlDoc->loadXML($chartHtml); + $canvas = $htmlDoc->getElementsByTagName('canvas')->item(0); + $result = $canvas->getAttribute('height'); + self::assertSame($expected, $result); + } + + + /** + * + */ + public function testRenderWidth() + { + $expected = '500'; + $this->chart->setWidth($expected); + $chartHtml = '
'.$this->chart->render(true).'
'; + $htmlDoc = new DOMDocument(); + $htmlDoc->loadXML($chartHtml); + $canvas = $htmlDoc->getElementsByTagName('canvas')->item(0); + $result = $canvas->getAttribute('width'); + self::assertSame($expected, $result); + } + + + /** + * + */ + public function testRenderScript() + { + $chartHtml = '
'.$this->chart->render(true).'
'; + $htmlDoc = new DOMDocument(); + $htmlDoc->loadXML($chartHtml); + $script = $htmlDoc->getElementsByTagName('script')->item(0); + self::assertNotEmpty($script->nodeValue); + } + + + /** + * + */ + public function testCreateDataSet() + { + $result = $this->chart->createDataSet(); + self::assertInstanceOf(BarDataSet::class, $result); + } + + + /** + * + */ + public function testOptions() + { + $result = $this->chart->options(); + self::assertInstanceOf(BarOptions::class, $result); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Collection/DataTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Collection/DataTest.php new file mode 100644 index 0000000000..6245bb44a2 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Collection/DataTest.php @@ -0,0 +1,42 @@ +data = new Data(); + } + + + /** + * + */ + public function testJsonSerializeEmpty() + { + $expected = []; + $result = $this->data->jsonSerialize(); + self::assertSame($expected, $result); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/ConfigDefaults/GlobalConfigTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/ConfigDefaults/GlobalConfigTest.php new file mode 100644 index 0000000000..bc40fabff8 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/ConfigDefaults/GlobalConfigTest.php @@ -0,0 +1,127 @@ + '', + 'defaultFontFamily' => '', + 'defaultFontSize' => 1, + 'defaultFontStyle' => '', + ]; + + /** + * @var array + */ + private $input_data = [ + 'defaultFontColor' => 'defaultFontColor', + 'defaultFontFamily' => 'defaultFontFamily', + 'defaultFontSize' => 2, + 'defaultFontStyle' => 'defaultFontStyle', + ]; + + /** + * @var array + */ + private $empty_data = [ + 'defaultFontColor' => null, + 'defaultFontFamily' => null, + 'defaultFontSize' => null, + 'defaultFontStyle' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->config = GlobalConfig::getInstance(); + } + + /** + * + */ + public function testEmpty() + { + $result = TestUtils::getAttributes($this->config, $this->data_types); + $expected = $this->empty_data; + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSet() + { + TestUtils::setAttributes($this->config, $this->input_data); + $result = TestUtils::getAttributes($this->config, $this->data_types); + $expected = $this->input_data; + self::assertSame($expected, $result); + } + + /** + * + */ + public function testLayout() + { + $result = $this->config->layout(); + self::assertInstanceOf(LayoutConfig::class, $result); + } + + /** + * + */ + public function testTooltips() + { + $result = $this->config->tooltips(); + self::assertInstanceOf(TooltipsConfig::class, $result); + } + + /** + * + */ + public function testHover() + { + $result = $this->config->hover(); + self::assertInstanceOf(HoverConfig::class, $result); + } + + /** + * + */ + public function testAnimation() + { + $result = $this->config->animation(); + self::assertInstanceOf(AnimationConfig::class, $result); + } + + /** + * + */ + public function testElements() + { + $result = $this->config->elements(); + self::assertInstanceOf(ElementsConfig::class, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/DataSetCollectionTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/DataSetCollectionTest.php new file mode 100644 index 0000000000..5d5252c333 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/DataSetCollectionTest.php @@ -0,0 +1,66 @@ +dataSetCollection = new DataSetCollection(); + } + + /** + * + */ + public function testGetArrayCopyEmpty() + { + $expected = []; + $result = $this->dataSetCollection->getArrayCopy(); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetArrayCopyNonEmpty() + { + $expected = [[]]; + $this->dataSetCollection->append(new DataSet()); + $result = $this->dataSetCollection->getArrayCopy(); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerializeEmpty() + { + $expected = []; + $result = $this->dataSetCollection->jsonSerialize(); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerializeNonEmpty() + { + $expected = [[]]; + $this->dataSetCollection->append(new DataSet()); + $result = $this->dataSetCollection->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/DataSetTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/DataSetTest.php new file mode 100644 index 0000000000..906259448c --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/DataSetTest.php @@ -0,0 +1,285 @@ +assertInstanceOf(ChartOwnedInterface::class, $dataSet, 'Class implements ChartOwnedInterface'); + $this->assertInstanceOf( + ArraySerializableInterface::class, + $dataSet, + 'Class implements ArraySerializableInterface' + ); + $this->assertInstanceOf(JsonSerializable::class, $dataSet, 'Class implements JsonSerializable'); + } + + /** + * + */ + public function testOwner() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->owner(), 'The dataset has no owner'); + + $chart = new Bar(); + $chart->addDataSet($dataSet); + + $this->assertEquals($chart, $dataSet->owner(), 'The owner of the dataSet is set and returned correctly'); + $this->assertInstanceOf( + ChartInterface::class, + $dataSet->owner(), + 'The owner of the dataSet implements the correct interface' + ); + } + + /** + * + */ + public function testType() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getType(), 'The type is not set'); + + $dataSet->setType(Bar::TYPE); + $this->assertEquals(Bar::TYPE, $dataSet->getType(), 'The type is set and returned correctly'); + } + + /** + * + */ + public function testData() + { + $dataSet = new DataSet(); + + $dataCollection = $dataSet->data(); + $this->assertInstanceOf(Data::class, $dataCollection, 'The data collection is the right class'); + $this->assertInstanceOf(ArrayAccess::class, $dataCollection, 'The data collection extends Collection'); + $this->assertInstanceOf( + JsonSerializable::class, + $dataCollection, + 'The data collection implements JsonSerializable' + ); + } + + /** + * + */ + public function testLabel() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getLabel(), 'The label should not be set'); + + $dataSet->setLabel('Foo'); + $this->assertEquals('Foo', $dataSet->getLabel(), 'The label should have been set correctly'); + } + + /** + * + */ + public function testBackgroundColor() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getBackgroundColor(), 'The background color is not set'); + + $dataSet->setBackgroundColor('#fff'); + $this->assertEquals('#fff', $dataSet->getBackgroundColor()); + + $backgroundColorArray = ['#fff', 'rgb( 255, 255, 255 )', 'rgba( 255, 255, 255, .5 )', 'white']; + $dataSet->setBackgroundColor($backgroundColorArray); + $this->assertEquals( + $backgroundColorArray, + $dataSet->getBackgroundColor(), + 'The background color is set again and returned correctly' + ); + } + + /** + * + */ + public function testBorderColor() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getBorderColor(), 'The border color is not set'); + + $dataSet->setBorderColor('#fff'); + $this->assertEquals('#fff', $dataSet->getBorderColor(), 'The border color is set and returned correctly'); + + $borderColorArray = ['#fff', 'rgb( 255, 255, 255 )', 'rgba( 255, 255, 255, .5 )', 'white']; + $dataSet->setBorderColor($borderColorArray); + $this->assertEquals( + $borderColorArray, + $dataSet->getBorderColor(), + 'The border color is set again and returned correctly' + ); + } + + /** + * Test setting and getting the border width. + */ + public function testBorderWidth() + { + $dataSet = new DataSet(); + $this->assertNull($dataSet->getBorderWidth(), 'The border width is not set'); + + $this->assertEquals( + $dataSet, + $dataSet->setBorderWidth(10), + 'Setting the border width should return the DataSet instance' + ); + $this->assertTrue(is_int($dataSet->getBorderWidth()), 'Return type should be int'); + $this->assertEquals(10, $dataSet->getBorderWidth(), 'The border width should equal int 10'); + + $dataSet->setBorderWidth('20'); + $this->assertTrue(is_int($dataSet->getBorderWidth()), 'Return type should be int'); + $this->assertEquals(20, $dataSet->getBorderWidth(), 'The border width should equal int 20'); + + $dataSet->setBorderWidth('30abc'); + $this->assertTrue(is_int($dataSet->getBorderWidth()), 'Return type should be int'); + $this->assertEquals(30, $dataSet->getBorderWidth(), 'The border width should equal int 30'); + + $dataSet->setBorderWidth(40.00); + $this->assertTrue(is_int($dataSet->getBorderWidth()), 'Return type should be int'); + $this->assertEquals(40, $dataSet->getBorderWidth(), 'The border width should equal int 40'); + + $dataSet->setBorderWidth('abc50'); + $this->assertTrue(is_int($dataSet->getBorderWidth()), 'Return type should be int'); + $this->assertEquals(0, $dataSet->getBorderWidth(), 'The border width should equal int 0'); + + $dataSet->setBorderWidth([10, '20', '30abc', 40.00, 'abc50']); + $this->assertTrue(is_array($dataSet->getBorderWidth()), 'Return type should be array'); + $this->assertEquals([10, 20, 30, 40, 0], $dataSet->getBorderWidth(), 'Return value should be array of int'); + } + + /** + * + */ + public function testBorderSkipped() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getBorderSkipped(), 'The border skipped value is not set'); + + $this->assertInstanceOf( + DataSet::class, + $dataSet->setBorderSkipped('bottom'), + 'The correct class is returned' + ); + $this->assertEquals('bottom', $dataSet->getBorderSkipped(), 'The correct value is returned'); + } + + /** + * + */ + public function testAxes() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getXAxisID(), 'The xAxisID value should not be set'); + + $this->assertInstanceOf(DataSet::class, $dataSet->setXAxisID('myXAxis')); + $this->assertEquals('myXAxis', $dataSet->getXAxisID(), 'The correct value is returned'); + $this->assertEquals('myXAxis', $dataSet->getArrayCopy()['xAxisID'], 'getArrayCopy is failing'); + $this->assertEquals('myXAxis', $dataSet->jsonSerialize()['xAxisID'], 'Serialized data is not correct'); + + $this->assertNull($dataSet->getYAxisID(), 'The yAxisID value is not set'); + + $this->assertInstanceOf(DataSet::class, $dataSet->setYAxisID('myYAxis')); + $this->assertEquals('myYAxis', $dataSet->getYAxisID(), 'The correct value is not returned'); + $this->assertEquals('myXAxis', $dataSet->jsonSerialize()['xAxisID'], 'Serialized data is not correct'); + } + + /** + * + */ + public function testHoverBackgroundColor() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getHoverBackgroundColor(), 'The hoverBackgroundColor value is not set'); + + $this->assertInstanceOf(DataSet::class, $dataSet->setHoverBackgroundColor('#fff')); + $this->assertEquals('#fff', $dataSet->getHoverBackgroundColor(), 'The correct value is returned'); + + $newColors = ['silver', '#fff', 'rgb( 0, 0, 0 )', 'rgba( 255, 255, 255, .5 )']; + $dataSet->setHoverBackgroundColor($newColors); + $this->assertEquals($newColors, $dataSet->getHoverBackgroundColor(), 'The correct value is returned'); + } + + /** + * + */ + public function testHoverBorderColor() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getHoverBorderColor(), 'The hoverBorderColor value is not set'); + + $this->assertInstanceOf(DataSet::class, $dataSet->setHoverBorderColor('#fff')); + $this->assertEquals('#fff', $dataSet->getHoverBorderColor(), 'The correct value is returned'); + + $dataSet->setHoverBorderColor(['silver', '#fff', 'rgb( 0, 0, 0 )', 'rgba( 255, 255, 255, .5 )', 0]); + $this->assertEquals( + ['silver', '#fff', 'rgb( 0, 0, 0 )', 'rgba( 255, 255, 255, .5 )', '0'], + $dataSet->getHoverBorderColor(), + 'The correct value is returned' + ); + } + + /** + * + */ + public function testHoverBorderWidth() + { + $dataSet = new DataSet(); + + $this->assertNull($dataSet->getHoverBorderWidth(), 'The hoverBorderWidth value is not set'); + + $this->assertInstanceOf(DataSet::class, $dataSet->setHoverBorderWidth(1)); + $this->assertEquals(1, $dataSet->getHoverBorderWidth(), 'The correct value is returned'); + + $dataSet->setHoverBorderWidth([1, 10, '5a', 0]); + $this->assertEquals([1, 10, 5, 0], $dataSet->getHoverBorderWidth(), 'The correct value is returned'); + } + + /** + * + */ + public function testVisibility() + { + $dataSet = new DataSet(); + $this->assertArrayNotHasKey('hidden', $dataSet->jsonSerialize(), 'Value should not be present'); + $this->assertFalse($dataSet->isHidden(), 'Default value should be false'); + $this->assertArrayHasKey('hidden', $dataSet->jsonSerialize(), 'Value should be present'); + $this->assertInstanceOf(DataSet::class, $dataSet->setHidden(true)); + $this->assertTrue($dataSet->isHidden(), 'Value should be true'); + $this->assertTrue($dataSet->jsonSerialize()['hidden'], 'Value should be true'); + $this->assertInstanceOf(DataSet::class, $dataSet->setHidden(null)); + $this->assertArrayNotHasKey('hidden', $dataSet->jsonSerialize(), 'Value should not be present'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Delegate/ArraySerializableTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Delegate/ArraySerializableTest.php new file mode 100644 index 0000000000..371a47a164 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Delegate/ArraySerializableTest.php @@ -0,0 +1,202 @@ +classA = new A(1, 2); + $this->classB = new B(3, 4, 5, 6); + } + + /** + * + */ + public function testSuperclass() + { + $expected = [ 'a' => 1, 'b' => 2 ]; + ksort($expected); + $result = $this->classA->getArrayCopy(); + ksort($result); + self::assertSame($expected, $result); + } +} + +/** + * Class A + * @package Test\Delegate + */ +class A +{ + use ArraySerializable; + + /** + * @var int + */ + protected $a; + + /** + * @var int + */ + protected $b; + + /** + * + * should not show (private) + * @var int + */ + private $x; + + /** + * should not show (private) + * @var int + */ + private $y; + + /** + * A constructor. + * + * @param $a int + * @param $b int + */ + public function __construct($a, $b) + { + $this->a = $a; + $this->b = $b; + } + + /** + * @return int + */ + public function getA() + { + return $this->a; + } + + /** + * @param int $a + */ + public function setA($a) + { + $this->a = $a; + } + + /** + * @return int + */ + public function getB() + { + return $this->b; + } + + /** + * @param int $b + */ + public function setB($b) + { + $this->b = $b; + } + + /** + * @return int + */ + public function getX() + { + return $this->x; + } + + /** + * this method should never be called by jsonSerialize because it is not + * a boolean. + * + * @return int + */ + public function getY() + { + return $this->y; + } +} + +/** + * Class B + * @package Test\Delegate + */ +class B extends A +{ + /** + * @var int + */ + protected $c; + + /** + * @var int + */ + protected $d; + + /** + * B constructor. + * + * @param $a int + * @param $b int + * @param $c int + * @param $d int + */ + public function __construct($a, $b, $c, $d) + { + parent::__construct($a, $b); + $this->c = $c; + $this->d = $d; + } + + /** + * @return int + */ + public function getC() + { + return $this->c; + } + + /** + * @param int $c + */ + public function setC($c) + { + $this->c = $c; + } + + /** + * @return int + */ + public function getD() + { + return $this->d; + } + + /** + * @param int $d + */ + public function setD($d) + { + $this->d = $d; + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/FactoryTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/FactoryTest.php new file mode 100644 index 0000000000..5d8df1308b --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/FactoryTest.php @@ -0,0 +1,140 @@ +assertInstanceOf(Factory::class, $factory, 'Factory is correct class'); + } + + /** + * + */ + public function testBar() + { + $factory = new Factory(); + $chart = $factory->create($factory::BAR); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(Bar::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testBubble() + { + $factory = new Factory(); + $chart = $factory->create($factory::BUBBLE); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(Bubble::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testDoughnut() + { + $factory = new Factory(); + $chart = $factory->create($factory::DOUGHNUT); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(Pie::class, $chart, 'The correct class has been extended'); + $this->assertInstanceOf(Doughnut::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testHorizontalBar() + { + $factory = new Factory(); + $chart = $factory->create($factory::HORIZONTAL_BAR); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(Bar::class, $chart, 'The correct class has been extended'); + $this->assertInstanceOf(HorizontalBar::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testLine() + { + $factory = new Factory(); + $chart = $factory->create($factory::LINE); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(Line::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testPie() + { + $factory = new Factory(); + $chart = $factory->create($factory::PIE); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(Pie::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testPolarArea() + { + $factory = new Factory(); + $chart = $factory->create($factory::POLAR_AREA); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(PolarArea::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testRadar() + { + $factory = new Factory(); + $chart = $factory->create($factory::RADAR); + + $this->assertInstanceOf(ChartInterface::class, $chart, 'The correct interface has been implemented'); + $this->assertInstanceOf(Radar::class, $chart, 'The correct class has been created'); + } + + /** + * + */ + public function testNonExisting() + { + $factory = new Factory(); + $this->expectException(\InvalidArgumentException::class); + + $factory->create('foo'); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/LabelsCollectionTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/LabelsCollectionTest.php new file mode 100644 index 0000000000..68f3a572e9 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/LabelsCollectionTest.php @@ -0,0 +1,69 @@ +labelsCollectionEmpty = new LabelsCollection(); + $this->labelsCollection = new LabelsCollection($this->labelsArray); + } + + + /** + * + */ + public function testJsonSerializeEmpty() + { + $expected = []; + $result = $this->labelsCollectionEmpty->jsonSerialize(); + self::assertSame($expected, $result); + } + + + /** + * + */ + public function testJsonSerialize() + { + $expected = $this->labelsArray; + $result = $this->labelsCollection->jsonSerialize(); + self::assertSame($expected, $result); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/AnimationTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/AnimationTest.php new file mode 100644 index 0000000000..7034596b7a --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/AnimationTest.php @@ -0,0 +1,100 @@ + 1, + 'easing' => '', + 'onProgress' => '', + 'onComplete' => '', + ]; + + /** + * @var array + */ + private $input_data_no_expressions = [ + 'duration' => 1, + 'easing' => '', + 'onProgress' => null, + 'onComplete' => null, + ]; + + /** + * @var array + */ + private $input_data_with_expressions = [ + 'duration' => 1, + 'easing' => '', + 'onProgress' => 'function() { echo "onProgress"; }', + 'onComplete' => 'function() { echo "onComplete"; }', + ]; + + /** + * @var array + */ + private $empty_data = [ + 'duration' => null, + 'easing' => null, + 'onProgress' => null, + 'onComplete' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->animation = new Animation(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->animation, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testGetAndSetWithExpressions() + { + $expected = $this->input_data_with_expressions; + TestUtils::setAttributes($this->animation, $this->input_data_with_expressions); + $result = TestUtils::getAttributes($this->animation, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testJsonSerializeNoExpressions() + { + $expected = TestUtils::removeNullsFromArray($this->input_data_no_expressions); + TestUtils::setAttributes($this->animation, $this->input_data_no_expressions); + $result = $this->animation->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ClickTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ClickTest.php new file mode 100644 index 0000000000..ed31e5dcfb --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ClickTest.php @@ -0,0 +1,70 @@ + null, + ]; + + private $input_data_no_expressions = ['onClick' => null]; + private $input_data_with_expressions = null; + + /** + * + */ + public function setUp(): void + { + $this->options = new Options(); + $this->input_data_with_expressions = ['onClick' => new Expr('function(event, array) { echo "onClick"; }')]; + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->input_data_no_expressions; + TestUtils::setAttributes($this->options, $this->input_data_no_expressions); + $result = TestUtils::getAttributes($this->options, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testGetAndSetWithExpressions() + { + $expected = $this->input_data_with_expressions; + TestUtils::setAttributes($this->options, $this->input_data_with_expressions); + $result = TestUtils::getAttributes($this->options, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testJsonSerializeWithoutExpressions() + { + $expected = TestUtils::removeNullsFromArray($this->input_data_no_expressions); + TestUtils::setAttributes($this->options, $this->input_data_no_expressions); + $result = $this->options->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/ArcTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/ArcTest.php new file mode 100644 index 0000000000..e890568bfd --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/ArcTest.php @@ -0,0 +1,87 @@ + '', /* string */ + 'borderColor' => '', /* string */ + 'borderWidth' => 1, /* int */ + ]; + + /** + * @var array + */ + private $empty_data = [ + 'backgroundColor' => null, /* string */ + 'borderColor' => null, /* string */ + 'borderWidth' => null, /* int */ + ]; + + /** + * @var array + */ + private $input_data = [ + 'backgroundColor' => 'backgroundColor', /* string */ + 'borderColor' => 'borderColor', /* string */ + 'borderWidth' => 2, /* int */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->arc = new Arc(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->arc, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetNoObjects() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->arc, $this->input_data); + $result = TestUtils::getAttributes($this->arc, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerialize() + { + $expected = TestUtils::removeNullsFromArray($this->input_data); + TestUtils::setAttributes($this->arc, $this->input_data); + $result = $this->arc->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/LineTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/LineTest.php new file mode 100644 index 0000000000..88795b2970 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/LineTest.php @@ -0,0 +1,111 @@ + 1.0, /* float */ + 'backgroundColor' => '', /* string */ + 'borderWidth' => 1, /* int */ + 'borderColor' => '', /* string */ + 'borderCapStyle' => '', /* string */ + 'borderDash' => [1, 2], /* int[] */ + 'borderDashOffset' => 1.0, /* float */ + 'borderJoinStyle' => '', /* string */ + 'capBezierPoints' => false, /* bool */ + 'fill' => '', /* string */ + 'stepped' => false, /* bool */ + ]; + + /** + * @var array + */ + private $empty_data = [ + 'tension' => null, /* float */ + 'backgroundColor' => null, /* string */ + 'borderWidth' => null, /* int */ + 'borderColor' => null, /* string */ + 'borderCapStyle' => null, /* string */ + 'borderDash' => null, /* int[] */ + 'borderDashOffset' => null, /* float */ + 'borderJoinStyle' => null, /* string */ + 'capBezierPoints' => null, /* bool */ + 'fill' => null, /* bool */ + 'stepped' => null, /* bool */ + ]; + + /** + * @var array + */ + private $input_data = [ + 'tension' => 0.2, /* float */ + 'backgroundColor' => 'backgroundColor', /* string */ + 'borderWidth' => 2, /* int */ + 'borderColor' => 'borderColor', /* string */ + 'borderCapStyle' => 'borderCapStyle', /* string */ + 'borderDash' => [5, 6], /* int[] */ + 'borderDashOffset' => 0.1, /* float */ + 'borderJoinStyle' => 'borderJoinStyle', /* string */ + 'capBezierPoints' => true, /* bool */ + 'fill' => true, /* bool */ + 'stepped' => true, /* bool */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->line = new Line(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->line, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetNoObjects() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->line, $this->input_data); + $result = TestUtils::getAttributes($this->line, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerialize() + { + $expected = TestUtils::removeNullsFromArray($this->input_data); + TestUtils::setAttributes($this->line, $this->input_data); + $result = $this->line->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/PointTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/PointTest.php new file mode 100644 index 0000000000..7f42cbe048 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/PointTest.php @@ -0,0 +1,105 @@ + 1, /* int */ + 'pointStyle' => '', /* string */ + 'rotation' => 1, /* int */ + 'backgroundColor' => '', /* string */ + 'borderWidth' => 1, /* int */ + 'borderColor' => '', /* string */ + 'hitRadius' => 1, /* int */ + 'hoverRadius' => 1, /* int */ + 'hoverBorderWidth' => 1, /* int */ + ]; + + /** + * @var array + */ + private $empty_data = [ + 'radius' => null, /* int */ + 'pointStyle' => null, /* string */ + 'rotation' => null, /* int */ + 'backgroundColor' => null, /* string */ + 'borderWidth' => null, /* int */ + 'borderColor' => null, /* string */ + 'hitRadius' => null, /* int */ + 'hoverRadius' => null, /* int */ + 'hoverBorderWidth' => null, /* int */ + ]; + + /** + * @var array + */ + private $input_data = [ + 'radius' => 2, /* int */ + 'pointStyle' => 'pointStyle', /* string */ + 'rotation' => 2, /* int */ + 'backgroundColor' => 'backgroundColor', /* string */ + 'borderWidth' => 2, /* int */ + 'borderColor' => 'borderColor', /* string */ + 'hitRadius' => 2, /* int */ + 'hoverRadius' => 2, /* int */ + 'hoverBorderWidth' => 2, /* int */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->point = new Point(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->point, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetNoObjects() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->point, $this->input_data); + $result = TestUtils::getAttributes($this->point, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerialize() + { + $expected = TestUtils::removeNullsFromArray($this->input_data); + TestUtils::setAttributes($this->point, $this->input_data); + $result = $this->point->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/RectangleTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/RectangleTest.php new file mode 100644 index 0000000000..34bdccd439 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Elements/RectangleTest.php @@ -0,0 +1,90 @@ + '', /* string */ + 'borderWidth' => 1, /* int */ + 'borderColor' => '', /* string */ + 'borderSkipped' => '', /* string */ + ]; + + /** + * @var array + */ + private $empty_data = [ + 'backgroundColor' => null, /* string */ + 'borderWidth' => null, /* int */ + 'borderColor' => null, /* string */ + 'borderSkipped' => null, /* string */ + ]; + + /** + * @var array + */ + private $input_data = [ + 'backgroundColor' => 'backgroundColor', /* string */ + 'borderWidth' => 2, /* int */ + 'borderColor' => 'borderColor', /* string */ + 'borderSkipped' => 'borderSkipped', /* string */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->rectangle = new Rectangle(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->rectangle, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetNoObjects() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->rectangle, $this->input_data); + $result = TestUtils::getAttributes($this->rectangle, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerialize() + { + $expected = TestUtils::removeNullsFromArray($this->input_data); + TestUtils::setAttributes($this->rectangle, $this->input_data); + $result = $this->rectangle->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ElementsTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ElementsTest.php new file mode 100644 index 0000000000..81bb02af3d --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ElementsTest.php @@ -0,0 +1,123 @@ + '', /* Rectangle */ + 'line' => '', /* Line */ + 'point' => '', /* Point */ + 'arc' => '', /* Arc */ + ]; + + /** + * @var array + */ + private $empty_data = [ + 'rectangle' => null, /* Rectangle */ + 'line' => null, /* Line */ + 'point' => null, /* Point */ + 'arc' => null, /* Arc */ + ]; + + /** + * @var array + */ + private $input_data = [ + 'rectangle' => null, /* Rectangle */ + 'line' => null, /* Line */ + 'point' => null, /* Point */ + 'arc' => null, /* Arc */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->elements = new Elements(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->elements, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testRectangle() + { + $rectangle = $this->elements->rectangle(); + self::assertNotNull($rectangle); + self::assertInstanceOf(Rectangle::class, $rectangle); + } + + /** + * + */ + public function testLine() + { + $line = $this->elements->line(); + self::assertNotNull($line); + self::assertInstanceOf(Line::class, $line); + } + + /** + * + */ + public function testPoint() + { + $point = $this->elements->point(); + self::assertNotNull($point); + self::assertInstanceOf(Point::class, $point); + } + + /** + * + */ + public function testArc() + { + $arc = $this->elements->arc(); + self::assertNotNull($arc); + self::assertInstanceOf(Arc::class, $arc); + } + + /** + * + */ + public function testJsonSerialize() + { + $expected = TestUtils::removeNullsFromArray($this->input_data); + TestUtils::setAttributes($this->elements, $this->input_data); + $result = $this->elements->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/HoverTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/HoverTest.php new file mode 100644 index 0000000000..b55a4e9ae3 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/HoverTest.php @@ -0,0 +1,100 @@ + '', + 'intersect' => false, + 'animationDuration' => 1, + 'onHover' => '', + ]; + + /** + * @var array + */ + private $input_data_no_expressions = [ + 'mode' => 'mode', + 'intersect' => true, + 'animationDuration' => 2, + 'onHover' => null, + ]; + + /** + * @var array + */ + private $input_data_with_expressions = [ + 'mode' => 'mode', + 'intersect' => true, + 'animationDuration' => 2, + 'onHover' => 'function() { echo "onHover"; }', + ]; + + /** + * @var array + */ + private $empty_data = [ + 'mode' => null, + 'intersect' => null, + 'animationDuration' => null, + 'onHover' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->hover = new Hover(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->hover, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testGetAndSetWithExpressions() + { + $expected = $this->input_data_with_expressions; + TestUtils::setAttributes($this->hover, $this->input_data_with_expressions); + $result = TestUtils::getAttributes($this->hover, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testJsonSerializeWithoutExpressions() + { + $expected = TestUtils::removeNullsFromArray($this->input_data_no_expressions); + TestUtils::setAttributes($this->hover, $this->input_data_no_expressions); + $result = $this->hover->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Layout/PaddingTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Layout/PaddingTest.php new file mode 100644 index 0000000000..9f2b365d98 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Layout/PaddingTest.php @@ -0,0 +1,86 @@ +padding = new Padding(); + } + + + /** + * + */ + public function testBottom() + { + $this->assertNull($this->padding->getBottom()); + $this->padding->setBottom(20); + $this->assertSame(20, $this->padding->getBottom()); + } + + + /** + * + */ + public function testLeft() + { + $this->assertNull($this->padding->getLeft()); + $this->padding->setLeft(20); + $this->assertSame(20, $this->padding->getLeft()); + } + + + /** + * + */ + public function testRight() + { + $this->assertNull($this->padding->getRight()); + $this->padding->setRight(20); + $this->assertSame(20, $this->padding->getRight()); + } + + + /** + * + */ + public function testTop() + { + $this->assertNull($this->padding->getTop()); + $this->padding->setTop(20); + $this->assertSame(20, $this->padding->getTop()); + } + + + /** + * + */ + public function testJsonSerialize() + { + $this->padding->setTop(20); + $result = $this->padding->jsonSerialize(); + $this->assertEquals(20, $result['top']); + } + + +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/LayoutTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/LayoutTest.php new file mode 100644 index 0000000000..198e00c7c1 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/LayoutTest.php @@ -0,0 +1,109 @@ + 1, + ]; + + /** + * @var array + */ + private $input_data = [ + 'padding' => 2, + ]; + + /** + * @var array + */ + private $empty_data = [ + 'padding' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->layout = new Layout(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->layout, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testPaddingInt() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->layout, $this->input_data); + $result = TestUtils::getAttributes($this->layout, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * Test whether Layout stores the Padding object correctly and + * relays changes correctly. We are *not* testing Padding + */ + public function testPaddingObject() + { + self::assertInstanceOf(Layout\Padding::class, $this->layout->padding()); + $this->layout->padding()->setLeft(2.1); + self::assertEquals(2, $this->layout->padding()->getLeft()); + } + + /** + * + */ + public function testJsonSerializeAllInt() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->layout, $this->input_data); + self::assertEquals($expected, $this->layout->jsonSerialize()); + } + + /** + * + */ + public function testJsonSerializePaddingObj() + { + $expected = $this->empty_data; + $expected['padding'] = [ + "bottom" => null, + "left" => null, + "right" => 5, + "top" => null, + ]; + + $this->layout->padding()->setRight(5); + $result = $this->layout->jsonSerialize(); + self::assertEquals(5, $result['padding']['right']); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Legend/LabelsTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Legend/LabelsTest.php new file mode 100644 index 0000000000..540baba46e --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Legend/LabelsTest.php @@ -0,0 +1,132 @@ + 1, + 'fontSize' => 1, + 'fontStyle' => '', + 'fontColor' => '', + 'fontFamily' => '', + 'generateLabels' => '', + 'padding' => 1, + 'usePointStyle' => false, + ]; + + /** + * @var array + */ + private $input_data_1 = [ + 'boxWidth' => 12, + 'fontSize' => 13, + 'fontStyle' => 'fontStyle', + 'fontColor' => 'fontColor', + 'fontFamily' => 'fontFamily', + 'generateLabels' => null, + 'padding' => 14, + 'usePointStyle' => true, + ]; + + /** + * @var array + */ + private $input_data_2 = [ + 'boxWidth' => 12, + 'fontSize' => 13, + 'fontStyle' => 'fontStyle', + 'fontColor' => 'fontColor', + 'fontFamily' => 'fontFamily', + 'generateLabels' => 'generateLabels', + 'padding' => 14, + 'usePointStyle' => true, + ]; + + /** + * @var array + */ + private $empty_data = [ + 'boxWidth' => null, + 'fontSize' => null, + 'fontStyle' => null, + 'fontColor' => null, + 'fontFamily' => null, + 'generateLabels' => null, + 'padding' => null, + 'usePointStyle' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->labels = new Labels(); + } + + /** + * + */ + public function testEmptyData() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->labels, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetWithoutExpr() + { + $expected = $this->input_data_1; + TestUtils::setAttributes($this->labels, $this->input_data_1); + $result = TestUtils::getAttributes($this->labels, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testExpr() + { + TestUtils::setAttributes($this->labels, $this->input_data_2); + $result = $this->labels->getGenerateLabels()->__toString(); + $expected = $this->input_data_2['generateLabels']; + self::assertSame($expected, $result); + } + + /** + * This test uses assertEquals in stead of assertSame because json_encode / json_decode + * transform the float numbers to string, after which the decimal zero's disappear. It is + * still a float, but will be recognized by assertSame as an int. For that reason assertSame + * will not work as expected. + * + */ + public function testJsonSerializeWithoutExpr() + { + $expected = TestUtils::removeNullsFromArray($this->input_data_1); + TestUtils::setAttributes($this->labels, $expected); + $result = $this->labels->jsonSerialize(); + self::assertEquals($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/LegendTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/LegendTest.php new file mode 100644 index 0000000000..9f29a6501d --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/LegendTest.php @@ -0,0 +1,125 @@ + false, + 'position' => '', + 'fullWidth' => false, + 'onClick' => '', + 'onHover' => '', + 'reverse' => false, + 'labels' => '', /* LabelsCollection */ + ]; + + /** + * @var array + */ + private $input_data_with_expressions = [ + 'display' => true, + 'position' => 'position', + 'fullWidth' => true, + 'onClick' => 'onClick', + 'onHover' => 'onHover', + 'reverse' => true, + ]; + + /** + * @var array + */ + private $input_data_no_expressions = [ + 'display' => true, + 'position' => 'position', + 'fullWidth' => true, + 'onClick' => null, + 'onHover' => null, + 'labels' => null, + 'reverse' => true, + ]; + + /** + * @var array + */ + private $empty_data = [ + 'display' => null, + 'position' => null, + 'fullWidth' => null, + 'onClick' => null, + 'onHover' => null, + 'reverse' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->legend = new Legend(); + + $this->input_data_with_expressions['onClick'] = new Expr($this->input_data_with_expressions['onClick']); + $this->input_data_with_expressions['onHover'] = new Expr($this->input_data_with_expressions['onHover']); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->legend, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetWithExpr() + { + $expected = $this->input_data_with_expressions; + TestUtils::setAttributes($this->legend, $this->input_data_with_expressions); + $result = TestUtils::getAttributes($this->legend, $this->data_types); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testLabelsCollection() + { + $labels = $this->legend->labels(); + self::assertNotNull($labels); + self::assertInstanceOf(LabelsCollection::class, $labels); + } + + /** + * + */ + public function testJsonSerializeWithoutExpressions() + { + $expected = TestUtils::removeNullsFromArray($this->input_data_no_expressions); + TestUtils::setAttributes($this->legend, $this->input_data_no_expressions); + $result = $this->legend->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ScaleTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ScaleTest.php new file mode 100644 index 0000000000..cd4826e088 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ScaleTest.php @@ -0,0 +1,186 @@ + '', /* string */ + 'display' => false, /* bool */ + 'id' => '', /* string */ + 'stacked' => false, /* bool */ + 'barThickness' => 1, /* int */ + 'position' => '', /* string */ + 'beforeUpdate' => '', /* string */ + 'beforeSetDimensions' => '', /* string */ + 'afterSetDimensions' => '', /* string */ + 'beforeDataLimits' => '', /* string */ + 'afterDataLimits' => '', /* string */ + 'beforeBuildTicks' => '', /* string */ + 'afterBuildTicks' => '', /* string */ + 'beforeTickToLabelConversion' => '', /* string */ + 'afterTickToLabelConversion' => '', /* string */ + 'beforeCalculateTickRotation' => '', /* string */ + 'afterCalculateTickRotation' => '', /* string */ + 'beforeFit' => '', /* string */ + 'afterFit' => '', /* string */ + 'afterUpdate' => '', /* string */ + 'gridLines' => '', /* GridLines */ + 'scaleLabel' => '', /* ScaleLabel */ + 'ticks' => '', /* Ticks */ + ]; + + /** + * @var array + */ + private $input_data = [ + 'type' => 'type', /* string */ + 'display' => true, /* bool */ + 'id' => 'id', /* string */ + 'stacked' => true, /* bool */ + 'barThickness' => 2, /* int */ + 'position' => 'position', /* string */ + 'beforeUpdate' => 'beforeUpdate', /* string */ + 'beforeSetDimensions' => 'beforeSetDimensions', /* string */ + 'afterSetDimensions' => 'afterSetDimensions', /* string */ + 'beforeDataLimits' => 'beforeDataLimits', /* string */ + 'afterDataLimits' => 'afterDataLimits', /* string */ + 'beforeBuildTicks' => 'beforeBuildTicks', /* string */ + 'afterBuildTicks' => 'afterBuildTicks', /* string */ + 'beforeTickToLabelConversion' => 'beforeTickToLabelConversion', /* string */ + 'afterTickToLabelConversion' => 'afterTickToLabelConversion', /* string */ + 'beforeCalculateTickRotation' => 'beforeCalculateTickRotation', /* string */ + 'afterCalculateTickRotation' => 'afterCalculateTickRotation', /* string */ + 'beforeFit' => 'beforeFit', /* string */ + 'afterFit' => 'afterFit', /* string */ + 'afterUpdate' => 'afterUpdate', /* string */ + 'gridLines' => null, /* GridLines */ + 'scaleLabel' => null, /* ScaleLabel */ + 'ticks' => null, /* Ticks */ + ]; + + /** + * @var array + */ + private $empty_data = [ + 'type' => null, /* string */ + 'display' => null, /* bool */ + 'id' => null, /* string */ + 'stacked' => null, /* bool */ + 'barThickness' => null, /* int */ + 'position' => null, /* string */ + 'beforeUpdate' => null, /* string */ + 'beforeSetDimensions' => null, /* string */ + 'afterSetDimensions' => null, /* string */ + 'beforeDataLimits' => null, /* string */ + 'afterDataLimits' => null, /* string */ + 'beforeBuildTicks' => null, /* string */ + 'afterBuildTicks' => null, /* string */ + 'beforeTickToLabelConversion' => null, /* string */ + 'afterTickToLabelConversion' => null, /* string */ + 'beforeCalculateTickRotation' => null, /* string */ + 'afterCalculateTickRotation' => null, /* string */ + 'beforeFit' => null, /* string */ + 'afterFit' => null, /* string */ + 'afterUpdate' => null, /* string */ + 'gridLines' => null, /* GridLines */ + 'scaleLabel' => null, /* ScaleLabel */ + 'ticks' => null, /* Ticks */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->scale = new AScale(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->scale, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetNoObjects() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->scale, $this->input_data); + $result = TestUtils::getAttributes($this->scale, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGridLines() + { + $g = $this->scale->gridLines(); + self::assertInstanceOf(GridLines::class, $g); + } + + /** + * + */ + public function testScaleLabel() + { + $s = $this->scale->scaleLabel(); + self::assertInstanceOf(ScaleLabel::class, $s); + } + + /** + * + */ + public function testTicks() + { + $t = $this->scale->ticks(); + self::assertInstanceOf(Ticks::class, $t); + } + + /** + * + */ + public function testJsonSerializeNoObjects() + { + $expected = TestUtils::removeNullsFromArray($this->input_data); + TestUtils::setAttributes($this->scale, $this->input_data); + $result = $this->scale->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/GridLinesTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/GridLinesTest.php new file mode 100644 index 0000000000..56de5f7d30 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/GridLinesTest.php @@ -0,0 +1,141 @@ + false, + 'color' => '', + 'borderDash' => 1.0, + 'borderDashOffset' => [1.0], + 'lineWidth' => 1, + 'drawBorder' => false, + 'drawOnChartArea' => false, + 'drawTicks' => false, + 'tickMarkLength' => 1, + 'zeroLineWidth' => 1, + 'zeroLineColor' => '', + 'offsetGridLines' => false, + ]; + + /** + * @var array + */ + private $input_data_single_value = [ + 'display' => true, + 'color' => 'color', + 'borderDash' => [2.0], + 'borderDashOffset' => 3.0, + 'lineWidth' => 4, + 'drawBorder' => true, + 'drawOnChartArea' => true, + 'drawTicks' => true, + 'tickMarkLength' => 5, + 'zeroLineWidth' => 6, + 'zeroLineColor' => 'zeroLineColor', + 'offsetGridLines' => true, + ]; + + private $input_data_nested_arrays = [ + 'display' => true, + 'color' => ['color1', 'color2', ['color3', 'color4']], + 'borderDash' => [2.0, 3.0, [4.0, 5.0]], + 'borderDashOffset' => 3.0, + 'lineWidth' => [4, 5, [6, 7], 8], + 'drawBorder' => true, + 'drawOnChartArea' => true, + 'drawTicks' => true, + 'tickMarkLength' => 5, + 'zeroLineWidth' => 6, + 'zeroLineColor' => 'zeroLineColor', + 'offsetGridLines' => true, + ]; + + /** + * @var array + */ + private $empty_data = [ + 'display' => null, + 'color' => null, + 'borderDash' => null, + 'borderDashOffset' => null, + 'lineWidth' => null, + 'drawBorder' => null, + 'drawOnChartArea' => null, + 'drawTicks' => null, + 'tickMarkLength' => null, + 'zeroLineWidth' => null, + 'zeroLineColor' => null, + 'offsetGridLines' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->gridLines = new GridLines(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->gridLines, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetSingleValues() + { + $expected = $this->input_data_single_value; + TestUtils::setAttributes($this->gridLines, $this->input_data_single_value); + $result = TestUtils::getAttributes($this->gridLines, $this->data_types); + self::assertSame($expected, $result); + } + + public function testGetAndSetNestedArrayValues() + { + $expected = $this->input_data_nested_arrays; + TestUtils::setAttributes($this->gridLines, $this->input_data_nested_arrays); + $result = TestUtils::getAttributes($this->gridLines, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * This test uses assertEquals in stead of assertSame because json_encode / json_decode + * transform the float numbers to string, after which the decimal zero's disappear. It is + * still a float, but will be recognized by assertSame as an int. For that reason assertSame + * will not work as expected. + * + */ + public function testJsonSerialize() + { + $expected = $this->input_data_single_value; + TestUtils::setAttributes($this->gridLines, $this->input_data_single_value); + $result = $this->gridLines->jsonSerialize(); + self::assertEquals($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/ScaleLabelTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/ScaleLabelTest.php new file mode 100644 index 0000000000..9d8fd8df85 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/ScaleLabelTest.php @@ -0,0 +1,100 @@ + false, + 'labelString' => '', + 'fontColor' => '', + 'fontFamily' => '', + 'fontSize' => 1, + 'fontStyle' => '', + ]; + + /** + * @var array + */ + private $input_data = [ + 'display' => false, + 'labelString' => 'labelString', + 'fontColor' => 'fontColor', + 'fontFamily' => 'fontFamily', + 'fontSize' => 1, + 'fontStyle' => 'fontStyle', + ]; + + /** + * @var array + */ + private $empty_data = [ + 'display' => null, + 'labelString' => null, + 'fontColor' => null, + 'fontFamily' => null, + 'fontSize' => null, + 'fontStyle' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->scaleLabel = new ScaleLabel(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->scaleLabel, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSet() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->scaleLabel, $this->input_data); + $result = TestUtils::getAttributes($this->scaleLabel, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * This test uses assertEquals in stead of assertSame because json_encode / json_decode + * transform the float numbers to string, after which the decimal zero's disappear. It is + * still a float, but will be recognized by assertSame as an int. For that reason assertSame + * will not work as expected. + * + */ + public function testJsonSerialize() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->scaleLabel, $this->input_data); + $result = $this->scaleLabel->jsonSerialize(); + self::assertEquals($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/TicksTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/TicksTest.php new file mode 100644 index 0000000000..616c47986f --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/TicksTest.php @@ -0,0 +1,167 @@ + 1.0, + 'beginAtZero' => false, + 'stepSize' => 1.0, + 'autoSkip' => false, + 'autoSkipPadding' => 1, + 'callback' => '', + 'display' => false, + 'fontColor' => '', + 'fontFamily' => '', + 'fontSize' => 1, + 'fontStyle' => '', + 'labelOffset' => 1, + 'maxRotation' => 1, + 'minRotation' => 1, + 'mirror' => false, + 'padding' => 1, + 'reverse' => false, + ]; + + /** + * @var array + */ + private $input_data_1 = [ + 'suggestedMin' => 2.0, + 'beginAtZero' => true, + 'stepSize' => 3.0, + 'autoSkip' => true, + 'autoSkipPadding' => 4, + 'callback' => null, + 'display' => true, + 'fontColor' => 'fontColor', + 'fontFamily' => 'fontFamily', + 'fontSize' => 5, + 'fontStyle' => 'fontStyle', + 'labelOffset' => 6, + 'maxRotation' => 7, + 'minRotation' => 8, + 'mirror' => true, + 'padding' => 9, + 'reverse' => true, + ]; + + /** + * @var array + */ + private $input_data_2 = [ + 'suggestedMin' => 2.0, + 'beginAtZero' => true, + 'stepSize' => 3.0, + 'autoSkip' => true, + 'autoSkipPadding' => 4, + 'callback' => "function () { console.log('Hello'); }", + 'display' => true, + 'fontColor' => 'fontColor', + 'fontFamily' => 'fontFamily', + 'fontSize' => 5, + 'fontStyle' => 'fontStyle', + 'labelOffset' => 6, + 'maxRotation' => 7, + 'minRotation' => 8, + 'mirror' => true, + 'padding' => 9, + 'reverse' => true, + ]; + + /** + * @var array + */ + private $empty_data = [ + 'suggestedMin' => null, + 'beginAtZero' => null, + 'stepSize' => null, + 'autoSkip' => null, + 'autoSkipPadding' => null, + 'callback' => null, + 'display' => null, + 'fontColor' => null, + 'fontFamily' => null, + 'fontSize' => null, + 'fontStyle' => null, + 'labelOffset' => null, + 'maxRotation' => null, + 'minRotation' => null, + 'mirror' => null, + 'padding' => null, + 'reverse' => null, + ]; + + /** + * + */ + public function setUp(): void + { + $this->ticks = new Ticks(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->ticks, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSetWithoutExpr() + { + $expected = $this->input_data_1; + TestUtils::setAttributes($this->ticks, $this->input_data_1); + $result = TestUtils::getAttributes($this->ticks, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testExpr() + { + TestUtils::setAttributes($this->ticks, $this->input_data_2); + $result = $this->ticks->getCallback()->__toString(); + $expected = $this->input_data_2['callback']; + self::assertSame($expected, $result); + } + + /** + * This test uses assertEquals in stead of assertSame because json_encode / json_decode + * transform the float numbers to string, after which the decimal zero's disappear. It is + * still a float, but will be recognized by assertSame as an int. For that reason assertSame + * will not work as expected. + * + */ + public function testJsonSerializeWithoutExpr() + { + $expected = TestUtils::removeNullsFromArray($this->input_data_1); + TestUtils::setAttributes($this->ticks, $expected); + $result = $this->ticks->jsonSerialize(); + self::assertEquals($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/XAxisCollectionTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/XAxisCollectionTest.php new file mode 100644 index 0000000000..01aaca8ec9 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Scales/XAxisCollectionTest.php @@ -0,0 +1,84 @@ +xAxisCollection = new XAxisCollection(); + $this->input_data = []; + foreach ($this->input_data as $value) { + $this->xAxisCollection[] = $value; + } + } + + /** + * + */ + public function testGetArrayCopyEmpty() + { + $expected = []; + $xAxisCollection = new XAxisCollection(); + $result = $xAxisCollection->getArrayCopy(); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetArrayCopyNonEmpty() + { + $expected = []; + $x = new XAxis(); + $expected[] = $x->getArrayCopy(); + $this->xAxisCollection[] = $x; + $result = $this->xAxisCollection->getArrayCopy(); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerializeEmpty() + { + $expected = []; + $result = $this->xAxisCollection->jsonSerialize(); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerializeNonEmpty() + { + $expected = []; + $x = new XAxis(); + $expected[] = $x->getArrayCopy(); + $this->xAxisCollection[] = $x; + $result = $this->xAxisCollection->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ScalesTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ScalesTest.php new file mode 100644 index 0000000000..a609d634aa --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/ScalesTest.php @@ -0,0 +1,89 @@ +scales = new Scales(); + } + + /** + * + */ + public function testCreateXAxis() + { + self::assertInstanceOf(Scales\XAxis::class, $this->scales->createXAxis()); + } + + /** + * + */ + public function testCreateYAxis() + { + self::assertInstanceOf(Scales\YAxis::class, $this->scales->createYAxis()); + } + + /** + * + */ + public function testEmptyXAxis() + { + $expected = new Scales\XAxisCollection(); + $result = $this->scales->getXAxes(); + self::assertInstanceOf(Scales\XAxisCollection::class, $result); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testEmptyYAxis() + { + $expected = new Scales\YAxisCollection(); + $result = $this->scales->getYAxes(); + self::assertInstanceOf(Scales\YAxisCollection::class, $result); + self::assertEquals($expected, $result); + } + + /** + * + */ + public function testJsonSerialize() + { + $xc = new Scales\XAxisCollection(); + $x1 = new Scales\XAxis(); + $x1->setBarThickness(2); + $xc[] = $x1; + $expected['xAxes'] = $xc->jsonSerialize(); + + $yc = new Scales\YAxisCollection(); + $y1 = new Scales\YAxis(); + $y1->setBarThickness(3); + $yc[] = $y1; + + $expected['xAxes'] = $xc->jsonSerialize(); + $expected['yAxes'] = $yc->jsonSerialize(); + $this->scales->getXAxes()[] = $x1; + $this->scales->getYAxes()[] = $y1; + $result = $this->scales->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/TitleTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/TitleTest.php new file mode 100644 index 0000000000..350dcaf68a --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/TitleTest.php @@ -0,0 +1,104 @@ + false, /* bool */ + 'position' => '', /* string */ + 'fullWidth' => false, /* bool */ + 'fontSize' => 1, /* int */ + 'fontFamily' => '', /* string */ + 'fontColor' => '', /* string */ + 'fontStyle' => '', /* string */ + 'padding' => 1, /* int */ + 'text' => '', /* string */ + ]; + + /** + * @var array + */ + private $input_data = [ + 'display' => true, /* bool */ + 'position' => 'position', /* string */ + 'fullWidth' => true, /* bool */ + 'fontSize' => 2, /* int */ + 'fontFamily' => 'fontFamily', /* string */ + 'fontColor' => 'fontColor', /* string */ + 'fontStyle' => 'fontStyle', /* string */ + 'padding' => 3, /* int */ + 'text' => 'text', /* string */ + ]; + + /** + * @var array + */ + private $empty_data = [ + 'display' => null, /* bool */ + 'position' => null, /* string */ + 'fullWidth' => null, /* bool */ + 'fontSize' => null, /* int */ + 'fontFamily' => null, /* string */ + 'fontColor' => null, /* string */ + 'fontStyle' => null, /* string */ + 'padding' => null, /* int */ + 'text' => null, /* string */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->title = new Title(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->title, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSet() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->title, $this->input_data); + $result = TestUtils::getAttributes($this->title, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testJsonSerialize() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->title, $this->input_data); + $result = $this->title->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Tooltips/CallbacksTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Tooltips/CallbacksTest.php new file mode 100644 index 0000000000..006cf4dc07 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/Tooltips/CallbacksTest.php @@ -0,0 +1,125 @@ + '', + 'title' => '', + 'afterTitle' => '', + 'beforeLabel' => '', + 'label' => '', + 'labelColor' => '', + 'afterLabel' => '', + 'afterBody' => '', + 'beforeFooter' => '', + 'footer' => '', + 'afterFooter' => '', + 'dataPoints' => '', + ]; + + /** + * @var array + */ + private $input_data = [ + 'beforeTitle' => "function() { alert( 'Hello' ); }", + 'title' => "function() { alert( 'Hello' ); }", + 'afterTitle' => "function() { alert( 'Hello' ); }", + 'beforeLabel' => "function() { alert( 'Hello' ); }", + 'label' => "function() { alert( 'Hello' ); }", + 'labelColor' => "function() { alert( 'Hello' ); }", + 'afterLabel' => "function() { alert( 'Hello' ); }", + 'afterBody' => "function() { alert( 'Hello' ); }", + 'beforeFooter' => "function() { alert( 'Hello' ); }", + 'footer' => "function() { alert( 'Hello' ); }", + 'afterFooter' => "function() { alert( 'Hello' ); }", + 'dataPoints' => "function() { alert( 'Hello' ); }", + ]; + + /** + * @var array + */ + private $empty_data = [ + 'beforeTitle' => null, + 'title' => null, + 'afterTitle' => null, + 'beforeLabel' => null, + 'label' => null, + 'labelColor' => null, + 'afterLabel' => null, + 'afterBody' => null, + 'beforeFooter' => null, + 'footer' => null, + 'afterFooter' => null, + 'dataPoints' => null, + ]; + + private $initial_data = []; + + /** + * + */ + public function setUp(): void + { + $this->callbacks = new Callbacks(); + + // Re-initialize Expr properties + $keys = array_keys($this->empty_data); + foreach ($keys as $key) { + $this->initial_data[$key] = new Expr(''); + } + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->callbacks, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSet() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->callbacks, $this->input_data); + $result = TestUtils::getAttributes($this->callbacks, $this->data_types); + array_walk( + $result, + function (&$value) { + $value = strval($value); + } + ); + self::assertSame($expected, $result); + } + + public function testJsonSerializeEmpty() + { + $expected = []; + $result = $this->callbacks->jsonSerialize(); + self::assertSame($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/Options/TooltipsTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/TooltipsTest.php new file mode 100644 index 0000000000..f640eb1180 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/Options/TooltipsTest.php @@ -0,0 +1,227 @@ + false, /* bool */ + 'custom' => '', /* Expr */ + 'mode' => '', /* string */ + 'intersect' => false, /* bool */ + 'position' => '', /* string */ + 'itemSort' => '', /* Expr */ + 'filter' => '', /* Expr */ + 'backgroundColor' => '', /* string */ + 'titleFontFamily' => '', /* string */ + 'titleFontSize' => 1, /* int */ + 'titleFontStyle' => '', /* string */ + 'titleFontColor' => '', /* string */ + 'titleSpacing' => '', /* int */ + 'titleMarginBottom' => '', /* int */ + 'bodyFontFamily' => '', /* string */ + 'bodyFontSize' => '', /* int */ + 'bodyFontStyle' => '', /* string */ + 'bodyFontColor' => '', /* string */ + 'bodySpacing' => '', /* int */ + 'footerFontFamily' => '', /* string */ + 'footerFontSize' => 1, /* int */ + 'footerFontStyle' => '', /* string */ + 'footerFontColor' => '', /* string */ + 'footerSpacing' => 1, /* int */ + 'footerMarginTop' => 1, /* int */ + 'xPadding' => 1, /* int */ + 'yPadding' => 1, /* int */ + 'caretSize' => 1, /* int */ + 'cornerRadius' => 1, /* int */ + 'multiKeyBackground' => '', /* string */ + 'displayColors' => false, /* bool */ + ]; + + /** + * @var array + */ + public $input_data = [ + 'enabled' => true, /* bool */ + 'custom' => 'custom', /* Expr */ + 'mode' => 'mode', /* string */ + 'intersect' => true, /* bool */ + 'position' => 'position', /* string */ + 'itemSort' => 'itemSort', /* Expr */ + 'filter' => 'filter', /* Expr */ + 'backgroundColor' => 'backgroundColor', /* string */ + 'titleFontFamily' => 'titleFontFamily', /* string */ + 'titleFontSize' => 2, /* int */ + 'titleFontStyle' => 'titleFontStyle', /* string */ + 'titleFontColor' => 'titleFontColor', /* string */ + 'titleSpacing' => 3, /* int */ + 'titleMarginBottom' => 4, /* int */ + 'bodyFontFamily' => 'bodyFontFamily', /* string */ + 'bodyFontSize' => 5, /* int */ + 'bodyFontStyle' => 'bodyFontStyle', /* string */ + 'bodyFontColor' => 'bodyFontColor', /* string */ + 'bodySpacing' => 6, /* int */ + 'footerFontFamily' => 'footerFontFamily', /* string */ + 'footerFontSize' => 7, /* int */ + 'footerFontStyle' => 'footerFontStyle', /* string */ + 'footerFontColor' => 'footerFontColor', /* string */ + 'footerSpacing' => 8, /* int */ + 'footerMarginTop' => 9, /* int */ + 'xPadding' => 10, /* int */ + 'yPadding' => 11, /* int */ + 'caretSize' => 12, /* int */ + 'cornerRadius' => 13, /* int */ + 'multiKeyBackground' => 'multiKeyBackground', /* string */ + 'displayColors' => true, /* bool */ + ]; + + /** + * @var array + */ + public $input_data_no_expressions = [ + 'enabled' => true, /* bool */ + 'custom' => null, /* Expr */ + 'mode' => 'mode', /* string */ + 'intersect' => true, /* bool */ + 'position' => 'position', /* string */ + 'itemSort' => null, /* Expr */ + 'filter' => null, /* Expr */ + 'backgroundColor' => 'backgroundColor', /* string */ + 'titleFontFamily' => 'titleFontFamily', /* string */ + 'titleFontSize' => 2, /* int */ + 'titleFontStyle' => 'titleFontStyle', /* string */ + 'titleFontColor' => 'titleFontColor', /* string */ + 'titleSpacing' => 3, /* int */ + 'titleMarginBottom' => 4, /* int */ + 'bodyFontFamily' => 'bodyFontFamily', /* string */ + 'bodyFontSize' => 5, /* int */ + 'bodyFontStyle' => 'bodyFontStyle', /* string */ + 'bodyFontColor' => 'bodyFontColor', /* string */ + 'bodySpacing' => 6, /* int */ + 'footerFontFamily' => 'footerFontFamily', /* string */ + 'footerFontSize' => 7, /* int */ + 'footerFontStyle' => 'footerFontStyle', /* string */ + 'footerFontColor' => 'footerFontColor', /* string */ + 'footerSpacing' => 8, /* int */ + 'footerMarginTop' => 9, /* int */ + 'xPadding' => 10, /* int */ + 'yPadding' => 11, /* int */ + 'caretSize' => 12, /* int */ + 'cornerRadius' => 13, /* int */ + 'multiKeyBackground' => 'multiKeyBackground', /* string */ + 'displayColors' => true, /* bool */ + ]; + + /** + * @var array + */ + public $empty_data = [ + 'enabled' => null, /* bool */ + 'custom' => null, /* Expr */ + 'mode' => null, /* string */ + 'intersect' => null, /* bool */ + 'position' => null, /* string */ + 'itemSort' => null, /* Expr */ + 'filter' => null, /* Expr */ + 'backgroundColor' => null, /* string */ + 'titleFontFamily' => null, /* string */ + 'titleFontSize' => null, /* int */ + 'titleFontStyle' => null, /* string */ + 'titleFontColor' => null, /* string */ + 'titleSpacing' => null, /* int */ + 'titleMarginBottom' => null, /* int */ + 'bodyFontFamily' => null, /* string */ + 'bodyFontSize' => null, /* int */ + 'bodyFontStyle' => null, /* string */ + 'bodyFontColor' => null, /* string */ + 'bodySpacing' => null, /* int */ + 'footerFontFamily' => null, /* string */ + 'footerFontSize' => null, /* int */ + 'footerFontStyle' => null, /* string */ + 'footerFontColor' => null, /* string */ + 'footerSpacing' => null, /* int */ + 'footerMarginTop' => null, /* int */ + 'xPadding' => null, /* int */ + 'yPadding' => null, /* int */ + 'caretSize' => null, /* int */ + 'cornerRadius' => null, /* int */ + 'multiKeyBackground' => null, /* string */ + 'displayColors' => null, /* bool */ + ]; + + /** + * + */ + public function setUp(): void + { + $this->tooltips = new Tooltips(); + } + + /** + * + */ + public function testEmpty() + { + $expected = $this->empty_data; + $result = TestUtils::getAttributes($this->tooltips, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testGetAndSet() + { + $expected = $this->input_data; + TestUtils::setAttributes($this->tooltips, $this->input_data); + $result = TestUtils::getAttributes($this->tooltips, $this->data_types); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testCallbackInstance() + { + $result = $this->tooltips->callbacks(); + self::assertInstanceOf(Tooltips\Callbacks::class, $result); + } + + /** + * + */ + public function testJsonSerializeNoExpressions() + { + $expected = TestUtils::removeNullsFromArray($this->input_data_no_expressions); + TestUtils::setAttributes($this->tooltips, $expected); + $result = $this->tooltips->jsonSerialize(); + self::assertSame($expected, $result); + } + + /** + * + */ + public function testCallbackEmpty() + { + $expected = new Tooltips\Callbacks(); + $result = $this->tooltips->callbacks(); + self::assertEquals($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/OptionsTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/OptionsTest.php new file mode 100644 index 0000000000..59921b533e --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/OptionsTest.php @@ -0,0 +1,139 @@ +options = new Options(); + } + + /** + * + */ + public function testTitle() + { + $title = $this->options->getTitle(); + self::assertInstanceOf(Title::class, $title); + } + + /** + * + */ + public function testLayout() + { + $layout = $this->options->getLayout(); + self::assertInstanceOf(Layout::class, $layout); + } + + /** + * + */ + public function testElements() + { + $layout = $this->options->getElements(); + self::assertInstanceOf(Elements::class, $layout); + } + + /** + * + */ + public function testAnimation() + { + $animation = $this->options->getAnimation(); + self::assertInstanceOf(Animation::class, $animation); + } + + /** + * + */ + public function testHover() + { + $hover = $this->options->getHover(); + self::assertInstanceOf(Hover::class, $hover); + } + + /** + * + */ + public function testScales() + { + $scales = $this->options->getScales(); + self::assertInstanceOf(Scales::class, $scales); + } + + /** + * + */ + public function testLagend() + { + $legend = $this->options->getLegend(); + self::assertInstanceOf(Legend::class, $legend); + } + + /** + * + */ + public function testTooltips() + { + $tooltips = $this->options->getTooltips(); + self::assertInstanceOf(Tooltips::class, $tooltips); + } + + /** + * + */ + public function testAspectRatio() + { + // Test default value. Should be true. + self::assertTrue($this->options->isMaintainAspectRatio()); + + // Set to false. + self::assertSame($this->options, $this->options->setMaintainAspectRatio(false)); + self::assertFalse($this->options->isMaintainAspectRatio()); + + // Set to true again. + self::assertSame($this->options, $this->options->setMaintainAspectRatio(true)); + self::assertTrue($this->options->isMaintainAspectRatio()); + } + + /** + * + */ + public function testJsonSerialize() + { + $expected = $this->empty_options; + $result = $this->options->jsonSerialize(); + self::assertEquals($expected, $result); + } +} diff --git a/pandora_console/vendor/artica/phpchartjs/test/unit/RendererTest.php b/pandora_console/vendor/artica/phpchartjs/test/unit/RendererTest.php new file mode 100644 index 0000000000..a406fed450 --- /dev/null +++ b/pandora_console/vendor/artica/phpchartjs/test/unit/RendererTest.php @@ -0,0 +1,81 @@ +setId('myChart') + ->addLabel('Label 1')->addLabel('Label 2') + ->setTitle('My beautiful chart') + ->setHeight(320) + ->setWidth(480); + + /** @var DataSet $dataSet */ + $chart->addDataSet($dataSet = $chart->createDataSet()); + $dataSet->setLabel('My First Dataset'); + + $chart->options()->getTitle()->setText('My cool graph'); + $chart->options()->getLegend()->setDisplay(false); + + $this->chart = $chart; + } + + /** + * Test and validate generated JSON. See http://www.ietf.org/rfc/rfc4627.txt and http://json.org/. + * JavaScript functions will not be recognized and will result in failures of this test. + */ + public function testJson() + { + $renderer = new Json($this->chart); + $json = $renderer->render(); + $regex = << -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? ) + (? true | false | null ) + (? " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " ) + (? \[ (?: (?&json) (?: , (?&json) )* )? \s* \] ) + (? \s* (?&string) \s* : (?&json) ) + (? \{ (?: (?&pair) (?: , (?&pair) )* )? \s* \} ) + (? \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* ) +) +\A (?&json) \Z/six +REGEX; + + $result = preg_match($regex, $json, $matches); + + $this->assertEquals(1, $result, 'Validate JSON output'); + } + + /** + * + */ + public function testHtml() + { + $renderer = new Html($this->chart); + + $this->assertTrue(is_string($renderer->render()), 'Validate HTML output'); + } +} diff --git a/pandora_console/vendor/autoload.php b/pandora_console/vendor/autoload.php index d95bcefe2b..cac482501f 100644 --- a/pandora_console/vendor/autoload.php +++ b/pandora_console/vendor/autoload.php @@ -3,8 +3,21 @@ // autoload.php @generated by Composer if (PHP_VERSION_ID < 50600) { - echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; - exit(1); + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, $err); + } elseif (!headers_sent()) { + echo $err; + } + } + trigger_error( + $err, + E_USER_ERROR + ); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/pandora_console/vendor/chrome-php/chrome/LICENSE b/pandora_console/vendor/chrome-php/chrome/LICENSE new file mode 100644 index 0000000000..034561bd81 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) 2017-2020 Soufiane Ghzal +Copyright (c) 2020-2022 Graham Campbell +Copyright (c) 2020-2022 Enrico Dias + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pandora_console/vendor/chrome-php/chrome/composer.json b/pandora_console/vendor/chrome-php/chrome/composer.json new file mode 100644 index 0000000000..4ff43d04e7 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/composer.json @@ -0,0 +1,55 @@ +{ + "name": "chrome-php/chrome", + "description": "Instrument headless chrome/chromium instances from PHP", + "keywords": ["chrome", "chromium", "crawl", "browser", "headless", "screenshot", "pdf", "puppeteer"], + "license": "MIT", + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Enrico Dias", + "email": "enrico@enricodias.com", + "homepage": "https://github.com/enricodias" + } + ], + "require": { + "php": "^7.3 || ^8.0", + "chrome-php/wrench": "^1.3", + "evenement/evenement": "^3.0.1", + "monolog/monolog": "^1.27.1 || ^2.8 || ^3.2", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/filesystem": "^4.4 || ^5.0 || ^6.0", + "symfony/polyfill-mbstring": "^1.26", + "symfony/process": "^4.4 || ^5.0 || ^6.0" + }, + "require-dev":{ + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^9.5.23", + "symfony/var-dumper": "^4.4 || ^5.0 || ^6.0" + }, + "autoload":{ + "psr-4" : { + "HeadlessChromium\\": "src/" + } + }, + "autoload-dev":{ + "psr-4" : { + "HeadlessChromium\\Test\\": "tests/" + } + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true + }, + "preferred-install": "dist" + }, + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/AutoDiscover.php b/pandora_console/vendor/chrome-php/chrome/src/AutoDiscover.php new file mode 100644 index 0000000000..38b02c8a45 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/AutoDiscover.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium; + +class AutoDiscover +{ + /** + * @var callable(): string + */ + private $osFamily; + + /** + * @param (callable(): string)|null $osFamily + */ + public function __construct(?callable $osFamily = null) + { + $this->osFamily = $osFamily ?? function (): string { + return \PHP_OS_FAMILY; + }; + } + + public function guessChromeBinaryPath(): string + { + if (\array_key_exists('CHROME_PATH', $_SERVER)) { + return $_SERVER['CHROME_PATH']; + } + + switch (($this->osFamily)()) { + case 'Darwin': + return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; + case 'Windows': + return self::getFromRegistry() ?? '%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe'; + default: + return null === self::shellExec('command -v google-chrome') ? 'chrome' : 'google-chrome'; + } + } + + private static function getFromRegistry(): ?string + { + $registryKey = self::shellExec( + 'reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe" /ve' + ); + + if (null === $registryKey) { + return null; + } + + \preg_match('/.:(?!.*:).*/', $registryKey, $matches); + + return $matches[0] ?? null; + } + + private static function shellExec(string $command): ?string + { + try { + $result = @\shell_exec($command); + + return \is_string($result) ? $result : null; + } catch (\Throwable $e) { + return null; + } + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Browser.php b/pandora_console/vendor/chrome-php/chrome/src/Browser.php new file mode 100644 index 0000000000..bdcd0ef06f --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Browser.php @@ -0,0 +1,254 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium; + +use HeadlessChromium\Communication\Connection; +use HeadlessChromium\Communication\Message; +use HeadlessChromium\Communication\Target; +use HeadlessChromium\Exception\CommunicationException; +use HeadlessChromium\Exception\CommunicationException\ResponseHasError; +use HeadlessChromium\Exception\NoResponseAvailable; +use HeadlessChromium\Exception\OperationTimedOut; + +class Browser +{ + /** + * @var Connection + */ + protected $connection; + + /** + * @var array + */ + protected $targets = []; + + /** + * @var array + */ + protected $pages = []; + + /** + * A preScript to be automatically added on every new pages. + * + * @var string|null + */ + protected $pagePreScript; + + public function __construct(Connection $connection) + { + $this->connection = $connection; + + // listen for target created + $this->connection->on(Connection::EVENT_TARGET_CREATED, function (array $params): void { + // create and store the target + $this->targets[$params['targetInfo']['targetId']] = new Target($params['targetInfo'], $this->connection); + }); + + // listen for target info changed + $this->connection->on(Connection::EVENT_TARGET_INFO_CHANGED, function (array $params): void { + // get target by id + $target = $this->getTarget($params['targetInfo']['targetId']); + + if ($target) { + $target->targetInfoChanged($params['targetInfo']); + } + }); + + // listen for target destroyed + $this->connection->on(Connection::EVENT_TARGET_DESTROYED, function (array $params): void { + // get target by id + $target = $this->getTarget($params['targetId']); + + if ($target) { + // remove the page + unset($this->pages[$params['targetId']]); + // remove the target + unset($this->targets[$params['targetId']]); + $target->destroy(); + $this->connection + ->getLogger() + ->debug('✘ target('.$params['targetId'].') was destroyed and unreferenced.'); + } + }); + + // enable target discovery + $connection->sendMessageSync(new Message('Target.setDiscoverTargets', ['discover' => true])); + } + + /** + * @return Connection + */ + public function getConnection(): Connection + { + return $this->connection; + } + + /** + * Set a preScript to be added on every new pages. + * Use null to disable it. + * + * @param string|null $script + */ + public function setPagePreScript(string $script = null): void + { + $this->pagePreScript = $script; + } + + /** + * Closes the browser. + * + * @throws \Exception + */ + public function close(): void + { + $this->sendCloseMessage(); + } + + /** + * Send close message to the browser. + * + * @throws OperationTimedOut + */ + final public function sendCloseMessage(): void + { + $r = $this->connection->sendMessageSync(new Message('Browser.close')); + if (!$r->isSuccessful()) { + // log + $this->connection->getLogger()->debug('process: ✗ could not close gracefully'); + throw new \Exception('cannot close, Browser.close not supported'); + } + } + + /** + * Creates a new page. + * + * @throws NoResponseAvailable + * @throws CommunicationException + * @throws OperationTimedOut + * + * @return Page + */ + public function createPage(): Page + { + // page url + $params = ['url' => 'about:blank']; + + // create page and get target id + $response = $this->connection->sendMessageSync(new Message('Target.createTarget', $params)); + $targetId = $response['result']['targetId']; + + // todo handle error + + $target = $this->getTarget($targetId); + if (!$target) { + throw new \RuntimeException('Target could not be created for page.'); + } + + $page = $this->getPage($targetId); + + return $page; + } + + /** + * @param string $targetId + * + * @return Target|null + */ + public function getTarget($targetId) + { + // make sure target was created (via Target.targetCreated event) + if (!\array_key_exists($targetId, $this->targets)) { + return null; + } + + return $this->targets[$targetId]; + } + + /** + * @return Target[] + */ + public function getTargets() + { + return \array_values($this->targets); + } + + /** + * @param string $targetId + * + * @throws CommunicationException + * + * @return Page|null + */ + public function getPage($targetId) + { + if (\array_key_exists($targetId, $this->pages)) { + return $this->pages[$targetId]; + } + + $target = $this->getTarget($targetId); + + if ('page' !== $target->getTargetInfo('type')) { + return null; + } + + // get initial frame tree + $frameTreeResponse = $target->getSession()->sendMessageSync(new Message('Page.getFrameTree')); + + // make sure frame tree was found + if (!$frameTreeResponse->isSuccessful()) { + throw new ResponseHasError('Cannot read frame tree. Please, consider upgrading chrome version.'); + } + + // create page + $page = new Page($target, $frameTreeResponse['result']['frameTree']); + + // Page.enable + $page->getSession()->sendMessageSync(new Message('Page.enable')); + + // Network.enable + $page->getSession()->sendMessageSync(new Message('Network.enable')); + + // Runtime.enable + $page->getSession()->sendMessageSync(new Message('Runtime.enable')); + + // Page.setLifecycleEventsEnabled + $page->getSession()->sendMessageSync(new Message('Page.setLifecycleEventsEnabled', ['enabled' => true])); + + // set up http headers + $headers = $this->connection->getConnectionHttpHeaders(); + + if (\count($headers) > 0) { + $page->setExtraHTTPHeaders($headers); + } + + // add prescript + if ($this->pagePreScript) { + $page->addPreScript($this->pagePreScript); + } + + $this->pages[$targetId] = $page; + + return $page; + } + + /** + * @return Page[] + */ + public function getPages() + { + $ids = \array_keys($this->targets); + + $pages = \array_filter(\array_map([$this, 'getPage'], $ids)); + + return \array_values($pages); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php b/pandora_console/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php new file mode 100644 index 0000000000..e01347f52f --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php @@ -0,0 +1,467 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Browser; + +use HeadlessChromium\Browser; +use HeadlessChromium\Communication\Connection; +use HeadlessChromium\Exception\OperationTimedOut; +use HeadlessChromium\Utils; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\Process; +use Wrench\Exception\SocketException; + +/** + * A browser process starter. Don't use directly, use BrowserFactory instead. + */ +class BrowserProcess implements LoggerAwareInterface +{ + use LoggerAwareTrait; + + /** + * chrome instance's user data data. + * + * @var string + */ + protected $userDataDir; + + /** + * @var Process + */ + protected $process; + + /** + * True if the user data dir is temporary and should be deleted on process closes. + * + * @var bool + */ + protected $userDataDirIsTemp; + + /** + * @var Connection + */ + protected $connection; + + /** + * @var ProcessAwareBrowser + */ + protected $browser; + + /** + * @var bool + */ + protected $wasKilled = false; + + /** + * @var bool + */ + protected $wasStarted = false; + + /** + * @var string + */ + protected $wsUri; + + /** + * BrowserProcess constructor. + * + * @param LoggerInterface|null $logger + */ + public function __construct(LoggerInterface $logger = null) + { + // set or create logger + $this->setLogger($logger ?? new NullLogger()); + } + + /** + * Starts the browser. + * + * @param string $binary + * @param array $options + */ + public function start($binary, $options): void + { + if ($this->wasStarted) { + // cannot start twice because once started this class contains the necessary data to cleanup the browser. + // starting in again would result in replacing those data. + throw new \RuntimeException('This process was already started'); + } + + $this->wasStarted = true; + + // log + $this->logger->debug('process: initializing'); + + // user data dir + if (!\array_key_exists('userDataDir', $options) || !$options['userDataDir']) { + // if no data dir specified create it + $options['userDataDir'] = $this->createTempDir(); + + // set user data dir to get removed on close + $this->userDataDirIsTemp = true; + } + $this->userDataDir = $options['userDataDir']; + + // log + $this->logger->debug('process: using directory: '.$options['userDataDir']); + + // get args for command line + $args = $this->getArgsFromOptions($binary, $options); + + // setup chrome process + if (!\array_key_exists('keepAlive', $options) || !$options['keepAlive']) { + $process = new Process($args, null, $options['envVariables'] ?? null); + } else { + $process = new ProcessKeepAlive($args, null, $options['envVariables'] ?? null); + } + $this->process = $process; + + // log + $this->logger->debug('process: starting process: '.$process->getCommandLine()); + + // and start + $process->start(); + + // wait for start and retrieve ws uri + $startupTimeout = $options['startupTimeout'] ?? 30; + $this->wsUri = $this->waitForStartup($process, $startupTimeout * 1000 * 1000); + + // log + $this->logger->debug('process: connecting using '.$this->wsUri); + + // connect to browser + $connection = new Connection($this->wsUri, $this->logger, $options['sendSyncDefaultTimeout'] ?? 5000); + $connection->connect(); + + // connection delay + if (\array_key_exists('connectionDelay', $options)) { + $connection->setConnectionDelay($options['connectionDelay']); + } + + // connection headers + if (\array_key_exists('headers', $options)) { + $connection->setConnectionHttpHeaders($options['headers']); + } + + // set connection to allow killing chrome + $this->connection = $connection; + + // create browser instance + $this->browser = new ProcessAwareBrowser($connection, $this); + } + + /** + * @return ProcessAwareBrowser + */ + public function getBrowser() + { + return $this->browser; + } + + /** + * @return string + */ + public function getSocketUri() + { + return $this->wsUri; + } + + /** + * Kills the process and clean temporary files. + * + * @throws OperationTimedOut + */ + public function kill(): void + { + // log + $this->logger->debug('process: killing chrome'); + + if ($this->wasKilled) { + // log + $this->logger->debug('process: chrome already killed, ignoring'); + + return; + } + + $this->wasKilled = true; + + if (isset($this->process)) { + // close gracefully if connection exists + if (isset($this->connection)) { + // if socket connect try graceful close + if ($this->connection->isConnected()) { + // first try to close with Browser.close + // if Browser.close is not implemented, try to kill by closing all pages + try { + // log + $this->logger->debug('process: trying to close chrome gracefully'); + $this->browser->sendCloseMessage(); + } catch (\Exception $e) { + // log + $this->logger->debug('process: closing chrome gracefully - compatibility'); + + // close all pages if connected + try { + $this->connection->isConnected() && Utils::closeAllPage($this->connection); + } catch (OperationTimedOut $e) { + // log + $this->logger->debug('process: failed to close all pages'); + } + } + + // disconnect socket + try { + $this->connection->disconnect(); + } catch (SocketException $e) { + // Socket might be already disconnected + } + + // log + $this->logger->debug('process: waiting for process to close'); + + // wait for process to close + $generator = function (Process $process) { + while ($process->isRunning()) { + yield 2 * 1000; // wait for 2ms + } + }; + $timeout = 8 * 1000 * 1000; // 8 seconds + + try { + Utils::tryWithTimeout($timeout, $generator($this->process)); + } catch (OperationTimedOut $e) { + // log + $this->logger->debug('process: process didn\'t close by itself'); + } + } + } + + // stop process if running + if ($this->process->isRunning()) { + // log + $this->logger->debug('process: stopping process'); + + // stop process + $exitCode = $this->process->stop(); + + // log + $this->logger->debug('process: process stopped with exit code '.$exitCode); + } + } + + // remove data dir + if ($this->userDataDirIsTemp && $this->userDataDir) { + try { + // log + $this->logger->debug('process: cleaning temporary resources:'.$this->userDataDir); + + // cleaning + $fs = new Filesystem(); + $fs->remove($this->userDataDir); + } catch (\Exception $e) { + // log + $this->logger->debug('process: ✗ could not clean temporary resources'); + } + } + } + + /** + * Get args for creating chrome's startup command. + * + * @param array $options + * + * @return array + */ + private function getArgsFromOptions($binary, array $options) + { + // command line args to add to start chrome (inspired by puppeteer configs) + // see https://peter.sh/experiments/chromium-command-line-switches/ + $args = [ + $binary, + + // auto debug port + '--remote-debugging-port=0', + + // disable undesired features + '--disable-background-networking', + '--disable-background-timer-throttling', + '--disable-client-side-phishing-detection', + '--disable-hang-monitor', + '--disable-popup-blocking', + '--disable-prompt-on-repost', + '--disable-sync', + '--disable-translate', + '--disable-features=ChromeWhatsNewUI', + '--metrics-recording-only', + '--no-first-run', + '--safebrowsing-disable-auto-update', + + // automation mode + '--enable-automation', + + // password settings + '--password-store=basic', + '--use-mock-keychain', // osX only + ]; + + // enable headless mode + if (!\array_key_exists('headless', $options) || $options['headless']) { + $args[] = '--headless'; + $args[] = '--disable-gpu'; + $args[] = '--font-render-hinting=none'; + $args[] = '--hide-scrollbars'; + $args[] = '--mute-audio'; + } + + // disable loading of images (currently can't be done via devtools, only CLI) + if (\array_key_exists('enableImages', $options) && (false === $options['enableImages'])) { + $args[] = '--blink-settings=imagesEnabled=false'; + } + + // window's size + if (\array_key_exists('windowSize', $options) && $options['windowSize']) { + if ( + !\is_array($options['windowSize']) || + 2 !== \count($options['windowSize']) || + !\is_numeric($options['windowSize'][0]) || + !\is_numeric($options['windowSize'][1]) + ) { + throw new \InvalidArgumentException('Option "windowSize" must be an array of dimensions (eg: [1000, 1200])'); + } + + $args[] = '--window-size='.\implode(',', $options['windowSize']); + } + + // sandbox mode - useful if you want to use chrome headless inside docker + if (\array_key_exists('noSandbox', $options) && $options['noSandbox']) { + $args[] = '--no-sandbox'; + } + + // user agent + if (\array_key_exists('userAgent', $options)) { + $args[] = '--user-agent='.$options['userAgent']; + } + + // ignore certificate errors + if (\array_key_exists('ignoreCertificateErrors', $options) && $options['ignoreCertificateErrors']) { + $args[] = '--ignore-certificate-errors'; + } + + // proxy server + if (\array_key_exists('proxyServer', $options)) { + $args[] = '--proxy-server='.$options['proxyServer']; + } + if (\array_key_exists('noProxyServer', $options) && $options['noProxyServer']) { + $args[] = '--no-proxy-server'; + } + if (\array_key_exists('proxyBypassList', $options)) { + $args[] = '--proxy-bypass-list='.$options['proxyBypassList']; + } + + // add custom flags + if (\array_key_exists('customFlags', $options) && \is_array($options['customFlags'])) { + $args = \array_merge($args, $options['customFlags']); + } + + // add user data dir to args + $args[] = '--user-data-dir='.$options['userDataDir']; + + return $args; + } + + /** + * Wait for chrome to startup (given a process) and return the ws uri to connect to. + * + * @param Process $process + * @param int $timeout + * + * @return mixed + */ + private function waitForStartup(Process $process, int $timeout) + { + // log + $this->logger->debug('process: waiting for '.$timeout / 1000000 .' seconds for startup'); + + try { + $generator = function (Process $process) { + while (true) { + if (!$process->isRunning()) { + // log + $this->logger->debug('process: ✗ chrome process stopped'); + + // exception + $message = 'Chrome process stopped before startup completed.'; + $error = \trim($process->getErrorOutput()); + if (!empty($error)) { + $message .= ' Additional info: '.$error; + } + throw new \RuntimeException($message); + } + + $output = \trim($process->getIncrementalErrorOutput()); + + if ($output) { + // log + $this->logger->debug('process: chrome output:'.$output); + + $outputs = \explode(\PHP_EOL, $output); + + foreach ($outputs as $output) { + $output = \trim($output); + + // ignore empty line + if (empty($output)) { + continue; + } + + // find socket uri + if (\preg_match('/DevTools listening on (ws:\/\/.*)/', $output, $matches)) { + // log + $this->logger->debug('process: ✓ accepted output'); + + return $matches[1]; + } else { + // log + $this->logger->debug('process: ignoring output:'.\trim($output)); + } + } + } + + // wait for 10ms + yield 10 * 1000; + } + }; + + return Utils::tryWithTimeout($timeout, $generator($process)); + } catch (OperationTimedOut $e) { + throw new \RuntimeException('Cannot start browser', 0, $e); + } + } + + /** + * Creates a temp directory for the app. + * + * @return string path to the new temp directory + */ + private function createTempDir() + { + $tmpFile = \tempnam(\sys_get_temp_dir(), 'chromium-php-'); + + \unlink($tmpFile); + \mkdir($tmpFile); + + return $tmpFile; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Browser/ProcessAwareBrowser.php b/pandora_console/vendor/chrome-php/chrome/src/Browser/ProcessAwareBrowser.php new file mode 100644 index 0000000000..6a57d27e8d --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Browser/ProcessAwareBrowser.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Browser; + +use HeadlessChromium\Browser; +use HeadlessChromium\Communication\Connection; + +class ProcessAwareBrowser extends Browser +{ + /** + * @var BrowserProcess + */ + protected $browserProcess; + + public function __construct(Connection $connection, BrowserProcess $browserProcess) + { + parent::__construct($connection); + + $this->browserProcess = $browserProcess; + } + + /** + * {@inheritdoc} + */ + public function close(): void + { + $this->browserProcess->kill(); + } + + /** + * @return string + */ + public function getSocketUri() + { + return $this->browserProcess->getSocketUri(); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Browser/ProcessKeepAlive.php b/pandora_console/vendor/chrome-php/chrome/src/Browser/ProcessKeepAlive.php new file mode 100644 index 0000000000..3caeb1b18a --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Browser/ProcessKeepAlive.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Browser; + +use Symfony\Component\Process\Process; + +class ProcessKeepAlive extends Process +{ + public function __destruct() + { + // Do nothing because we are in mode keep alive, default behavior is to kill the process + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/BrowserFactory.php b/pandora_console/vendor/chrome-php/chrome/src/BrowserFactory.php new file mode 100644 index 0000000000..8155c68831 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/BrowserFactory.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium; + +use HeadlessChromium\Browser\BrowserProcess; +use HeadlessChromium\Browser\ProcessAwareBrowser; +use HeadlessChromium\Communication\Connection; +use HeadlessChromium\Exception\BrowserConnectionFailed; +use Monolog\Handler\StreamHandler; +use Monolog\Logger; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\Process\Process; +use Wrench\Exception\HandshakeException; + +class BrowserFactory +{ + protected $chromeBinary; + + /** + * Options for browser creation. + * + * - connectionDelay: Delay to apply between each operation for debugging purposes (default: none) + * - customFlags: An array of flags to pass to the command line. + * - debugLogger: A string (e.g "php://stdout"), or resource, or PSR-3 logger instance to print debug messages (default: none) + * - enableImages: Toggles loading of images (default: true) + * - envVariables: An array of environment variables to pass to the process (example DISPLAY variable) + * - headers: An array of custom HTTP headers + * - headless: Enable or disable headless mode (default: true) + * - ignoreCertificateErrors: Set chrome to ignore ssl errors + * - keepAlive: Set to `true` to keep alive the chrome instance when the script terminates (default: false) + * - noSandbox: Enable no sandbox mode, useful to run in a docker container (default: false) + * - proxyServer: Proxy server to use. ex: `127.0.0.1:8080` (default: none) + * - sendSyncDefaultTimeout: Default timeout (ms) for sending sync messages (default 5000 ms) + * - startupTimeout: Maximum time in seconds to wait for chrome to start (default: 30 sec) + * - userAgent: User agent to use for the whole browser + * - userDataDir: Chrome user data dir (default: a new empty dir is generated temporarily) + * - windowSize: Size of the window. ex: `[1920, 1080]` (default: none) + */ + protected $options = []; + + public function __construct(string $chromeBinary = null) + { + $this->chromeBinary = $chromeBinary ?? (new AutoDiscover())->guessChromeBinaryPath(); + } + + /** + * Start a chrome process and allows to interact with it. + * + * @see BrowserFactory::$options + * + * @param array|null $options overwrite options for browser creation + * + * @return ProcessAwareBrowser a Browser instance to interact with the new chrome process + */ + public function createBrowser(?array $options = null): ProcessAwareBrowser + { + $options = $options ?? $this->options; + + // create logger from options + $logger = self::createLogger($options); + + // create browser process + $browserProcess = new BrowserProcess($logger); + + // instruct the runtime to kill chrome and clean temp files on exit + if (!\array_key_exists('keepAlive', $options) || !$options['keepAlive']) { + \register_shutdown_function([$browserProcess, 'kill']); + } + + // start the browser and connect to it + $browserProcess->start($this->chromeBinary, $options); + + return $browserProcess->getBrowser(); + } + + public function addHeader(string $name, string $value): void + { + $this->options['headers'][$name] = $value; + } + + /** + * @param array $headers + */ + public function addHeaders(array $headers): void + { + foreach ($headers as $name => $value) { + $this->addHeader($name, $value); + } + } + + /** + * Connects to an existing browser using it's web socket uri. + * + * usage: + * + * ``` + * $browserFactory = new BrowserFactory(); + * $browser = $browserFactory->createBrowser(); + * + * $uri = $browser->getSocketUri(); + * + * $existingBrowser = BrowserFactory::connectToBrowser($uri); + * ``` + * + * @param string $uri + * @param array $options options when creating the connection to the browser: + * - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none) + * - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none) + * - sendSyncDefaultTimeout: maximum time in ms to wait for synchronous messages to send (default 5000 ms) + * + * @throws BrowserConnectionFailed + * + * @return Browser + */ + public static function connectToBrowser(string $uri, array $options = []): Browser + { + $logger = self::createLogger($options); + + if ($logger) { + $logger->debug('Browser Factory: connecting using '.$uri); + } + + // connect to browser + $connection = new Connection($uri, $logger, $options['sendSyncDefaultTimeout'] ?? 5000); + + // try to connect + try { + $connection->connect(); + } catch (HandshakeException $e) { + throw new BrowserConnectionFailed('Invalid socket uri', 0, $e); + } + + // make sure it is connected + if (!$connection->isConnected()) { + throw new BrowserConnectionFailed('Cannot connect to the browser, make sure it was not closed'); + } + + // connection delay + if (\array_key_exists('connectionDelay', $options)) { + $connection->setConnectionDelay($options['connectionDelay']); + } + + return new Browser($connection); + } + + /** + * Set default options to be used in all browser instances. + * + * @see BrowserFactory::$options + */ + public function setOptions(array $options): void + { + $this->options = $options; + } + + /** + * Add or overwrite options to the default options list. + * + * @see BrowserFactory::$options + */ + public function addOptions(array $options): void + { + $this->options = \array_merge($this->options, $options); + } + + public function getOptions(): array + { + return $this->options; + } + + /** + * Create a logger instance from given options. + */ + private static function createLogger(array $options): LoggerInterface + { + $logger = $options['debugLogger'] ?? null; + + if ($logger instanceof LoggerInterface) { + return $logger; + } + + if (\is_string($logger) || \is_resource($logger)) { + $log = new Logger('chrome'); + $log->pushHandler(new StreamHandler($logger)); + + return $log; + } + + return new NullLogger(); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Clip.php b/pandora_console/vendor/chrome-php/chrome/src/Clip.php new file mode 100644 index 0000000000..7be7cd7eb8 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Clip.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium; + +class Clip +{ + protected $x; + protected $y; + protected $height; + protected $width; + protected $scale; + + /** + * Clip constructor. + * + * @param int $x + * @param int $y + * @param int $height + * @param int $width + * @param float $scale + */ + public function __construct($x, $y, $width, $height, $scale = 1.0) + { + $this->x = $x; + $this->y = $y; + $this->height = $height; + $this->width = $width; + $this->scale = $scale; + } + + /** + * @return mixed + */ + public function getX() + { + return $this->x; + } + + /** + * @return mixed + */ + public function getY() + { + return $this->y; + } + + /** + * @return mixed + */ + public function getHeight() + { + return $this->height; + } + + /** + * @return mixed + */ + public function getWidth() + { + return $this->width; + } + + /** + * @return mixed + */ + public function getScale() + { + return $this->scale; + } + + /** + * @param mixed $x + */ + public function setX($x): void + { + $this->x = $x; + } + + /** + * @param mixed $y + */ + public function setY($y): void + { + $this->y = $y; + } + + /** + * @param mixed $height + */ + public function setHeight($height): void + { + $this->height = $height; + } + + /** + * @param mixed $width + */ + public function setWidth($width): void + { + $this->width = $width; + } + + /** + * @param mixed $scale + */ + public function setScale($scale): void + { + $this->scale = $scale; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Connection.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Connection.php new file mode 100644 index 0000000000..cbd7543382 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Connection.php @@ -0,0 +1,458 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication; + +use Evenement\EventEmitter; +use HeadlessChromium\Communication\Socket\SocketInterface; +use HeadlessChromium\Communication\Socket\Wrench; +use HeadlessChromium\Exception\CommunicationException; +use HeadlessChromium\Exception\CommunicationException\CannotReadResponse; +use HeadlessChromium\Exception\CommunicationException\InvalidResponse; +use HeadlessChromium\Exception\OperationTimedOut; +use HeadlessChromium\Exception\TargetDestroyed; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Wrench\Client as WrenchBaseClient; + +class Connection extends EventEmitter implements LoggerAwareInterface +{ + use LoggerAwareTrait; + + public const EVENT_TARGET_CREATED = 'method:Target.targetCreated'; + public const EVENT_TARGET_INFO_CHANGED = 'method:Target.targetInfoChanged'; + public const EVENT_TARGET_DESTROYED = 'method:Target.targetDestroyed'; + + /** + * When strict mode is enabled communication error will result in exceptions. + * + * @var bool + */ + protected $strict = true; + + /** + * time in ms to wait between each message to be sent + * That helps to see what is happening when debugging. + * + * @var int + */ + protected $delay; + + /** + * time in ms when the previous message was sent. Used to know how long to wait for before send next message + * (only when $delay is set). + * + * @var int + */ + private $lastMessageSentTime; + + /** + * @var SocketInterface + */ + protected $wsClient; + + /** + * List of response sent from the remote host and that are waiting to be read. + * + * @var array + */ + protected $responseBuffer = []; + + /** + * Default timeout for send sync in ms. + * + * @var int + */ + protected $sendSyncDefaultTimeout; + + /** + * @var Session[] + */ + protected $sessions = []; + + /** + * @var array array of data received and waiting to be read + */ + protected $receivedData = []; + + /** + * @var array + */ + protected $httpHeaders = []; + + /** + * CommunicationChannel constructor. + * + * @param SocketInterface|string $socketClient + * @param int|null $sendSyncDefaultTimeout + */ + public function __construct($socketClient, LoggerInterface $logger = null, int $sendSyncDefaultTimeout = null) + { + // set or create logger + $this->setLogger($logger ?? new NullLogger()); + + // set timeout + $this->sendSyncDefaultTimeout = $sendSyncDefaultTimeout ?? 5000; + + // create socket client + if (\is_string($socketClient)) { + $socketClient = new Wrench(new WrenchBaseClient($socketClient, 'http://127.0.0.1'), $this->logger); + } elseif (!\is_object($socketClient) && !$socketClient instanceof SocketInterface) { + throw new \InvalidArgumentException('$socketClient param should be either a SockInterface instance or a web socket uri string'); + } + + $this->wsClient = $socketClient; + } + + /** + * @return LoggerInterface + */ + public function getLogger(): LoggerInterface + { + return $this->logger; + } + + /** + * Set the delay to apply everytime before data are sent. + * + * @param int $delay + */ + public function setConnectionDelay(int $delay): void + { + $this->delay = $delay; + } + + /** + * @param array $headers + * + * @return void + */ + public function setConnectionHttpHeaders(array $headers): void + { + $this->httpHeaders = $headers; + } + + /** + * @return array + */ + public function getConnectionHttpHeaders(): array + { + return $this->httpHeaders; + } + + /** + * Gets the default timeout used when sending a message synchronously. + * + * @return int + */ + public function getSendSyncDefaultTimeout(): int + { + return $this->sendSyncDefaultTimeout; + } + + /** + * @return bool + */ + public function isStrict(): bool + { + return $this->strict; + } + + /** + * @param bool $strict + */ + public function setStrict(bool $strict): void + { + $this->strict = $strict; + } + + /** + * Connects to the server. + * + * @return bool Whether a new connection was made + */ + public function connect() + { + return $this->wsClient->connect(); + } + + /** + * Disconnects the underlying socket, and marks the client as disconnected. + * + * @return bool + */ + public function disconnect() + { + return $this->wsClient->disconnect(); + } + + /** + * Returns whether the client is currently connected. + * + * @return bool true if connected + */ + public function isConnected() + { + return $this->wsClient->isConnected(); + } + + /** + * Wait before sending next message. + */ + private function waitForDelay(): void + { + if ($this->lastMessageSentTime) { + $currentTime = (int) (\hrtime(true) / 1000 / 1000); + // if not enough time was spent until last message was sent, wait + if ($this->lastMessageSentTime + $this->delay > $currentTime) { + $timeToWait = ($this->lastMessageSentTime + $this->delay) - $currentTime; + \usleep($timeToWait * 1000); + } + } + + $this->lastMessageSentTime = (int) (\hrtime(true) / 1000 / 1000); + } + + /** + * Sends the given message and returns a response reader. + * + * @param Message $message + * + * @throws CommunicationException + * + * @return ResponseReader + */ + public function sendMessage(Message $message): ResponseReader + { + // if delay enabled wait before sending message + if ($this->delay > 0) { + $this->waitForDelay(); + } + + $sent = $this->wsClient->sendData((string) $message); + + if (!$sent) { + $message = 'Message could not be sent.'; + + if (!$this->isConnected()) { + $message .= ' Reason: the connection is closed.'; + } else { + $message .= ' Reason: unknown.'; + } + + throw new CommunicationException($message); + } + + return new ResponseReader($message, $this); + } + + /** + * @param Message $message + * @param int|null $timeout + * + * @throws OperationTimedOut + * + * @return Response + */ + public function sendMessageSync(Message $message, int $timeout = null): Response + { + $responseReader = $this->sendMessage($message); + $response = $responseReader->waitForResponse($timeout ?? $this->sendSyncDefaultTimeout); + + return $response; + } + + /** + * Create a session for the given target id. + * + * @param string $targetId + * @param ?string $sessionId + * + * @return Session + */ + public function createSession($targetId, $sessionId = null): Session + { + if (null === $sessionId) { + $response = $this->sendMessageSync( + new Message('Target.attachToTarget', ['targetId' => $targetId, 'flatten' => true]) + ); + if (empty($response['result'])) { + throw new TargetDestroyed('The target was destroyed.'); + } + $sessionId = $response['result']['sessionId']; + } + $session = new Session($targetId, $sessionId, $this); + + $this->sessions[$sessionId] = $session; + + $session->on('destroyed', function () use ($sessionId): void { + $this->logger->debug('✘ session('.$sessionId.') was destroyed and unreferenced.'); + unset($this->sessions[$sessionId]); + }); + + return $session; + } + + /** + * Receive and stack data from the socket. + */ + private function receiveData(): void + { + $this->receivedData = \array_merge($this->receivedData, $this->wsClient->receiveData()); + } + + /** + * Read data from CRI and store messages. + * + * @throws CannotReadResponse + * @throws InvalidResponse + * + * @return bool true if data were received + */ + public function readData() + { + $hasData = false; + + while ($this->readLine()) { + $hasData = true; + } + + return $hasData; + } + + public function readLine() + { + // if buffer empty, then read from input + if (empty($this->receivedData)) { + $this->receiveData(); + } + + // dispatch first line of buffer + $datum = \array_shift($this->receivedData); + if ($datum) { + return $this->dispatchMessage($datum); + } + + return false; + } + + /** + * Dispatches the message and either stores the response or emits an event. + * + * @throws InvalidResponse + * + * @return bool + * + * @internal + */ + private function dispatchMessage(string $message, Session $session = null) + { + // responses come as json string + $response = \json_decode($message, true); + + // if json not valid throw exception + $jsonError = \json_last_error(); + if (\JSON_ERROR_NONE !== $jsonError) { + if ($this->isStrict()) { + throw new CannotReadResponse(\sprintf('Response from chrome remote interface is not a valid json response. JSON error: %s', $jsonError)); + } + + return false; + } + + // response must be array + if (!\is_array($response)) { + if ($this->isStrict()) { + throw new CannotReadResponse('Response from chrome remote interface was not a valid array'); + } + + return false; + } + + // id is required to identify the response + if (!isset($response['id'])) { + if (isset($response['method'])) { + if ('Target.receivedMessageFromTarget' == $response['method']) { + $session = $this->sessions[$response['params']['sessionId']]; + + return $this->dispatchMessage($response['params']['message'], $session); + } else { + if (!$session && isset($response['sessionId'])) { + $session = $this->sessions[$response['sessionId']] ?? null; + } + if ($session) { + $this->logger->debug( + 'session('.$session->getSessionId().'): ⇶ dispatching method:'.$response['method'] + ); + $session->emit('method:'.$response['method'], [$response['params']]); + } else { + $this->logger->debug('connection: ⇶ dispatching method:'.$response['method']); + $this->emit('method:'.$response['method'], [$response['params']]); + } + } + + return false; + } + + if ($this->isStrict()) { + throw new InvalidResponse('Response from chrome remote interface did not provide a valid message id'); + } + + return false; + } + + // store response + $this->responseBuffer[$response['id']] = $response; + + return true; + } + + /** + * True if a response for the given id exists. + * + * @param string $id + * + * @return bool + */ + public function hasResponseForId($id) + { + return \array_key_exists($id, $this->responseBuffer); + } + + /** + * @param string $id + * + * @return array|null + */ + public function getResponseForId($id) + { + if (\array_key_exists($id, $this->responseBuffer)) { + $data = $this->responseBuffer[$id]; + unset($this->responseBuffer[$id]); + + return $data; + } + + return null; + } + + /** + * @param string $sessionId + * + * @return bool + */ + public function isSessionDestroyed($sessionId) + { + return !isset($this->sessions[$sessionId]); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Message.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Message.php new file mode 100644 index 0000000000..c25ac7c945 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Message.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication; + +class Message +{ + /** + * global message id auto incremented for each message sent. + * + * @var int + */ + private static $messageId = 0; + + /** + * @var int + */ + protected $id; + + /** + * @var string + */ + protected $method; + + /** + * @var array + */ + protected $params; + + /** + * @var ?string + */ + protected $sessionId; + + /** + * get the last generated message id. + * + * @return int + */ + public static function getLastMessageId() + { + return self::$messageId; + } + + /** + * @param string $method + * @param array $params + */ + public function __construct(string $method, array $params = [], ?string $sessionId = null) + { + $this->id = ++self::$messageId; + $this->method = $method; + $this->params = $params; + $this->sessionId = $sessionId; + } + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return string + */ + public function getMethod(): string + { + return $this->method; + } + + /** + * @return array + */ + public function getParams(): array + { + return $this->params; + } + + public function __toString(): string + { + $message = [ + 'id' => $this->getId(), + 'method' => $this->getMethod(), + 'params' => (object) $this->getParams(), + ]; + if (null !== $this->sessionId) { + $message['sessionId'] = $this->sessionId; + } + + return \json_encode($message); + } + + public function getSessionId(): ?string + { + return $this->sessionId; + } + + public function setSessionId(string $sessionId): void + { + $this->sessionId = $sessionId; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Response.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Response.php new file mode 100644 index 0000000000..a72ab13eee --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Response.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication; + +class Response implements \ArrayAccess +{ + protected $message; + + protected $data; + + /** + * Response constructor. + */ + public function __construct(array $data, Message $message) + { + $this->data = $data; + $this->message = $message; + } + + /** + * True if the response is error free. + * + * @return bool + */ + public function isSuccessful() + { + return !\array_key_exists('error', $this->data); + } + + /** + * Get the error message if set. + * + * @return string|null + */ + public function getErrorMessage(bool $extended = true) + { + $message = []; + + if ($extended && isset($this->data['error']['code'])) { + $message[] = $this->data['error']['code']; + } + + if (isset($this->data['error']['message'])) { + $message[] = $this->data['error']['message']; + } + + if ($extended && isset($this->data['error']['data']) && \is_string($this->data['error']['data'])) { + $message[] = $this->data['error']['data']; + } + + return \implode(' - ', $message); + } + + /** + * Get the error code if set. + * + * @return string|null + */ + public function getErrorCode() + { + return $this->data['error']['code'] ?? null; + } + + /** + * @param string $name + * + * @return mixed + */ + public function getResultData($name) + { + return $this->data['result'][$name] ?? null; + } + + /** + * @return Message + */ + public function getMessage(): Message + { + return $this->message; + } + + /** + * The data returned by chrome dev tools. + * + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * {@inheritdoc} + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return \array_key_exists($offset, $this->data); + } + + /** + * {@inheritdoc} + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->data[$offset]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value): void + { + throw new \Exception('Responses are immutable'); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset): void + { + throw new \Exception('Responses are immutable'); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/ResponseReader.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/ResponseReader.php new file mode 100644 index 0000000000..016dbc7d1f --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/ResponseReader.php @@ -0,0 +1,188 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication; + +use HeadlessChromium\Exception\NoResponseAvailable; +use HeadlessChromium\Exception\OperationTimedOut; +use HeadlessChromium\Utils; + +class ResponseReader +{ + /** + * @var Message + */ + protected $message; + + /** + * @var Connection + */ + protected $connection; + + /** + * @var Response|null + */ + protected $response = null; + + /** + * Response constructor. + * + * @param Message $message + * @param Connection $connection + */ + public function __construct(Message $message, Connection $connection) + { + $this->message = $message; + $this->connection = $connection; + } + + /** + * True if a response is available. + * + * @return bool + */ + public function hasResponse() + { + return null !== $this->response; + } + + /** + * the message to get a response for. + * + * @return Message + */ + public function getMessage(): Message + { + return $this->message; + } + + /** + * The connection to check messages for. + * + * @return Connection + */ + public function getConnection(): Connection + { + return $this->connection; + } + + /** + * Get the response. + * + * Note: response will always be missing until checkForResponse is called + * and the response is available in the buffer + * + * @throws NoResponseAvailable + * + * @return Response + */ + public function getResponse(): Response + { + if (!$this->response) { + throw new NoResponseAvailable('Response is not available. Try to use the method waitForResponse instead.'); + } + + return $this->response; + } + + /** + * Wait for a response. + * + * @param int $timeout time to wait for a response (milliseconds) + * + * @throws NoResponseAvailable + * @throws OperationTimedOut + * + * @return Response + */ + public function waitForResponse(int $timeout = null): Response + { + if ($this->hasResponse()) { + return $this->getResponse(); + } + + // default 2000ms + $timeout = $timeout ?? 2000; + + return Utils::tryWithTimeout($timeout * 1000, $this->waitForResponseGenerator()); + } + + /** + * To be used in waitForResponse method. + * + * @throws NoResponseAvailable + * + * @return \Generator|Response + * + * @internal + */ + private function waitForResponseGenerator() + { + while (true) { + // 50 microseconds between each iteration + $tryDelay = 50; + + // read available response + $hasResponse = $this->checkForResponse(); + + // if found return it + if ($hasResponse) { + return $this->getResponse(); + } + + // wait before next check + yield $tryDelay; + } + } + + /** + * Check in the connection if a response exists for the message and store it if the response exists. + * + * @return bool + */ + public function checkForResponse() + { + // if response is already read, ignore + if ($this->hasResponse()) { + return true; + } + + $id = $this->message->getId(); + + // if response exists store it + if ($this->connection->hasResponseForId($id)) { + $this->response = new Response($this->connection->getResponseForId($id), $this->message); + + return true; + } + + // read data + while (!$this->connection->hasResponseForId($id)) { + if (!$this->connection->readLine()) { + break; + } + } + + // if response store it + if ($this->connection->hasResponseForId($id)) { + $this->response = new Response($this->connection->getResponseForId($id), $this->message); + + return true; + } + + // check if the session was destroyed in the mean time + if (null !== $this->message->getSessionId() && $this->connection->isSessionDestroyed($this->message->getSessionId())) { + throw new \HeadlessChromium\Exception\TargetDestroyed('The session is destroyed.'); + } + + return false; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Session.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Session.php new file mode 100644 index 0000000000..627a285239 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Session.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication; + +use Evenement\EventEmitter; +use HeadlessChromium\Exception\CommunicationException; +use HeadlessChromium\Exception\NoResponseAvailable; +use HeadlessChromium\Exception\TargetDestroyed; + +class Session extends EventEmitter +{ + /** + * @var string + */ + protected $sessionId; + + /** + * @var string + */ + protected $targetId; + + /** + * @var Connection|null + */ + protected $connection; + + /** + * @var bool + */ + protected $destroyed = false; + + /** + * Session constructor. + * + * @param string $targetId + * @param string $sessionId + * @param Connection $connection + */ + public function __construct(string $targetId, string $sessionId, Connection $connection) + { + $this->sessionId = $sessionId; + $this->targetId = $targetId; + $this->connection = $connection; + } + + /** + * @param Message $message + * + * @throws CommunicationException + * + * @return ResponseReader + */ + public function sendMessage(Message $message): ResponseReader + { + if ($this->destroyed) { + throw new TargetDestroyed('The session was destroyed.'); + } + + if (null === $message->getSessionId()) { + $message->setSessionId($this->getSessionId()); + } + $topResponse = $this->getConnection()->sendMessage($message); + + return $topResponse; + } + + /** + * @param Message $message + * @param int $timeout + * + * @throws NoResponseAvailable + * @throws CommunicationException + * + * @return Response + */ + public function sendMessageSync(Message $message, int $timeout = null): Response + { + $responseReader = $this->sendMessage($message); + + $response = $responseReader->waitForResponse($timeout ?? $this->getConnection()->getSendSyncDefaultTimeout()); + + if (!$response) { + throw new NoResponseAvailable('No response was sent in the given timeout'); + } + + return $response; + } + + /** + * @return string + */ + public function getSessionId() + { + return $this->sessionId; + } + + /** + * @return string + */ + public function getTargetId() + { + return $this->targetId; + } + + /** + * @return Connection + */ + public function getConnection() + { + if ($this->destroyed) { + throw new TargetDestroyed('The session was destroyed.'); + } + + return $this->connection; + } + + /** + * Marks the session as destroyed. + * + * @internal + */ + public function destroy(): void + { + if ($this->destroyed) { + throw new TargetDestroyed('The session was already destroyed.'); + } + $this->emit('destroyed'); + $this->connection = null; + $this->destroyed = true; + $this->removeAllListeners(); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/MockSocket.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/MockSocket.php new file mode 100644 index 0000000000..66a450b5e8 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/MockSocket.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication\Socket; + +/** + * A mock adapter for unit tests. + */ +class MockSocket implements SocketInterface +{ + protected $sentData = []; + + protected $receivedData = []; + protected $receivedDataForNextMessage = []; + + protected $isConnected = false; + + protected $shouldConnect = true; + + /** + * {@inheritdoc} + */ + public function sendData($data) + { + if (!$this->isConnected()) { + return false; + } + + $this->sentData[] = $data; + + if (!empty($this->receivedDataForNextMessage)) { + $data = \json_decode($data, true); + + if ($data['id']) { + $next = \array_shift($this->receivedDataForNextMessage); + $next = \json_decode($next, true); + $next['id'] = $data['id']; + $this->receivedData[] = \json_encode($next); + + if (isset($data['method']) && 'Target.sendMessageToTarget' == $data['method']) { + --$next['id']; + $this->receivedData[] = \json_encode($next); + } + } + } + + return true; + } + + /** + * resets the data stored with sendData. + */ + public function flushData(): void + { + $this->sentData = []; + } + + /** + * gets the data stored with sendData. + */ + public function getSentData() + { + return $this->sentData; + } + + /** + * {@inheritdoc} + */ + public function receiveData(): array + { + $data = $this->receivedData; + $this->receivedData = []; + + return $data; + } + + /** + * Add data to be returned with receiveData. + * + * @param bool $forNextMessage true to set the response id automatically + * for next message (can stack for multiple messages + */ + public function addReceivedData($data, $forNextMessage = false): void + { + if ($forNextMessage) { + $this->receivedDataForNextMessage[] = $data; + } else { + $this->receivedData[] = $data; + } + } + + /** + * {@inheritdoc} + */ + public function connect() + { + $this->isConnected = $this->shouldConnect; + + return $this->isConnected; + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return $this->isConnected; + } + + /** + * {@inheritdoc} + */ + public function disconnect($reason = 1000) + { + $this->isConnected = false; + + return true; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/SocketInterface.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/SocketInterface.php new file mode 100644 index 0000000000..d151617c28 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/SocketInterface.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication\Socket; + +/** + * A simplified interface to wrap a socket client. + */ +interface SocketInterface +{ + /** + * Sends data to the socket. + * + * @return bool whether the data were sent + */ + public function sendData($data); + + /** + * Receives data sent by the server. + * + * @return array Payload received since the last call to receive() + */ + public function receiveData(): array; + + /** + * Connect to the server. + * + * @return bool Whether a new connection was made + */ + public function connect(); + + /** + * Whether the client is currently connected. + * + * @return bool + */ + public function isConnected(); + + /** + * Disconnects the underlying socket, and marks the client as disconnected. + * + * @param int $reason see http://tools.ietf.org/html/rfc6455#section-7.4 + * + * @return bool + */ + public function disconnect($reason = 1000); +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/Wrench.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/Wrench.php new file mode 100644 index 0000000000..fa3ffa381d --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Socket/Wrench.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication\Socket; + +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Wrench\Client as WrenchClient; +use Wrench\Payload\Payload; + +class Wrench implements SocketInterface, LoggerAwareInterface +{ + use LoggerAwareTrait; + + /** + * An auto incremented counter to uniquely identify each socket instance. + * + * @var int + */ + private static $socketIdCounter = 0; + + /** + * @var WrenchClient + */ + protected $client; + + /** + * Id of this socket generated from self::$socketIdCounter. + * + * @var int + */ + protected $socketId = 0; + + /** + * @param WrenchClient $client + */ + public function __construct(WrenchClient $client, LoggerInterface $logger = null) + { + $this->client = $client; + + $this->setLogger($logger ?? new NullLogger()); + + $this->socketId = ++self::$socketIdCounter; + } + + /** + * {@inheritdoc} + */ + public function sendData($data) + { + // log + $this->logger->debug('socket('.$this->socketId.'): → sending data:'.$data); + + // send data + return $this->client->sendData($data); + } + + /** + * {@inheritdoc} + */ + public function receiveData(): array + { + $playloads = $this->client->receive(); + + $data = []; + + if ($playloads) { + foreach ($playloads as $playload) { + /** @var Payload */ + $dataString = $playload->getPayload(); + $data[] = $dataString; + + // log + $this->logger->debug('socket('.$this->socketId.'): ← receiving data:'.$dataString); + } + } + + return $data; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + // log + $this->logger->debug('socket('.$this->socketId.'): connecting'); + + $connected = $this->client->connect(); + + if ($connected) { + // log + $this->logger->debug('socket('.$this->socketId.'): ✓ connected'); + } else { + // log + $this->logger->debug('socket('.$this->socketId.'): ✗ could not connect'); + } + + return $connected; + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return $this->client->isConnected(); + } + + /** + * {@inheritdoc} + */ + public function disconnect($reason = 1000) + { + // log + $this->logger->debug('socket('.$this->socketId.'): disconnecting'); + + $disconnected = $this->client->disconnect($reason); + + if ($disconnected) { + // log + $this->logger->debug('socket('.$this->socketId.'): ✓ disconnected'); + } else { + // log + $this->logger->debug('socket('.$this->socketId.'): ✗ could not disconnect'); + } + + return $disconnected; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Communication/Target.php b/pandora_console/vendor/chrome-php/chrome/src/Communication/Target.php new file mode 100644 index 0000000000..d3b7b198bb --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Communication/Target.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Communication; + +use HeadlessChromium\Exception\TargetDestroyed; + +class Target +{ + /** + * @var array + */ + protected $targetInfo; + + /** + * @var Session|null + */ + protected $session; + + /** + * @var Connection + */ + private $connection; + + /** + * @var bool + */ + protected $destroyed = false; + + /** + * Target constructor. + */ + public function __construct(array $targetInfo, Connection $connection) + { + $this->targetInfo = $targetInfo; + $this->connection = $connection; + } + + /** + * @param ?string $sessionId + * + * @return Session + */ + public function getSession(?string $sessionId = null): Session + { + if ($this->destroyed) { + throw new TargetDestroyed('The target was destroyed.'); + } + + // if not already done, create a session for the target + if (!$this->session) { + $this->session = $this->connection->createSession($this->getTargetInfo('targetId'), $sessionId); + } + + return $this->session; + } + + /** + * Marks the target as destroyed. + * + * @internal + */ + public function destroy(): void + { + if ($this->destroyed) { + throw new TargetDestroyed('The target was already destroyed.'); + } + + if ($this->session) { + $this->session->destroy(); + $this->session = null; + } + + $this->destroyed = true; + } + + /** + * @return bool + */ + public function isDestroyed(): bool + { + return $this->destroyed; + } + + /** + * Get target info value by it's name or null if it does not exist. + * + * @param string $infoName + * + * @return mixed + */ + public function getTargetInfo($infoName) + { + return $this->targetInfo[$infoName] ?? null; + } + + /** + * To be called when Target.targetInfoChanged is triggered. + * + * @param array $targetInfo + * + * @internal + */ + public function targetInfoChanged($targetInfo): void + { + $this->targetInfo = $targetInfo; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Cookies/Cookie.php b/pandora_console/vendor/chrome-php/chrome/src/Cookies/Cookie.php new file mode 100644 index 0000000000..046fd687c7 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Cookies/Cookie.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Cookies; + +class Cookie implements \ArrayAccess +{ + /** + * @var array + */ + protected $data; + + /** + * Cookie constructor. + */ + public function __construct(array $data) + { + if (isset($data['expires']) && \is_string($data['expires']) && !\is_numeric($data['expires'])) { + $data['expires'] = \strtotime($data['expires']); + } + + $this->data = $data; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->offsetGet('value'); + } + + /** + * @return mixed + */ + public function getName() + { + return $this->offsetGet('name'); + } + + /** + * @return mixed + */ + public function getDomain() + { + return $this->offsetGet('domain'); + } + + /** + * {@inheritdoc} + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return \array_key_exists($offset, $this->data); + } + + /** + * {@inheritdoc} + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->data[$offset] ?? null; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value): void + { + throw new \RuntimeException('Cannot set cookie values'); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset): void + { + throw new \RuntimeException('Cannot unset cookie values'); + } + + /** + * @param string $name + * @param string $value + * @param array $params + * + * @return Cookie + */ + public static function create($name, $value, array $params = []) + { + $params['name'] = $name; + $params['value'] = $value; + + return new self($params); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Cookies/CookiesCollection.php b/pandora_console/vendor/chrome-php/chrome/src/Cookies/CookiesCollection.php new file mode 100644 index 0000000000..23ba52c8cd --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Cookies/CookiesCollection.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Cookies; + +class CookiesCollection implements \IteratorAggregate, \Countable +{ + /** + * @var Cookie[] + */ + protected $cookies = []; + + /** + * CookiesCollection constructor. + */ + public function __construct(array $cookies = null) + { + if ($cookies) { + foreach ($cookies as $cookie) { + if (\is_array($cookie)) { + $cookie = new Cookie($cookie); + } + $this->addCookie($cookie); + } + } + } + + /** + * Adds a cookie. + */ + public function addCookie(Cookie $cookie): void + { + $this->cookies[] = $cookie; + } + + /** + * {@inheritdoc} + */ + #[\ReturnTypeWillChange] + public function getIterator() + { + return new \ArrayIterator($this->cookies); + } + + /** + * {@inheritdoc} + */ + #[\ReturnTypeWillChange] + public function count() + { + return \count($this->cookies); + } + + /** + * Get the cookie at the given index. + * + * @param int $i + * + * @return Cookie + */ + public function getAt($i): Cookie + { + if (!isset($this->cookies[$i])) { + throw new \RuntimeException(\sprintf('No cookie at index %s', $i)); + } + + return $this->cookies[$i]; + } + + /** + * Find cookies with matching values. + * + * usage: + * + * ``` + * // find cookies having name == 'foo' + * $newCookies = $cookies->filterBy('name', 'foo'); + * + * // find cookies having domain == 'example.com' + * $newCookies = $cookies->filterBy('domain', 'example.com'); + * ``` + * + * @param string $param + * @param string $value + * + * @return CookiesCollection + */ + public function filterBy(string $param, string $value) + { + return new self(\array_filter($this->cookies, function (Cookie $cookie) use ($param, $value) { + return $cookie[$param] == $value; + })); + } + + /** + * Find first cookies with matching value. + * + * usage: + * + * ``` + * // find first cookie having name == 'foo' + * $cookie = $cookies->findOneBy('name', 'foo'); + * + * if ($cookie) { + * // do something + * } + * ``` + * + * @param string $param + * @param string $value + * + * @return Cookie|null + */ + public function findOneBy(string $param, string $value) + { + foreach ($this->cookies as $cookie) { + if ($cookie[$param] == $value) { + return $cookie; + } + } + + return null; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Dom/Dom.php b/pandora_console/vendor/chrome-php/chrome/src/Dom/Dom.php new file mode 100644 index 0000000000..99ca137de3 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Dom/Dom.php @@ -0,0 +1,56 @@ +getSession()->sendMessage($message); + $response = $stream->waitForResponse(1000); + + $rootNodeId = $response->getResultData('root')['nodeId']; + + parent::__construct($page, $rootNodeId); + } + + public function search(string $selector): array + { + $message = new Message('DOM.performSearch', [ + 'query' => $selector, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + + $searchId = $response->getResultData('searchId'); + $count = $response->getResultData('resultCount'); + + if (0 === $count) { + return []; + } + $message = new Message('DOM.getSearchResults', [ + 'searchId' => $searchId, + 'fromIndex' => 0, + 'toIndex' => $count, + ]); + + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + + $nodes = []; + $nodeIds = $response->getResultData('nodeIds'); + foreach ($nodeIds as $nodeId) { + $nodes[] = new Node($this->page, $nodeId); + } + + return $nodes; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Dom/Node.php b/pandora_console/vendor/chrome-php/chrome/src/Dom/Node.php new file mode 100644 index 0000000000..fb3362454d --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Dom/Node.php @@ -0,0 +1,206 @@ +page = $page; + $this->nodeId = $nodeId; + } + + public function getAttributes(): NodeAttributes + { + $message = new Message('DOM.getAttributes', [ + 'nodeId' => $this->nodeId, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + + $attributes = $response->getResultData('attributes'); + + return new NodeAttributes($attributes); + } + + public function setAttributeValue(string $name, string $value): void + { + $message = new Message('DOM.setAttributeValue', [ + 'nodeId' => $this->nodeId, + 'name' => $name, + 'value' => $value, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + } + + public function querySelector(string $selector): ?self + { + $message = new Message('DOM.querySelector', [ + 'nodeId' => $this->nodeId, + 'selector' => $selector, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + $this->assertNotError($response); + + $nodeId = $response->getResultData('nodeId'); + + if (null !== $nodeId && 0 !== $nodeId) { + return new self($this->page, $nodeId); + } + + return null; + } + + public function querySelectorAll(string $selector): array + { + $message = new Message('DOM.querySelectorAll', [ + 'nodeId' => $this->nodeId, + 'selector' => $selector, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + + $nodes = []; + $nodeIds = $response->getResultData('nodeIds'); + foreach ($nodeIds as $nodeId) { + $nodes[] = new self($this->page, $nodeId); + } + + return $nodes; + } + + public function focus(): void + { + $message = new Message('DOM.focus', [ + 'nodeId' => $this->nodeId, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + } + + public function getAttribute(string $name): ?string + { + return $this->getAttributes()->get($name); + } + + public function getPosition(): ?NodePosition + { + $message = new Message('DOM.getBoxModel', [ + 'nodeId' => $this->nodeId, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + + $points = $response->getResultData('model')['content']; + + if (null !== $points) { + return new NodePosition($points); + } else { + return null; + } + } + + public function hasPosition(): bool + { + return null !== $this->getPosition(); + } + + public function getHTML(): string + { + $message = new Message('DOM.getOuterHTML', [ + 'nodeId' => $this->nodeId, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + + return $response->getResultData('outerHTML'); + } + + public function getText(): string + { + return \strip_tags($this->getHTML()); + } + + public function scrollIntoView(): void + { + $message = new Message('DOM.scrollIntoViewIfNeeded', [ + 'nodeId' => $this->nodeId, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + } + + /** + * @throws DomException + */ + public function click(): void + { + if (false === $this->hasPosition()) { + throw new DomException('Failed to click element without position'); + } + $this->scrollIntoView(); + $position = $this->getPosition(); + $this->page->mouse() + ->move($position->getCenterX(), $position->getCenterY()) + ->click(); + } + + public function sendKeys(string $text): void + { + $this->scrollIntoView(); + $this->focus(); + $this->page->keyboard() + ->typeText($text); + } + + public function sendFile(string $filePath): void + { + $this->sendFiles([$filePath]); + } + + public function sendFiles(array $filePaths): void + { + $message = new Message('DOM.setFileInputFiles', [ + 'files' => $filePaths, + 'nodeId' => $this->nodeId, + ]); + $response = $this->page->getSession()->sendMessageSync($message); + + $this->assertNotError($response); + } + + /** + * @throws DomException + */ + public function assertNotError(Response $response): void + { + if (!$response->isSuccessful()) { + throw new DOMException($response->getErrorMessage()); + } + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Dom/NodeAttributes.php b/pandora_console/vendor/chrome-php/chrome/src/Dom/NodeAttributes.php new file mode 100644 index 0000000000..92322834da --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Dom/NodeAttributes.php @@ -0,0 +1,35 @@ +attributes[$attrs[$i]] = $attrs[$i + 1]; + } + } + + public function toArray(): array + { + return $this->attributes; + } + + public function has(string $name): bool + { + return isset($this->attributes[$name]); + } + + public function get(string $name): ?string + { + return $this->attributes[$name] ?? null; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Dom/NodePosition.php b/pandora_console/vendor/chrome-php/chrome/src/Dom/NodePosition.php new file mode 100644 index 0000000000..0d4f07ec35 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Dom/NodePosition.php @@ -0,0 +1,76 @@ +x = $leftTopX; + $this->y = $leftTopY; + + $this->height = $leftBottomY - $leftTopY; + $this->width = $rightBottomX - $leftBottomX; + } + + public function getX(): int + { + return (int) $this->x; + } + + public function getY(): int + { + return (int) $this->y; + } + + public function getWidth(): int + { + return (int) $this->width; + } + + public function getHeight(): int + { + return (int) $this->height; + } + + public function getCenterX(): int + { + return (int) ($this->x + ($this->width / 2)); + } + + public function getCenterY(): int + { + return (int) ($this->y + ($this->height / 2)); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Dom/Selector/CssSelector.php b/pandora_console/vendor/chrome-php/chrome/src/Dom/Selector/CssSelector.php new file mode 100644 index 0000000000..26b518f5e3 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Dom/Selector/CssSelector.php @@ -0,0 +1,29 @@ +expression = $expression; + } + + public function expressionCount(): string + { + return \sprintf('document.querySelectorAll("%s").length', $this->expression); + } + + public function expressionFindOne(int $position): string + { + return \sprintf('document.querySelectorAll("%s")[%d]', $this->expression, $position - 1); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Dom/Selector/Selector.php b/pandora_console/vendor/chrome-php/chrome/src/Dom/Selector/Selector.php new file mode 100644 index 0000000000..1d2ce01dd5 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Dom/Selector/Selector.php @@ -0,0 +1,12 @@ +expression = $expression; + } + + public function expressionCount(): string + { + return \sprintf( + 'document.evaluate("%s", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength', + \addslashes($this->expression) + ); + } + + public function expressionFindOne(int $position): string + { + return \sprintf( + 'document.evaluate("%s[%d]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue', + \addslashes($this->expression), + $position + ); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/BrowserConnectionFailed.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/BrowserConnectionFailed.php new file mode 100644 index 0000000000..5eb8f18e3d --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/BrowserConnectionFailed.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class BrowserConnectionFailed extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException.php new file mode 100644 index 0000000000..f6a48819ee --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class CommunicationException extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/CannotReadResponse.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/CannotReadResponse.php new file mode 100644 index 0000000000..d2578fef1d --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/CannotReadResponse.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception\CommunicationException; + +use HeadlessChromium\Exception\CommunicationException; + +class CannotReadResponse extends CommunicationException +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/InvalidResponse.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/InvalidResponse.php new file mode 100644 index 0000000000..f77ad25f22 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/InvalidResponse.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception\CommunicationException; + +use HeadlessChromium\Exception\CommunicationException; + +class InvalidResponse extends CommunicationException +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/ResponseHasError.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/ResponseHasError.php new file mode 100644 index 0000000000..d734fbec89 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/CommunicationException/ResponseHasError.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception\CommunicationException; + +use HeadlessChromium\Exception\CommunicationException; + +class ResponseHasError extends CommunicationException +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/DomException.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/DomException.php new file mode 100644 index 0000000000..a6226c5c84 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/DomException.php @@ -0,0 +1,9 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class ElementNotFoundException extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/EvaluationFailed.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/EvaluationFailed.php new file mode 100644 index 0000000000..46a6fa7ba6 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/EvaluationFailed.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class EvaluationFailed extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/FilesystemException.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/FilesystemException.php new file mode 100644 index 0000000000..9e81f3125f --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/FilesystemException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class FilesystemException extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/InvalidTimezoneId.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/InvalidTimezoneId.php new file mode 100644 index 0000000000..ee3850a8f6 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/InvalidTimezoneId.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class InvalidTimezoneId extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/JavascriptException.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/JavascriptException.php new file mode 100644 index 0000000000..383b5d79da --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/JavascriptException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class JavascriptException extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/NavigationExpired.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/NavigationExpired.php new file mode 100644 index 0000000000..b58fad2993 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/NavigationExpired.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class NavigationExpired extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/NoResponseAvailable.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/NoResponseAvailable.php new file mode 100644 index 0000000000..d4df245c1f --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/NoResponseAvailable.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class NoResponseAvailable extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/OperationTimedOut.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/OperationTimedOut.php new file mode 100644 index 0000000000..8e4e5bc057 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/OperationTimedOut.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class OperationTimedOut extends \Exception +{ + public static function createFromTimeout(int $timeoutMicroSec): self + { + return new self(\sprintf('Operation timed out after %s.', self::getTimeoutPhrase($timeoutMicroSec))); + } + + private static function getTimeoutPhrase(int $timeoutMicroSec): string + { + if ($timeoutMicroSec > 1000 * 1000) { + return \sprintf('%ds', (int) ($timeoutMicroSec / (1000 * 1000))); + } + + if ($timeoutMicroSec > 1000) { + return \sprintf('%dms', (int) ($timeoutMicroSec / 1000)); + } + + return \sprintf('%dμs', (int) ($timeoutMicroSec)); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/PdfFailed.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/PdfFailed.php new file mode 100644 index 0000000000..ff96f3a1e9 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/PdfFailed.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class PdfFailed extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/ScreenshotFailed.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/ScreenshotFailed.php new file mode 100644 index 0000000000..cc6a6d8917 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/ScreenshotFailed.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class ScreenshotFailed extends \Exception +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Exception/TargetDestroyed.php b/pandora_console/vendor/chrome-php/chrome/src/Exception/TargetDestroyed.php new file mode 100644 index 0000000000..b6ca33f647 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Exception/TargetDestroyed.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Exception; + +class TargetDestroyed extends \RuntimeException +{ +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Frame.php b/pandora_console/vendor/chrome-php/chrome/src/Frame.php new file mode 100644 index 0000000000..cdb05e6432 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Frame.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium; + +class Frame +{ + public const LIFECYCLE_INIT = 'init'; + + /** + * @var array + */ + protected $frameData; + + /** + * @var array + */ + protected $lifeCycleEvents = []; + + /** + * @var string + */ + protected $latestLoaderId; + + /** + * @var string + */ + protected $frameId; + + /** + * @var int + */ + protected $executionContextId; + + /** + * Frame constructor. + * + * @param array $frameData + */ + public function __construct(array $frameData) + { + $this->frameData = $frameData; + $this->latestLoaderId = $frameData['loaderId']; + $this->frameId = $frameData['id']; + } + + /** + * @internal + */ + public function onLifecycleEvent(array $params): void + { + if (self::LIFECYCLE_INIT === $params['name']) { + $this->lifeCycleEvents = []; + $this->latestLoaderId = $params['loaderId']; + $this->frameId = $params['frameId']; + } + + $this->lifeCycleEvents[$params['name']] = $params['timestamp']; + } + + /** + * @return int + */ + public function getExecutionContextId(): int + { + return $this->executionContextId; + } + + /** + * @param int $executionContextId + */ + public function setExecutionContextId(int $executionContextId): void + { + $this->executionContextId = $executionContextId; + } + + /** + * @return string + */ + public function getFrameId(): string + { + return $this->frameId; + } + + /** + * @return string + */ + public function getLatestLoaderId(): string + { + return $this->latestLoaderId; + } + + /** + * Gets the life cycle events of the frame with the time they occurred at. + * + * @return array + */ + public function getLifeCycle(): array + { + return $this->lifeCycleEvents; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/FrameManager.php b/pandora_console/vendor/chrome-php/chrome/src/FrameManager.php new file mode 100644 index 0000000000..8c13aa474f --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/FrameManager.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium; + +class FrameManager +{ + /** + * @var Page + */ + protected $page; + + /** + * @var Frame[] + */ + protected $frames = []; + + /** + * @var Frame + */ + protected $mainFrame; + + /** + * FrameManager constructor. + */ + public function __construct(Page $page, array $frameTree) + { + $this->page = $page; + + if (isset($frameTree['frame'])) { + // TODO parse children frames + $this->frames[$frameTree['frame']['id']] = new Frame($frameTree['frame']); + + // associate main frame + $this->mainFrame = $this->frames[$frameTree['frame']['id']]; + } + + // TODO listen for frame events + + // update frame on init + $this->page->getSession()->on('method:Page.lifecycleEvent', function (array $params): void { + if (isset($this->frames[$params['frameId']])) { + $frame = $this->frames[$params['frameId']]; + $frame->onLifecycleEvent($params); + } + }); + + // attach context id to frame + $this->page->getSession()->on('method:Runtime.executionContextCreated', function (array $params): void { + if (isset($params['context']['auxData']['frameId']) && $params['context']['auxData']['isDefault']) { + if ($this->hasFrame($params['context']['auxData']['frameId'])) { + $frame = $this->getFrame($params['context']['auxData']['frameId']); + $frame->setExecutionContextId($params['context']['id']); + } + } + }); + + // TODO maybe implement Runtime.executionContextDestroyed and Runtime.executionContextsCleared + } + + /** + * Checks if the given frame exists. + * + * @param string $frameId + * + * @return bool + */ + public function hasFrame($frameId): bool + { + return \array_key_exists($frameId, $this->frames); + } + + /** + * Get a frame given its id. + * + * @param string $frameId + * + * @return Frame + */ + public function getFrame($frameId): Frame + { + if (!isset($this->frames[$frameId])) { + throw new \RuntimeException(\sprintf('No such frame "%s"', $frameId)); + } + + return $this->frames[$frameId]; + } + + /** + * Gets the main frame. + * + * @return Frame + */ + public function getMainFrame(): Frame + { + return $this->mainFrame; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Input/Key.php b/pandora_console/vendor/chrome-php/chrome/src/Input/Key.php new file mode 100644 index 0000000000..b36db44619 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Input/Key.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Input; + +/** + * Holds key constants and their respective bit values. + * + * @see https://chromedevtools.github.io/devtools-protocol/1-2/Input/ + */ +abstract class Key +{ + public const ALT = 1; + public const CONTROL = 2; + public const META = 4; + public const SHIFT = 8; + public const COMMAND = self::META; +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Input/Keyboard.php b/pandora_console/vendor/chrome-php/chrome/src/Input/Keyboard.php new file mode 100644 index 0000000000..766f82d6c8 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Input/Keyboard.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Input; + +use HeadlessChromium\Communication\Message; +use HeadlessChromium\Page; + +class Keyboard +{ + use KeyboardKeys; + + /** + * @var Page + */ + protected $page; + + /** + * @var int + */ + protected $sleep = 0; + + /** + * @param Page $page + */ + public function __construct(Page $page) + { + $this->page = $page; + } + + /** + * Type a text string, char by char, without applying modifiers. + * + * @param string $text text string to be typed + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function typeText(string $text) + { + $this->page->assertNotClosed(); + + $length = \mb_strlen($text); + + for ($i = 0; $i < $length; ++$i) { + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchKeyEvent', [ + 'type' => 'char', + 'modifiers' => $this->getModifiers(), + 'text' => \mb_substr($text, $i, 1), + ])); + + \usleep($this->sleep); + } + + return $this; + } + + /** + * Type a raw key using the rawKeyDown event, without sending any codes or modifiers. + * + * Example: + * + * ```php + * $page->keyboard()->typeRawKey('Tab'); + * ``` + * + * @param string $key single raw key to be typed + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function typeRawKey(string $key): self + { + $this->page->assertNotClosed(); + + $this->onKeyPress($key); + + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchKeyEvent', [ + 'type' => 'rawKeyDown', + 'key' => $key, + ])); + + \usleep($this->sleep); + + $this->release($key); + + return $this; + } + + /** + * Press and release a single key. + * + * Example: + * + * ```php + * $page->keyboard()->type('a'); + * ``` + * + * @param string $key single key to be typed + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function type(string $key): self + { + return $this->press($key)->release($key); + } + + /** + * Press a single key with key codes and modifiers. + * + * A key can be pressed multiple times sequentially. This is what happens + * in a real browser when the user presses and holds down hown the key. + * + * Example: + * + * ```php + * $page->keyboard()->press('Control')->press('c'); // press ctrl + c + * ``` + * + * @param string $key single key to be pressed + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function press(string $key): self + { + $this->page->assertNotClosed(); + + $this->onKeyPress($key); + + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchKeyEvent', [ + 'type' => 'keyDown', + 'modifiers' => $this->getModifiers(), + 'text' => $key, + 'key' => $this->getCurrentKey(), + 'windowsVirtualKeyCode' => $this->getKeyCode(), + ])); + + \usleep($this->sleep); + + return $this; + } + + /** + * Release a single key. + * + * A key is released only once, even if it was pressed multiple times. + * If no key is given, all pressed keys will be released. + * + * Example: + * + * ```php + * $page->keyboard()->release('Control'); // release Control + * $page->keyboard()->release(); // release all + * ``` + * + * @param string $key (optional) single key to be released + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function release(string $key = null): self + { + $this->page->assertNotClosed(); + + if (null === $key) { + $this->releaseAll(); + + return $this; + } + + $this->onKeyRelease($key); + + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchKeyEvent', [ + 'type' => 'keyUp', + 'key' => $this->getCurrentKey(), + ])); + + \usleep($this->sleep); + + return $this; + } + + /** + * Release all pressed keys. + * + * @return self + */ + private function releaseAll(): self + { + foreach ($this->pressedKeys as $key => $value) { + if (true === $value) { + $this->release($key); + } + } + + return $this; + } + + /** + * Set the time interval between key strokes in milliseconds. + * + * @param int $milliseconds + * + * @return $this + */ + public function setKeyInterval(int $milliseconds) + { + if ($milliseconds < 0) { + $milliseconds = 0; + } + + $this->sleep = $milliseconds * 1000; + + return $this; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Input/KeyboardKeys.php b/pandora_console/vendor/chrome-php/chrome/src/Input/KeyboardKeys.php new file mode 100644 index 0000000000..459c770505 --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Input/KeyboardKeys.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Input; + +/** + * Translates typed keys to their respective codes. + * + * @see https://chromedevtools.github.io/devtools-protocol/1-2/Input/ + */ +trait KeyboardKeys +{ + /** + * Array of currently pressed keys (keyDown events). + * + * The elements of this array should be unique. A real keyboard can create several keyDown events + * by holding down a key, but only one keyUp event will be sent when the key is released. + */ + protected $pressedKeys = []; + + /** + * Current key as a sanitized string. + * + * Single letters like "v" must be in uppercase, otherwise key combinations like ctrl + v won't work. + */ + protected $currentKey = ''; + + /** + * Bit field representing pressed modifier keys. + */ + protected $modifiers = 0; + + /** + * Aliases for modifier keys, in lowercase. + */ + protected $keyAliases = [ + Key::ALT => [ + 'alt', + 'altgr', + 'alt gr', + ], + Key::CONTROL => [ + 'control', + 'ctrl', + 'ctr', + ], + Key::META => [ + 'meta', + 'command', + 'cmd', + ], + Key::SHIFT => [ + 'shift', + ], + ]; + + /** + * Register a pressed key and apply modifiers. + * + * @param string $key pressed key + * + * @return void + */ + protected function onKeyPress(string $key): void + { + $this->setCurrentKey($key); + + if (true === $this->isKeyPressed()) { + return; + } + + $this->pressedKeys[$this->currentKey] = true; + + $this->toggleModifierFromKey(); + } + + /** + * Register a released key and remove modifiers. + * + * @param string $key released key + * + * @return void + */ + protected function onKeyRelease(string $key): void + { + $this->setCurrentKey($key); + + if (false === $this->isKeyPressed()) { + return; + } + + unset($this->pressedKeys[$this->currentKey]); + + $this->toggleModifierFromKey(); + } + + /** + * Check the current key against the list of aliases. + * If it match, try to add or remove its bits to the modifier. + * + * @see self::$keyAliases + * @see self::$modifiers + * + * @return void + */ + protected function toggleModifierFromKey(): void + { + $key = \strtolower($this->currentKey); + + foreach ($this->keyAliases as $modifier => $aliases) { + if (true === \in_array($key, $aliases)) { + $this->toggleModifier($modifier); + break; + } + } + } + + /** + * Perform bit operations to add or remove bits from the modifier. + * + * Examples: + * + * 0001 + * | 0100 + * = 0101 + * + * 0101 + * & 0100 + * = 0100 + * + * 0101 + * & 0010 + * = 0000 + * + * @see self::$modifiers + * + * @return void + */ + protected function toggleModifier(int $bit): void + { + if (($this->modifiers & $bit) === $bit) { + $this->modifiers &= ~$bit; + + return; + } + + $this->modifiers |= $bit; + } + + /** + * Check if the current key was pressed and not released yet. + * + * @return bool true if they key is listed as pressed + */ + protected function isKeyPressed(): bool + { + return \array_key_exists($this->currentKey, $this->pressedKeys); + } + + /** + * Return the current key code. + * + * @return int the key code + */ + public function getKeyCode(): int + { + return \ord($this->currentKey); + } + + /** + * Return the current bit modifier. + * The browser expects to receive this value as int. + * + * @return int current bit modifier + */ + public function getModifiers(): int + { + return $this->modifiers; + } + + /** + * Return the current key being processed. + * + * @return string the current key + */ + public function getCurrentKey(): string + { + return $this->currentKey; + } + + /** + * Return the list of unique pressed keys that were not released yet. + * + * @return array list of pressed keys + */ + public function getPressedKeys(): array + { + return $this->pressedKeys; + } + + /** + * Set a key as the current key. + * + * Single character keys must be in uppercase, otherwhie things like ctrl + v won't work. + * Triming the string will also prevent future mistakes during normal usage. + * + * @param string $key key to be set as current + * + * @return void + */ + protected function setCurrentKey(string $key): void + { + $this->currentKey = \ucfirst(\trim($key)); + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Input/Mouse.php b/pandora_console/vendor/chrome-php/chrome/src/Input/Mouse.php new file mode 100644 index 0000000000..49526d150f --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Input/Mouse.php @@ -0,0 +1,392 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium\Input; + +use HeadlessChromium\Communication\Message; +use HeadlessChromium\Dom\Selector\CssSelector; +use HeadlessChromium\Dom\Selector\Selector; +use HeadlessChromium\Exception\ElementNotFoundException; +use HeadlessChromium\Exception\JavascriptException; +use HeadlessChromium\Page; +use HeadlessChromium\Utils; + +class Mouse +{ + public const BUTTON_LEFT = 'left'; + public const BUTTON_NONE = 'none'; + public const BUTTON_RIGHT = 'right'; + public const BUTTON_MIDDLE = 'middle'; + + /** + * @var Page + */ + protected $page; + + protected $x = 0; + protected $y = 0; + + protected $button = self::BUTTON_NONE; + + /** + * @param Page $page + */ + public function __construct(Page $page) + { + $this->page = $page; + } + + /** + * @param int $x + * @param int $y + * @param array|null $options + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function move(int $x, int $y, array $options = null) + { + $this->page->assertNotClosed(); + + // get origin of the move + $originX = $this->x; + $originY = $this->y; + + // set new position after move + $this->x = $x; + $this->y = $y; + + // number of steps to achieve the move + $steps = $options['steps'] ?? 1; + if ($steps <= 0) { + throw new \InvalidArgumentException('options "steps" for mouse move must be a positive integer'); + } + + // move + for ($i = 1; $i <= $steps; ++$i) { + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchMouseEvent', [ + 'x' => $originX + ($this->x - $originX) * ($i / $steps), + 'y' => $originY + ($this->y - $originY) * ($i / $steps), + 'type' => 'mouseMoved', + ])); + } + + return $this; + } + + /** + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + */ + public function press(array $options = null) + { + $this->page->assertNotClosed(); + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchMouseEvent', [ + 'x' => $this->x, + 'y' => $this->y, + 'type' => 'mousePressed', + 'button' => $options['button'] ?? self::BUTTON_LEFT, + 'clickCount' => 1, + ])); + + return $this; + } + + /** + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + */ + public function release(array $options = null) + { + $this->page->assertNotClosed(); + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchMouseEvent', [ + 'x' => $this->x, + 'y' => $this->y, + 'type' => 'mouseReleased', + 'button' => $options['button'] ?? self::BUTTON_LEFT, + 'clickCount' => 1, + ])); + + return $this; + } + + /** + * @param array|null $options + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + */ + public function click(array $options = null) + { + $this->press($options); + $this->release($options); + + return $this; + } + + /** + * Scroll up using the mouse wheel. + * + * @param int $distance Distance in pixels + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function scrollUp(int $distance) + { + return $this->scroll((-1 * \abs($distance))); + } + + /** + * Scroll down using the mouse wheel. + * + * @param int $distance Distance in pixels + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this + */ + public function scrollDown(int $distance) + { + return $this->scroll(\abs($distance)); + } + + /** + * Scroll a positive or negative distance using the mouseWheel event type. + * + * @param int $distanceY Distance in pixels for the Y axis + * @param int $distanceX (optional) Distance in pixels for the X axis + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * @throws \HeadlessChromium\Exception\OperationTimedOut + * + * @return $this + */ + private function scroll(int $distanceY, int $distanceX = 0): self + { + $this->page->assertNotClosed(); + + $scrollableArea = $this->page->getLayoutMetrics()->getCssContentSize(); + $visibleArea = $this->page->getLayoutMetrics()->getCssVisualViewport(); + + $maximumX = $scrollableArea['width'] - $visibleArea['clientWidth']; + $maximumY = $scrollableArea['height'] - $visibleArea['clientHeight']; + + $distanceX = $this->getMaximumDistance($distanceX, $visibleArea['pageX'], $maximumX); + $distanceY = $this->getMaximumDistance($distanceY, $visibleArea['pageY'], $maximumY); + + $targetX = $visibleArea['pageX'] + $distanceX; + $targetY = $visibleArea['pageY'] + $distanceY; + + // make sure the mouse is on the screen + $this->move($this->x, $this->y); + + // scroll + $this->page->getSession()->sendMessageSync(new Message('Input.dispatchMouseEvent', [ + 'type' => 'mouseWheel', + 'x' => $this->x, + 'y' => $this->y, + 'deltaX' => $distanceX, + 'deltaY' => $distanceY, + ])); + + // wait until the scroll is done + Utils::tryWithTimeout(30000 * 1000, $this->waitForScroll($targetX, $targetY)); + + // set new position after move + $this->x += $distanceX; + $this->y += $distanceY; + + return $this; + } + + /** + * Scroll in both X and Y axis until the given boundaries fit in the screen. + * + * This method currently scrolls only to right and bottom. If the desired element is outside the visible screen + * to the left or top, thie method will not work. Its visibility will stay private until it works for both cases. + * + * @param int $right The element right boundary + * @param int $bottom The element bottom boundary + * + * @return $this + */ + private function scrollToBoundary(int $right, int $bottom): self + { + $visibleArea = $this->page->getLayoutMetrics()->getCssLayoutViewport(); + + $distanceX = $distanceY = 0; + + if ($right > $visibleArea['clientWidth']) { + $distanceX = $right - $visibleArea['clientWidth']; + } + + if ($bottom > $visibleArea['clientHeight']) { + $distanceY = $bottom - $visibleArea['clientHeight']; + } + + return $this->scroll($distanceY, $distanceX); + } + + /** + * Find an element and move the mouse to a random position over it. + * + * The search could result in several elements. The $position param can be used to select a specific element. + * The given position can only be between 1 and the maximum number or elements. It will be adjusted to the + * minimum and maximum values if needed. + * + * Example: + * $page->mouse()->find('#a'): + * $page->mouse()->find('.a', 2); + * + * @see https://developer.mozilla.org/docs/Web/API/Document/querySelector + * + * @param string $selectors selectors to use with document.querySelector + * @param int $position (optional) which element of the result set should be used + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * @throws \HeadlessChromium\Exception\ElementNotFoundException + * + * @return $this + */ + public function find(string $selectors, int $position = 1): self + { + $this->findElement(new CssSelector($selectors), $position); + + return $this; + } + + /** + * Find an element and move the mouse to a random position over it. + * + * The search could result in several elements. The $position param can be used to select a specific element. + * The given position can only be between 1 and the maximum number or elements. It will be adjusted to the + * minimum and maximum values if needed. + * + * Example: + * $page->mouse()->findElement(new CssSelector('#a')): + * $page->mouse()->findElement(new CssSelector('.a'), 2); + * $page->mouse()->findElement(new XPathSelector('//*[@id="a"]'), 2); + * + * @param Selector $selector selector to use + * @param int $position (optional) which element of the result set should be used + * + * @throws \HeadlessChromium\Exception\CommunicationException + * @throws \HeadlessChromium\Exception\NoResponseAvailable + * @throws \HeadlessChromium\Exception\ElementNotFoundException + * + * @return $this + */ + public function findElement(Selector $selector, int $position = 1): self + { + $this->page->assertNotClosed(); + + try { + $element = Utils::getElementPositionFromPage($this->page, $selector, $position); + } catch (JavascriptException $exception) { + throw new ElementNotFoundException('The search for "'.$selector->expressionCount().'" returned no result.'); + } + + if (false === \array_key_exists('x', $element)) { + throw new ElementNotFoundException('The search for "'.$selector->expressionFindOne($position).'" returned an element with no position.'); + } + + $rightBoundary = \floor($element['right']); + $bottomBoundary = \floor($element['bottom']); + + $this->scrollToBoundary($rightBoundary, $bottomBoundary); + + $visibleArea = $this->page->getLayoutMetrics()->getLayoutViewport(); + + $offsetX = $visibleArea['pageX']; + $offsetY = $visibleArea['pageY']; + $minX = $element['left'] - $offsetX; + $minY = $element['top'] - $offsetY; + + $positionX = \floor($minX + (($rightBoundary - $offsetX) - $minX) / 2); + $positionY = \ceil($minY + (($bottomBoundary - $offsetY) - $minY) / 2); + + $this->move($positionX, $positionY); + + return $this; + } + + /** + * Get the maximum distance to scroll a page. + * + * @param int $distance Distance to scroll, positive or negative + * @param int $current Current position + * @param int $maximum Maximum possible distance + * + * @return int allowed distance to scroll + */ + private function getMaximumDistance(int $distance, int $current, int $maximum): int + { + $result = $current + $distance; + + if ($result < 0) { + return $distance + \abs($result); + } + + if ($result > $maximum) { + return $maximum - $current; + } + + return $distance; + } + + /** + * Wait for the browser to process the scroll command. + * + * Return the number of microseconds to wait before trying again or true in case of success. + * + * @see \HeadlessChromium\Utils::tryWithTimeout + * + * @param int $targetX + * @param int $targetY + * + * @throws \HeadlessChromium\Exception\OperationTimedOut + * + * @return bool|\Generator + */ + private function waitForScroll(int $targetX, int $targetY) + { + while (true) { + $visibleArea = $this->page->getLayoutMetrics()->getCssVisualViewport(); + + if ($visibleArea['pageX'] === $targetX && $visibleArea['pageY'] === $targetY) { + return true; + } + + yield 1000; + } + } + + /** + * Get the current mouse position. + * + * @return array [x, y] + */ + public function getPosition(): array + { + return [ + 'x' => $this->x, + 'y' => $this->y, + ]; + } +} diff --git a/pandora_console/vendor/chrome-php/chrome/src/Page.php b/pandora_console/vendor/chrome-php/chrome/src/Page.php new file mode 100644 index 0000000000..6a10c81b9c --- /dev/null +++ b/pandora_console/vendor/chrome-php/chrome/src/Page.php @@ -0,0 +1,1050 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace HeadlessChromium; + +use HeadlessChromium\Communication\Message; +use HeadlessChromium\Communication\Session; +use HeadlessChromium\Communication\Target; +use HeadlessChromium\Cookies\Cookie; +use HeadlessChromium\Cookies\CookiesCollection; +use HeadlessChromium\Dom\Dom; +use HeadlessChromium\Dom\Selector\CssSelector; +use HeadlessChromium\Exception\CommunicationException; +use HeadlessChromium\Exception\InvalidTimezoneId; +use HeadlessChromium\Exception\JavascriptException; +use HeadlessChromium\Exception\NoResponseAvailable; +use HeadlessChromium\Exception\TargetDestroyed; +use HeadlessChromium\Input\Keyboard; +use HeadlessChromium\Input\Mouse; +use HeadlessChromium\PageUtils\CookiesGetter; +use HeadlessChromium\PageUtils\PageEvaluation; +use HeadlessChromium\PageUtils\PageLayoutMetrics; +use HeadlessChromium\PageUtils\PageNavigation; +use HeadlessChromium\PageUtils\PagePdf; +use HeadlessChromium\PageUtils\PageScreenshot; +use HeadlessChromium\PageUtils\ResponseWaiter; + +class Page +{ + public const DOM_CONTENT_LOADED = 'DOMContentLoaded'; + public const LOAD = 'load'; + public const NETWORK_IDLE = 'networkIdle'; + + /** + * @var Target + */ + protected $target; + + /** + * @var FrameManager + */ + protected $frameManager; + + /** + * @var Mouse|null + */ + protected $mouse; + + /** + * @var Keyboard|null + */ + protected $keyboard; + + /** + * Page constructor. + * + * @param Target $target + * @param array $frameTree + */ + public function __construct(Target $target, array $frameTree) + { + $this->target = $target; + $this->frameManager = new FrameManager($this, $frameTree); + } + + /** + * Adds a script to be evaluated upon page navigation. + * + * @param string $script + * @param array $options + * - onLoad: defer script execution after page has loaded (useful for scripts that require the dom to be populated) + * + * @throws CommunicationException + * @throws NoResponseAvailable + */ + public function addPreScript(string $script, array $options = []): void + { + // defer script execution + if (isset($options['onLoad']) && $options['onLoad']) { + $script = 'window.onload = () => {'.$script.'}'; + } + + // add script + $this->getSession()->sendMessageSync( + new Message('Page.addScriptToEvaluateOnNewDocument', ['source' => $script]) + ); + } + + /** + * Retrieves layout metrics of the page. + * + * Example: + * + * ```php + * $metrics = $page->getLayoutMetrics(); + * $contentSize = $metrics->getContentSize(); + * ``` + * + * @throws CommunicationException + * + * @return PageLayoutMetrics + */ + public function getLayoutMetrics() + { + $this->assertNotClosed(); + + $reader = $this->getSession()->sendMessage( + new Message('Page.getLayoutMetrics') + ); + + return new PageLayoutMetrics($reader); + } + + /** + * @return FrameManager + */ + public function getFrameManager(): FrameManager + { + $this->assertNotClosed(); + + return $this->frameManager; + } + + /** + * Get the session this page is attached to. + * + * @return Session + */ + public function getSession(): Session + { + $this->assertNotClosed(); + + return $this->target->getSession(); + } + + /** + * Sets the HTTP header necessary for basic authentication. + * + * @param string $username + * @param string $password + */ + public function setBasicAuthHeader(string $username, string $password): void + { + $header = \base64_encode($username.':'.$password); + $this->setExtraHTTPHeaders([ + 'Authorization' => 'Basic '.$header, + ]); + } + + /** + * Sets the path to save downloaded files. + * + * @param string $path + */ + public function setDownloadPath(string $path): void + { + $this->getSession()->sendMessage(new Message( + 'Page.setDownloadBehavior', + ['behavior' => 'allow', 'downloadPath' => $path] + )); + } + + /** + * Set extra http headers. + * + * If headers are not passed, all instances of Page::class will use global settings from the BrowserFactory::class + * + * @see https://chromedevtools.github.io/devtools-protocol/1-2/Network/#method-setExtraHTTPHeaders + * + * @param array $headers + * + * @throws CommunicationException + */ + public function setExtraHTTPHeaders(array $headers = []): void + { + $response = $this->getSession()->sendMessage(new Message( + 'Network.setExtraHTTPHeaders', + ['headers' => $headers] + ))->waitForResponse(); + + if (false === $response->isSuccessful()) { + throw new CommunicationException($response->getErrorMessage()); + } + } + + /** + * @param string $url + * @param array $options + * - strict: make waitForNAvigation to fail if a new navigation is initiated. Default: false + * + * @throws CommunicationException + * + * @return PageNavigation + */ + public function navigate(string $url, array $options = []) + { + $this->assertNotClosed(); + + return new PageNavigation($this, $url, $options['strict'] ?? false); + } + + /** + * Evaluates the given string in the page context. + * + * Example: + * + * ```php + * $evaluation = $page->evaluate('document.querySelector("title").innerHTML'); + * $response = $evaluation->getReturnValue(); + * ``` + * + * @param string $expression + * + * @throws CommunicationException + * + * @return PageEvaluation + */ + public function evaluate(string $expression) + { + $this->assertNotClosed(); + + $currentLoaderId = $this->frameManager->getMainFrame()->getLatestLoaderId(); + $reader = $this->getSession()->sendMessage( + new Message( + 'Runtime.evaluate', + [ + 'awaitPromise' => true, + 'returnByValue' => true, + 'expression' => $expression, + 'userGesture' => true, + ] + ) + ); + + return new PageEvaluation($reader, $currentLoaderId, $this); + } + + /** + * Call a js function with the given argument in the page context. + * + * Example: + * + * ```php + * $evaluation = $page->callFunction('function(a, b) {return a + b}', [1, 2]); + * + * echo $evaluation->getReturnValue(); + * // 3 + * ``` + * + * @param string $functionDeclaration + * @param array $arguments + * + * @throws CommunicationException + * + * @return PageEvaluation + */ + public function callFunction(string $functionDeclaration, array $arguments = []): PageEvaluation + { + $this->assertNotClosed(); + + $currentLoaderId = $this->frameManager->getMainFrame()->getLatestLoaderId(); + $executionContextId = $this->frameManager->getMainFrame()->getExecutionContextId(); + $reader = $this->getSession()->sendMessage( + new Message( + 'Runtime.callFunctionOn', + [ + 'functionDeclaration' => $functionDeclaration, + 'arguments' => \array_map(function ($arg) { + return [ + 'value' => $arg, + ]; + }, $arguments), + 'executionContextId' => $executionContextId, + 'awaitPromise' => true, + 'returnByValue' => true, + 'userGesture' => true, + ] + ) + ); + + return new PageEvaluation($reader, $currentLoaderId, $this); + } + + /** + * Add a script tag to the page (ie. '); + } elseif ($format === self::FORMAT_JS) { + static::writeOutput(static::generateScript()); + } + static::resetStatic(); + } + } + + public function close(): void + { + self::resetStatic(); + } + + public function reset() + { + parent::reset(); + + self::resetStatic(); + } + + /** + * Forget all logged records + */ + public static function resetStatic(): void + { + static::$records = []; + } + + /** + * Wrapper for register_shutdown_function to allow overriding + */ + protected function registerShutdownFunction(): void + { + if (PHP_SAPI !== 'cli') { + register_shutdown_function(['Monolog\Handler\BrowserConsoleHandler', 'send']); + } + } + + /** + * Wrapper for echo to allow overriding + */ + protected static function writeOutput(string $str): void + { + echo $str; + } + + /** + * Checks the format of the response + * + * If Content-Type is set to application/javascript or text/javascript -> js + * If Content-Type is set to text/html, or is unset -> html + * If Content-Type is anything else -> unknown + * + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormat(): string + { + // Check content type + foreach (headers_list() as $header) { + if (stripos($header, 'content-type:') === 0) { + return static::getResponseFormatFromContentType($header); + } + } + + return self::FORMAT_HTML; + } + + /** + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormatFromContentType(string $contentType): string + { + // This handler only works with HTML and javascript outputs + // text/javascript is obsolete in favour of application/javascript, but still used + if (stripos($contentType, 'application/javascript') !== false || stripos($contentType, 'text/javascript') !== false) { + return self::FORMAT_JS; + } + + if (stripos($contentType, 'text/html') !== false) { + return self::FORMAT_HTML; + } + + return self::FORMAT_UNKNOWN; + } + + private static function generateScript(): string + { + $script = []; + foreach (static::$records as $record) { + $context = static::dump('Context', $record['context']); + $extra = static::dump('Extra', $record['extra']); + + if (empty($context) && empty($extra)) { + $script[] = static::call_array('log', static::handleStyles($record['formatted'])); + } else { + $script = array_merge( + $script, + [static::call_array('groupCollapsed', static::handleStyles($record['formatted']))], + $context, + $extra, + [static::call('groupEnd')] + ); + } + } + + return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; + } + + /** + * @return string[] + */ + private static function handleStyles(string $formatted): array + { + $args = []; + $format = '%c' . $formatted; + preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + + foreach (array_reverse($matches) as $match) { + $args[] = '"font-weight: normal"'; + $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); + + $pos = $match[0][1]; + $format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0])); + } + + $args[] = static::quote('font-weight: normal'); + $args[] = static::quote($format); + + return array_reverse($args); + } + + private static function handleCustomStyles(string $style, string $string): string + { + static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey']; + static $labels = []; + + $style = preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) { + if (trim($m[1]) === 'autolabel') { + // Format the string as a label with consistent auto assigned background color + if (!isset($labels[$string])) { + $labels[$string] = $colors[count($labels) % count($colors)]; + } + $color = $labels[$string]; + + return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px"; + } + + return $m[1]; + }, $style); + + if (null === $style) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + + return $style; + } + + /** + * @param mixed[] $dict + * @return mixed[] + */ + private static function dump(string $title, array $dict): array + { + $script = []; + $dict = array_filter($dict); + if (empty($dict)) { + return $script; + } + $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); + foreach ($dict as $key => $value) { + $value = json_encode($value); + if (empty($value)) { + $value = static::quote(''); + } + $script[] = static::call('log', static::quote('%s: %o'), static::quote((string) $key), $value); + } + + return $script; + } + + private static function quote(string $arg): string + { + return '"' . addcslashes($arg, "\"\n\\") . '"'; + } + + /** + * @param mixed $args + */ + private static function call(...$args): string + { + $method = array_shift($args); + if (!is_string($method)) { + throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true)); + } + + return static::call_array($method, $args); + } + + /** + * @param mixed[] $args + */ + private static function call_array(string $method, array $args): string + { + return 'c.' . $method . '(' . implode(', ', $args) . ');'; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 0000000000..fcce5d6309 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** @var HandlerInterface */ + protected $handler; + /** @var int */ + protected $bufferSize = 0; + /** @var int */ + protected $bufferLimit; + /** @var bool */ + protected $flushOnOverflow; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $initialized = false; + + /** + * @param HandlerInterface $handler Handler. + * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + */ + public function __construct(HandlerInterface $handler, int $bufferLimit = 0, $level = Logger::DEBUG, bool $bubble = true, bool $flushOnOverflow = false) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferLimit = $bufferLimit; + $this->flushOnOverflow = $flushOnOverflow; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($record['level'] < $this->level) { + return false; + } + + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function([$this, 'close']); + $this->initialized = true; + } + + if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { + if ($this->flushOnOverflow) { + $this->flush(); + } else { + array_shift($this->buffer); + $this->bufferSize--; + } + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->buffer[] = $record; + $this->bufferSize++; + + return false === $this->bubble; + } + + public function flush(): void + { + if ($this->bufferSize === 0) { + return; + } + + $this->handler->handleBatch($this->buffer); + $this->clear(); + } + + public function __destruct() + { + // suppress the parent behavior since we already have register_shutdown_function() + // to call close(), and the reference contained there will prevent this from being + // GC'd until the end of the request + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flush(); + + $this->handler->close(); + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + */ + public function clear(): void + { + $this->bufferSize = 0; + $this->buffer = []; + } + + public function reset() + { + $this->flush(); + + parent::reset(); + + $this->resetProcessors(); + + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php new file mode 100644 index 0000000000..234ecf614e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\ChromePHPFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * This also works out of the box with Firefox 43+ + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class ChromePHPHandler extends AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + + /** + * Version of the extension + */ + protected const VERSION = '4.0'; + + /** + * Header name + */ + protected const HEADER_NAME = 'X-ChromeLogger-Data'; + + /** + * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) + */ + protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; + + /** @var bool */ + protected static $initialized = false; + + /** + * Tracks whether we sent too much data + * + * Chrome limits the headers to 4KB, so when we sent 3KB we stop sending + * + * @var bool + */ + protected static $overflowed = false; + + /** @var mixed[] */ + protected static $json = [ + 'version' => self::VERSION, + 'columns' => ['label', 'log', 'backtrace', 'type'], + 'rows' => [], + ]; + + /** @var bool */ + protected static $sendHeaders = true; + + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); + } + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if (!$this->isWebRequest()) { + return; + } + + $messages = []; + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ChromePHPFormatter(); + } + + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + */ + protected function write(array $record): void + { + if (!$this->isWebRequest()) { + return; + } + + self::$json['rows'][] = $record['formatted']; + + $this->send(); + } + + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send(): void + { + if (self::$overflowed || !self::$sendHeaders) { + return; + } + + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? ''; + } + + $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true); + $data = base64_encode($json); + if (strlen($data) > 3 * 1024) { + self::$overflowed = true; + + $record = [ + 'message' => 'Incomplete logs, chrome header size limit reached', + 'context' => [], + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'monolog', + 'datetime' => new \DateTimeImmutable(), + 'extra' => [], + ]; + self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); + $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true); + $data = base64_encode($json); + } + + if (trim($data) !== '') { + $this->sendHeader(static::HEADER_NAME, $data); + } + } + + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content): void + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted(): bool + { + if (empty($_SERVER['HTTP_USER_AGENT'])) { + return false; + } + + return preg_match(static::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']) === 1; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php new file mode 100644 index 0000000000..5265761323 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\JsonFormatter; +use Monolog\Logger; + +/** + * CouchDB handler + * + * @author Markus Bachmann + */ +class CouchDBHandler extends AbstractProcessingHandler +{ + /** @var mixed[] */ + private $options; + + /** + * @param mixed[] $options + */ + public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + $this->options = array_merge([ + 'host' => 'localhost', + 'port' => 5984, + 'dbname' => 'logger', + 'username' => null, + 'password' => null, + ], $options); + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $basicAuth = null; + if ($this->options['username']) { + $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']); + } + + $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname']; + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'content' => $record['formatted'], + 'ignore_errors' => true, + 'max_redirects' => 0, + 'header' => 'Content-type: application/json', + ], + ]); + + if (false === @file_get_contents($url, false, $context)) { + throw new \RuntimeException(sprintf('Could not connect to %s', $url)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php new file mode 100644 index 0000000000..3535a4fcd7 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Logs to Cube. + * + * @link https://github.com/square/cube/wiki + * @author Wan Chen + * @deprecated Since 2.8.0 and 3.2.0, Cube appears abandoned and thus we will drop this handler in Monolog 4 + */ +class CubeHandler extends AbstractProcessingHandler +{ + /** @var resource|\Socket|null */ + private $udpConnection = null; + /** @var resource|\CurlHandle|null */ + private $httpConnection = null; + /** @var string */ + private $scheme; + /** @var string */ + private $host; + /** @var int */ + private $port; + /** @var string[] */ + private $acceptedSchemes = ['http', 'udp']; + + /** + * Create a Cube handler + * + * @throws \UnexpectedValueException when given url is not a valid url. + * A valid url must consist of three parts : protocol://host:port + * Only valid protocols used by Cube are http and udp + */ + public function __construct(string $url, $level = Logger::DEBUG, bool $bubble = true) + { + $urlInfo = parse_url($url); + + if ($urlInfo === false || !isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { + throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); + } + + if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) { + throw new \UnexpectedValueException( + 'Invalid protocol (' . $urlInfo['scheme'] . ').' + . ' Valid options are ' . implode(', ', $this->acceptedSchemes) + ); + } + + $this->scheme = $urlInfo['scheme']; + $this->host = $urlInfo['host']; + $this->port = (int) $urlInfo['port']; + + parent::__construct($level, $bubble); + } + + /** + * Establish a connection to an UDP socket + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when there is no socket extension + */ + protected function connectUdp(): void + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); + } + + $udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0); + if (false === $udpConnection) { + throw new \LogicException('Unable to create a socket'); + } + + $this->udpConnection = $udpConnection; + if (!socket_connect($this->udpConnection, $this->host, $this->port)) { + throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); + } + } + + /** + * Establish a connection to an http server + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when no curl extension + */ + protected function connectHttp(): void + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler'); + } + + $httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put'); + if (false === $httpConnection) { + throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); + } + + $this->httpConnection = $httpConnection; + curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST"); + curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $date = $record['datetime']; + + $data = ['time' => $date->format('Y-m-d\TH:i:s.uO')]; + unset($record['datetime']); + + if (isset($record['context']['type'])) { + $data['type'] = $record['context']['type']; + unset($record['context']['type']); + } else { + $data['type'] = $record['channel']; + } + + $data['data'] = $record['context']; + $data['data']['level'] = $record['level']; + + if ($this->scheme === 'http') { + $this->writeHttp(Utils::jsonEncode($data)); + } else { + $this->writeUdp(Utils::jsonEncode($data)); + } + } + + private function writeUdp(string $data): void + { + if (!$this->udpConnection) { + $this->connectUdp(); + } + + socket_send($this->udpConnection, $data, strlen($data), 0); + } + + private function writeHttp(string $data): void + { + if (!$this->httpConnection) { + $this->connectHttp(); + } + + if (null === $this->httpConnection) { + throw new \LogicException('No connection could be established'); + } + + curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); + curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'Content-Length: ' . strlen('['.$data.']'), + ]); + + Curl\Util::execute($this->httpConnection, 5, false); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php new file mode 100644 index 0000000000..7213e8ee23 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Curl; + +use CurlHandle; + +/** + * This class is marked as internal and it is not under the BC promise of the package. + * + * @internal + */ +final class Util +{ + /** @var array */ + private static $retriableErrorCodes = [ + CURLE_COULDNT_RESOLVE_HOST, + CURLE_COULDNT_CONNECT, + CURLE_HTTP_NOT_FOUND, + CURLE_READ_ERROR, + CURLE_OPERATION_TIMEOUTED, + CURLE_HTTP_POST_ERROR, + CURLE_SSL_CONNECT_ERROR, + ]; + + /** + * Executes a CURL request with optional retries and exception on failure + * + * @param resource|CurlHandle $ch curl handler + * @param int $retries + * @param bool $closeAfterDone + * @return bool|string @see curl_exec + */ + public static function execute($ch, int $retries = 5, bool $closeAfterDone = true) + { + while ($retries--) { + $curlResponse = curl_exec($ch); + if ($curlResponse === false) { + $curlErrno = curl_errno($ch); + + if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) { + $curlError = curl_error($ch); + + if ($closeAfterDone) { + curl_close($ch); + } + + throw new \RuntimeException(sprintf('Curl error (code %d): %s', $curlErrno, $curlError)); + } + + continue; + } + + if ($closeAfterDone) { + curl_close($ch); + } + + return $curlResponse; + } + + return false; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php new file mode 100644 index 0000000000..9b85ae7edc --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Simple handler wrapper that deduplicates log records across multiple requests + * + * It also includes the BufferHandler functionality and will buffer + * all messages until the end of the request or flush() is called. + * + * This works by storing all log records' messages above $deduplicationLevel + * to the file specified by $deduplicationStore. When further logs come in at the end of the + * request (or when flush() is called), all those above $deduplicationLevel are checked + * against the existing stored logs. If they match and the timestamps in the stored log is + * not older than $time seconds, the new log record is discarded. If no log record is new, the + * whole data set is discarded. + * + * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers + * that send messages to people, to avoid spamming with the same message over and over in case of + * a major component failure like a database server being down which makes all requests fail in the + * same way. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class DeduplicationHandler extends BufferHandler +{ + /** + * @var string + */ + protected $deduplicationStore; + + /** + * @var Level + */ + protected $deduplicationLevel; + + /** + * @var int + */ + protected $time; + + /** + * @var bool + */ + private $gc = false; + + /** + * @param HandlerInterface $handler Handler. + * @param string $deduplicationStore The file/path where the deduplication log should be kept + * @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes + * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel + */ + public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true) + { + parent::__construct($handler, 0, Logger::DEBUG, $bubble, false); + + $this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore; + $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); + $this->time = $time; + } + + public function flush(): void + { + if ($this->bufferSize === 0) { + return; + } + + $passthru = null; + + foreach ($this->buffer as $record) { + if ($record['level'] >= $this->deduplicationLevel) { + $passthru = $passthru || !$this->isDuplicate($record); + if ($passthru) { + $this->appendRecord($record); + } + } + } + + // default of null is valid as well as if no record matches duplicationLevel we just pass through + if ($passthru === true || $passthru === null) { + $this->handler->handleBatch($this->buffer); + } + + $this->clear(); + + if ($this->gc) { + $this->collectLogs(); + } + } + + /** + * @phpstan-param Record $record + */ + private function isDuplicate(array $record): bool + { + if (!file_exists($this->deduplicationStore)) { + return false; + } + + $store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!is_array($store)) { + return false; + } + + $yesterday = time() - 86400; + $timestampValidity = $record['datetime']->getTimestamp() - $this->time; + $expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']); + + for ($i = count($store) - 1; $i >= 0; $i--) { + list($timestamp, $level, $message) = explode(':', $store[$i], 3); + + if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { + return true; + } + + if ($timestamp < $yesterday) { + $this->gc = true; + } + } + + return false; + } + + private function collectLogs(): void + { + if (!file_exists($this->deduplicationStore)) { + return; + } + + $handle = fopen($this->deduplicationStore, 'rw+'); + + if (!$handle) { + throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore); + } + + flock($handle, LOCK_EX); + $validLogs = []; + + $timestampValidity = time() - $this->time; + + while (!feof($handle)) { + $log = fgets($handle); + if ($log && substr($log, 0, 10) >= $timestampValidity) { + $validLogs[] = $log; + } + } + + ftruncate($handle, 0); + rewind($handle); + foreach ($validLogs as $log) { + fwrite($handle, $log); + } + + flock($handle, LOCK_UN); + fclose($handle); + + $this->gc = false; + } + + /** + * @phpstan-param Record $record + */ + private function appendRecord(array $record): void + { + file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php new file mode 100644 index 0000000000..ebd52c3a0d --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; +use Doctrine\CouchDB\CouchDBClient; + +/** + * CouchDB handler for Doctrine CouchDB ODM + * + * @author Markus Bachmann + */ +class DoctrineCouchDBHandler extends AbstractProcessingHandler +{ + /** @var CouchDBClient */ + private $client; + + public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true) + { + $this->client = $client; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->client->postDocument($record['formatted']); + } + + protected function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php new file mode 100644 index 0000000000..21840bf60b --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Aws\Sdk; +use Aws\DynamoDb\DynamoDbClient; +use Monolog\Formatter\FormatterInterface; +use Aws\DynamoDb\Marshaler; +use Monolog\Formatter\ScalarFormatter; +use Monolog\Logger; + +/** + * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) + * + * @link https://github.com/aws/aws-sdk-php/ + * @author Andrew Lawson + */ +class DynamoDbHandler extends AbstractProcessingHandler +{ + public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; + + /** + * @var DynamoDbClient + */ + protected $client; + + /** + * @var string + */ + protected $table; + + /** + * @var int + */ + protected $version; + + /** + * @var Marshaler + */ + protected $marshaler; + + public function __construct(DynamoDbClient $client, string $table, $level = Logger::DEBUG, bool $bubble = true) + { + /** @phpstan-ignore-next-line */ + if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) { + $this->version = 3; + $this->marshaler = new Marshaler; + } else { + $this->version = 2; + } + + $this->client = $client; + $this->table = $table; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $filtered = $this->filterEmptyFields($record['formatted']); + if ($this->version === 3) { + $formatted = $this->marshaler->marshalItem($filtered); + } else { + /** @phpstan-ignore-next-line */ + $formatted = $this->client->formatAttributes($filtered); + } + + $this->client->putItem([ + 'TableName' => $this->table, + 'Item' => $formatted, + ]); + } + + /** + * @param mixed[] $record + * @return mixed[] + */ + protected function filterEmptyFields(array $record): array + { + return array_filter($record, function ($value) { + return !empty($value) || false === $value || 0 === $value; + }); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ScalarFormatter(self::DATE_FORMAT); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php new file mode 100644 index 0000000000..fc92ca42d5 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Elastica\Document; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticaFormatter; +use Monolog\Logger; +use Elastica\Client; +use Elastica\Exception\ExceptionInterface; + +/** + * Elastic Search handler + * + * Usage example: + * + * $client = new \Elastica\Client(); + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', Types have been removed in Elastica 7 + * ); + * $handler = new ElasticaHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Jelle Vink + */ +class ElasticaHandler extends AbstractProcessingHandler +{ + /** + * @var Client + */ + protected $client; + + /** + * @var mixed[] Handler config options + */ + protected $options = []; + + /** + * @param Client $client Elastica Client object + * @param mixed[] $options Handler configuration + */ + public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + [ + 'index' => 'monolog', // Elastic index name + 'type' => 'record', // Elastic document type + 'ignore_error' => false, // Suppress Elastica exceptions + ], + $options + ); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->bulkSend([$record['formatted']]); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($formatter instanceof ElasticaFormatter) { + return parent::setFormatter($formatter); + } + + throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter'); + } + + /** + * @return mixed[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ElasticaFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param Document[] $documents + * + * @throws \RuntimeException + */ + protected function bulkSend(array $documents): void + { + try { + $this->client->addDocuments($documents); + } catch (ExceptionInterface $e) { + if (!$this->options['ignore_error']) { + throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); + } + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php new file mode 100644 index 0000000000..e88375c0e5 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Elastic\Elasticsearch\Response\Elasticsearch; +use Throwable; +use RuntimeException; +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticsearchFormatter; +use InvalidArgumentException; +use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException; +use Elasticsearch\Client; +use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException; +use Elastic\Elasticsearch\Client as Client8; + +/** + * Elasticsearch handler + * + * @link https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html + * + * Simple usage example: + * + * $client = \Elasticsearch\ClientBuilder::create() + * ->setHosts($hosts) + * ->build(); + * + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', + * ); + * $handler = new ElasticsearchHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Avtandil Kikabidze + */ +class ElasticsearchHandler extends AbstractProcessingHandler +{ + /** + * @var Client|Client8 + */ + protected $client; + + /** + * @var mixed[] Handler config options + */ + protected $options = []; + + /** + * @var bool + */ + private $needsType; + + /** + * @param Client|Client8 $client Elasticsearch Client object + * @param mixed[] $options Handler configuration + */ + public function __construct($client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + if (!$client instanceof Client && !$client instanceof Client8) { + throw new \TypeError('Elasticsearch\Client or Elastic\Elasticsearch\Client instance required'); + } + + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + [ + 'index' => 'monolog', // Elastic index name + 'type' => '_doc', // Elastic document type + 'ignore_error' => false, // Suppress Elasticsearch exceptions + ], + $options + ); + + if ($client instanceof Client8 || $client::VERSION[0] === '7') { + $this->needsType = false; + // force the type to _doc for ES8/ES7 + $this->options['type'] = '_doc'; + } else { + $this->needsType = true; + } + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->bulkSend([$record['formatted']]); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($formatter instanceof ElasticsearchFormatter) { + return parent::setFormatter($formatter); + } + + throw new InvalidArgumentException('ElasticsearchHandler is only compatible with ElasticsearchFormatter'); + } + + /** + * Getter options + * + * @return mixed[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ElasticsearchFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param array[] $records Records + _index/_type keys + * @throws \RuntimeException + */ + protected function bulkSend(array $records): void + { + try { + $params = [ + 'body' => [], + ]; + + foreach ($records as $record) { + $params['body'][] = [ + 'index' => $this->needsType ? [ + '_index' => $record['_index'], + '_type' => $record['_type'], + ] : [ + '_index' => $record['_index'], + ], + ]; + unset($record['_index'], $record['_type']); + + $params['body'][] = $record; + } + + /** @var Elasticsearch */ + $responses = $this->client->bulk($params); + + if ($responses['errors'] === true) { + throw $this->createExceptionFromResponses($responses); + } + } catch (Throwable $e) { + if (! $this->options['ignore_error']) { + throw new RuntimeException('Error sending messages to Elasticsearch', 0, $e); + } + } + } + + /** + * Creates elasticsearch exception from responses array + * + * Only the first error is converted into an exception. + * + * @param mixed[]|Elasticsearch $responses returned by $this->client->bulk() + */ + protected function createExceptionFromResponses($responses): Throwable + { + foreach ($responses['items'] ?? [] as $item) { + if (isset($item['index']['error'])) { + return $this->createExceptionFromError($item['index']['error']); + } + } + + if (class_exists(ElasticInvalidArgumentException::class)) { + return new ElasticInvalidArgumentException('Elasticsearch failed to index one or more records.'); + } + + return new ElasticsearchRuntimeException('Elasticsearch failed to index one or more records.'); + } + + /** + * Creates elasticsearch exception from error array + * + * @param mixed[] $error + */ + protected function createExceptionFromError(array $error): Throwable + { + $previous = isset($error['caused_by']) ? $this->createExceptionFromError($error['caused_by']) : null; + + if (class_exists(ElasticInvalidArgumentException::class)) { + return new ElasticInvalidArgumentException($error['type'] . ': ' . $error['reason'], 0, $previous); + } + + return new ElasticsearchRuntimeException($error['type'] . ': ' . $error['reason'], 0, $previous); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php new file mode 100644 index 0000000000..f2e22036bd --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores to PHP error_log() handler. + * + * @author Elan Ruusamäe + */ +class ErrorLogHandler extends AbstractProcessingHandler +{ + public const OPERATING_SYSTEM = 0; + public const SAPI = 4; + + /** @var int */ + protected $messageType; + /** @var bool */ + protected $expandNewlines; + + /** + * @param int $messageType Says where the error should go. + * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + */ + public function __construct(int $messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, bool $bubble = true, bool $expandNewlines = false) + { + parent::__construct($level, $bubble); + + if (false === in_array($messageType, self::getAvailableTypes(), true)) { + $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true)); + + throw new \InvalidArgumentException($message); + } + + $this->messageType = $messageType; + $this->expandNewlines = $expandNewlines; + } + + /** + * @return int[] With all available types + */ + public static function getAvailableTypes(): array + { + return [ + self::OPERATING_SYSTEM, + self::SAPI, + ]; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->expandNewlines) { + error_log((string) $record['formatted'], $this->messageType); + + return; + } + + $lines = preg_split('{[\r\n]+}', (string) $record['formatted']); + if ($lines === false) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode)); + } + foreach ($lines as $line) { + error_log($line, $this->messageType); + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php new file mode 100644 index 0000000000..d4e234ce05 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Throwable; + +/** + * Forwards records to at most one handler + * + * If a handler fails, the exception is suppressed and the record is forwarded to the next handler. + * + * As soon as one handler handles a record successfully, the handling stops there. + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class FallbackGroupHandler extends GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + break; + } catch (Throwable $e) { + // What throwable? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + break; + } catch (Throwable $e) { + // What throwable? + } + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php new file mode 100644 index 0000000000..718f17ef19 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; +use Psr\Log\LogLevel; + +/** + * Simple handler wrapper that filters records based on a list of levels + * + * It can be configured with an exact list of levels to allow, or a min/max level. + * + * @author Hennadiy Verkh + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * Handler or factory callable($record, $this) + * + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + + /** + * Minimum level for logs that are passed to handler + * + * @var int[] + * @phpstan-var array + */ + protected $acceptedLevels; + + /** + * Whether the messages that are handled can bubble up the stack or not + * + * @var bool + */ + protected $bubble; + + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). + * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided + * @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true) + { + $this->handler = $handler; + $this->bubble = $bubble; + $this->setAcceptedLevels($minLevelOrList, $maxLevel); + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * @phpstan-return array + */ + public function getAcceptedLevels(): array + { + return array_flip($this->acceptedLevels); + } + + /** + * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided + * @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self + { + if (is_array($minLevelOrList)) { + $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList); + } else { + $minLevelOrList = Logger::toMonologLevel($minLevelOrList); + $maxLevel = Logger::toMonologLevel($maxLevel); + $acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) { + return $level >= $minLevelOrList && $level <= $maxLevel; + })); + } + $this->acceptedLevels = array_flip($acceptedLevels); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return isset($this->acceptedLevels[$record['level']]); + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->getHandler($record)->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $filtered = []; + foreach ($records as $record) { + if ($this->isHandling($record)) { + $filtered[] = $record; + } + } + + if (count($filtered) > 0) { + $this->getHandler($filtered[count($filtered) - 1])->handleBatch($filtered); + } + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + public function reset() + { + $this->resetProcessors(); + + if ($this->getHandler() instanceof ResettableInterface) { + $this->getHandler()->reset(); + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100644 index 0000000000..0aa5607b15 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record): bool; +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php new file mode 100644 index 0000000000..7b9abb582e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Channel and Error level based monolog activation strategy. Allows to trigger activation + * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except + * for records of the 'sql' channel; those should trigger activation on level 'WARN'. + * + * Example: + * + * + * $activationStrategy = new ChannelLevelActivationStrategy( + * Logger::CRITICAL, + * array( + * 'request' => Logger::ALERT, + * 'sensitive' => Logger::ERROR, + * ) + * ); + * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); + * + * + * @author Mike Meessen + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ChannelLevelActivationStrategy implements ActivationStrategyInterface +{ + /** + * @var Level + */ + private $defaultActionLevel; + + /** + * @var array + */ + private $channelToActionLevel; + + /** + * @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any + * @param array $channelToActionLevel An array that maps channel names to action levels. + * + * @phpstan-param array $channelToActionLevel + * @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel + */ + public function __construct($defaultActionLevel, array $channelToActionLevel = []) + { + $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); + $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel); + } + + /** + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record): bool + { + if (isset($this->channelToActionLevel[$record['channel']])) { + return $record['level'] >= $this->channelToActionLevel[$record['channel']]; + } + + return $record['level'] >= $this->defaultActionLevel; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000000..5ec88eab6e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + /** + * @var Level + */ + private $actionLevel; + + /** + * @param int|string $actionLevel Level or name or value + * + * @phpstan-param Level|LevelName|LogLevel::* $actionLevel + */ + public function __construct($actionLevel) + { + $this->actionLevel = Logger::toMonologLevel($actionLevel); + } + + public function isHandlerActivated(array $record): bool + { + return $record['level'] >= $this->actionLevel; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php new file mode 100644 index 0000000000..0627b44514 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; +use Psr\Log\LogLevel; + +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * You can then have a passthruLevel as well which means that at the end of the request, + * even if it did not get activated, it will still send through log records of e.g. at least a + * warning level. + * + * You can find the various activation strategies in the + * Monolog\Handler\FingersCrossed\ namespace. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + /** @var ActivationStrategyInterface */ + protected $activationStrategy; + /** @var bool */ + protected $buffering = true; + /** @var int */ + protected $bufferSize; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $stopBuffering; + /** + * @var ?int + * @phpstan-var ?Level + */ + protected $passthruLevel; + /** @var bool */ + protected $bubble; + + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). + * @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $passthruLevel + * @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy + */ + public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + + // convert simple int activationStrategy to an object + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + + if ($passthruLevel !== null) { + $this->passthruLevel = Logger::toMonologLevel($passthruLevel); + } + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return true; + } + + /** + * Manually activate this logger regardless of the activation strategy + */ + public function activate(): void + { + if ($this->stopBuffering) { + $this->buffering = false; + } + + $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer); + $this->buffer = []; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + $this->activate(); + } + } else { + $this->getHandler($record)->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flushBuffer(); + + $this->getHandler()->close(); + } + + public function reset() + { + $this->flushBuffer(); + + $this->resetProcessors(); + + if ($this->getHandler() instanceof ResettableInterface) { + $this->getHandler()->reset(); + } + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + * + * It also resets the handler to its initial buffering state. + */ + public function clear(): void + { + $this->buffer = []; + $this->reset(); + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + private function flushBuffer(): void + { + if (null !== $this->passthruLevel) { + $level = $this->passthruLevel; + $this->buffer = array_filter($this->buffer, function ($record) use ($level) { + return $record['level'] >= $level; + }); + if (count($this->buffer) > 0) { + $this->getHandler(end($this->buffer))->handleBatch($this->buffer); + } + } + + $this->buffer = []; + $this->buffering = true; + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000000..72718de631 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\WildfireFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FirePHPHandler extends AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + + /** + * WildFire JSON header message format + */ + protected const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + + /** + * FirePHP structure for parsing messages & their presentation + */ + protected const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + protected const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + + /** + * Header prefix for Wildfire to recognize & parse headers + */ + protected const HEADER_PREFIX = 'X-Wf'; + + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + * @var bool + */ + protected static $initialized = false; + + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + + /** @var bool */ + protected static $sendHeaders = true; + + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * + * @return array Complete header string ready for the client as key and message as value + * + * @phpstan-return non-empty-array + */ + protected function createHeader(array $meta, string $message): array + { + $header = sprintf('%s-%s', static::HEADER_PREFIX, join('-', $meta)); + + return [$header => $message]; + } + + /** + * Creates message header from record + * + * @return array + * + * @phpstan-return non-empty-array + * + * @see createHeader() + * + * @phpstan-param FormattedRecord $record + */ + protected function createRecordHeader(array $record): array + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader( + [1, 1, 1, self::$messageIndex++], + $record['formatted'] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new WildfireFormatter(); + } + + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * + * @return array + */ + protected function getInitHeaders(): array + { + // Initial payload consists of required headers for Wildfire + return array_merge( + $this->createHeader(['Protocol', 1], static::PROTOCOL_URI), + $this->createHeader([1, 'Structure', 1], static::STRUCTURE_URI), + $this->createHeader([1, 'Plugin', 1], static::PLUGIN_URI) + ); + } + + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content): void + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + */ + protected function write(array $record): void + { + if (!self::$sendHeaders || !$this->isWebRequest()) { + return; + } + + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + } + + $header = $this->createRecordHeader($record); + if (trim(current($header)) !== '') { + $this->sendHeader(key($header), current($header)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted(): bool + { + if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { + return true; + } + + return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php new file mode 100644 index 0000000000..85c95b9d76 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Sends logs to Fleep.io using Webhook integrations + * + * You'll need a Fleep.io account to use this handler. + * + * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation + * @author Ando Roots + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FleepHookHandler extends SocketHandler +{ + protected const FLEEP_HOST = 'fleep.io'; + + protected const FLEEP_HOOK_URI = '/hook/'; + + /** + * @var string Webhook token (specifies the conversation where logs are sent) + */ + protected $token; + + /** + * Construct a new Fleep.io Handler. + * + * For instructions on how to create a new web hook in your conversations + * see https://fleep.io/integrations/webhooks/ + * + * @param string $token Webhook token + * @throws MissingExtensionException + */ + public function __construct( + string $token, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); + } + + $this->token = $token; + + $connectionString = 'ssl://' . static::FLEEP_HOST . ':443'; + parent::__construct( + $connectionString, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + } + + /** + * Returns the default formatter to use with this handler + * + * Overloaded to remove empty context and extra arrays from the end of the log message. + * + * @return LineFormatter + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(null, null, true, true); + } + + /** + * Handles a log record + */ + public function write(array $record): void + { + parent::write($record); + $this->closeSocket(); + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST " . static::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; + $header .= "Host: " . static::FLEEP_HOST . "\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + $dataArray = [ + 'message' => $record['formatted'], + ]; + + return http_build_query($dataArray); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php new file mode 100644 index 0000000000..b837bdb66e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FlowdockFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Sends notifications through the Flowdock push API + * + * This must be configured with a FlowdockFormatter instance via setFormatter() + * + * Notes: + * API token - Flowdock API token + * + * @author Dominik Liebler + * @see https://www.flowdock.com/api/push + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FlowdockHandler extends SocketHandler +{ + /** + * @var string + */ + protected $apiToken; + + /** + * @throws MissingExtensionException if OpenSSL is missing + */ + public function __construct( + string $apiToken, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); + } + + parent::__construct( + 'ssl://api.flowdock.com:443', + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->apiToken = $apiToken; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if (!$formatter instanceof FlowdockFormatter) { + throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + return parent::setFormatter($formatter); + } + + /** + * Gets the default formatter. + */ + protected function getDefaultFormatter(): FormatterInterface + { + throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + parent::write($record); + + $this->closeSocket(); + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + return Utils::jsonEncode($record['formatted']['flowdock']); + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; + $header .= "Host: api.flowdock.com\r\n"; + $header .= "Content-Type: application/json\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php new file mode 100644 index 0000000000..fc1693cd08 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Interface to describe loggers that have a formatter + * + * @author Jordi Boggiano + */ +interface FormattableHandlerInterface +{ + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + * @return HandlerInterface self + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface; + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(): FormatterInterface; +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php new file mode 100644 index 0000000000..b60bdce0ef --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Helper trait for implementing FormattableInterface + * + * @author Jordi Boggiano + */ +trait FormattableHandlerTrait +{ + /** + * @var ?FormatterInterface + */ + protected $formatter; + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $this->formatter = $formatter; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + + return $this->formatter; + } + + /** + * Gets the default formatter. + * + * Overwrite this if the LineFormatter is not a good default for your handler. + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000000..4ff26c4cd0 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\PublisherInterface; +use Monolog\Logger; +use Monolog\Formatter\GelfMessageFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + * @author Benjamin Zikarsky + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var PublisherInterface the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param PublisherInterface $publisher a gelf publisher object + */ + public function __construct(PublisherInterface $publisher, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->publisher = $publisher; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->publisher->publish($record['formatted']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new GelfMessageFormatter(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php new file mode 100644 index 0000000000..3c9dc4b3b7 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\ResettableInterface; + +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface +{ + use ProcessableHandlerTrait; + + /** @var HandlerInterface[] */ + protected $handlers; + /** @var bool */ + protected $bubble; + + /** + * @param HandlerInterface[] $handlers Array of Handlers. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, bool $bubble = true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + + $this->handlers = $handlers; + $this->bubble = $bubble; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } + + public function reset() + { + $this->resetProcessors(); + + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } + } + } + + public function close(): void + { + parent::close(); + + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + foreach ($this->handlers as $handler) { + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + } + } + + return $this; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Handler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Handler.php new file mode 100644 index 0000000000..34b4935dd8 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Handler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base Handler class providing basic close() support as well as handleBatch + * + * @author Jordi Boggiano + */ +abstract class Handler implements HandlerInterface +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + foreach ($records as $record) { + $this->handle($record); + } + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + } + + public function __destruct() + { + try { + $this->close(); + } catch (\Throwable $e) { + // do nothing + } + } + + public function __sleep() + { + $this->close(); + + $reflClass = new \ReflectionClass($this); + + $keys = []; + foreach ($reflClass->getProperties() as $reflProp) { + if (!$reflProp->isStatic()) { + $keys[] = $reflProp->getName(); + } + } + + return $keys; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php new file mode 100644 index 0000000000..affcc51fca --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * Handlers should still check the record levels within handle(), returning false in isHandling() + * is no guarantee that handle() will not be called, and isHandling() might not be called + * for a given record. + * + * @param array $record Partial log record containing only a level key + * + * @return bool + * + * @phpstan-param array{level: Level} $record + */ + public function isHandling(array $record): bool; + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * @param array $record The record to handle + * @return bool true means that this handler handled the record, and that bubbling is not permitted. + * false means the record was either not processed or that this handler allows bubbling. + * + * @phpstan-param Record $record + */ + public function handle(array $record): bool; + + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + * + * @phpstan-param Record[] $records + */ + public function handleBatch(array $records): void; + + /** + * Closes the handler. + * + * Ends a log cycle and frees all resources used by the handler. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * + * Implementations have to be idempotent (i.e. it should be possible to call close several times without breakage) + * and ideally handlers should be able to reopen themselves on handle() after they have been closed. + * + * This is useful at the end of a request and will be called automatically when the object + * is destroyed if you extend Monolog\Handler\Handler. + * + * If you are thinking of calling this method yourself, most likely you should be + * calling ResettableInterface::reset instead. Have a look. + */ + public function close(): void; +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php new file mode 100644 index 0000000000..d4351b9f9d --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * This simple wrapper class can be used to extend handlers functionality. + * + * Example: A custom filtering that can be applied to any handler. + * + * Inherit from this class and override handle() like this: + * + * public function handle(array $record) + * { + * if ($record meets certain conditions) { + * return false; + * } + * return $this->handler->handle($record); + * } + * + * @author Alexey Karapetov + */ +class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface +{ + /** + * @var HandlerInterface + */ + protected $handler; + + public function __construct(HandlerInterface $handler) + { + $this->handler = $handler; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $this->handler->isHandling($record); + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return $this->handler->handle($record); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $this->handler->handleBatch($records); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->handler->close(); + } + + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback): HandlerInterface + { + if ($this->handler instanceof ProcessableHandlerInterface) { + $this->handler->pushProcessor($callback); + + return $this; + } + + throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function popProcessor(): callable + { + if ($this->handler instanceof ProcessableHandlerInterface) { + return $this->handler->popProcessor(); + } + + throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class); + } + + public function reset() + { + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php new file mode 100644 index 0000000000..000ccea40e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * IFTTTHandler uses cURL to trigger IFTTT Maker actions + * + * Register a secret key and trigger/event name at https://ifttt.com/maker + * + * value1 will be the channel from monolog's Logger constructor, + * value2 will be the level name (ERROR, WARNING, ..) + * value3 will be the log record's message + * + * @author Nehal Patel + */ +class IFTTTHandler extends AbstractProcessingHandler +{ + /** @var string */ + private $eventName; + /** @var string */ + private $secretKey; + + /** + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + */ + public function __construct(string $eventName, string $secretKey, $level = Logger::ERROR, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler'); + } + + $this->eventName = $eventName; + $this->secretKey = $secretKey; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + public function write(array $record): void + { + $postData = [ + "value1" => $record["channel"], + "value2" => $record["level_name"], + "value3" => $record["message"], + ]; + $postString = Utils::jsonEncode($postData); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postString); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + "Content-Type: application/json", + ]); + + Curl\Util::execute($ch); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php new file mode 100644 index 0000000000..71f64a267d --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Inspired on LogEntriesHandler. + * + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by InsightOps + * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. + * @param bool $useSSL Whether or not SSL encryption should be used + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + string $region = 'us', + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler'); + } + + $endpoint = $useSSL + ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' + : $region . '.data.logs.insight.rapid7.com:80'; + + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->logToken = $token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php new file mode 100644 index 0000000000..25fcd1594b --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * @author Robert Kaufmann III + */ +class LogEntriesHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by LogEntries + * @param bool $useSSL Whether or not SSL encryption should be used. + * @param string $host Custom hostname to send the data to if needed + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + string $host = 'data.logentries.com', + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + + $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->logToken = $token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php new file mode 100644 index 0000000000..6d13db375a --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LogglyFormatter; +use function array_key_exists; +use CurlHandle; + +/** + * Sends errors to Loggly. + * + * @author Przemek Sobstel + * @author Adam Pancutt + * @author Gregory Barchard + */ +class LogglyHandler extends AbstractProcessingHandler +{ + protected const HOST = 'logs-01.loggly.com'; + protected const ENDPOINT_SINGLE = 'inputs'; + protected const ENDPOINT_BATCH = 'bulk'; + + /** + * Caches the curl handlers for every given endpoint. + * + * @var resource[]|CurlHandle[] + */ + protected $curlHandlers = []; + + /** @var string */ + protected $token; + + /** @var string[] */ + protected $tag = []; + + /** + * @param string $token API token supplied by Loggly + * + * @throws MissingExtensionException If the curl extension is missing + */ + public function __construct(string $token, $level = Logger::DEBUG, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler'); + } + + $this->token = $token; + + parent::__construct($level, $bubble); + } + + /** + * Loads and returns the shared curl handler for the given endpoint. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + protected function getCurlHandler(string $endpoint) + { + if (!array_key_exists($endpoint, $this->curlHandlers)) { + $this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint); + } + + return $this->curlHandlers[$endpoint]; + } + + /** + * Starts a fresh curl session for the given endpoint and returns its handler. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + private function loadCurlHandle(string $endpoint) + { + $url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token); + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + return $ch; + } + + /** + * @param string[]|string $tag + */ + public function setTag($tag): self + { + $tag = !empty($tag) ? $tag : []; + $this->tag = is_array($tag) ? $tag : [$tag]; + + return $this; + } + + /** + * @param string[]|string $tag + */ + public function addTag($tag): self + { + if (!empty($tag)) { + $tag = is_array($tag) ? $tag : [$tag]; + $this->tag = array_unique(array_merge($this->tag, $tag)); + } + + return $this; + } + + protected function write(array $record): void + { + $this->send($record["formatted"], static::ENDPOINT_SINGLE); + } + + public function handleBatch(array $records): void + { + $level = $this->level; + + $records = array_filter($records, function ($record) use ($level) { + return ($record['level'] >= $level); + }); + + if ($records) { + $this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH); + } + } + + protected function send(string $data, string $endpoint): void + { + $ch = $this->getCurlHandler($endpoint); + + $headers = ['Content-Type: application/json']; + + if (!empty($this->tag)) { + $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag); + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + Curl\Util::execute($ch, 5, false); + } + + protected function getDefaultFormatter(): FormatterInterface + { + return new LogglyFormatter(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php new file mode 100644 index 0000000000..859a469065 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LogmaticFormatter; + +/** + * @author Julien Breux + */ +class LogmaticHandler extends SocketHandler +{ + /** + * @var string + */ + private $logToken; + + /** + * @var string + */ + private $hostname; + + /** + * @var string + */ + private $appname; + + /** + * @param string $token Log token supplied by Logmatic. + * @param string $hostname Host name supplied by Logmatic. + * @param string $appname Application name supplied by Logmatic. + * @param bool $useSSL Whether or not SSL encryption should be used. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + string $hostname = '', + string $appname = '', + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use SSL encrypted connection for LogmaticHandler'); + } + + $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514'; + $endpoint .= '/v1/'; + + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->logToken = $token; + $this->hostname = $hostname; + $this->appname = $appname; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + $formatter = new LogmaticFormatter(); + + if (!empty($this->hostname)) { + $formatter->setHostname($this->hostname); + } + if (!empty($this->appname)) { + $formatter->setAppname($this->appname); + } + + return $formatter; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php new file mode 100644 index 0000000000..97f3432028 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\HtmlFormatter; + +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + */ +abstract class MailHandler extends AbstractProcessingHandler +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $messages = []; + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + + /** + * Send a mail with the given content + * + * @param string $content formatted email body to be sent + * @param array $records the array of log records that formed this content + * + * @phpstan-param Record[] $records + */ + abstract protected function send(string $content, array $records): void; + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->send((string) $record['formatted'], [$record]); + } + + /** + * @phpstan-param non-empty-array $records + * @phpstan-return Record + */ + protected function getHighestRecord(array $records): array + { + $highestRecord = null; + foreach ($records as $record) { + if ($highestRecord === null || $highestRecord['level'] < $record['level']) { + $highestRecord = $record; + } + } + + return $highestRecord; + } + + protected function isHtmlBody(string $body): bool + { + return ($body[0] ?? null) === '<'; + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new HtmlFormatter(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php new file mode 100644 index 0000000000..3003500ec0 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Swift; +use Swift_Message; + +/** + * MandrillHandler uses cURL to send the emails to the Mandrill API + * + * @author Adam Nicholson + */ +class MandrillHandler extends MailHandler +{ + /** @var Swift_Message */ + protected $message; + /** @var string */ + protected $apiKey; + + /** + * @psalm-param Swift_Message|callable(): Swift_Message $message + * + * @param string $apiKey A valid Mandrill API key + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(string $apiKey, $message, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + if (!$message instanceof Swift_Message && is_callable($message)) { + $message = $message(); + } + if (!$message instanceof Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); + } + $this->message = $message; + $this->apiKey = $apiKey; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + + $message = clone $this->message; + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (version_compare(Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(time()); + } + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ + 'key' => $this->apiKey, + 'raw_message' => (string) $message, + 'async' => false, + ])); + + Curl\Util::execute($ch); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php new file mode 100644 index 0000000000..3965aeea5d --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Exception can be thrown if an extension for a handler is missing + * + * @author Christian Bergau + */ +class MissingExtensionException extends \Exception +{ +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php new file mode 100644 index 0000000000..3063091199 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Manager; +use MongoDB\Client; +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\MongoDBFormatter; + +/** + * Logs to a MongoDB database. + * + * Usage example: + * + * $log = new \Monolog\Logger('application'); + * $client = new \MongoDB\Client('mongodb://localhost:27017'); + * $mongodb = new \Monolog\Handler\MongoDBHandler($client, 'logs', 'prod'); + * $log->pushHandler($mongodb); + * + * The above examples uses the MongoDB PHP library's client class; however, the + * MongoDB\Driver\Manager class from ext-mongodb is also supported. + */ +class MongoDBHandler extends AbstractProcessingHandler +{ + /** @var \MongoDB\Collection */ + private $collection; + /** @var Client|Manager */ + private $manager; + /** @var string */ + private $namespace; + + /** + * Constructor. + * + * @param Client|Manager $mongodb MongoDB library or driver client + * @param string $database Database name + * @param string $collection Collection name + */ + public function __construct($mongodb, string $database, string $collection, $level = Logger::DEBUG, bool $bubble = true) + { + if (!($mongodb instanceof Client || $mongodb instanceof Manager)) { + throw new \InvalidArgumentException('MongoDB\Client or MongoDB\Driver\Manager instance required'); + } + + if ($mongodb instanceof Client) { + $this->collection = $mongodb->selectCollection($database, $collection); + } else { + $this->manager = $mongodb; + $this->namespace = $database . '.' . $collection; + } + + parent::__construct($level, $bubble); + } + + protected function write(array $record): void + { + if (isset($this->collection)) { + $this->collection->insertOne($record['formatted']); + } + + if (isset($this->manager, $this->namespace)) { + $bulk = new BulkWrite; + $bulk->insert($record["formatted"]); + $this->manager->executeBulkWrite($this->namespace, $bulk); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new MongoDBFormatter; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php new file mode 100644 index 0000000000..0c0a3bdb1c --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + * @author Mark Garrett + */ +class NativeMailerHandler extends MailHandler +{ + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * Optional headers for the message + * @var string[] + */ + protected $headers = []; + + /** + * Optional parameters for the message + * @var string[] + */ + protected $parameters = []; + + /** + * The wordwrap length for the message + * @var int + */ + protected $maxColumnWidth; + + /** + * The Content-type for the message + * @var string|null + */ + protected $contentType; + + /** + * The encoding for the message + * @var string + */ + protected $encoding = 'utf-8'; + + /** + * @param string|string[] $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param int $maxColumnWidth The maximum column width that the message lines will have + */ + public function __construct($to, string $subject, string $from, $level = Logger::ERROR, bool $bubble = true, int $maxColumnWidth = 70) + { + parent::__construct($level, $bubble); + $this->to = (array) $to; + $this->subject = $subject; + $this->addHeader(sprintf('From: %s', $from)); + $this->maxColumnWidth = $maxColumnWidth; + } + + /** + * Add headers to the message + * + * @param string|string[] $headers Custom added headers + */ + public function addHeader($headers): self + { + foreach ((array) $headers as $header) { + if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) { + throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); + } + $this->headers[] = $header; + } + + return $this; + } + + /** + * Add parameters to the message + * + * @param string|string[] $parameters Custom added parameters + */ + public function addParameter($parameters): self + { + $this->parameters = array_merge($this->parameters, (array) $parameters); + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $contentType = $this->getContentType() ?: ($this->isHtmlBody($content) ? 'text/html' : 'text/plain'); + + if ($contentType !== 'text/html') { + $content = wordwrap($content, $this->maxColumnWidth); + } + + $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); + $headers .= 'Content-type: ' . $contentType . '; charset=' . $this->getEncoding() . "\r\n"; + if ($contentType === 'text/html' && false === strpos($headers, 'MIME-Version:')) { + $headers .= 'MIME-Version: 1.0' . "\r\n"; + } + + $subject = $this->subject; + if ($records) { + $subjectFormatter = new LineFormatter($this->subject); + $subject = $subjectFormatter->format($this->getHighestRecord($records)); + } + + $parameters = implode(' ', $this->parameters); + foreach ($this->to as $to) { + mail($to, $subject, $content, $headers, $parameters); + } + } + + public function getContentType(): ?string + { + return $this->contentType; + } + + public function getEncoding(): string + { + return $this->encoding; + } + + /** + * @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML messages. + */ + public function setContentType(string $contentType): self + { + if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) { + throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); + } + + $this->contentType = $contentType; + + return $this; + } + + public function setEncoding(string $encoding): self + { + if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) { + throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); + } + + $this->encoding = $encoding; + + return $this; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php new file mode 100644 index 0000000000..114d749eb9 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Class to record a log on a NewRelic application. + * Enabling New Relic High Security mode may prevent capture of useful information. + * + * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted'] + * + * @see https://docs.newrelic.com/docs/agents/php-agent + * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security + */ +class NewRelicHandler extends AbstractProcessingHandler +{ + /** + * Name of the New Relic application that will receive logs from this handler. + * + * @var ?string + */ + protected $appName; + + /** + * Name of the current transaction + * + * @var ?string + */ + protected $transactionName; + + /** + * Some context and extra data is passed into the handler as arrays of values. Do we send them as is + * (useful if we are using the API), or explode them for display on the NewRelic RPM website? + * + * @var bool + */ + protected $explodeArrays; + + /** + * {@inheritDoc} + * + * @param string|null $appName + * @param bool $explodeArrays + * @param string|null $transactionName + */ + public function __construct( + $level = Logger::ERROR, + bool $bubble = true, + ?string $appName = null, + bool $explodeArrays = false, + ?string $transactionName = null + ) { + parent::__construct($level, $bubble); + + $this->appName = $appName; + $this->explodeArrays = $explodeArrays; + $this->transactionName = $transactionName; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->isNewRelicEnabled()) { + throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); + } + + if ($appName = $this->getAppName($record['context'])) { + $this->setNewRelicAppName($appName); + } + + if ($transactionName = $this->getTransactionName($record['context'])) { + $this->setNewRelicTransactionName($transactionName); + unset($record['formatted']['context']['transaction_name']); + } + + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + newrelic_notice_error($record['message'], $record['context']['exception']); + unset($record['formatted']['context']['exception']); + } else { + newrelic_notice_error($record['message']); + } + + if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) { + foreach ($record['formatted']['context'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('context_' . $key, $parameter); + } + } + } + + if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) { + foreach ($record['formatted']['extra'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('extra_' . $key, $parameter); + } + } + } + } + + /** + * Checks whether the NewRelic extension is enabled in the system. + * + * @return bool + */ + protected function isNewRelicEnabled(): bool + { + return extension_loaded('newrelic'); + } + + /** + * Returns the appname where this log should be sent. Each log can override the default appname, set in this + * handler's constructor, by providing the appname in it's context. + * + * @param mixed[] $context + */ + protected function getAppName(array $context): ?string + { + if (isset($context['appname'])) { + return $context['appname']; + } + + return $this->appName; + } + + /** + * Returns the name of the current transaction. Each log can override the default transaction name, set in this + * handler's constructor, by providing the transaction_name in it's context + * + * @param mixed[] $context + */ + protected function getTransactionName(array $context): ?string + { + if (isset($context['transaction_name'])) { + return $context['transaction_name']; + } + + return $this->transactionName; + } + + /** + * Sets the NewRelic application that should receive this log. + */ + protected function setNewRelicAppName(string $appName): void + { + newrelic_set_appname($appName); + } + + /** + * Overwrites the name of the current transaction + */ + protected function setNewRelicTransactionName(string $transactionName): void + { + newrelic_name_transaction($transactionName); + } + + /** + * @param string $key + * @param mixed $value + */ + protected function setNewRelicParameter(string $key, $value): void + { + if (null === $value || is_scalar($value)) { + newrelic_add_custom_parameter($key, $value); + } else { + newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, true)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php new file mode 100644 index 0000000000..1ddf0beb91 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * No-op + * + * This handler handles anything, but does nothing, and does not stop bubbling to the rest of the stack. + * This can be used for testing, or to disable a handler when overriding a configuration without + * influencing the rest of the stack. + * + * @author Roel Harbers + */ +class NoopHandler extends Handler +{ + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return true; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return false; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php new file mode 100644 index 0000000000..e75ee0c6e9 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class NullHandler extends Handler +{ + /** + * @var int + */ + private $level; + + /** + * @param string|int $level The minimum logging level at which this handler will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $record['level'] >= $this->level; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return $record['level'] >= $this->level; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php new file mode 100644 index 0000000000..22068c9a39 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; + +/** + * Handler to only pass log messages when a certain threshold of number of messages is reached. + * + * This can be useful in cases of processing a batch of data, but you're for example only interested + * in case it fails catastrophically instead of a warning for 1 or 2 events. Worse things can happen, right? + * + * Usage example: + * + * ``` + * $log = new Logger('application'); + * $handler = new SomeHandler(...) + * + * // Pass all warnings to the handler when more than 10 & all error messages when more then 5 + * $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]); + * + * $log->pushHandler($overflow); + *``` + * + * @author Kris Buist + */ +class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface +{ + /** @var HandlerInterface */ + private $handler; + + /** @var int[] */ + private $thresholdMap = [ + Logger::DEBUG => 0, + Logger::INFO => 0, + Logger::NOTICE => 0, + Logger::WARNING => 0, + Logger::ERROR => 0, + Logger::CRITICAL => 0, + Logger::ALERT => 0, + Logger::EMERGENCY => 0, + ]; + + /** + * Buffer of all messages passed to the handler before the threshold was reached + * + * @var mixed[][] + */ + private $buffer = []; + + /** + * @param HandlerInterface $handler + * @param int[] $thresholdMap Dictionary of logger level => threshold + */ + public function __construct( + HandlerInterface $handler, + array $thresholdMap = [], + $level = Logger::DEBUG, + bool $bubble = true + ) { + $this->handler = $handler; + foreach ($thresholdMap as $thresholdLevel => $threshold) { + $this->thresholdMap[$thresholdLevel] = $threshold; + } + parent::__construct($level, $bubble); + } + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($record['level'] < $this->level) { + return false; + } + + $level = $record['level']; + + if (!isset($this->thresholdMap[$level])) { + $this->thresholdMap[$level] = 0; + } + + if ($this->thresholdMap[$level] > 0) { + // The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1 + $this->thresholdMap[$level]--; + $this->buffer[$level][] = $record; + + return false === $this->bubble; + } + + if ($this->thresholdMap[$level] == 0) { + // This current message is breaking the threshold. Flush the buffer and continue handling the current record + foreach ($this->buffer[$level] ?? [] as $buffered) { + $this->handler->handle($buffered); + } + $this->thresholdMap[$level]--; + unset($this->buffer[$level]); + } + + $this->handler->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php new file mode 100644 index 0000000000..23a1d1178f --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use PhpConsole\Connector; +use PhpConsole\Handler as VendorPhpConsoleHandler; +use PhpConsole\Helper; + +/** + * Monolog handler for Google Chrome extension "PHP Console" + * + * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely + * + * Usage: + * 1. Install Google Chrome extension [now dead and removed from the chrome store] + * 2. See overview https://github.com/barbushin/php-console#overview + * 3. Install PHP Console library https://github.com/barbushin/php-console#installation + * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) + * + * $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); + * \Monolog\ErrorHandler::register($logger); + * echo $undefinedVar; + * $logger->debug('SELECT * FROM users', array('db', 'time' => 0.012)); + * PC::debug($_SERVER); // PHP Console debugger for any type of vars + * + * @author Sergey Barbushin https://www.linkedin.com/in/barbushin + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4 + */ +class PHPConsoleHandler extends AbstractProcessingHandler +{ + /** @var array */ + private $options = [ + 'enabled' => true, // bool Is PHP Console server enabled + 'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with... + 'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled + 'useOwnErrorsHandler' => false, // bool Enable errors handling + 'useOwnExceptionsHandler' => false, // bool Enable exceptions handling + 'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths + 'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') + 'serverEncoding' => null, // string|null Server internal encoding + 'headersLimit' => null, // int|null Set headers size limit for your web-server + 'password' => null, // string|null Protect PHP Console connection by password + 'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed + 'ipMasks' => [], // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') + 'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) + 'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings + 'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level + 'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number + 'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item + 'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON + 'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug + 'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) + ]; + + /** @var Connector */ + private $connector; + + /** + * @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details + * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) + * @throws \RuntimeException + */ + public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true) + { + if (!class_exists('PhpConsole\Connector')) { + throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); + } + parent::__construct($level, $bubble); + $this->options = $this->initOptions($options); + $this->connector = $this->initConnector($connector); + } + + /** + * @param array $options + * + * @return array + */ + private function initOptions(array $options): array + { + $wrongOptions = array_diff(array_keys($options), array_keys($this->options)); + if ($wrongOptions) { + throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions)); + } + + return array_replace($this->options, $options); + } + + private function initConnector(?Connector $connector = null): Connector + { + if (!$connector) { + if ($this->options['dataStorage']) { + Connector::setPostponeStorage($this->options['dataStorage']); + } + $connector = Connector::getInstance(); + } + + if ($this->options['registerHelper'] && !Helper::isRegistered()) { + Helper::register(); + } + + if ($this->options['enabled'] && $connector->isActiveClient()) { + if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { + $handler = VendorPhpConsoleHandler::getInstance(); + $handler->setHandleErrors($this->options['useOwnErrorsHandler']); + $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); + $handler->start(); + } + if ($this->options['sourcesBasePath']) { + $connector->setSourcesBasePath($this->options['sourcesBasePath']); + } + if ($this->options['serverEncoding']) { + $connector->setServerEncoding($this->options['serverEncoding']); + } + if ($this->options['password']) { + $connector->setPassword($this->options['password']); + } + if ($this->options['enableSslOnlyMode']) { + $connector->enableSslOnlyMode(); + } + if ($this->options['ipMasks']) { + $connector->setAllowedIpMasks($this->options['ipMasks']); + } + if ($this->options['headersLimit']) { + $connector->setHeadersLimit($this->options['headersLimit']); + } + if ($this->options['detectDumpTraceAndSource']) { + $connector->getDebugDispatcher()->detectTraceAndSource = true; + } + $dumper = $connector->getDumper(); + $dumper->levelLimit = $this->options['dumperLevelLimit']; + $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; + $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; + $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; + $dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; + if ($this->options['enableEvalListener']) { + $connector->startEvalRequestsListener(); + } + } + + return $connector; + } + + public function getConnector(): Connector + { + return $this->connector; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + public function handle(array $record): bool + { + if ($this->options['enabled'] && $this->connector->isActiveClient()) { + return parent::handle($record); + } + + return !$this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + */ + protected function write(array $record): void + { + if ($record['level'] < Logger::NOTICE) { + $this->handleDebugRecord($record); + } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + $this->handleExceptionRecord($record); + } else { + $this->handleErrorRecord($record); + } + } + + /** + * @phpstan-param Record $record + */ + private function handleDebugRecord(array $record): void + { + $tags = $this->getRecordTags($record); + $message = $record['message']; + if ($record['context']) { + $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true); + } + $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); + } + + /** + * @phpstan-param Record $record + */ + private function handleExceptionRecord(array $record): void + { + $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); + } + + /** + * @phpstan-param Record $record + */ + private function handleErrorRecord(array $record): void + { + $context = $record['context']; + + $this->connector->getErrorsDispatcher()->dispatchError( + $context['code'] ?? null, + $context['message'] ?? $record['message'], + $context['file'] ?? null, + $context['line'] ?? null, + $this->options['classesPartialsTraceIgnore'] + ); + } + + /** + * @phpstan-param Record $record + * @return string + */ + private function getRecordTags(array &$record) + { + $tags = null; + if (!empty($record['context'])) { + $context = & $record['context']; + foreach ($this->options['debugTagsKeysInContext'] as $key) { + if (!empty($context[$key])) { + $tags = $context[$key]; + if ($key === 0) { + array_shift($context); + } else { + unset($context[$key]); + } + break; + } + } + } + + return $tags ?: strtolower($record['level_name']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('%message%'); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php new file mode 100644 index 0000000000..8a8cf1be66 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to STDIN of any process, specified by a command. + * + * Usage example: + *
+ * $log = new Logger('myLogger');
+ * $log->pushHandler(new ProcessHandler('/usr/bin/php /var/www/monolog/someScript.php'));
+ * 
+ * + * @author Kolja Zuelsdorf + */ +class ProcessHandler extends AbstractProcessingHandler +{ + /** + * Holds the process to receive data on its STDIN. + * + * @var resource|bool|null + */ + private $process; + + /** + * @var string + */ + private $command; + + /** + * @var string|null + */ + private $cwd; + + /** + * @var resource[] + */ + private $pipes = []; + + /** + * @var array + */ + protected const DESCRIPTOR_SPEC = [ + 0 => ['pipe', 'r'], // STDIN is a pipe that the child will read from + 1 => ['pipe', 'w'], // STDOUT is a pipe that the child will write to + 2 => ['pipe', 'w'], // STDERR is a pipe to catch the any errors + ]; + + /** + * @param string $command Command for the process to start. Absolute paths are recommended, + * especially if you do not use the $cwd parameter. + * @param string|null $cwd "Current working directory" (CWD) for the process to be executed in. + * @throws \InvalidArgumentException + */ + public function __construct(string $command, $level = Logger::DEBUG, bool $bubble = true, ?string $cwd = null) + { + if ($command === '') { + throw new \InvalidArgumentException('The command argument must be a non-empty string.'); + } + if ($cwd === '') { + throw new \InvalidArgumentException('The optional CWD argument must be a non-empty string or null.'); + } + + parent::__construct($level, $bubble); + + $this->command = $command; + $this->cwd = $cwd; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @throws \UnexpectedValueException + */ + protected function write(array $record): void + { + $this->ensureProcessIsStarted(); + + $this->writeProcessInput($record['formatted']); + + $errors = $this->readProcessErrors(); + if (empty($errors) === false) { + throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors)); + } + } + + /** + * Makes sure that the process is actually started, and if not, starts it, + * assigns the stream pipes, and handles startup errors, if any. + */ + private function ensureProcessIsStarted(): void + { + if (is_resource($this->process) === false) { + $this->startProcess(); + + $this->handleStartupErrors(); + } + } + + /** + * Starts the actual process and sets all streams to non-blocking. + */ + private function startProcess(): void + { + $this->process = proc_open($this->command, static::DESCRIPTOR_SPEC, $this->pipes, $this->cwd); + + foreach ($this->pipes as $pipe) { + stream_set_blocking($pipe, false); + } + } + + /** + * Selects the STDERR stream, handles upcoming startup errors, and throws an exception, if any. + * + * @throws \UnexpectedValueException + */ + private function handleStartupErrors(): void + { + $selected = $this->selectErrorStream(); + if (false === $selected) { + throw new \UnexpectedValueException('Something went wrong while selecting a stream.'); + } + + $errors = $this->readProcessErrors(); + + if (is_resource($this->process) === false || empty($errors) === false) { + throw new \UnexpectedValueException( + sprintf('The process "%s" could not be opened: ' . $errors, $this->command) + ); + } + } + + /** + * Selects the STDERR stream. + * + * @return int|bool + */ + protected function selectErrorStream() + { + $empty = []; + $errorPipes = [$this->pipes[2]]; + + return stream_select($errorPipes, $empty, $empty, 1); + } + + /** + * Reads the errors of the process, if there are any. + * + * @codeCoverageIgnore + * @return string Empty string if there are no errors. + */ + protected function readProcessErrors(): string + { + return (string) stream_get_contents($this->pipes[2]); + } + + /** + * Writes to the input stream of the opened process. + * + * @codeCoverageIgnore + */ + protected function writeProcessInput(string $string): void + { + fwrite($this->pipes[0], $string); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + if (is_resource($this->process)) { + foreach ($this->pipes as $pipe) { + fclose($pipe); + } + proc_close($this->process); + $this->process = null; + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php new file mode 100644 index 0000000000..3adec7a4d8 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Processor\ProcessorInterface; + +/** + * Interface to describe loggers that have processors + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessableHandlerInterface +{ + /** + * Adds a processor in the stack. + * + * @psalm-param ProcessorInterface|callable(Record): Record $callback + * + * @param ProcessorInterface|callable $callback + * @return HandlerInterface self + */ + public function pushProcessor(callable $callback): HandlerInterface; + + /** + * Removes the processor on top of the stack and returns it. + * + * @psalm-return ProcessorInterface|callable(Record): Record $callback + * + * @throws \LogicException In case the processor stack is empty + * @return callable|ProcessorInterface + */ + public function popProcessor(): callable; +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php new file mode 100644 index 0000000000..9ef6e301c5 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\ResettableInterface; +use Monolog\Processor\ProcessorInterface; + +/** + * Helper trait for implementing ProcessableInterface + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +trait ProcessableHandlerTrait +{ + /** + * @var callable[] + * @phpstan-var array + */ + protected $processors = []; + + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback): HandlerInterface + { + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function popProcessor(): callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * Processes a record. + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + protected function processRecord(array $record): array + { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + + return $record; + } + + protected function resetProcessors(): void + { + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php new file mode 100644 index 0000000000..36e19cccf2 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LoggerInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * Proxies log messages to an existing PSR-3 compliant logger. + * + * If a formatter is configured, the formatter's output MUST be a string and the + * formatted message will be fed to the wrapped PSR logger instead of the original + * log record's message. + * + * @author Michael Moussa + */ +class PsrHandler extends AbstractHandler implements FormattableHandlerInterface +{ + /** + * PSR-3 compliant logger + * + * @var LoggerInterface + */ + protected $logger; + + /** + * @var FormatterInterface|null + */ + protected $formatter; + + /** + * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied + */ + public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->formatter) { + $formatted = $this->formatter->format($record); + $this->logger->log(strtolower($record['level_name']), (string) $formatted, $record['context']); + } else { + $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']); + } + + return false === $this->bubble; + } + + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $this->formatter = $formatter; + + return $this; + } + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(): FormatterInterface + { + if (!$this->formatter) { + throw new \LogicException('No formatter has been set and this handler does not have a default formatter'); + } + + return $this->formatter; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php new file mode 100644 index 0000000000..fed2303d7b --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Psr\Log\LogLevel; + +/** + * Sends notifications through the pushover api to mobile phones + * + * @author Sebastian Göttschkes + * @see https://www.pushover.net/api + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class PushoverHandler extends SocketHandler +{ + /** @var string */ + private $token; + /** @var array */ + private $users; + /** @var string */ + private $title; + /** @var string|int|null */ + private $user = null; + /** @var int */ + private $retry; + /** @var int */ + private $expire; + + /** @var int */ + private $highPriorityLevel; + /** @var int */ + private $emergencyLevel; + /** @var bool */ + private $useFormattedMessage = false; + + /** + * All parameters that can be sent to Pushover + * @see https://pushover.net/api + * @var array + */ + private $parameterNames = [ + 'token' => true, + 'user' => true, + 'message' => true, + 'device' => true, + 'title' => true, + 'url' => true, + 'url_title' => true, + 'priority' => true, + 'timestamp' => true, + 'sound' => true, + 'retry' => true, + 'expire' => true, + 'callback' => true, + ]; + + /** + * Sounds the api supports by default + * @see https://pushover.net/api#sounds + * @var string[] + */ + private $sounds = [ + 'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', + 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', + 'persistent', 'echo', 'updown', 'none', + ]; + + /** + * @param string $token Pushover api token + * @param string|array $users Pushover user id or array of ids the message will be sent to + * @param string|null $title Title sent to the Pushover API + * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * the pushover.net app owner. OpenSSL is required for this option. + * @param string|int $highPriorityLevel The minimum logging level at which this handler will start + * sending "high priority" requests to the Pushover API + * @param string|int $emergencyLevel The minimum logging level at which this handler will start + * sending "emergency" requests to the Pushover API + * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will + * send the same notification to the user. + * @param int $expire The expire parameter specifies how many seconds your notification will continue + * to be retried for (every retry seconds). + * + * @phpstan-param string|array $users + * @phpstan-param Level|LevelName|LogLevel::* $highPriorityLevel + * @phpstan-param Level|LevelName|LogLevel::* $emergencyLevel + */ + public function __construct( + string $token, + $users, + ?string $title = null, + $level = Logger::CRITICAL, + bool $bubble = true, + bool $useSSL = true, + $highPriorityLevel = Logger::CRITICAL, + $emergencyLevel = Logger::EMERGENCY, + int $retry = 30, + int $expire = 25200, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; + parent::__construct( + $connectionString, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->token = $token; + $this->users = (array) $users; + $this->title = $title ?: (string) gethostname(); + $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); + $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); + $this->retry = $retry; + $this->expire = $expire; + } + + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + // Pushover has a limit of 512 characters on title and message combined. + $maxMessageLength = 512 - strlen($this->title); + + $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message']; + $message = Utils::substr($message, 0, $maxMessageLength); + + $timestamp = $record['datetime']->getTimestamp(); + + $dataArray = [ + 'token' => $this->token, + 'user' => $this->user, + 'message' => $message, + 'title' => $this->title, + 'timestamp' => $timestamp, + ]; + + if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { + $dataArray['priority'] = 2; + $dataArray['retry'] = $this->retry; + $dataArray['expire'] = $this->expire; + } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { + $dataArray['priority'] = 1; + } + + // First determine the available parameters + $context = array_intersect_key($record['context'], $this->parameterNames); + $extra = array_intersect_key($record['extra'], $this->parameterNames); + + // Least important info should be merged with subsequent info + $dataArray = array_merge($extra, $context, $dataArray); + + // Only pass sounds that are supported by the API + if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) { + unset($dataArray['sound']); + } + + return http_build_query($dataArray); + } + + private function buildHeader(string $content): string + { + $header = "POST /1/messages.json HTTP/1.1\r\n"; + $header .= "Host: api.pushover.net\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + protected function write(array $record): void + { + foreach ($this->users as $user) { + $this->user = $user; + + parent::write($record); + $this->closeSocket(); + } + + $this->user = null; + } + + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setHighPriorityLevel($value): self + { + $this->highPriorityLevel = Logger::toMonologLevel($value); + + return $this; + } + + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setEmergencyLevel($value): self + { + $this->emergencyLevel = Logger::toMonologLevel($value); + + return $this; + } + + /** + * Use the formatted message? + */ + public function useFormattedMessage(bool $value): self + { + $this->useFormattedMessage = $value; + + return $this; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php new file mode 100644 index 0000000000..91d16eaf64 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; + +/** + * Logs to a Redis key using rpush + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); + * $log->pushHandler($redis); + * + * @author Thomas Tourlourat + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class RedisHandler extends AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $redisKey; + /** @var int */ + protected $capSize; + + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The key name to push records to + * @param int $capSize Number of entries to limit list size to, 0 = unlimited + */ + public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true, int $capSize = 0) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->redisKey = $key; + $this->capSize = $capSize; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if ($this->capSize) { + $this->writeCapped($record); + } else { + $this->redisClient->rpush($this->redisKey, $record["formatted"]); + } + } + + /** + * Write and cap the collection + * Writes the record to the redis list and caps its + * + * @phpstan-param FormattedRecord $record + */ + protected function writeCapped(array $record): void + { + if ($this->redisClient instanceof \Redis) { + $mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1; + $this->redisClient->multi($mode) + ->rpush($this->redisKey, $record["formatted"]) + ->ltrim($this->redisKey, -$this->capSize, -1) + ->exec(); + } else { + $redisKey = $this->redisKey; + $capSize = $this->capSize; + $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { + $tx->rpush($redisKey, $record["formatted"]); + $tx->ltrim($redisKey, -$capSize, -1); + }); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php new file mode 100644 index 0000000000..7789309c1c --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; + +/** + * Sends the message to a Redis Pub/Sub channel using PUBLISH + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Logger::WARNING); + * $log->pushHandler($redis); + * + * @author Gaëtan Faugère + */ +class RedisPubSubHandler extends AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $channelKey; + + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The channel key to publish records to + */ + public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->channelKey = $key; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->redisClient->publish($this->channelKey, $record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php new file mode 100644 index 0000000000..adcc9395a5 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Rollbar\RollbarLogger; +use Throwable; +use Monolog\Logger; + +/** + * Sends errors to Rollbar + * + * If the context data contains a `payload` key, that is used as an array + * of payload options to RollbarLogger's log method. + * + * Rollbar's context info will contain the context + extra keys from the log record + * merged, and then on top of that a few keys: + * + * - level (rollbar level name) + * - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) + * - channel + * - datetime (unix timestamp) + * + * @author Paul Statezny + */ +class RollbarHandler extends AbstractProcessingHandler +{ + /** + * @var RollbarLogger + */ + protected $rollbarLogger; + + /** @var string[] */ + protected $levelMap = [ + Logger::DEBUG => 'debug', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warning', + Logger::ERROR => 'error', + Logger::CRITICAL => 'critical', + Logger::ALERT => 'critical', + Logger::EMERGENCY => 'critical', + ]; + + /** + * Records whether any log records have been added since the last flush of the rollbar notifier + * + * @var bool + */ + private $hasRecords = false; + + /** @var bool */ + protected $initialized = false; + + /** + * @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token + */ + public function __construct(RollbarLogger $rollbarLogger, $level = Logger::ERROR, bool $bubble = true) + { + $this->rollbarLogger = $rollbarLogger; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + $this->initialized = true; + } + + $context = $record['context']; + $context = array_merge($context, $record['extra'], [ + 'level' => $this->levelMap[$record['level']], + 'monolog_level' => $record['level_name'], + 'channel' => $record['channel'], + 'datetime' => $record['datetime']->format('U'), + ]); + + if (isset($context['exception']) && $context['exception'] instanceof Throwable) { + $exception = $context['exception']; + unset($context['exception']); + $toLog = $exception; + } else { + $toLog = $record['message']; + } + + // @phpstan-ignore-next-line + $this->rollbarLogger->log($context['level'], $toLog, $context); + + $this->hasRecords = true; + } + + public function flush(): void + { + if ($this->hasRecords) { + $this->rollbarLogger->flush(); + $this->hasRecords = false; + } + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flush(); + } + + /** + * {@inheritDoc} + */ + public function reset() + { + $this->flush(); + + parent::reset(); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php new file mode 100644 index 0000000000..17745d2215 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use InvalidArgumentException; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + * @author Jordi Boggiano + */ +class RotatingFileHandler extends StreamHandler +{ + public const FILE_PER_DAY = 'Y-m-d'; + public const FILE_PER_MONTH = 'Y-m'; + public const FILE_PER_YEAR = 'Y'; + + /** @var string */ + protected $filename; + /** @var int */ + protected $maxFiles; + /** @var bool */ + protected $mustRotate; + /** @var \DateTimeImmutable */ + protected $nextRotation; + /** @var string */ + protected $filenameFormat; + /** @var string */ + protected $dateFormat; + + /** + * @param string $filename + * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + */ + public function __construct(string $filename, int $maxFiles = 0, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) + { + $this->filename = Utils::canonicalizePath($filename); + $this->maxFiles = $maxFiles; + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + $this->filenameFormat = '{filename}-{date}'; + $this->dateFormat = static::FILE_PER_DAY; + + parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + /** + * {@inheritDoc} + */ + public function reset() + { + parent::reset(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + public function setFilenameFormat(string $filenameFormat, string $dateFormat): self + { + if (!preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { + throw new InvalidArgumentException( + 'Invalid date format - format must be one of '. + 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '. + 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '. + 'date formats using slashes, underscores and/or dots instead of dashes.' + ); + } + if (substr_count($filenameFormat, '{date}') === 0) { + throw new InvalidArgumentException( + 'Invalid filename format - format must contain at least `{date}`, because otherwise rotating is impossible.' + ); + } + $this->filenameFormat = $filenameFormat; + $this->dateFormat = $dateFormat; + $this->url = $this->getTimedFilename(); + $this->close(); + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + // on the first record written, if the log is new, we should rotate (once per day) + if (null === $this->mustRotate) { + $this->mustRotate = null === $this->url || !file_exists($this->url); + } + + if ($this->nextRotation <= $record['datetime']) { + $this->mustRotate = true; + $this->close(); + } + + parent::write($record); + } + + /** + * Rotates the files. + */ + protected function rotate(): void + { + // update filename + $this->url = $this->getTimedFilename(); + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + + // skip GC of old logs if files are unlimited + if (0 === $this->maxFiles) { + return; + } + + $logFiles = glob($this->getGlobPattern()); + if (false === $logFiles) { + // failed to glob + return; + } + + if ($this->maxFiles >= count($logFiles)) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + usort($logFiles, function ($a, $b) { + return strcmp($b, $a); + }); + + foreach (array_slice($logFiles, $this->maxFiles) as $file) { + if (is_writable($file)) { + // suppress errors here as unlink() might fail if two processes + // are cleaning up/rotating at the same time + set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool { + return false; + }); + unlink($file); + restore_error_handler(); + } + } + + $this->mustRotate = false; + } + + protected function getTimedFilename(): string + { + $fileInfo = pathinfo($this->filename); + $timedFilename = str_replace( + ['{filename}', '{date}'], + [$fileInfo['filename'], date($this->dateFormat)], + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + + if (isset($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + return $timedFilename; + } + + protected function getGlobPattern(): string + { + $fileInfo = pathinfo($this->filename); + $glob = str_replace( + ['{filename}', '{date}'], + [$fileInfo['filename'], str_replace( + ['Y', 'y', 'm', 'd'], + ['[0-9][0-9][0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]'], + $this->dateFormat) + ], + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + if (isset($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + + return $glob; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php new file mode 100644 index 0000000000..c128a32d18 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Sampling handler + * + * A sampled event stream can be useful for logging high frequency events in + * a production environment where you only need an idea of what is happening + * and are not concerned with capturing every occurrence. Since the decision to + * handle or not handle a particular event is determined randomly, the + * resulting sampled log is not guaranteed to contain 1/N of the events that + * occurred in the application, but based on the Law of large numbers, it will + * tend to be close to this ratio with a large number of attempts. + * + * @author Bryan Davis + * @author Kunal Mehta + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * @var HandlerInterface|callable + * @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface + */ + protected $handler; + + /** + * @var int $factor + */ + protected $factor; + + /** + * @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). + * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) + */ + public function __construct($handler, int $factor) + { + parent::__construct(); + $this->handler = $handler; + $this->factor = $factor; + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + public function isHandling(array $record): bool + { + return $this->getHandler($record)->isHandling($record); + } + + public function handle(array $record): bool + { + if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->getHandler($record)->handle($record); + } + + return false === $this->bubble; + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @phpstan-param Record|array{level: Level}|null $record + * + * @return HandlerInterface + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php new file mode 100644 index 0000000000..1280ee7039 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html + * + * @author Ricardo Fontanelli + */ +class SendGridHandler extends MailHandler +{ + /** + * The SendGrid API User + * @var string + */ + protected $apiUser; + + /** + * The SendGrid API Key + * @var string + */ + protected $apiKey; + + /** + * The email addresses to which the message will be sent + * @var string + */ + protected $from; + + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * @param string $apiUser The SendGrid API User + * @param string $apiKey The SendGrid API Key + * @param string $from The sender of the email + * @param string|string[] $to The recipients of the email + * @param string $subject The subject of the mail + */ + public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = Logger::ERROR, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler'); + } + + parent::__construct($level, $bubble); + $this->apiUser = $apiUser; + $this->apiKey = $apiKey; + $this->from = $from; + $this->to = (array) $to; + $this->subject = $subject; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $message = []; + $message['api_user'] = $this->apiUser; + $message['api_key'] = $this->apiKey; + $message['from'] = $this->from; + foreach ($this->to as $recipient) { + $message['to[]'] = $recipient; + } + $message['subject'] = $this->subject; + $message['date'] = date('r'); + + if ($this->isHtmlBody($content)) { + $message['html'] = $content; + } else { + $message['text'] = $content; + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.sendgrid.com/api/mail.send.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($message)); + Curl\Util::execute($ch, 2); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php new file mode 100644 index 0000000000..71a4109461 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php @@ -0,0 +1,387 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Slack; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Slack record utility helping to log to Slack webhooks or API. + * + * @author Greg Kedzierski + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + * @see https://api.slack.com/docs/message-attachments + * + * @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler + * @phpstan-import-type Record from \Monolog\Logger + */ +class SlackRecord +{ + public const COLOR_DANGER = 'danger'; + + public const COLOR_WARNING = 'warning'; + + public const COLOR_GOOD = 'good'; + + public const COLOR_DEFAULT = '#e3e4e6'; + + /** + * Slack channel (encoded ID or name) + * @var string|null + */ + private $channel; + + /** + * Name of a bot + * @var string|null + */ + private $username; + + /** + * User icon e.g. 'ghost', 'http://example.com/user.png' + * @var string|null + */ + private $userIcon; + + /** + * Whether the message should be added to Slack as attachment (plain text otherwise) + * @var bool + */ + private $useAttachment; + + /** + * Whether the the context/extra messages added to Slack as attachments are in a short style + * @var bool + */ + private $useShortAttachment; + + /** + * Whether the attachment should include context and extra data + * @var bool + */ + private $includeContextAndExtra; + + /** + * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @var string[] + */ + private $excludeFields; + + /** + * @var ?FormatterInterface + */ + private $formatter; + + /** + * @var NormalizerFormatter + */ + private $normalizerFormatter; + + /** + * @param string[] $excludeFields + */ + public function __construct( + ?string $channel = null, + ?string $username = null, + bool $useAttachment = true, + ?string $userIcon = null, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + array $excludeFields = array(), + FormatterInterface $formatter = null + ) { + $this + ->setChannel($channel) + ->setUsername($username) + ->useAttachment($useAttachment) + ->setUserIcon($userIcon) + ->useShortAttachment($useShortAttachment) + ->includeContextAndExtra($includeContextAndExtra) + ->excludeFields($excludeFields) + ->setFormatter($formatter); + + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new NormalizerFormatter(); + } + } + + /** + * Returns required data in format that Slack + * is expecting. + * + * @phpstan-param FormattedRecord $record + * @phpstan-return mixed[] + */ + public function getSlackData(array $record): array + { + $dataArray = array(); + $record = $this->removeExcludedFields($record); + + if ($this->username) { + $dataArray['username'] = $this->username; + } + + if ($this->channel) { + $dataArray['channel'] = $this->channel; + } + + if ($this->formatter && !$this->useAttachment) { + /** @phpstan-ignore-next-line */ + $message = $this->formatter->format($record); + } else { + $message = $record['message']; + } + + if ($this->useAttachment) { + $attachment = array( + 'fallback' => $message, + 'text' => $message, + 'color' => $this->getAttachmentColor($record['level']), + 'fields' => array(), + 'mrkdwn_in' => array('fields'), + 'ts' => $record['datetime']->getTimestamp(), + 'footer' => $this->username, + 'footer_icon' => $this->userIcon, + ); + + if ($this->useShortAttachment) { + $attachment['title'] = $record['level_name']; + } else { + $attachment['title'] = 'Message'; + $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']); + } + + if ($this->includeContextAndExtra) { + foreach (array('extra', 'context') as $key) { + if (empty($record[$key])) { + continue; + } + + if ($this->useShortAttachment) { + $attachment['fields'][] = $this->generateAttachmentField( + (string) $key, + $record[$key] + ); + } else { + // Add all extra fields as individual fields in attachment + $attachment['fields'] = array_merge( + $attachment['fields'], + $this->generateAttachmentFields($record[$key]) + ); + } + } + } + + $dataArray['attachments'] = array($attachment); + } else { + $dataArray['text'] = $message; + } + + if ($this->userIcon) { + if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) { + $dataArray['icon_url'] = $this->userIcon; + } else { + $dataArray['icon_emoji'] = ":{$this->userIcon}:"; + } + } + + return $dataArray; + } + + /** + * Returns a Slack message attachment color associated with + * provided level. + */ + public function getAttachmentColor(int $level): string + { + switch (true) { + case $level >= Logger::ERROR: + return static::COLOR_DANGER; + case $level >= Logger::WARNING: + return static::COLOR_WARNING; + case $level >= Logger::INFO: + return static::COLOR_GOOD; + default: + return static::COLOR_DEFAULT; + } + } + + /** + * Stringifies an array of key/value pairs to be used in attachment fields + * + * @param mixed[] $fields + */ + public function stringify(array $fields): string + { + /** @var Record $fields */ + $normalized = $this->normalizerFormatter->format($fields); + + $hasSecondDimension = count(array_filter($normalized, 'is_array')); + $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric')); + + return $hasSecondDimension || $hasNonNumericKeys + ? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS) + : Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS); + } + + /** + * Channel used by the bot when posting + * + * @param ?string $channel + * + * @return static + */ + public function setChannel(?string $channel = null): self + { + $this->channel = $channel; + + return $this; + } + + /** + * Username used by the bot when posting + * + * @param ?string $username + * + * @return static + */ + public function setUsername(?string $username = null): self + { + $this->username = $username; + + return $this; + } + + public function useAttachment(bool $useAttachment = true): self + { + $this->useAttachment = $useAttachment; + + return $this; + } + + public function setUserIcon(?string $userIcon = null): self + { + $this->userIcon = $userIcon; + + if (\is_string($userIcon)) { + $this->userIcon = trim($userIcon, ':'); + } + + return $this; + } + + public function useShortAttachment(bool $useShortAttachment = false): self + { + $this->useShortAttachment = $useShortAttachment; + + return $this; + } + + public function includeContextAndExtra(bool $includeContextAndExtra = false): self + { + $this->includeContextAndExtra = $includeContextAndExtra; + + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new NormalizerFormatter(); + } + + return $this; + } + + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields = []): self + { + $this->excludeFields = $excludeFields; + + return $this; + } + + public function setFormatter(?FormatterInterface $formatter = null): self + { + $this->formatter = $formatter; + + return $this; + } + + /** + * Generates attachment field + * + * @param string|mixed[] $value + * + * @return array{title: string, value: string, short: false} + */ + private function generateAttachmentField(string $title, $value): array + { + $value = is_array($value) + ? sprintf('```%s```', substr($this->stringify($value), 0, 1990)) + : $value; + + return array( + 'title' => ucfirst($title), + 'value' => $value, + 'short' => false, + ); + } + + /** + * Generates a collection of attachment fields from array + * + * @param mixed[] $data + * + * @return array + */ + private function generateAttachmentFields(array $data): array + { + /** @var Record $data */ + $normalized = $this->normalizerFormatter->format($data); + + $fields = array(); + foreach ($normalized as $key => $value) { + $fields[] = $this->generateAttachmentField((string) $key, $value); + } + + return $fields; + } + + /** + * Get a copy of record with fields excluded according to $this->excludeFields + * + * @phpstan-param FormattedRecord $record + * + * @return mixed[] + */ + private function removeExcludedFields(array $record): array + { + foreach ($this->excludeFields as $field) { + $keys = explode('.', $field); + $node = &$record; + $lastKey = end($keys); + foreach ($keys as $key) { + if (!isset($node[$key])) { + break; + } + if ($lastKey === $key) { + unset($node[$key]); + break; + } + $node = &$node[$key]; + } + } + + return $record; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php new file mode 100644 index 0000000000..a648513e0e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack API + * + * @author Greg Kedzierski + * @see https://api.slack.com/ + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SlackHandler extends SocketHandler +{ + /** + * Slack API token + * @var string + */ + private $token; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $token Slack API token + * @param string $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @throws MissingExtensionException If no OpenSSL PHP extension configured + */ + public function __construct( + string $token, + string $channel, + ?string $username = null, + bool $useAttachment = true, + ?string $iconEmoji = null, + $level = Logger::CRITICAL, + bool $bubble = true, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + array $excludeFields = array(), + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); + } + + parent::__construct( + 'ssl://slack.com:443', + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields + ); + + $this->token = $token; + } + + public function getSlackRecord(): SlackRecord + { + return $this->slackRecord; + } + + public function getToken(): string + { + return $this->token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + $dataArray = $this->prepareContentData($record); + + return http_build_query($dataArray); + } + + /** + * @phpstan-param FormattedRecord $record + * @return string[] + */ + protected function prepareContentData(array $record): array + { + $dataArray = $this->slackRecord->getSlackData($record); + $dataArray['token'] = $this->token; + + if (!empty($dataArray['attachments'])) { + $dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']); + } + + return $dataArray; + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST /api/chat.postMessage HTTP/1.1\r\n"; + $header .= "Host: slack.com\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + parent::write($record); + $this->finalizeWrite(); + } + + /** + * Finalizes the request by reading some bytes and then closing the socket + * + * If we do not read some but close the socket too early, slack sometimes + * drops the request entirely. + */ + protected function finalizeWrite(): void + { + $res = $this->getResource(); + if (is_resource($res)) { + @fread($res, 2048); + } + $this->closeSocket(); + } + + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter(): FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } + + /** + * Channel used by the bot when posting + */ + public function setChannel(string $channel): self + { + $this->slackRecord->setChannel($channel); + + return $this; + } + + /** + * Username used by the bot when posting + */ + public function setUsername(string $username): self + { + $this->slackRecord->setUsername($username); + + return $this; + } + + public function useAttachment(bool $useAttachment): self + { + $this->slackRecord->useAttachment($useAttachment); + + return $this; + } + + public function setIconEmoji(string $iconEmoji): self + { + $this->slackRecord->setUserIcon($iconEmoji); + + return $this; + } + + public function useShortAttachment(bool $useShortAttachment): self + { + $this->slackRecord->useShortAttachment($useShortAttachment); + + return $this; + } + + public function includeContextAndExtra(bool $includeContextAndExtra): self + { + $this->slackRecord->includeContextAndExtra($includeContextAndExtra); + + return $this; + } + + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields): self + { + $this->slackRecord->excludeFields($excludeFields); + + return $this; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php new file mode 100644 index 0000000000..8ae3c78829 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack Webhooks + * + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + */ +class SlackWebhookHandler extends AbstractProcessingHandler +{ + /** + * Slack Webhook token + * @var string + */ + private $webhookUrl; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $webhookUrl Slack Webhook URL + * @param string|null $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + */ + public function __construct( + string $webhookUrl, + ?string $channel = null, + ?string $username = null, + bool $useAttachment = true, + ?string $iconEmoji = null, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + $level = Logger::CRITICAL, + bool $bubble = true, + array $excludeFields = array() + ) { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler'); + } + + parent::__construct($level, $bubble); + + $this->webhookUrl = $webhookUrl; + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields + ); + } + + public function getSlackRecord(): SlackRecord + { + return $this->slackRecord; + } + + public function getWebhookUrl(): string + { + return $this->webhookUrl; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $postData = $this->slackRecord->getSlackData($record); + $postString = Utils::jsonEncode($postData); + + $ch = curl_init(); + $options = array( + CURLOPT_URL => $this->webhookUrl, + CURLOPT_POST => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => array('Content-type: application/json'), + CURLOPT_POSTFIELDS => $postString, + ); + if (defined('CURLOPT_SAFE_UPLOAD')) { + $options[CURLOPT_SAFE_UPLOAD] = true; + } + + curl_setopt_array($ch, $options); + + Curl\Util::execute($ch); + } + + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter(): FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php new file mode 100644 index 0000000000..21701afa25 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -0,0 +1,448 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SocketHandler extends AbstractProcessingHandler +{ + /** @var string */ + private $connectionString; + /** @var float */ + private $connectionTimeout; + /** @var resource|null */ + private $resource; + /** @var float */ + private $timeout; + /** @var float */ + private $writingTimeout; + /** @var ?int */ + private $lastSentBytes = null; + /** @var ?int */ + private $chunkSize; + /** @var bool */ + private $persistent; + /** @var ?int */ + private $errno = null; + /** @var ?string */ + private $errstr = null; + /** @var ?float */ + private $lastWritingAt = null; + + /** + * @param string $connectionString Socket connection string + * @param bool $persistent Flag to enable/disable persistent connections + * @param float $timeout Socket timeout to wait until the request is being aborted + * @param float $writingTimeout Socket timeout to wait until the request should've been sent/written + * @param float|null $connectionTimeout Socket connect timeout to wait until the connection should've been + * established + * @param int|null $chunkSize Sets the chunk size. Only has effect during connection in the writing cycle + * + * @throws \InvalidArgumentException If an invalid timeout value (less than 0) is passed. + */ + public function __construct( + string $connectionString, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + + if ($connectionTimeout !== null) { + $this->validateTimeout($connectionTimeout); + } + + $this->connectionTimeout = $connectionTimeout ?? (float) ini_get('default_socket_timeout'); + $this->persistent = $persistent; + $this->validateTimeout($timeout); + $this->timeout = $timeout; + $this->validateTimeout($writingTimeout); + $this->writingTimeout = $writingTimeout; + $this->chunkSize = $chunkSize; + } + + /** + * Connect (if necessary) and write to the socket + * + * {@inheritDoc} + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + protected function write(array $record): void + { + $this->connectIfNotConnected(); + $data = $this->generateDataStream($record); + $this->writeToSocket($data); + } + + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close(): void + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + + /** + * Close socket, if open + */ + public function closeSocket(): void + { + if (is_resource($this->resource)) { + fclose($this->resource); + $this->resource = null; + } + } + + /** + * Set socket connection to be persistent. It only has effect before the connection is initiated. + */ + public function setPersistent(bool $persistent): self + { + $this->persistent = $persistent; + + return $this; + } + + /** + * Set connection timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->connectionTimeout = $seconds; + + return $this; + } + + /** + * Set write timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->timeout = $seconds; + + return $this; + } + + /** + * Set writing timeout. Only has effect during connection in the writing cycle. + * + * @param float $seconds 0 for no timeout + */ + public function setWritingTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->writingTimeout = $seconds; + + return $this; + } + + /** + * Set chunk size. Only has effect during connection in the writing cycle. + */ + public function setChunkSize(int $bytes): self + { + $this->chunkSize = $bytes; + + return $this; + } + + /** + * Get current connection string + */ + public function getConnectionString(): string + { + return $this->connectionString; + } + + /** + * Get persistent setting + */ + public function isPersistent(): bool + { + return $this->persistent; + } + + /** + * Get current connection timeout setting + */ + public function getConnectionTimeout(): float + { + return $this->connectionTimeout; + } + + /** + * Get current in-transfer timeout + */ + public function getTimeout(): float + { + return $this->timeout; + } + + /** + * Get current local writing timeout + * + * @return float + */ + public function getWritingTimeout(): float + { + return $this->writingTimeout; + } + + /** + * Get current chunk size + */ + public function getChunkSize(): ?int + { + return $this->chunkSize; + } + + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + */ + public function isConnected(): bool + { + return is_resource($this->resource) + && !feof($this->resource); // on TCP - other party can close connection. + } + + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function pfsockopen() + { + return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function fsockopen() + { + return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + * + * @return bool + */ + protected function streamSetTimeout() + { + $seconds = floor($this->timeout); + $microseconds = round(($this->timeout - $seconds) * 1e6); + + if (!is_resource($this->resource)) { + throw new \LogicException('streamSetTimeout called but $this->resource is not a resource'); + } + + return stream_set_timeout($this->resource, (int) $seconds, (int) $microseconds); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-chunk-size.php + * + * @return int|bool + */ + protected function streamSetChunkSize() + { + if (!is_resource($this->resource)) { + throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource'); + } + + if (null === $this->chunkSize) { + throw new \LogicException('streamSetChunkSize called but $this->chunkSize is not set'); + } + + return stream_set_chunk_size($this->resource, $this->chunkSize); + } + + /** + * Wrapper to allow mocking + * + * @return int|bool + */ + protected function fwrite(string $data) + { + if (!is_resource($this->resource)) { + throw new \LogicException('fwrite called but $this->resource is not a resource'); + } + + return @fwrite($this->resource, $data); + } + + /** + * Wrapper to allow mocking + * + * @return mixed[]|bool + */ + protected function streamGetMetadata() + { + if (!is_resource($this->resource)) { + throw new \LogicException('streamGetMetadata called but $this->resource is not a resource'); + } + + return stream_get_meta_data($this->resource); + } + + private function validateTimeout(float $value): void + { + if ($value < 0) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)"); + } + } + + private function connectIfNotConnected(): void + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + + /** + * @phpstan-param FormattedRecord $record + */ + protected function generateDataStream(array $record): string + { + return (string) $record['formatted']; + } + + /** + * @return resource|null + */ + protected function getResource() + { + return $this->resource; + } + + private function connect(): void + { + $this->createSocketResource(); + $this->setSocketTimeout(); + $this->setStreamChunkSize(); + } + + private function createSocketResource(): void + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (is_bool($resource)) { + throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); + } + $this->resource = $resource; + } + + private function setSocketTimeout(): void + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + + private function setStreamChunkSize(): void + { + if ($this->chunkSize && !$this->streamSetChunkSize()) { + throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); + } + } + + private function writeToSocket(string $data): void + { + $length = strlen($data); + $sent = 0; + $this->lastSentBytes = $sent; + while ($this->isConnected() && $sent < $length) { + if (0 == $sent) { + $chunk = $this->fwrite($data); + } else { + $chunk = $this->fwrite(substr($data, $sent)); + } + if ($chunk === false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if (is_array($socketInfo) && $socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + + if ($this->writingIsTimedOut($sent)) { + throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); + } + } + + private function writingIsTimedOut(int $sent): bool + { + // convert to ms + if (0.0 == $this->writingTimeout) { + return false; + } + + if ($sent !== $this->lastSentBytes) { + $this->lastWritingAt = microtime(true); + $this->lastSentBytes = $sent; + + return false; + } else { + usleep(100); + } + + if ((microtime(true) - $this->lastWritingAt) >= $this->writingTimeout) { + $this->closeSocket(); + + return true; + } + + return false; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php new file mode 100644 index 0000000000..dcf282b454 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Aws\Sqs\SqsClient; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Writes to any sqs queue. + * + * @author Martijn van Calker + */ +class SqsHandler extends AbstractProcessingHandler +{ + /** 256 KB in bytes - maximum message size in SQS */ + protected const MAX_MESSAGE_SIZE = 262144; + /** 100 KB in bytes - head message size for new error log */ + protected const HEAD_MESSAGE_SIZE = 102400; + + /** @var SqsClient */ + private $client; + /** @var string */ + private $queueUrl; + + public function __construct(SqsClient $sqsClient, string $queueUrl, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->client = $sqsClient; + $this->queueUrl = $queueUrl; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!isset($record['formatted']) || 'string' !== gettype($record['formatted'])) { + throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record)); + } + + $messageBody = $record['formatted']; + if (strlen($messageBody) >= static::MAX_MESSAGE_SIZE) { + $messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE); + } + + $this->client->sendMessage([ + 'QueueUrl' => $this->queueUrl, + 'MessageBody' => $messageBody, + ]); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php new file mode 100644 index 0000000000..651835122e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class StreamHandler extends AbstractProcessingHandler +{ + /** @const int */ + protected const MAX_CHUNK_SIZE = 2147483647; + /** @const int 10MB */ + protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024; + /** @var int */ + protected $streamChunkSize; + /** @var resource|null */ + protected $stream; + /** @var ?string */ + protected $url = null; + /** @var ?string */ + private $errorMessage = null; + /** @var ?int */ + protected $filePermission; + /** @var bool */ + protected $useLocking; + /** @var true|null */ + private $dirCreated = null; + + /** + * @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + * + * @throws \InvalidArgumentException If stream is not a resource or string + */ + public function __construct($stream, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) + { + parent::__construct($level, $bubble); + + if (($phpMemoryLimit = Utils::expandIniShorthandBytes(ini_get('memory_limit'))) !== false) { + if ($phpMemoryLimit > 0) { + // use max 10% of allowed memory for the chunk size, and at least 100KB + $this->streamChunkSize = min(static::MAX_CHUNK_SIZE, max((int) ($phpMemoryLimit / 10), 100 * 1024)); + } else { + // memory is unlimited, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + } else { + // no memory limit information, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + + if (is_resource($stream)) { + $this->stream = $stream; + + stream_set_chunk_size($this->stream, $this->streamChunkSize); + } elseif (is_string($stream)) { + $this->url = Utils::canonicalizePath($stream); + } else { + throw new \InvalidArgumentException('A stream must either be a resource or a string.'); + } + + $this->filePermission = $filePermission; + $this->useLocking = $useLocking; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + if ($this->url && is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + $this->dirCreated = null; + } + + /** + * Return the currently active stream if it is open + * + * @return resource|null + */ + public function getStream() + { + return $this->stream; + } + + /** + * Return the stream URL if it was configured with a URL and not an active resource + * + * @return string|null + */ + public function getUrl(): ?string + { + return $this->url; + } + + /** + * @return int + */ + public function getStreamChunkSize(): int + { + return $this->streamChunkSize; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!is_resource($this->stream)) { + $url = $this->url; + if (null === $url || '' === $url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().' . Utils::getRecordMessageForException($record)); + } + $this->createDir($url); + $this->errorMessage = null; + set_error_handler([$this, 'customErrorHandler']); + $stream = fopen($url, 'a'); + if ($this->filePermission !== null) { + @chmod($url, $this->filePermission); + } + restore_error_handler(); + if (!is_resource($stream)) { + $this->stream = null; + + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened in append mode: '.$this->errorMessage, $url) . Utils::getRecordMessageForException($record)); + } + stream_set_chunk_size($stream, $this->streamChunkSize); + $this->stream = $stream; + } + + $stream = $this->stream; + if (!is_resource($stream)) { + throw new \LogicException('No stream was opened yet' . Utils::getRecordMessageForException($record)); + } + + if ($this->useLocking) { + // ignoring errors here, there's not much we can do about them + flock($stream, LOCK_EX); + } + + $this->streamWrite($stream, $record); + + if ($this->useLocking) { + flock($stream, LOCK_UN); + } + } + + /** + * Write to stream + * @param resource $stream + * @param array $record + * + * @phpstan-param FormattedRecord $record + */ + protected function streamWrite($stream, array $record): void + { + fwrite($stream, (string) $record['formatted']); + } + + private function customErrorHandler(int $code, string $msg): bool + { + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + + return true; + } + + private function getDirFromStream(string $stream): ?string + { + $pos = strpos($stream, '://'); + if ($pos === false) { + return dirname($stream); + } + + if ('file://' === substr($stream, 0, 7)) { + return dirname(substr($stream, 7)); + } + + return null; + } + + private function createDir(string $url): void + { + // Do not try to create dir if it has already been tried. + if ($this->dirCreated) { + return; + } + + $dir = $this->getDirFromStream($url); + if (null !== $dir && !is_dir($dir)) { + $this->errorMessage = null; + set_error_handler([$this, 'customErrorHandler']); + $status = mkdir($dir, 0777, true); + restore_error_handler(); + if (false === $status && !is_dir($dir) && strpos((string) $this->errorMessage, 'File exists') === false) { + throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and it could not be created: '.$this->errorMessage, $dir)); + } + } + $this->dirCreated = true; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000000..fae9251412 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Swift_Message; +use Swift; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since Monolog 2.6. Use SymfonyMailerHandler instead. + */ +class SwiftMailerHandler extends MailHandler +{ + /** @var \Swift_Mailer */ + protected $mailer; + /** @var Swift_Message|callable(string, Record[]): Swift_Message */ + private $messageTemplate; + + /** + * @psalm-param Swift_Message|callable(string, Record[]): Swift_Message $message + * + * @param \Swift_Mailer $mailer The mailer to use + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + @trigger_error('The SwiftMailerHandler is deprecated since Monolog 2.6. Use SymfonyMailerHandler instead.', E_USER_DEPRECATED); + + $this->mailer = $mailer; + $this->messageTemplate = $message; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format): FormatterInterface + { + return new LineFormatter($format); + } + + /** + * Creates instance of Swift_Message to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * @return Swift_Message + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records): Swift_Message + { + $message = null; + if ($this->messageTemplate instanceof Swift_Message) { + $message = clone $this->messageTemplate; + $message->generateId(); + } elseif (is_callable($this->messageTemplate)) { + $message = ($this->messageTemplate)($content, $records); + } + + if (!$message instanceof Swift_Message) { + $record = reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : '')); + } + + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); + } + + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (version_compare(Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(time()); + } + + return $message; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php new file mode 100644 index 0000000000..130e6f1f36 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\Email; + +/** + * SymfonyMailerHandler uses Symfony's Mailer component to send the emails + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class SymfonyMailerHandler extends MailHandler +{ + /** @var MailerInterface|TransportInterface */ + protected $mailer; + /** @var Email|callable(string, Record[]): Email */ + private $emailTemplate; + + /** + * @psalm-param Email|callable(string, Record[]): Email $email + * + * @param MailerInterface|TransportInterface $mailer The mailer to use + * @param callable|Email $email An email template, the subject/body will be replaced + */ + public function __construct($mailer, $email, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->mailer = $mailer; + $this->emailTemplate = $email; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format): FormatterInterface + { + return new LineFormatter($format); + } + + /** + * Creates instance of Email to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records): Email + { + $message = null; + if ($this->emailTemplate instanceof Email) { + $message = clone $this->emailTemplate; + } elseif (is_callable($this->emailTemplate)) { + $message = ($this->emailTemplate)($content, $records); + } + + if (!$message instanceof Email) { + $record = reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : '')); + } + + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->subject($subjectFormatter->format($this->getHighestRecord($records))); + } + + if ($this->isHtmlBody($content)) { + if (null !== ($charset = $message->getHtmlCharset())) { + $message->html($content, $charset); + } else { + $message->html($content); + } + } else { + if (null !== ($charset = $message->getTextCharset())) { + $message->text($content, $charset); + } else { + $message->text($content); + } + } + + return $message->date(new \DateTimeImmutable()); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php new file mode 100644 index 0000000000..1d543b7eca --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends AbstractSyslogHandler +{ + /** @var string */ + protected $ident; + /** @var int */ + protected $logopts; + + /** + * @param string $ident + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + */ + public function __construct(string $ident, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, int $logopts = LOG_PID) + { + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->logopts = $logopts; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + closelog(); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!openlog($this->ident, $this->logopts, $this->facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"' . Utils::getRecordMessageForException($record)); + } + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php new file mode 100644 index 0000000000..dbd8ef69d9 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\SyslogUdp; + +use Monolog\Utils; +use Socket; + +class UdpSocket +{ + protected const DATAGRAM_MAX_LENGTH = 65023; + + /** @var string */ + protected $ip; + /** @var int */ + protected $port; + /** @var resource|Socket|null */ + protected $socket = null; + + public function __construct(string $ip, int $port = 514) + { + $this->ip = $ip; + $this->port = $port; + } + + /** + * @param string $line + * @param string $header + * @return void + */ + public function write($line, $header = "") + { + $this->send($this->assembleMessage($line, $header)); + } + + public function close(): void + { + if (is_resource($this->socket) || $this->socket instanceof Socket) { + socket_close($this->socket); + $this->socket = null; + } + } + + /** + * @return resource|Socket + */ + protected function getSocket() + { + if (null !== $this->socket) { + return $this->socket; + } + + $domain = AF_INET; + $protocol = SOL_UDP; + // Check if we are using unix sockets. + if ($this->port === 0) { + $domain = AF_UNIX; + $protocol = IPPROTO_IP; + } + + $this->socket = socket_create($domain, SOCK_DGRAM, $protocol) ?: null; + if (null === $this->socket) { + throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' could not be opened via socket_create'); + } + + return $this->socket; + } + + protected function send(string $chunk): void + { + socket_sendto($this->getSocket(), $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port); + } + + protected function assembleMessage(string $line, string $header): string + { + $chunkSize = static::DATAGRAM_MAX_LENGTH - strlen($header); + + return $header . Utils::substr($line, 0, $chunkSize); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php new file mode 100644 index 0000000000..deaa19f80c --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use DateTimeInterface; +use Monolog\Logger; +use Monolog\Handler\SyslogUdp\UdpSocket; +use Monolog\Utils; + +/** + * A Handler for logging to a remote syslogd server. + * + * @author Jesper Skovgaard Nielsen + * @author Dominik Kukacka + */ +class SyslogUdpHandler extends AbstractSyslogHandler +{ + const RFC3164 = 0; + const RFC5424 = 1; + const RFC5424e = 2; + + /** @var array */ + private $dateFormats = array( + self::RFC3164 => 'M d H:i:s', + self::RFC5424 => \DateTime::RFC3339, + self::RFC5424e => \DateTime::RFC3339_EXTENDED, + ); + + /** @var UdpSocket */ + protected $socket; + /** @var string */ + protected $ident; + /** @var self::RFC* */ + protected $rfc; + + /** + * @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then) + * @param int $port Port number, or 0 if $host is a unix socket + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $ident Program name or tag for each log message. + * @param int $rfc RFC to format the message for. + * @throws MissingExtensionException + * + * @phpstan-param self::RFC* $rfc + */ + public function __construct(string $host, int $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424) + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler'); + } + + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->rfc = $rfc; + + $this->socket = new UdpSocket($host, $port); + } + + protected function write(array $record): void + { + $lines = $this->splitMessageIntoLines($record['formatted']); + + $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']], $record['datetime']); + + foreach ($lines as $line) { + $this->socket->write($line, $header); + } + } + + public function close(): void + { + $this->socket->close(); + } + + /** + * @param string|string[] $message + * @return string[] + */ + private function splitMessageIntoLines($message): array + { + if (is_array($message)) { + $message = implode("\n", $message); + } + + $lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY); + if (false === $lines) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + + return $lines; + } + + /** + * Make common syslog header (see rfc5424 or rfc3164) + */ + protected function makeCommonSyslogHeader(int $severity, DateTimeInterface $datetime): string + { + $priority = $severity + $this->facility; + + if (!$pid = getmypid()) { + $pid = '-'; + } + + if (!$hostname = gethostname()) { + $hostname = '-'; + } + + if ($this->rfc === self::RFC3164) { + // see https://github.com/phpstan/phpstan/issues/5348 + // @phpstan-ignore-next-line + $dateNew = $datetime->setTimezone(new \DateTimeZone('UTC')); + $date = $dateNew->format($this->dateFormats[$this->rfc]); + + return "<$priority>" . + $date . " " . + $hostname . " " . + $this->ident . "[" . $pid . "]: "; + } + + $date = $datetime->format($this->dateFormats[$this->rfc]); + + return "<$priority>1 " . + $date . " " . + $hostname . " " . + $this->ident . " " . + $pid . " - - "; + } + + /** + * Inject your own socket, mainly used for testing + */ + public function setSocket(UdpSocket $socket): self + { + $this->socket = $socket; + + return $this; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php new file mode 100644 index 0000000000..8912eba510 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use RuntimeException; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Handler send logs to Telegram using Telegram Bot API. + * + * How to use: + * 1) Create telegram bot with https://telegram.me/BotFather + * 2) Create a telegram channel where logs will be recorded. + * 3) Add created bot from step 1 to the created channel from step 2. + * + * Use telegram bot API key from step 1 and channel name with '@' prefix from step 2 to create instance of TelegramBotHandler + * + * @link https://core.telegram.org/bots/api + * + * @author Mazur Alexandr + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class TelegramBotHandler extends AbstractProcessingHandler +{ + private const BOT_API = 'https://api.telegram.org/bot'; + + /** + * The available values of parseMode according to the Telegram api documentation + */ + private const AVAILABLE_PARSE_MODES = [ + 'HTML', + 'MarkdownV2', + 'Markdown', // legacy mode without underline and strikethrough, use MarkdownV2 instead + ]; + + /** + * The maximum number of characters allowed in a message according to the Telegram api documentation + */ + private const MAX_MESSAGE_LENGTH = 4096; + + /** + * Telegram bot access token provided by BotFather. + * Create telegram bot with https://telegram.me/BotFather and use access token from it. + * @var string + */ + private $apiKey; + + /** + * Telegram channel name. + * Since to start with '@' symbol as prefix. + * @var string + */ + private $channel; + + /** + * The kind of formatting that is used for the message. + * See available options at https://core.telegram.org/bots/api#formatting-options + * or in AVAILABLE_PARSE_MODES + * @var ?string + */ + private $parseMode; + + /** + * Disables link previews for links in the message. + * @var ?bool + */ + private $disableWebPagePreview; + + /** + * Sends the message silently. Users will receive a notification with no sound. + * @var ?bool + */ + private $disableNotification; + + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @var bool + */ + private $splitLongMessages; + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @var bool + */ + private $delayBetweenMessages; + + /** + * @param string $apiKey Telegram bot access token provided by BotFather + * @param string $channel Telegram channel name + * @param bool $splitLongMessages Split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages + * @param bool $delayBetweenMessages Adds delay between sending a split message according to Telegram API + * @throws MissingExtensionException + */ + public function __construct( + string $apiKey, + string $channel, + $level = Logger::DEBUG, + bool $bubble = true, + string $parseMode = null, + bool $disableWebPagePreview = null, + bool $disableNotification = null, + bool $splitLongMessages = false, + bool $delayBetweenMessages = false + ) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the TelegramBotHandler'); + } + + parent::__construct($level, $bubble); + + $this->apiKey = $apiKey; + $this->channel = $channel; + $this->setParseMode($parseMode); + $this->disableWebPagePreview($disableWebPagePreview); + $this->disableNotification($disableNotification); + $this->splitLongMessages($splitLongMessages); + $this->delayBetweenMessages($delayBetweenMessages); + } + + public function setParseMode(string $parseMode = null): self + { + if ($parseMode !== null && !in_array($parseMode, self::AVAILABLE_PARSE_MODES)) { + throw new \InvalidArgumentException('Unknown parseMode, use one of these: ' . implode(', ', self::AVAILABLE_PARSE_MODES) . '.'); + } + + $this->parseMode = $parseMode; + + return $this; + } + + public function disableWebPagePreview(bool $disableWebPagePreview = null): self + { + $this->disableWebPagePreview = $disableWebPagePreview; + + return $this; + } + + public function disableNotification(bool $disableNotification = null): self + { + $this->disableNotification = $disableNotification; + + return $this; + } + + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @param bool $splitLongMessages + * @return $this + */ + public function splitLongMessages(bool $splitLongMessages = false): self + { + $this->splitLongMessages = $splitLongMessages; + + return $this; + } + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @param bool $delayBetweenMessages + * @return $this + */ + public function delayBetweenMessages(bool $delayBetweenMessages = false): self + { + $this->delayBetweenMessages = $delayBetweenMessages; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + /** @var Record[] $messages */ + $messages = []; + + foreach ($records as $record) { + if (!$this->isHandling($record)) { + continue; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $messages[] = $record; + } + + if (!empty($messages)) { + $this->send((string)$this->getFormatter()->formatBatch($messages)); + } + } + + /** + * @inheritDoc + */ + protected function write(array $record): void + { + $this->send($record['formatted']); + } + + /** + * Send request to @link https://api.telegram.org/bot on SendMessage action. + * @param string $message + */ + protected function send(string $message): void + { + $messages = $this->handleMessageLength($message); + + foreach ($messages as $key => $msg) { + if ($this->delayBetweenMessages && $key > 0) { + sleep(1); + } + + $this->sendCurl($msg); + } + } + + protected function sendCurl(string $message): void + { + $ch = curl_init(); + $url = self::BOT_API . $this->apiKey . '/SendMessage'; + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ + 'text' => $message, + 'chat_id' => $this->channel, + 'parse_mode' => $this->parseMode, + 'disable_web_page_preview' => $this->disableWebPagePreview, + 'disable_notification' => $this->disableNotification, + ])); + + $result = Curl\Util::execute($ch); + if (!is_string($result)) { + throw new RuntimeException('Telegram API error. Description: No response'); + } + $result = json_decode($result, true); + + if ($result['ok'] === false) { + throw new RuntimeException('Telegram API error. Description: ' . $result['description']); + } + } + + /** + * Handle a message that is too long: truncates or splits into several + * @param string $message + * @return string[] + */ + private function handleMessageLength(string $message): array + { + $truncatedMarker = ' (...truncated)'; + if (!$this->splitLongMessages && strlen($message) > self::MAX_MESSAGE_LENGTH) { + return [Utils::substr($message, 0, self::MAX_MESSAGE_LENGTH - strlen($truncatedMarker)) . $truncatedMarker]; + } + + return str_split($message, self::MAX_MESSAGE_LENGTH); + } +} diff --git a/pandora_console/vendor/psr/log/Psr/Log/Test/TestLogger.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php similarity index 52% rename from pandora_console/vendor/psr/log/Psr/Log/Test/TestLogger.php rename to pandora_console/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php index 1be3230496..0986da2708 100644 --- a/pandora_console/vendor/psr/log/Psr/Log/Test/TestLogger.php +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -1,14 +1,26 @@ - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ -use Psr\Log\AbstractLogger; +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; /** * Used for testing purposes. * * It records all records and gives you access to them for verification. * + * @author Jordi Boggiano + * * @method bool hasEmergency($record) * @method bool hasAlert($record) * @method bool hasCritical($record) @@ -53,41 +65,80 @@ use Psr\Log\AbstractLogger; * @method bool hasNoticeThatPasses($message) * @method bool hasInfoThatPasses($message) * @method bool hasDebugThatPasses($message) + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger */ -class TestLogger extends AbstractLogger +class TestHandler extends AbstractProcessingHandler { - /** - * @var array - */ - public $records = []; - - public $recordsByLevel = []; + /** @var Record[] */ + protected $records = []; + /** @var array */ + protected $recordsByLevel = []; + /** @var bool */ + private $skipReset = false; /** - * @inheritdoc + * @return array + * + * @phpstan-return Record[] */ - public function log($level, $message, array $context = []) + public function getRecords() { - $record = [ - 'level' => $level, - 'message' => $message, - 'context' => $context, - ]; - - $this->recordsByLevel[$record['level']][] = $record; - $this->records[] = $record; + return $this->records; } - public function hasRecords($level) + /** + * @return void + */ + public function clear() { - return isset($this->recordsByLevel[$level]); + $this->records = []; + $this->recordsByLevel = []; } - public function hasRecord($record, $level) + /** + * @return void + */ + public function reset() + { + if (!$this->skipReset) { + $this->clear(); + } + } + + /** + * @return void + */ + public function setSkipReset(bool $skipReset) + { + $this->skipReset = $skipReset; + } + + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecords($level): bool + { + return isset($this->recordsByLevel[Logger::toMonologLevel($level)]); + } + + /** + * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records + * @param string|int $level Logging level value or name + * + * @phpstan-param array{message: string, context?: mixed[]}|string $record + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecord($record, $level): bool { if (is_string($record)) { - $record = ['message' => $record]; + $record = array('message' => $record); } + return $this->hasRecordThatPasses(function ($rec) use ($record) { if ($rec['message'] !== $record['message']) { return false; @@ -95,53 +146,86 @@ class TestLogger extends AbstractLogger if (isset($record['context']) && $rec['context'] !== $record['context']) { return false; } + return true; }, $level); } - public function hasRecordThatContains($message, $level) + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatContains(string $message, $level): bool { return $this->hasRecordThatPasses(function ($rec) use ($message) { return strpos($rec['message'], $message) !== false; }, $level); } - public function hasRecordThatMatches($regex, $level) + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatMatches(string $regex, $level): bool { - return $this->hasRecordThatPasses(function ($rec) use ($regex) { + return $this->hasRecordThatPasses(function (array $rec) use ($regex): bool { return preg_match($regex, $rec['message']) > 0; }, $level); } + /** + * @param string|int $level Logging level value or name + * @return bool + * + * @psalm-param callable(Record, int): mixed $predicate + * @phpstan-param Level|LevelName|LogLevel::* $level + */ public function hasRecordThatPasses(callable $predicate, $level) { + $level = Logger::toMonologLevel($level); + if (!isset($this->recordsByLevel[$level])) { return false; } + foreach ($this->recordsByLevel[$level] as $i => $rec) { - if (call_user_func($predicate, $rec, $i)) { + if ($predicate($rec, $i)) { return true; } } + return false; } + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + /** + * @param string $method + * @param mixed[] $args + * @return bool + */ public function __call($method, $args) { if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; - $level = strtolower($matches[2]); - if (method_exists($this, $genericMethod)) { + $level = constant('Monolog\Logger::' . strtoupper($matches[2])); + $callback = [$this, $genericMethod]; + if (is_callable($callback)) { $args[] = $level; - return call_user_func_array([$this, $genericMethod], $args); + + return call_user_func_array($callback, $args); } } + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); } - - public function reset() - { - $this->records = []; - $this->recordsByLevel = []; - } } diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php new file mode 100644 index 0000000000..c81835288e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +trait WebRequestRecognizerTrait +{ + /** + * Checks if PHP's serving a web request + * @return bool + */ + protected function isWebRequest(): bool + { + return 'cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php new file mode 100644 index 0000000000..2dd136720e --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Forwards records to multiple handlers suppressing failures of each handler + * and continuing through to give every handler a chance to succeed. + * + * @author Craig D'Amelio + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class WhatFailureGroupHandler extends GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + } catch (\Throwable $e) { + // What failure? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = array(); + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + } catch (\Throwable $e) { + // What failure? + } + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php new file mode 100644 index 0000000000..ddd46d8c5c --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Logger; + +/** + * Handler sending logs to Zend Monitor + * + * @author Christian Bergau + * @author Jason Davis + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class ZendMonitorHandler extends AbstractProcessingHandler +{ + /** + * Monolog level / ZendMonitor Custom Event priority map + * + * @var array + */ + protected $levelMap = []; + + /** + * @throws MissingExtensionException + */ + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + if (!function_exists('zend_monitor_custom_event')) { + throw new MissingExtensionException( + 'You must have Zend Server installed with Zend Monitor enabled in order to use this handler' + ); + } + //zend monitor constants are not defined if zend monitor is not enabled. + $this->levelMap = [ + Logger::DEBUG => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::INFO => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::NOTICE => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::WARNING => \ZEND_MONITOR_EVENT_SEVERITY_WARNING, + Logger::ERROR => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::CRITICAL => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::ALERT => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::EMERGENCY => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + ]; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->writeZendMonitorCustomEvent( + Logger::getLevelName($record['level']), + $record['message'], + $record['formatted'], + $this->levelMap[$record['level']] + ); + } + + /** + * Write to Zend Monitor Events + * @param string $type Text displayed in "Class Name (custom)" field + * @param string $message Text displayed in "Error String" + * @param array $formatted Displayed in Custom Variables tab + * @param int $severity Set the event severity level (-1,0,1) + * + * @phpstan-param FormattedRecord $formatted + */ + protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void + { + zend_monitor_custom_event($type, $message, $formatted, $severity); + } + + /** + * {@inheritDoc} + */ + public function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter(); + } + + /** + * @return array + */ + public function getLevelMap(): array + { + return $this->levelMap; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/LogRecord.php b/pandora_console/vendor/monolog/monolog/src/Monolog/LogRecord.php new file mode 100644 index 0000000000..702807d718 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/LogRecord.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use ArrayAccess; + +/** + * Monolog log record interface for forward compatibility with Monolog 3.0 + * + * This is just present in Monolog 2.4+ to allow interoperable code to be written against + * both versions by type-hinting arguments as `array|\Monolog\LogRecord $record` + * + * Do not rely on this interface for other purposes, and do not implement it. + * + * @author Jordi Boggiano + * @template-extends \ArrayAccess<'message'|'level'|'context'|'level_name'|'channel'|'datetime'|'extra'|'formatted', mixed> + * @phpstan-import-type Record from Logger + */ +interface LogRecord extends \ArrayAccess +{ + /** + * @phpstan-return Record + */ + public function toArray(): array; +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Logger.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Logger.php new file mode 100644 index 0000000000..1ab75b9e09 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,701 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use DateTimeZone; +use Monolog\Handler\HandlerInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Throwable; +use Stringable; + +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + * + * @phpstan-type Level Logger::DEBUG|Logger::INFO|Logger::NOTICE|Logger::WARNING|Logger::ERROR|Logger::CRITICAL|Logger::ALERT|Logger::EMERGENCY + * @phpstan-type LevelName 'DEBUG'|'INFO'|'NOTICE'|'WARNING'|'ERROR'|'CRITICAL'|'ALERT'|'EMERGENCY' + * @phpstan-type Record array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[]} + */ +class Logger implements LoggerInterface, ResettableInterface +{ + /** + * Detailed debug information + */ + public const DEBUG = 100; + + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + public const INFO = 200; + + /** + * Uncommon events + */ + public const NOTICE = 250; + + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + public const WARNING = 300; + + /** + * Runtime errors + */ + public const ERROR = 400; + + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + public const CRITICAL = 500; + + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + public const ALERT = 550; + + /** + * Urgent alert. + */ + public const EMERGENCY = 600; + + /** + * Monolog API version + * + * This is only bumped when API breaks are done and should + * follow the major version of the library + * + * @var int + */ + public const API = 2; + + /** + * This is a static variable and not a constant to serve as an extension point for custom levels + * + * @var array $levels Logging levels with the levels as key + * + * @phpstan-var array $levels Logging levels with the levels as key + */ + protected static $levels = [ + self::DEBUG => 'DEBUG', + self::INFO => 'INFO', + self::NOTICE => 'NOTICE', + self::WARNING => 'WARNING', + self::ERROR => 'ERROR', + self::CRITICAL => 'CRITICAL', + self::ALERT => 'ALERT', + self::EMERGENCY => 'EMERGENCY', + ]; + + /** + * Mapping between levels numbers defined in RFC 5424 and Monolog ones + * + * @phpstan-var array $rfc_5424_levels + */ + private const RFC_5424_LEVELS = [ + 7 => self::DEBUG, + 6 => self::INFO, + 5 => self::NOTICE, + 4 => self::WARNING, + 3 => self::ERROR, + 2 => self::CRITICAL, + 1 => self::ALERT, + 0 => self::EMERGENCY, + ]; + + /** + * @var string + */ + protected $name; + + /** + * The handler stack + * + * @var HandlerInterface[] + */ + protected $handlers; + + /** + * Processors that will process all log records + * + * To process records of a single handler instead, add the processor on that specific handler + * + * @var callable[] + */ + protected $processors; + + /** + * @var bool + */ + protected $microsecondTimestamps = true; + + /** + * @var DateTimeZone + */ + protected $timezone; + + /** + * @var callable|null + */ + protected $exceptionHandler; + + /** + * @var int Keeps track of depth to prevent infinite logging loops + */ + private $logDepth = 0; + + /** + * @var bool Whether to detect infinite logging loops + * + * This can be disabled via {@see useLoggingLoopDetection} if you have async handlers that do not play well with this + */ + private $detectCycles = true; + + /** + * @psalm-param array $processors + * + * @param string $name The logging channel, a simple descriptive name that is attached to all log records + * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. + * @param callable[] $processors Optional array of processors + * @param DateTimeZone|null $timezone Optional timezone, if not provided date_default_timezone_get() will be used + */ + public function __construct(string $name, array $handlers = [], array $processors = [], ?DateTimeZone $timezone = null) + { + $this->name = $name; + $this->setHandlers($handlers); + $this->processors = $processors; + $this->timezone = $timezone ?: new DateTimeZone(date_default_timezone_get() ?: 'UTC'); + } + + public function getName(): string + { + return $this->name; + } + + /** + * Return a new cloned instance with the name changed + */ + public function withName(string $name): self + { + $new = clone $this; + $new->name = $name; + + return $new; + } + + /** + * Pushes a handler on to the stack. + */ + public function pushHandler(HandlerInterface $handler): self + { + array_unshift($this->handlers, $handler); + + return $this; + } + + /** + * Pops a handler from the stack + * + * @throws \LogicException If empty handler stack + */ + public function popHandler(): HandlerInterface + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + + return array_shift($this->handlers); + } + + /** + * Set handlers, replacing all existing ones. + * + * If a map is passed, keys will be ignored. + * + * @param HandlerInterface[] $handlers + */ + public function setHandlers(array $handlers): self + { + $this->handlers = []; + foreach (array_reverse($handlers) as $handler) { + $this->pushHandler($handler); + } + + return $this; + } + + /** + * @return HandlerInterface[] + */ + public function getHandlers(): array + { + return $this->handlers; + } + + /** + * Adds a processor on to the stack. + */ + public function pushProcessor(callable $callback): self + { + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * Removes the processor on top of the stack and returns it. + * + * @throws \LogicException If empty processor stack + * @return callable + */ + public function popProcessor(): callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * @return callable[] + */ + public function getProcessors(): array + { + return $this->processors; + } + + /** + * Control the use of microsecond resolution timestamps in the 'datetime' + * member of new records. + * + * As of PHP7.1 microseconds are always included by the engine, so + * there is no performance penalty and Monolog 2 enabled microseconds + * by default. This function lets you disable them though in case you want + * to suppress microseconds from the output. + * + * @param bool $micro True to use microtime() to create timestamps + */ + public function useMicrosecondTimestamps(bool $micro): self + { + $this->microsecondTimestamps = $micro; + + return $this; + } + + public function useLoggingLoopDetection(bool $detectCycles): self + { + $this->detectCycles = $detectCycles; + + return $this; + } + + /** + * Adds a log record. + * + * @param int $level The logging level (a Monolog or RFC 5424 level) + * @param string $message The log message + * @param mixed[] $context The log context + * @param DateTimeImmutable $datetime Optional log date to log into the past or future + * @return bool Whether the record has been processed + * + * @phpstan-param Level $level + */ + public function addRecord(int $level, string $message, array $context = [], DateTimeImmutable $datetime = null): bool + { + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + + if ($this->detectCycles) { + $this->logDepth += 1; + } + if ($this->logDepth === 3) { + $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.'); + return false; + } elseif ($this->logDepth >= 5) { // log depth 4 is let through so we can log the warning above + return false; + } + + try { + $record = null; + + foreach ($this->handlers as $handler) { + if (null === $record) { + // skip creating the record as long as no handler is going to handle it + if (!$handler->isHandling(['level' => $level])) { + continue; + } + + $levelName = static::getLevelName($level); + + $record = [ + 'message' => $message, + 'context' => $context, + 'level' => $level, + 'level_name' => $levelName, + 'channel' => $this->name, + 'datetime' => $datetime ?? new DateTimeImmutable($this->microsecondTimestamps, $this->timezone), + 'extra' => [], + ]; + + try { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + } catch (Throwable $e) { + $this->handleException($e, $record); + + return true; + } + } + + // once the record exists, send it to all handlers as long as the bubbling chain is not interrupted + try { + if (true === $handler->handle($record)) { + break; + } + } catch (Throwable $e) { + $this->handleException($e, $record); + + return true; + } + } + } finally { + if ($this->detectCycles) { + $this->logDepth--; + } + } + + return null !== $record; + } + + /** + * Ends a log cycle and frees all resources used by handlers. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * Handlers that have been closed should be able to accept log records again and re-open + * themselves on demand, but this may not always be possible depending on implementation. + * + * This is useful at the end of a request and will be called automatically on every handler + * when they get destructed. + */ + public function close(): void + { + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + + /** + * Ends a log cycle and resets all handlers and processors to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + */ + public function reset(): void + { + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } + } + + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } + } + + /** + * Gets all supported logging levels. + * + * @return array Assoc array with human-readable level names => level codes. + * @phpstan-return array + */ + public static function getLevels(): array + { + return array_flip(static::$levels); + } + + /** + * Gets the name of the logging level. + * + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level $level + * @phpstan-return LevelName + */ + public static function getLevelName(int $level): string + { + if (!isset(static::$levels[$level])) { + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels))); + } + + return static::$levels[$level]; + } + + /** + * Converts PSR-3 levels to Monolog ones if necessary + * + * @param string|int $level Level number (monolog) or name (PSR-3) + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level|LevelName|LogLevel::* $level + * @phpstan-return Level + */ + public static function toMonologLevel($level): int + { + if (is_string($level)) { + if (is_numeric($level)) { + /** @phpstan-ignore-next-line */ + return intval($level); + } + + // Contains chars of all log levels and avoids using strtoupper() which may have + // strange results depending on locale (for example, "i" will become "İ" in Turkish locale) + $upper = strtr($level, 'abcdefgilmnortuwy', 'ABCDEFGILMNORTUWY'); + if (defined(__CLASS__.'::'.$upper)) { + return constant(__CLASS__ . '::' . $upper); + } + + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels)); + } + + if (!is_int($level)) { + throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels)); + } + + return $level; + } + + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @phpstan-param Level $level + */ + public function isHandling(int $level): bool + { + $record = [ + 'level' => $level, + ]; + + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * Set a custom exception handler that will be called if adding a new record fails + * + * The callable will receive an exception object and the record that failed to be logged + */ + public function setExceptionHandler(?callable $callback): self + { + $this->exceptionHandler = $callback; + + return $this; + } + + public function getExceptionHandler(): ?callable + { + return $this->exceptionHandler; + } + + /** + * Adds a log record at an arbitrary level. + * + * This method allows for compatibility with common interfaces. + * + * @param mixed $level The log level (a Monolog, PSR-3 or RFC 5424 level) + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function log($level, $message, array $context = []): void + { + if (!is_int($level) && !is_string($level)) { + throw new \InvalidArgumentException('$level is expected to be a string or int'); + } + + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + + $level = static::toMonologLevel($level); + + $this->addRecord($level, (string) $message, $context); + } + + /** + * Adds a log record at the DEBUG level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function debug($message, array $context = []): void + { + $this->addRecord(static::DEBUG, (string) $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function info($message, array $context = []): void + { + $this->addRecord(static::INFO, (string) $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function notice($message, array $context = []): void + { + $this->addRecord(static::NOTICE, (string) $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function warning($message, array $context = []): void + { + $this->addRecord(static::WARNING, (string) $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function error($message, array $context = []): void + { + $this->addRecord(static::ERROR, (string) $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function critical($message, array $context = []): void + { + $this->addRecord(static::CRITICAL, (string) $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function alert($message, array $context = []): void + { + $this->addRecord(static::ALERT, (string) $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function emergency($message, array $context = []): void + { + $this->addRecord(static::EMERGENCY, (string) $message, $context); + } + + /** + * Sets the timezone to be used for the timestamp of log records. + */ + public function setTimezone(DateTimeZone $tz): self + { + $this->timezone = $tz; + + return $this; + } + + /** + * Returns the timezone to be used for the timestamp of log records. + */ + public function getTimezone(): DateTimeZone + { + return $this->timezone; + } + + /** + * Delegates exception management to the custom exception handler, + * or throws the exception if no custom handler is set. + * + * @param array $record + * @phpstan-param Record $record + */ + protected function handleException(Throwable $e, array $record): void + { + if (!$this->exceptionHandler) { + throw $e; + } + + ($this->exceptionHandler)($e, $record); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php new file mode 100644 index 0000000000..8166bdca29 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects Git branch and Git commit SHA in all records + * + * @author Nick Otter + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class GitProcessor implements ProcessorInterface +{ + /** @var int */ + private $level; + /** @var array{branch: string, commit: string}|array|null */ + private static $cache = null; + + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['git'] = self::getGitInfo(); + + return $record; + } + + /** + * @return array{branch: string, commit: string}|array + */ + private static function getGitInfo(): array + { + if (self::$cache) { + return self::$cache; + } + + $branches = `git branch -v --no-abbrev`; + if ($branches && preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) { + return self::$cache = [ + 'branch' => $matches[1], + 'commit' => $matches[2], + ]; + } + + return self::$cache = []; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php new file mode 100644 index 0000000000..91fda7d6da --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects value of gethostname in all records + */ +class HostnameProcessor implements ProcessorInterface +{ + /** @var string */ + private static $host; + + public function __construct() + { + self::$host = (string) gethostname(); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['hostname'] = self::$host; + + return $record; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php new file mode 100644 index 0000000000..a32e76b21a --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class IntrospectionProcessor implements ProcessorInterface +{ + /** @var int */ + private $level; + /** @var string[] */ + private $skipClassesPartials; + /** @var int */ + private $skipStackFramesCount; + /** @var string[] */ + private $skipFunctions = [ + 'call_user_func', + 'call_user_func_array', + ]; + + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * @param string[] $skipClassesPartials + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0) + { + $this->level = Logger::toMonologLevel($level); + $this->skipClassesPartials = array_merge(['Monolog\\'], $skipClassesPartials); + $this->skipStackFramesCount = $skipStackFramesCount; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + // skip first since it's always the current method + array_shift($trace); + // the call_user_func call is also skipped + array_shift($trace); + + $i = 0; + + while ($this->isTraceClassOrSkippedFunction($trace, $i)) { + if (isset($trace[$i]['class'])) { + foreach ($this->skipClassesPartials as $part) { + if (strpos($trace[$i]['class'], $part) !== false) { + $i++; + + continue 2; + } + } + } elseif (in_array($trace[$i]['function'], $this->skipFunctions)) { + $i++; + + continue; + } + + break; + } + + $i += $this->skipStackFramesCount; + + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + [ + 'file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, + 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'callType' => isset($trace[$i]['type']) ? $trace[$i]['type'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ] + ); + + return $record; + } + + /** + * @param array[] $trace + */ + private function isTraceClassOrSkippedFunction(array $trace, int $index): bool + { + if (!isset($trace[$index])) { + return false; + } + + return isset($trace[$index]['class']) || in_array($trace[$index]['function'], $this->skipFunctions); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100644 index 0000000000..37c756fcb3 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $usage = memory_get_peak_usage($this->realUsage); + + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + + $record['extra']['memory_peak_usage'] = $usage; + + return $record; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php new file mode 100644 index 0000000000..227deb7c86 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor implements ProcessorInterface +{ + /** + * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. + */ + protected $realUsage; + + /** + * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + protected $useFormatting; + + /** + * @param bool $realUsage Set this to true to get the real size of memory allocated from system. + * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + public function __construct(bool $realUsage = true, bool $useFormatting = true) + { + $this->realUsage = $realUsage; + $this->useFormatting = $useFormatting; + } + + /** + * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is + * + * @param int $bytes + * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as int + */ + protected function formatBytes(int $bytes) + { + if (!$this->useFormatting) { + return $bytes; + } + + if ($bytes > 1024 * 1024) { + return round($bytes / 1024 / 1024, 2).' MB'; + } elseif ($bytes > 1024) { + return round($bytes / 1024, 2).' KB'; + } + + return $bytes . ' B'; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php new file mode 100644 index 0000000000..e141921e95 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $usage = memory_get_usage($this->realUsage); + + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + + $record['extra']['memory_usage'] = $usage; + + return $record; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php new file mode 100644 index 0000000000..d4a628f552 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects Hg branch and Hg revision number in all records + * + * @author Jonathan A. Schweder + * + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class MercurialProcessor implements ProcessorInterface +{ + /** @var Level */ + private $level; + /** @var array{branch: string, revision: string}|array|null */ + private static $cache = null; + + /** + * @param int|string $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['hg'] = self::getMercurialInfo(); + + return $record; + } + + /** + * @return array{branch: string, revision: string}|array + */ + private static function getMercurialInfo(): array + { + if (self::$cache) { + return self::$cache; + } + + $result = explode(' ', trim(`hg id -nb`)); + + if (count($result) >= 3) { + return self::$cache = [ + 'branch' => $result[1], + 'revision' => $result[2], + ]; + } + + return self::$cache = []; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php new file mode 100644 index 0000000000..3b939a951b --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds value of getmypid into records + * + * @author Andreas Hörnicke + */ +class ProcessIdProcessor implements ProcessorInterface +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['process_id'] = getmypid(); + + return $record; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php new file mode 100644 index 0000000000..5defb7eb4f --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * An optional interface to allow labelling Monolog processors. + * + * @author Nicolas Grekas + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessorInterface +{ + /** + * @return array The processed record + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + public function __invoke(array $record); +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php new file mode 100644 index 0000000000..2c2a00e75f --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Utils; + +/** + * Processes a record's message according to PSR-3 rules + * + * It replaces {foo} with the value from $context['foo'] + * + * @author Jordi Boggiano + */ +class PsrLogMessageProcessor implements ProcessorInterface +{ + public const SIMPLE_DATE = "Y-m-d\TH:i:s.uP"; + + /** @var string|null */ + private $dateFormat; + + /** @var bool */ + private $removeUsedContextFields; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $removeUsedContextFields If set to true the fields interpolated into message gets unset + */ + public function __construct(?string $dateFormat = null, bool $removeUsedContextFields = false) + { + $this->dateFormat = $dateFormat; + $this->removeUsedContextFields = $removeUsedContextFields; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + if (false === strpos($record['message'], '{')) { + return $record; + } + + $replacements = []; + foreach ($record['context'] as $key => $val) { + $placeholder = '{' . $key . '}'; + if (strpos($record['message'], $placeholder) === false) { + continue; + } + + if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) { + $replacements[$placeholder] = $val; + } elseif ($val instanceof \DateTimeInterface) { + if (!$this->dateFormat && $val instanceof \Monolog\DateTimeImmutable) { + // handle monolog dates using __toString if no specific dateFormat was asked for + // so that it follows the useMicroseconds flag + $replacements[$placeholder] = (string) $val; + } else { + $replacements[$placeholder] = $val->format($this->dateFormat ?: static::SIMPLE_DATE); + } + } elseif (is_object($val)) { + $replacements[$placeholder] = '[object '.Utils::getClass($val).']'; + } elseif (is_array($val)) { + $replacements[$placeholder] = 'array'.Utils::jsonEncode($val, null, true); + } else { + $replacements[$placeholder] = '['.gettype($val).']'; + } + + if ($this->removeUsedContextFields) { + unset($record['context'][$key]); + } + } + + $record['message'] = strtr($record['message'], $replacements); + + return $record; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php new file mode 100644 index 0000000000..80f18747aa --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds a tags array into record + * + * @author Martijn Riemers + */ +class TagProcessor implements ProcessorInterface +{ + /** @var string[] */ + private $tags; + + /** + * @param string[] $tags + */ + public function __construct(array $tags = []) + { + $this->setTags($tags); + } + + /** + * @param string[] $tags + */ + public function addTags(array $tags = []): self + { + $this->tags = array_merge($this->tags, $tags); + + return $this; + } + + /** + * @param string[] $tags + */ + public function setTags(array $tags = []): self + { + $this->tags = $tags; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['tags'] = $this->tags; + + return $record; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php new file mode 100644 index 0000000000..a27b74dbf2 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\ResettableInterface; + +/** + * Adds a unique identifier into records + * + * @author Simon Mönch + */ +class UidProcessor implements ProcessorInterface, ResettableInterface +{ + /** @var string */ + private $uid; + + public function __construct(int $length = 7) + { + if ($length > 32 || $length < 1) { + throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); + } + + $this->uid = $this->generateUid($length); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['uid'] = $this->uid; + + return $record; + } + + public function getUid(): string + { + return $this->uid; + } + + public function reset() + { + $this->uid = $this->generateUid(strlen($this->uid)); + } + + private function generateUid(int $length): string + { + return substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000000..51850e17f7 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor implements ProcessorInterface +{ + /** + * @var array|\ArrayAccess + */ + protected $serverData; + + /** + * Default fields + * + * Array is structured as [key in record.extra => key in $serverData] + * + * @var array + */ + protected $extraFields = [ + 'url' => 'REQUEST_URI', + 'ip' => 'REMOTE_ADDR', + 'http_method' => 'REQUEST_METHOD', + 'server' => 'SERVER_NAME', + 'referrer' => 'HTTP_REFERER', + 'user_agent' => 'HTTP_USER_AGENT', + ]; + + /** + * @param array|\ArrayAccess|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data + * @param array|array|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data + */ + public function __construct($serverData = null, array $extraFields = null) + { + if (null === $serverData) { + $this->serverData = &$_SERVER; + } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + + $defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer']; + if (isset($this->serverData['UNIQUE_ID'])) { + $this->extraFields['unique_id'] = 'UNIQUE_ID'; + $defaultEnabled[] = 'unique_id'; + } + + if (null === $extraFields) { + $extraFields = $defaultEnabled; + } + if (isset($extraFields[0])) { + foreach (array_keys($this->extraFields) as $fieldName) { + if (!in_array($fieldName, $extraFields)) { + unset($this->extraFields[$fieldName]); + } + } + } else { + $this->extraFields = $extraFields; + } + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + + $record['extra'] = $this->appendExtraFields($record['extra']); + + return $record; + } + + public function addExtraField(string $extraName, string $serverName): self + { + $this->extraFields[$extraName] = $serverName; + + return $this; + } + + /** + * @param mixed[] $extra + * @return mixed[] + */ + private function appendExtraFields(array $extra): array + { + foreach ($this->extraFields as $extraName => $serverName) { + $extra[$extraName] = $this->serverData[$serverName] ?? null; + } + + return $extra; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Registry.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Registry.php new file mode 100644 index 0000000000..ae94ae6cc3 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Registry.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use InvalidArgumentException; + +/** + * Monolog log registry + * + * Allows to get `Logger` instances in the global scope + * via static method calls on this class. + * + * + * $application = new Monolog\Logger('application'); + * $api = new Monolog\Logger('api'); + * + * Monolog\Registry::addLogger($application); + * Monolog\Registry::addLogger($api); + * + * function testLogger() + * { + * Monolog\Registry::api()->error('Sent to $api Logger instance'); + * Monolog\Registry::application()->error('Sent to $application Logger instance'); + * } + * + * + * @author Tomas Tatarko + */ +class Registry +{ + /** + * List of all loggers in the registry (by named indexes) + * + * @var Logger[] + */ + private static $loggers = []; + + /** + * Adds new logging channel to the registry + * + * @param Logger $logger Instance of the logging channel + * @param string|null $name Name of the logging channel ($logger->getName() by default) + * @param bool $overwrite Overwrite instance in the registry if the given name already exists? + * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists + * @return void + */ + public static function addLogger(Logger $logger, ?string $name = null, bool $overwrite = false) + { + $name = $name ?: $logger->getName(); + + if (isset(self::$loggers[$name]) && !$overwrite) { + throw new InvalidArgumentException('Logger with the given name already exists'); + } + + self::$loggers[$name] = $logger; + } + + /** + * Checks if such logging channel exists by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function hasLogger($logger): bool + { + if ($logger instanceof Logger) { + $index = array_search($logger, self::$loggers, true); + + return false !== $index; + } + + return isset(self::$loggers[$logger]); + } + + /** + * Removes instance from registry by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function removeLogger($logger): void + { + if ($logger instanceof Logger) { + if (false !== ($idx = array_search($logger, self::$loggers, true))) { + unset(self::$loggers[$idx]); + } + } else { + unset(self::$loggers[$logger]); + } + } + + /** + * Clears the registry + */ + public static function clear(): void + { + self::$loggers = []; + } + + /** + * Gets Logger instance from the registry + * + * @param string $name Name of the requested Logger instance + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function getInstance($name): Logger + { + if (!isset(self::$loggers[$name])) { + throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name)); + } + + return self::$loggers[$name]; + } + + /** + * Gets Logger instance from the registry via static method call + * + * @param string $name Name of the requested Logger instance + * @param mixed[] $arguments Arguments passed to static method call + * @throws \InvalidArgumentException If named Logger instance is not in the registry + * @return Logger Requested instance of Logger + */ + public static function __callStatic($name, $arguments) + { + return self::getInstance($name); + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/ResettableInterface.php b/pandora_console/vendor/monolog/monolog/src/Monolog/ResettableInterface.php new file mode 100644 index 0000000000..2c5fd78511 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/ResettableInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +/** + * Handler or Processor implementing this interface will be reset when Logger::reset() is called. + * + * Resetting ends a log cycle gets them back to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + * + * @author Grégoire Pineau + */ +interface ResettableInterface +{ + /** + * @return void + */ + public function reset(); +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/SignalHandler.php b/pandora_console/vendor/monolog/monolog/src/Monolog/SignalHandler.php new file mode 100644 index 0000000000..d730eea3ad --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/SignalHandler.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use ReflectionExtension; + +/** + * Monolog POSIX signal handler + * + * @author Robert Gust-Bardon + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class SignalHandler +{ + /** @var LoggerInterface */ + private $logger; + + /** @var array SIG_DFL, SIG_IGN or previous callable */ + private $previousSignalHandler = []; + /** @var array */ + private $signalLevelMap = []; + /** @var array */ + private $signalRestartSyscalls = []; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @param int|string $level Level or level name + * @param bool $callPrevious + * @param bool $restartSyscalls + * @param bool|null $async + * @return $this + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function registerSignalHandler(int $signo, $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self + { + if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) { + return $this; + } + + $level = Logger::toMonologLevel($level); + + if ($callPrevious) { + $handler = pcntl_signal_get_handler($signo); + $this->previousSignalHandler[$signo] = $handler; + } else { + unset($this->previousSignalHandler[$signo]); + } + $this->signalLevelMap[$signo] = $level; + $this->signalRestartSyscalls[$signo] = $restartSyscalls; + + if ($async !== null) { + pcntl_async_signals($async); + } + + pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + + return $this; + } + + /** + * @param mixed $siginfo + */ + public function handleSignal(int $signo, $siginfo = null): void + { + static $signals = []; + + if (!$signals && extension_loaded('pcntl')) { + $pcntl = new ReflectionExtension('pcntl'); + // HHVM 3.24.2 returns an empty array. + foreach ($pcntl->getConstants() ?: get_defined_constants(true)['Core'] as $name => $value) { + if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && is_int($value)) { + $signals[$value] = $name; + } + } + } + + $level = $this->signalLevelMap[$signo] ?? LogLevel::CRITICAL; + $signal = $signals[$signo] ?? $signo; + $context = $siginfo ?? []; + $this->logger->log($level, sprintf('Program received signal %s', $signal), $context); + + if (!isset($this->previousSignalHandler[$signo])) { + return; + } + + if ($this->previousSignalHandler[$signo] === SIG_DFL) { + if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch') + && extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill') + ) { + $restartSyscalls = $this->signalRestartSyscalls[$signo] ?? true; + pcntl_signal($signo, SIG_DFL, $restartSyscalls); + pcntl_sigprocmask(SIG_UNBLOCK, [$signo], $oldset); + posix_kill(posix_getpid(), $signo); + pcntl_signal_dispatch(); + pcntl_sigprocmask(SIG_SETMASK, $oldset); + pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + } + } elseif (is_callable($this->previousSignalHandler[$signo])) { + $this->previousSignalHandler[$signo]($signo, $siginfo); + } + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Test/TestCase.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Test/TestCase.php new file mode 100644 index 0000000000..bc0b425ea4 --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Test/TestCase.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Test; + +use Monolog\Logger; +use Monolog\DateTimeImmutable; +use Monolog\Formatter\FormatterInterface; + +/** + * Lets you easily generate log records and a dummy formatter for testing purposes + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * + * @internal feel free to reuse this to test your own handlers, this is marked internal to avoid issues with PHPStorm https://github.com/Seldaek/monolog/issues/1677 + */ +class TestCase extends \PHPUnit\Framework\TestCase +{ + public function tearDown(): void + { + parent::tearDown(); + + if (isset($this->handler)) { + unset($this->handler); + } + } + + /** + * @param mixed[] $context + * + * @return array Record + * + * @phpstan-param Level $level + * @phpstan-return Record + */ + protected function getRecord(int $level = Logger::WARNING, string $message = 'test', array $context = []): array + { + return [ + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'test', + 'datetime' => new DateTimeImmutable(true), + 'extra' => [], + ]; + } + + /** + * @phpstan-return Record[] + */ + protected function getMultipleRecords(): array + { + return [ + $this->getRecord(Logger::DEBUG, 'debug message 1'), + $this->getRecord(Logger::DEBUG, 'debug message 2'), + $this->getRecord(Logger::INFO, 'information'), + $this->getRecord(Logger::WARNING, 'warning'), + $this->getRecord(Logger::ERROR, 'error'), + ]; + } + + protected function getIdentityFormatter(): FormatterInterface + { + $formatter = $this->createMock(FormatterInterface::class); + $formatter->expects($this->any()) + ->method('format') + ->will($this->returnCallback(function ($record) { + return $record['message']; + })); + + return $formatter; + } +} diff --git a/pandora_console/vendor/monolog/monolog/src/Monolog/Utils.php b/pandora_console/vendor/monolog/monolog/src/Monolog/Utils.php new file mode 100644 index 0000000000..360c42199c --- /dev/null +++ b/pandora_console/vendor/monolog/monolog/src/Monolog/Utils.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +final class Utils +{ + const DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR; + + public static function getClass(object $object): string + { + $class = \get_class($object); + + if (false === ($pos = \strpos($class, "@anonymous\0"))) { + return $class; + } + + if (false === ($parent = \get_parent_class($class))) { + return \substr($class, 0, $pos + 10); + } + + return $parent . '@anonymous'; + } + + public static function substr(string $string, int $start, ?int $length = null): string + { + if (extension_loaded('mbstring')) { + return mb_strcut($string, $start, $length); + } + + return substr($string, $start, (null === $length) ? strlen($string) : $length); + } + + /** + * Makes sure if a relative path is passed in it is turned into an absolute path + * + * @param string $streamUrl stream URL or path without protocol + */ + public static function canonicalizePath(string $streamUrl): string + { + $prefix = ''; + if ('file://' === substr($streamUrl, 0, 7)) { + $streamUrl = substr($streamUrl, 7); + $prefix = 'file://'; + } + + // other type of stream, not supported + if (false !== strpos($streamUrl, '://')) { + return $streamUrl; + } + + // already absolute + if (substr($streamUrl, 0, 1) === '/' || substr($streamUrl, 1, 1) === ':' || substr($streamUrl, 0, 2) === '\\\\') { + return $prefix.$streamUrl; + } + + $streamUrl = getcwd() . '/' . $streamUrl; + + return $prefix.$streamUrl; + } + + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @param int $encodeFlags flags to pass to json encode, defaults to DEFAULT_JSON_FLAGS + * @param bool $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null + * @throws \RuntimeException if encoding fails and errors are not ignored + * @return string when errors are ignored and the encoding fails, "null" is returned which is valid json for null + */ + public static function jsonEncode($data, ?int $encodeFlags = null, bool $ignoreErrors = false): string + { + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + + if ($ignoreErrors) { + $json = @json_encode($data, $encodeFlags); + if (false === $json) { + return 'null'; + } + + return $json; + } + + $json = json_encode($data, $encodeFlags); + if (false === $json) { + $json = self::handleJsonError(json_last_error(), $data); + } + + return $json; + } + + /** + * Handle a json_encode failure. + * + * If the failure is due to invalid string encoding, try to clean the + * input and encode again. If the second encoding attempt fails, the + * initial error is not encoding related or the input can't be cleaned then + * raise a descriptive exception. + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION + * @throws \RuntimeException if failure can't be corrected + * @return string JSON encoded data after error correction + */ + public static function handleJsonError(int $code, $data, ?int $encodeFlags = null): string + { + if ($code !== JSON_ERROR_UTF8) { + self::throwEncodeError($code, $data); + } + + if (is_string($data)) { + self::detectAndCleanUtf8($data); + } elseif (is_array($data)) { + array_walk_recursive($data, array('Monolog\Utils', 'detectAndCleanUtf8')); + } else { + self::throwEncodeError($code, $data); + } + + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + + $json = json_encode($data, $encodeFlags); + + if ($json === false) { + self::throwEncodeError(json_last_error(), $data); + } + + return $json; + } + + /** + * @internal + */ + public static function pcreLastErrorMessage(int $code): string + { + if (PHP_VERSION_ID >= 80000) { + return preg_last_error_msg(); + } + + $constants = (get_defined_constants(true))['pcre']; + $constants = array_filter($constants, function ($key) { + return substr($key, -6) == '_ERROR'; + }, ARRAY_FILTER_USE_KEY); + + $constants = array_flip($constants); + + return $constants[$code] ?? 'UNDEFINED_ERROR'; + } + + /** + * Throws an exception according to a given code with a customized message + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @throws \RuntimeException + * + * @return never + */ + private static function throwEncodeError(int $code, $data): void + { + switch ($code) { + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + } + + throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); + } + + /** + * Detect invalid UTF-8 string characters and convert to valid UTF-8. + * + * Valid UTF-8 input will be left unmodified, but strings containing + * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed + * original encoding of ISO-8859-15. This conversion may result in + * incorrect output if the actual encoding was not ISO-8859-15, but it + * will be clean UTF-8 output and will not rely on expensive and fragile + * detection algorithms. + * + * Function converts the input in place in the passed variable so that it + * can be used as a callback for array_walk_recursive. + * + * @param mixed $data Input to check and convert if needed, passed by ref + */ + private static function detectAndCleanUtf8(&$data): void + { + if (is_string($data) && !preg_match('//u', $data)) { + $data = preg_replace_callback( + '/[\x80-\xFF]+/', + function ($m) { + return function_exists('mb_convert_encoding') ? mb_convert_encoding($m[0], 'UTF-8', 'ISO-8859-1') : utf8_encode($m[0]); + }, + $data + ); + if (!is_string($data)) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to preg_replace_callback: ' . $pcreErrorCode . ' / ' . self::pcreLastErrorMessage($pcreErrorCode)); + } + $data = str_replace( + ['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'], + ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'], + $data + ); + } + } + + /** + * Converts a string with a valid 'memory_limit' format, to bytes. + * + * @param string|false $val + * @return int|false Returns an integer representing bytes. Returns FALSE in case of error. + */ + public static function expandIniShorthandBytes($val) + { + if (!is_string($val)) { + return false; + } + + // support -1 + if ((int) $val < 0) { + return (int) $val; + } + + if (!preg_match('/^\s*(?\d+)(?:\.\d+)?\s*(?[gmk]?)\s*$/i', $val, $match)) { + return false; + } + + $val = (int) $match['val']; + switch (strtolower($match['unit'] ?? '')) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; + } + + /** + * @param array $record + */ + public static function getRecordMessageForException(array $record): string + { + $context = ''; + $extra = ''; + try { + if ($record['context']) { + $context = "\nContext: " . json_encode($record['context']); + } + if ($record['extra']) { + $extra = "\nExtra: " . json_encode($record['extra']); + } + } catch (\Throwable $e) { + // noop + } + + return "\nThe exception occurred while attempting to log: " . $record['message'] . $context . $extra; + } +} diff --git a/pandora_console/vendor/psr/log/Psr/Log/AbstractLogger.php b/pandora_console/vendor/psr/log/Psr/Log/AbstractLogger.php deleted file mode 100644 index e02f9daf3d..0000000000 --- a/pandora_console/vendor/psr/log/Psr/Log/AbstractLogger.php +++ /dev/null @@ -1,128 +0,0 @@ -log(LogLevel::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function alert($message, array $context = array()) - { - $this->log(LogLevel::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function critical($message, array $context = array()) - { - $this->log(LogLevel::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function error($message, array $context = array()) - { - $this->log(LogLevel::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function warning($message, array $context = array()) - { - $this->log(LogLevel::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function notice($message, array $context = array()) - { - $this->log(LogLevel::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function info($message, array $context = array()) - { - $this->log(LogLevel::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function debug($message, array $context = array()) - { - $this->log(LogLevel::DEBUG, $message, $context); - } -} diff --git a/pandora_console/vendor/psr/log/Psr/Log/Test/DummyTest.php b/pandora_console/vendor/psr/log/Psr/Log/Test/DummyTest.php deleted file mode 100644 index 9638c11018..0000000000 --- a/pandora_console/vendor/psr/log/Psr/Log/Test/DummyTest.php +++ /dev/null @@ -1,18 +0,0 @@ - ". - * - * Example ->error('Foo') would yield "error Foo". - * - * @return string[] - */ - abstract public function getLogs(); - - public function testImplements() - { - $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); - } - - /** - * @dataProvider provideLevelsAndMessages - */ - public function testLogsAtAllLevels($level, $message) - { - $logger = $this->getLogger(); - $logger->{$level}($message, array('user' => 'Bob')); - $logger->log($level, $message, array('user' => 'Bob')); - - $expected = array( - $level.' message of level '.$level.' with context: Bob', - $level.' message of level '.$level.' with context: Bob', - ); - $this->assertEquals($expected, $this->getLogs()); - } - - public function provideLevelsAndMessages() - { - return array( - LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), - LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), - LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), - LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), - LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), - LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), - LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), - LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), - ); - } - - /** - * @expectedException \Psr\Log\InvalidArgumentException - */ - public function testThrowsOnInvalidLevel() - { - $logger = $this->getLogger(); - $logger->log('invalid level', 'Foo'); - } - - public function testContextReplacement() - { - $logger = $this->getLogger(); - $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); - - $expected = array('info {Message {nothing} Bob Bar a}'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testObjectCastToString() - { - if (method_exists($this, 'createPartialMock')) { - $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); - } else { - $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); - } - $dummy->expects($this->once()) - ->method('__toString') - ->will($this->returnValue('DUMMY')); - - $this->getLogger()->warning($dummy); - - $expected = array('warning DUMMY'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextCanContainAnything() - { - $closed = fopen('php://memory', 'r'); - fclose($closed); - - $context = array( - 'bool' => true, - 'null' => null, - 'string' => 'Foo', - 'int' => 0, - 'float' => 0.5, - 'nested' => array('with object' => new DummyTest), - 'object' => new \DateTime, - 'resource' => fopen('php://memory', 'r'), - 'closed' => $closed, - ); - - $this->getLogger()->warning('Crazy context data', $context); - - $expected = array('warning Crazy context data'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextExceptionKeyCanBeExceptionOrOtherValues() - { - $logger = $this->getLogger(); - $logger->warning('Random message', array('exception' => 'oops')); - $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); - - $expected = array( - 'warning Random message', - 'critical Uncaught Exception!' - ); - $this->assertEquals($expected, $this->getLogs()); - } -} diff --git a/pandora_console/vendor/psr/log/composer.json b/pandora_console/vendor/psr/log/composer.json index ca05695377..f3f066719d 100644 --- a/pandora_console/vendor/psr/log/composer.json +++ b/pandora_console/vendor/psr/log/composer.json @@ -11,16 +11,16 @@ } ], "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.0.x-dev" } } } diff --git a/pandora_console/vendor/psr/log/src/AbstractLogger.php b/pandora_console/vendor/psr/log/src/AbstractLogger.php new file mode 100644 index 0000000000..d60a091aff --- /dev/null +++ b/pandora_console/vendor/psr/log/src/AbstractLogger.php @@ -0,0 +1,15 @@ +log(LogLevel::EMERGENCY, $message, $context); } @@ -31,12 +31,12 @@ trait LoggerTrait * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void */ - public function alert($message, array $context = array()) + public function alert(string|\Stringable $message, array $context = []) { $this->log(LogLevel::ALERT, $message, $context); } @@ -46,12 +46,12 @@ trait LoggerTrait * * Example: Application component unavailable, unexpected exception. * - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void */ - public function critical($message, array $context = array()) + public function critical(string|\Stringable $message, array $context = []) { $this->log(LogLevel::CRITICAL, $message, $context); } @@ -60,12 +60,12 @@ trait LoggerTrait * Runtime errors that do not require immediate action but should typically * be logged and monitored. * - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void */ - public function error($message, array $context = array()) + public function error(string|\Stringable $message, array $context = []) { $this->log(LogLevel::ERROR, $message, $context); } @@ -76,12 +76,12 @@ trait LoggerTrait * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void */ - public function warning($message, array $context = array()) + public function warning(string|\Stringable $message, array $context = []) { $this->log(LogLevel::WARNING, $message, $context); } @@ -89,12 +89,12 @@ trait LoggerTrait /** * Normal but significant events. * - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void */ - public function notice($message, array $context = array()) + public function notice(string|\Stringable $message, array $context = []) { $this->log(LogLevel::NOTICE, $message, $context); } @@ -104,12 +104,12 @@ trait LoggerTrait * * Example: User logs in, SQL logs. * - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void */ - public function info($message, array $context = array()) + public function info(string|\Stringable $message, array $context = []) { $this->log(LogLevel::INFO, $message, $context); } @@ -117,12 +117,12 @@ trait LoggerTrait /** * Detailed debug information. * - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void */ - public function debug($message, array $context = array()) + public function debug(string|\Stringable $message, array $context = []) { $this->log(LogLevel::DEBUG, $message, $context); } @@ -131,12 +131,12 @@ trait LoggerTrait * Logs with an arbitrary level. * * @param mixed $level - * @param string $message + * @param string|\Stringable $message * @param array $context * * @return void * * @throws \Psr\Log\InvalidArgumentException */ - abstract public function log($level, $message, array $context = array()); + abstract public function log($level, string|\Stringable $message, array $context = []); } diff --git a/pandora_console/vendor/psr/log/Psr/Log/NullLogger.php b/pandora_console/vendor/psr/log/src/NullLogger.php similarity index 79% rename from pandora_console/vendor/psr/log/Psr/Log/NullLogger.php rename to pandora_console/vendor/psr/log/src/NullLogger.php index c8f7293b1c..5607705715 100644 --- a/pandora_console/vendor/psr/log/Psr/Log/NullLogger.php +++ b/pandora_console/vendor/psr/log/src/NullLogger.php @@ -16,14 +16,14 @@ class NullLogger extends AbstractLogger * Logs with an arbitrary level. * * @param mixed $level - * @param string $message - * @param array $context + * @param string|\Stringable $message + * @param array $context * * @return void * * @throws \Psr\Log\InvalidArgumentException */ - public function log($level, $message, array $context = array()) + public function log($level, string|\Stringable $message, array $context = []) { // noop } diff --git a/pandora_console/vendor/symfony/filesystem/CHANGELOG.md b/pandora_console/vendor/symfony/filesystem/CHANGELOG.md new file mode 100644 index 0000000000..fcb7170ca5 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/CHANGELOG.md @@ -0,0 +1,82 @@ +CHANGELOG +========= + +5.4 +--- + + * Add `Path` class + * Add `$lock` argument to `Filesystem::appendToFile()` + +5.0.0 +----- + + * `Filesystem::dumpFile()` and `appendToFile()` don't accept arrays anymore + +4.4.0 +----- + + * support for passing a `null` value to `Filesystem::isAbsolutePath()` is deprecated and will be removed in 5.0 + * `tempnam()` now accepts a third argument `$suffix`. + +4.3.0 +----- + + * support for passing arrays to `Filesystem::dumpFile()` is deprecated and will be removed in 5.0 + * support for passing arrays to `Filesystem::appendToFile()` is deprecated and will be removed in 5.0 + +4.0.0 +----- + + * removed `LockHandler` + * Support for passing relative paths to `Filesystem::makePathRelative()` has been removed. + +3.4.0 +----- + + * support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0 + +3.3.0 +----- + + * added `appendToFile()` to append contents to existing files + +3.2.0 +----- + + * added `readlink()` as a platform independent method to read links + +3.0.0 +----- + + * removed `$mode` argument from `Filesystem::dumpFile()` + +2.8.0 +----- + + * added tempnam() a stream aware version of PHP's native tempnam() + +2.6.0 +----- + + * added LockHandler + +2.3.12 +------ + + * deprecated dumpFile() file mode argument. + +2.3.0 +----- + + * added the dumpFile() method to atomically write files + +2.2.0 +----- + + * added a delete option for the mirror() method + +2.1.0 +----- + + * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value + * created the component diff --git a/pandora_console/vendor/symfony/filesystem/Exception/ExceptionInterface.php b/pandora_console/vendor/symfony/filesystem/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..fc438d9f31 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Romain Neutron + */ +interface ExceptionInterface extends \Throwable +{ +} diff --git a/pandora_console/vendor/symfony/filesystem/Exception/FileNotFoundException.php b/pandora_console/vendor/symfony/filesystem/Exception/FileNotFoundException.php new file mode 100644 index 0000000000..48b6408095 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Exception/FileNotFoundException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a file couldn't be found. + * + * @author Fabien Potencier + * @author Christian Gärtner + */ +class FileNotFoundException extends IOException +{ + public function __construct(string $message = null, int $code = 0, \Throwable $previous = null, string $path = null) + { + if (null === $message) { + if (null === $path) { + $message = 'File could not be found.'; + } else { + $message = sprintf('File "%s" could not be found.', $path); + } + } + + parent::__construct($message, $code, $previous, $path); + } +} diff --git a/pandora_console/vendor/symfony/filesystem/Exception/IOException.php b/pandora_console/vendor/symfony/filesystem/Exception/IOException.php new file mode 100644 index 0000000000..fea26e4ddc --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Exception/IOException.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a filesystem operation failure happens. + * + * @author Romain Neutron + * @author Christian Gärtner + * @author Fabien Potencier + */ +class IOException extends \RuntimeException implements IOExceptionInterface +{ + private $path; + + public function __construct(string $message, int $code = 0, \Throwable $previous = null, string $path = null) + { + $this->path = $path; + + parent::__construct($message, $code, $previous); + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->path; + } +} diff --git a/pandora_console/vendor/symfony/filesystem/Exception/IOExceptionInterface.php b/pandora_console/vendor/symfony/filesystem/Exception/IOExceptionInterface.php new file mode 100644 index 0000000000..42829ab6c2 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Exception/IOExceptionInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * IOException interface for file and input/output stream related exceptions thrown by the component. + * + * @author Christian Gärtner + */ +interface IOExceptionInterface extends ExceptionInterface +{ + /** + * Returns the associated path for the exception. + * + * @return string|null + */ + public function getPath(); +} diff --git a/pandora_console/vendor/symfony/filesystem/Exception/InvalidArgumentException.php b/pandora_console/vendor/symfony/filesystem/Exception/InvalidArgumentException.php new file mode 100644 index 0000000000..abadc20029 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * @author Christian Flothmann + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/pandora_console/vendor/symfony/filesystem/Exception/RuntimeException.php b/pandora_console/vendor/symfony/filesystem/Exception/RuntimeException.php new file mode 100644 index 0000000000..a7512dca73 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * @author Théo Fidry + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/pandora_console/vendor/symfony/filesystem/Filesystem.php b/pandora_console/vendor/symfony/filesystem/Filesystem.php new file mode 100644 index 0000000000..fafd364925 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Filesystem.php @@ -0,0 +1,769 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\FileNotFoundException; +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Exception\IOException; + +/** + * Provides basic utility to manipulate the file system. + * + * @author Fabien Potencier + */ +class Filesystem +{ + private static $lastError; + + /** + * Copies a file. + * + * If the target file is older than the origin file, it's always overwritten. + * If the target file is newer, it is overwritten only when the + * $overwriteNewerFiles option is set to true. + * + * @throws FileNotFoundException When originFile doesn't exist + * @throws IOException When copy fails + */ + public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false) + { + $originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://'); + if ($originIsLocal && !is_file($originFile)) { + throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); + } + + $this->mkdir(\dirname($targetFile)); + + $doCopy = true; + if (!$overwriteNewerFiles && null === parse_url($originFile, \PHP_URL_HOST) && is_file($targetFile)) { + $doCopy = filemtime($originFile) > filemtime($targetFile); + } + + if ($doCopy) { + // https://bugs.php.net/64634 + if (!$source = self::box('fopen', $originFile, 'r')) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile); + } + + // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default + if (!$target = self::box('fopen', $targetFile, 'w', false, stream_context_create(['ftp' => ['overwrite' => true]]))) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile); + } + + $bytesCopied = stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + unset($source, $target); + + if (!is_file($targetFile)) { + throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); + } + + if ($originIsLocal) { + // Like `cp`, preserve executable permission bits + self::box('chmod', $targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); + + if ($bytesCopied !== $bytesOrigin = filesize($originFile)) { + throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + } + } + } + } + + /** + * Creates a directory recursively. + * + * @param string|iterable $dirs The directory path + * + * @throws IOException On any directory creation failure + */ + public function mkdir($dirs, int $mode = 0777) + { + foreach ($this->toIterable($dirs) as $dir) { + if (is_dir($dir)) { + continue; + } + + if (!self::box('mkdir', $dir, $mode, true) && !is_dir($dir)) { + throw new IOException(sprintf('Failed to create "%s": ', $dir).self::$lastError, 0, null, $dir); + } + } + } + + /** + * Checks the existence of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to check + * + * @return bool + */ + public function exists($files) + { + $maxPathLength = \PHP_MAXPATHLEN - 2; + + foreach ($this->toIterable($files) as $file) { + if (\strlen($file) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file); + } + + if (!file_exists($file)) { + return false; + } + } + + return true; + } + + /** + * Sets access and modification time of file. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to create + * @param int|null $time The touch time as a Unix timestamp, if not supplied the current system time is used + * @param int|null $atime The access time as a Unix timestamp, if not supplied the current system time is used + * + * @throws IOException When touch fails + */ + public function touch($files, int $time = null, int $atime = null) + { + foreach ($this->toIterable($files) as $file) { + if (!($time ? self::box('touch', $file, $time, $atime) : self::box('touch', $file))) { + throw new IOException(sprintf('Failed to touch "%s": ', $file).self::$lastError, 0, null, $file); + } + } + } + + /** + * Removes files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to remove + * + * @throws IOException When removal fails + */ + public function remove($files) + { + if ($files instanceof \Traversable) { + $files = iterator_to_array($files, false); + } elseif (!\is_array($files)) { + $files = [$files]; + } + + self::doRemove($files, false); + } + + private static function doRemove(array $files, bool $isRecursive): void + { + $files = array_reverse($files); + foreach ($files as $file) { + if (is_link($file)) { + // See https://bugs.php.net/52176 + if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove symlink "%s": ', $file).self::$lastError); + } + } elseif (is_dir($file)) { + if (!$isRecursive) { + $tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-.')); + + if (file_exists($tmpName)) { + try { + self::doRemove([$tmpName], true); + } catch (IOException $e) { + } + } + + if (!file_exists($tmpName) && self::box('rename', $file, $tmpName)) { + $origFile = $file; + $file = $tmpName; + } else { + $origFile = null; + } + } + + $files = new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS); + self::doRemove(iterator_to_array($files, true), true); + + if (!self::box('rmdir', $file) && file_exists($file) && !$isRecursive) { + $lastError = self::$lastError; + + if (null !== $origFile && self::box('rename', $file, $origFile)) { + $file = $origFile; + } + + throw new IOException(sprintf('Failed to remove directory "%s": ', $file).$lastError); + } + } elseif (!self::box('unlink', $file) && (str_contains(self::$lastError, 'Permission denied') || file_exists($file))) { + throw new IOException(sprintf('Failed to remove file "%s": ', $file).self::$lastError); + } + } + } + + /** + * Change mode for an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change mode + * @param int $mode The new mode (octal) + * @param int $umask The mode mask (octal) + * @param bool $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fails + */ + public function chmod($files, int $mode, int $umask = 0000, bool $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if ((\PHP_VERSION_ID < 80000 || \is_int($mode)) && !self::box('chmod', $file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file "%s": ', $file).self::$lastError, 0, null, $file); + } + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); + } + } + } + + /** + * Change the owner of an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner + * @param string|int $user A user name or number + * @param bool $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fails + */ + public function chown($files, $user, bool $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chown(new \FilesystemIterator($file), $user, true); + } + if (is_link($file) && \function_exists('lchown')) { + if (!self::box('lchown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file); + } + } else { + if (!self::box('chown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file); + } + } + } + } + + /** + * Change the group of an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group + * @param string|int $group A group name or number + * @param bool $recursive Whether change the group recursively or not + * + * @throws IOException When the change fails + */ + public function chgrp($files, $group, bool $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chgrp(new \FilesystemIterator($file), $group, true); + } + if (is_link($file) && \function_exists('lchgrp')) { + if (!self::box('lchgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file); + } + } else { + if (!self::box('chgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file); + } + } + } + } + + /** + * Renames a file or a directory. + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename(string $origin, string $target, bool $overwrite = false) + { + // we check that target does not exist + if (!$overwrite && $this->isReadable($target)) { + throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); + } + + if (!self::box('rename', $origin, $target)) { + if (is_dir($origin)) { + // See https://bugs.php.net/54097 & https://php.net/rename#113943 + $this->mirror($origin, $target, null, ['override' => $overwrite, 'delete' => $overwrite]); + $this->remove($origin); + + return; + } + throw new IOException(sprintf('Cannot rename "%s" to "%s": ', $origin, $target).self::$lastError, 0, null, $target); + } + } + + /** + * Tells whether a file exists and is readable. + * + * @throws IOException When windows path is longer than 258 characters + */ + private function isReadable(string $filename): bool + { + $maxPathLength = \PHP_MAXPATHLEN - 2; + + if (\strlen($filename) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename); + } + + return is_readable($filename); + } + + /** + * Creates a symbolic link or copy a directory. + * + * @throws IOException When symlink fails + */ + public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false) + { + self::assertFunctionExists('symlink'); + + if ('\\' === \DIRECTORY_SEPARATOR) { + $originDir = strtr($originDir, '/', '\\'); + $targetDir = strtr($targetDir, '/', '\\'); + + if ($copyOnWindows) { + $this->mirror($originDir, $targetDir); + + return; + } + } + + $this->mkdir(\dirname($targetDir)); + + if (is_link($targetDir)) { + if (readlink($targetDir) === $originDir) { + return; + } + $this->remove($targetDir); + } + + if (!self::box('symlink', $originDir, $targetDir)) { + $this->linkException($originDir, $targetDir, 'symbolic'); + } + } + + /** + * Creates a hard link, or several hard links to a file. + * + * @param string|string[] $targetFiles The target file(s) + * + * @throws FileNotFoundException When original file is missing or not a file + * @throws IOException When link fails, including if link already exists + */ + public function hardlink(string $originFile, $targetFiles) + { + self::assertFunctionExists('link'); + + if (!$this->exists($originFile)) { + throw new FileNotFoundException(null, 0, null, $originFile); + } + + if (!is_file($originFile)) { + throw new FileNotFoundException(sprintf('Origin file "%s" is not a file.', $originFile)); + } + + foreach ($this->toIterable($targetFiles) as $targetFile) { + if (is_file($targetFile)) { + if (fileinode($originFile) === fileinode($targetFile)) { + continue; + } + $this->remove($targetFile); + } + + if (!self::box('link', $originFile, $targetFile)) { + $this->linkException($originFile, $targetFile, 'hard'); + } + } + } + + /** + * @param string $linkType Name of the link type, typically 'symbolic' or 'hard' + */ + private function linkException(string $origin, string $target, string $linkType) + { + if (self::$lastError) { + if ('\\' === \DIRECTORY_SEPARATOR && str_contains(self::$lastError, 'error code(1314)')) { + throw new IOException(sprintf('Unable to create "%s" link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); + } + } + throw new IOException(sprintf('Failed to create "%s" link from "%s" to "%s": ', $linkType, $origin, $target).self::$lastError, 0, null, $target); + } + + /** + * Resolves links in paths. + * + * With $canonicalize = false (default) + * - if $path does not exist or is not a link, returns null + * - if $path is a link, returns the next direct target of the link without considering the existence of the target + * + * With $canonicalize = true + * - if $path does not exist, returns null + * - if $path exists, returns its absolute fully resolved final version + * + * @return string|null + */ + public function readlink(string $path, bool $canonicalize = false) + { + if (!$canonicalize && !is_link($path)) { + return null; + } + + if ($canonicalize) { + if (!$this->exists($path)) { + return null; + } + + if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70410) { + $path = readlink($path); + } + + return realpath($path); + } + + if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70400) { + return realpath($path); + } + + return readlink($path); + } + + /** + * Given an existing path, convert it to a path relative to a given starting path. + * + * @return string + */ + public function makePathRelative(string $endPath, string $startPath) + { + if (!$this->isAbsolutePath($startPath)) { + throw new InvalidArgumentException(sprintf('The start path "%s" is not absolute.', $startPath)); + } + + if (!$this->isAbsolutePath($endPath)) { + throw new InvalidArgumentException(sprintf('The end path "%s" is not absolute.', $endPath)); + } + + // Normalize separators on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + $endPath = str_replace('\\', '/', $endPath); + $startPath = str_replace('\\', '/', $startPath); + } + + $splitDriveLetter = function ($path) { + return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) + ? [substr($path, 2), strtoupper($path[0])] + : [$path, null]; + }; + + $splitPath = function ($path) { + $result = []; + + foreach (explode('/', trim($path, '/')) as $segment) { + if ('..' === $segment) { + array_pop($result); + } elseif ('.' !== $segment && '' !== $segment) { + $result[] = $segment; + } + } + + return $result; + }; + + [$endPath, $endDriveLetter] = $splitDriveLetter($endPath); + [$startPath, $startDriveLetter] = $splitDriveLetter($startPath); + + $startPathArr = $splitPath($startPath); + $endPathArr = $splitPath($endPath); + + if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) { + // End path is on another drive, so no relative path exists + return $endDriveLetter.':/'.($endPathArr ? implode('/', $endPathArr).'/' : ''); + } + + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + ++$index; + } + + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + if (1 === \count($startPathArr) && '' === $startPathArr[0]) { + $depth = 0; + } else { + $depth = \count($startPathArr) - $index; + } + + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); + + $endPathRemainder = implode('/', \array_slice($endPathArr, $index)); + + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : ''); + + return '' === $relativePath ? './' : $relativePath; + } + + /** + * Mirrors a directory to another. + * + * Copies files and directories from the origin directory into the target directory. By default: + * + * - existing files in the target directory will be overwritten, except if they are newer (see the `override` option) + * - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option) + * + * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When file type is unknown + */ + public function mirror(string $originDir, string $targetDir, \Traversable $iterator = null, array $options = []) + { + $targetDir = rtrim($targetDir, '/\\'); + $originDir = rtrim($originDir, '/\\'); + $originDirLen = \strlen($originDir); + + if (!$this->exists($originDir)) { + throw new IOException(sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir); + } + + // Iterate in destination folder to remove obsolete entries + if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { + $deleteIterator = $iterator; + if (null === $deleteIterator) { + $flags = \FilesystemIterator::SKIP_DOTS; + $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); + } + $targetDirLen = \strlen($targetDir); + foreach ($deleteIterator as $file) { + $origin = $originDir.substr($file->getPathname(), $targetDirLen); + if (!$this->exists($origin)) { + $this->remove($file); + } + } + } + + $copyOnWindows = $options['copy_on_windows'] ?? false; + + if (null === $iterator) { + $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); + } + + $this->mkdir($targetDir); + $filesCreatedWhileMirroring = []; + + foreach ($iterator as $file) { + if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || isset($filesCreatedWhileMirroring[$file->getRealPath()])) { + continue; + } + + $target = $targetDir.substr($file->getPathname(), $originDirLen); + $filesCreatedWhileMirroring[$target] = true; + + if (!$copyOnWindows && is_link($file)) { + $this->symlink($file->getLinkTarget(), $target); + } elseif (is_dir($file)) { + $this->mkdir($target); + } elseif (is_file($file)) { + $this->copy($file, $target, $options['override'] ?? false); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } + } + + /** + * Returns whether the file path is an absolute path. + * + * @return bool + */ + public function isAbsolutePath(string $file) + { + return '' !== $file && (strspn($file, '/\\', 0, 1) + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] + && strspn($file, '/\\', 2, 1) + ) + || null !== parse_url($file, \PHP_URL_SCHEME) + ); + } + + /** + * Creates a temporary file with support for custom stream wrappers. + * + * @param string $prefix The prefix of the generated temporary filename + * Note: Windows uses only the first three characters of prefix + * @param string $suffix The suffix of the generated temporary filename + * + * @return string The new temporary filename (with path), or throw an exception on failure + */ + public function tempnam(string $dir, string $prefix/* , string $suffix = '' */) + { + $suffix = \func_num_args() > 2 ? func_get_arg(2) : ''; + [$scheme, $hierarchy] = $this->getSchemeAndHierarchy($dir); + + // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem + if ((null === $scheme || 'file' === $scheme || 'gs' === $scheme) && '' === $suffix) { + // If tempnam failed or no scheme return the filename otherwise prepend the scheme + if ($tmpFile = self::box('tempnam', $hierarchy, $prefix)) { + if (null !== $scheme && 'gs' !== $scheme) { + return $scheme.'://'.$tmpFile; + } + + return $tmpFile; + } + + throw new IOException('A temporary file could not be created: '.self::$lastError); + } + + // Loop until we create a valid temp file or have reached 10 attempts + for ($i = 0; $i < 10; ++$i) { + // Create a unique filename + $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true).$suffix; + + // Use fopen instead of file_exists as some streams do not support stat + // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability + if (!$handle = self::box('fopen', $tmpFile, 'x+')) { + continue; + } + + // Close the file if it was successfully opened + self::box('fclose', $handle); + + return $tmpFile; + } + + throw new IOException('A temporary file could not be created: '.self::$lastError); + } + + /** + * Atomically dumps content into a file. + * + * @param string|resource $content The data to write into the file + * + * @throws IOException if the file cannot be written to + */ + public function dumpFile(string $filename, $content) + { + if (\is_array($content)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); + } + + $dir = \dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } + + // Will create a temp file with 0600 access rights + // when the filesystem supports chmod. + $tmpFile = $this->tempnam($dir, basename($filename)); + + try { + if (false === self::box('file_put_contents', $tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename); + } + + self::box('chmod', $tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); + + $this->rename($tmpFile, $filename, true); + } finally { + if (file_exists($tmpFile)) { + self::box('unlink', $tmpFile); + } + } + } + + /** + * Appends content to an existing file. + * + * @param string|resource $content The content to append + * @param bool $lock Whether the file should be locked when writing to it + * + * @throws IOException If the file is not writable + */ + public function appendToFile(string $filename, $content/* , bool $lock = false */) + { + if (\is_array($content)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); + } + + $dir = \dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } + + $lock = \func_num_args() > 2 && func_get_arg(2); + + if (false === self::box('file_put_contents', $filename, $content, \FILE_APPEND | ($lock ? \LOCK_EX : 0))) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename); + } + } + + private function toIterable($files): iterable + { + return is_iterable($files) ? $files : [$files]; + } + + /** + * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> [file, tmp]). + */ + private function getSchemeAndHierarchy(string $filename): array + { + $components = explode('://', $filename, 2); + + return 2 === \count($components) ? [$components[0], $components[1]] : [null, $components[0]]; + } + + private static function assertFunctionExists(string $func): void + { + if (!\function_exists($func)) { + throw new IOException(sprintf('Unable to perform filesystem operation because the "%s()" function has been disabled.', $func)); + } + } + + /** + * @param mixed ...$args + * + * @return mixed + */ + private static function box(string $func, ...$args) + { + self::assertFunctionExists($func); + + self::$lastError = null; + set_error_handler(__CLASS__.'::handleError'); + try { + return $func(...$args); + } finally { + restore_error_handler(); + } + } + + /** + * @internal + */ + public static function handleError(int $type, string $msg) + { + self::$lastError = $msg; + } +} diff --git a/pandora_console/vendor/symfony/filesystem/LICENSE b/pandora_console/vendor/symfony/filesystem/LICENSE new file mode 100644 index 0000000000..88bf75bb4d --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2022 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pandora_console/vendor/symfony/filesystem/Path.php b/pandora_console/vendor/symfony/filesystem/Path.php new file mode 100644 index 0000000000..9aa37355a8 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/Path.php @@ -0,0 +1,819 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Exception\RuntimeException; + +/** + * Contains utility methods for handling path strings. + * + * The methods in this class are able to deal with both UNIX and Windows paths + * with both forward and backward slashes. All methods return normalized parts + * containing only forward slashes and no excess "." and ".." segments. + * + * @author Bernhard Schussek + * @author Thomas Schulz + * @author Théo Fidry + */ +final class Path +{ + /** + * The number of buffer entries that triggers a cleanup operation. + */ + private const CLEANUP_THRESHOLD = 1250; + + /** + * The buffer size after the cleanup operation. + */ + private const CLEANUP_SIZE = 1000; + + /** + * Buffers input/output of {@link canonicalize()}. + * + * @var array + */ + private static $buffer = []; + + /** + * @var int + */ + private static $bufferSize = 0; + + /** + * Canonicalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Furthermore, all "." and ".." segments are removed as far as possible. + * ".." segments at the beginning of relative paths are not removed. + * + * ```php + * echo Path::canonicalize("\symfony\puli\..\css\style.css"); + * // => /symfony/css/style.css + * + * echo Path::canonicalize("../css/./style.css"); + * // => ../css/style.css + * ``` + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function canonicalize(string $path): string + { + if ('' === $path) { + return ''; + } + + // This method is called by many other methods in this class. Buffer + // the canonicalized paths to make up for the severe performance + // decrease. + if (isset(self::$buffer[$path])) { + return self::$buffer[$path]; + } + + // Replace "~" with user's home directory. + if ('~' === $path[0]) { + $path = self::getHomeDirectory().substr($path, 1); + } + + $path = self::normalize($path); + + [$root, $pathWithoutRoot] = self::split($path); + + $canonicalParts = self::findCanonicalParts($root, $pathWithoutRoot); + + // Add the root directory again + self::$buffer[$path] = $canonicalPath = $root.implode('/', $canonicalParts); + ++self::$bufferSize; + + // Clean up regularly to prevent memory leaks + if (self::$bufferSize > self::CLEANUP_THRESHOLD) { + self::$buffer = \array_slice(self::$buffer, -self::CLEANUP_SIZE, null, true); + self::$bufferSize = self::CLEANUP_SIZE; + } + + return $canonicalPath; + } + + /** + * Normalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Contrary to {@link canonicalize()}, this method does not remove invalid + * or dot path segments. Consequently, it is much more efficient and should + * be used whenever the given path is known to be a valid, absolute system + * path. + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function normalize(string $path): string + { + return str_replace('\\', '/', $path); + } + + /** + * Returns the directory part of the path. + * + * This method is similar to PHP's dirname(), but handles various cases + * where dirname() returns a weird result: + * + * - dirname() does not accept backslashes on UNIX + * - dirname("C:/symfony") returns "C:", not "C:/" + * - dirname("C:/") returns ".", not "C:/" + * - dirname("C:") returns ".", not "C:/" + * - dirname("symfony") returns ".", not "" + * - dirname() does not canonicalize the result + * + * This method fixes these shortcomings and behaves like dirname() + * otherwise. + * + * The result is a canonical path. + * + * @return string The canonical directory part. Returns the root directory + * if the root directory is passed. Returns an empty string + * if a relative path is passed that contains no slashes. + * Returns an empty string if an empty string is passed. + */ + public static function getDirectory(string $path): string + { + if ('' === $path) { + return ''; + } + + $path = self::canonicalize($path); + + // Maintain scheme + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $scheme = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + if (false === $dirSeparatorPosition = strrpos($path, '/')) { + return ''; + } + + // Directory equals root directory "/" + if (0 === $dirSeparatorPosition) { + return $scheme.'/'; + } + + // Directory equals Windows root "C:/" + if (2 === $dirSeparatorPosition && ctype_alpha($path[0]) && ':' === $path[1]) { + return $scheme.substr($path, 0, 3); + } + + return $scheme.substr($path, 0, $dirSeparatorPosition); + } + + /** + * Returns canonical path of the user's home directory. + * + * Supported operating systems: + * + * - UNIX + * - Windows8 and upper + * + * If your operating system or environment isn't supported, an exception is thrown. + * + * The result is a canonical path. + * + * @throws RuntimeException If your operating system or environment isn't supported + */ + public static function getHomeDirectory(): string + { + // For UNIX support + if (getenv('HOME')) { + return self::canonicalize(getenv('HOME')); + } + + // For >= Windows8 support + if (getenv('HOMEDRIVE') && getenv('HOMEPATH')) { + return self::canonicalize(getenv('HOMEDRIVE').getenv('HOMEPATH')); + } + + throw new RuntimeException("Cannot find the home directory path: Your environment or operating system isn't supported."); + } + + /** + * Returns the root directory of a path. + * + * The result is a canonical path. + * + * @return string The canonical root directory. Returns an empty string if + * the given path is relative or empty. + */ + public static function getRoot(string $path): string + { + if ('' === $path) { + return ''; + } + + // Maintain scheme + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $scheme = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + $firstCharacter = $path[0]; + + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return $scheme.'/'; + } + + $length = \strlen($path); + + // Windows root + if ($length > 1 && ':' === $path[1] && ctype_alpha($firstCharacter)) { + // Special case: "C:" + if (2 === $length) { + return $scheme.$path.'/'; + } + + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return $scheme.$firstCharacter.$path[1].'/'; + } + } + + return ''; + } + + /** + * Returns the file name without the extension from a file path. + * + * @param string|null $extension if specified, only that extension is cut + * off (may contain leading dot) + */ + public static function getFilenameWithoutExtension(string $path, string $extension = null): string + { + if ('' === $path) { + return ''; + } + + if (null !== $extension) { + // remove extension and trailing dot + return rtrim(basename($path, $extension), '.'); + } + + return pathinfo($path, \PATHINFO_FILENAME); + } + + /** + * Returns the extension from a file path (without leading dot). + * + * @param bool $forceLowerCase forces the extension to be lower-case + */ + public static function getExtension(string $path, bool $forceLowerCase = false): string + { + if ('' === $path) { + return ''; + } + + $extension = pathinfo($path, \PATHINFO_EXTENSION); + + if ($forceLowerCase) { + $extension = self::toLower($extension); + } + + return $extension; + } + + /** + * Returns whether the path has an (or the specified) extension. + * + * @param string $path the path string + * @param string|string[]|null $extensions if null or not provided, checks if + * an extension exists, otherwise + * checks for the specified extension + * or array of extensions (with or + * without leading dot) + * @param bool $ignoreCase whether to ignore case-sensitivity + */ + public static function hasExtension(string $path, $extensions = null, bool $ignoreCase = false): bool + { + if ('' === $path) { + return false; + } + + $actualExtension = self::getExtension($path, $ignoreCase); + + // Only check if path has any extension + if ([] === $extensions || null === $extensions) { + return '' !== $actualExtension; + } + + if (\is_string($extensions)) { + $extensions = [$extensions]; + } + + foreach ($extensions as $key => $extension) { + if ($ignoreCase) { + $extension = self::toLower($extension); + } + + // remove leading '.' in extensions array + $extensions[$key] = ltrim($extension, '.'); + } + + return \in_array($actualExtension, $extensions, true); + } + + /** + * Changes the extension of a path string. + * + * @param string $path The path string with filename.ext to change. + * @param string $extension new extension (with or without leading dot) + * + * @return string the path string with new file extension + */ + public static function changeExtension(string $path, string $extension): string + { + if ('' === $path) { + return ''; + } + + $actualExtension = self::getExtension($path); + $extension = ltrim($extension, '.'); + + // No extension for paths + if ('/' === substr($path, -1)) { + return $path; + } + + // No actual extension in path + if (empty($actualExtension)) { + return $path.('.' === substr($path, -1) ? '' : '.').$extension; + } + + return substr($path, 0, -\strlen($actualExtension)).$extension; + } + + public static function isAbsolute(string $path): bool + { + if ('' === $path) { + return false; + } + + // Strip scheme + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $path = substr($path, $schemeSeparatorPosition + 3); + } + + $firstCharacter = $path[0]; + + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return true; + } + + // Windows root + if (\strlen($path) > 1 && ctype_alpha($firstCharacter) && ':' === $path[1]) { + // Special case: "C:" + if (2 === \strlen($path)) { + return true; + } + + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return true; + } + } + + return false; + } + + public static function isRelative(string $path): bool + { + return !self::isAbsolute($path); + } + + /** + * Turns a relative path into an absolute path in canonical form. + * + * Usually, the relative path is appended to the given base path. Dot + * segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * echo Path::makeAbsolute("../style.css", "/symfony/puli/css"); + * // => /symfony/puli/style.css + * ``` + * + * If an absolute path is passed, that path is returned unless its root + * directory is different than the one of the base path. In that case, an + * exception is thrown. + * + * ```php + * Path::makeAbsolute("/style.css", "/symfony/puli/css"); + * // => /style.css + * + * Path::makeAbsolute("C:/style.css", "C:/symfony/puli/css"); + * // => C:/style.css + * + * Path::makeAbsolute("C:/style.css", "/symfony/puli/css"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @param string $basePath an absolute base path + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path is an absolute path with + * a different root than the base path + */ + public static function makeAbsolute(string $path, string $basePath): string + { + if ('' === $basePath) { + throw new InvalidArgumentException(sprintf('The base path must be a non-empty string. Got: "%s".', $basePath)); + } + + if (!self::isAbsolute($basePath)) { + throw new InvalidArgumentException(sprintf('The base path "%s" is not an absolute path.', $basePath)); + } + + if (self::isAbsolute($path)) { + return self::canonicalize($path); + } + + if (false !== $schemeSeparatorPosition = strpos($basePath, '://')) { + $scheme = substr($basePath, 0, $schemeSeparatorPosition + 3); + $basePath = substr($basePath, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + return $scheme.self::canonicalize(rtrim($basePath, '/\\').'/'.$path); + } + + /** + * Turns a path into a relative path. + * + * The relative path is created relative to the given base path: + * + * ```php + * echo Path::makeRelative("/symfony/style.css", "/symfony/puli"); + * // => ../style.css + * ``` + * + * If a relative path is passed and the base path is absolute, the relative + * path is returned unchanged: + * + * ```php + * Path::makeRelative("style.css", "/symfony/puli/css"); + * // => style.css + * ``` + * + * If both paths are relative, the relative path is created with the + * assumption that both paths are relative to the same directory: + * + * ```php + * Path::makeRelative("style.css", "symfony/puli/css"); + * // => ../../../style.css + * ``` + * + * If both paths are absolute, their root directory must be the same, + * otherwise an exception is thrown: + * + * ```php + * Path::makeRelative("C:/symfony/style.css", "/symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the passed path is absolute, but the base path is not, an exception + * is thrown as well: + * + * ```php + * Path::makeRelative("/symfony/style.css", "symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path has a different root + * than the base path + */ + public static function makeRelative(string $path, string $basePath): string + { + $path = self::canonicalize($path); + $basePath = self::canonicalize($basePath); + + [$root, $relativePath] = self::split($path); + [$baseRoot, $relativeBasePath] = self::split($basePath); + + // If the base path is given as absolute path and the path is already + // relative, consider it to be relative to the given absolute path + // already + if ('' === $root && '' !== $baseRoot) { + // If base path is already in its root + if ('' === $relativeBasePath) { + $relativePath = ltrim($relativePath, './\\'); + } + + return $relativePath; + } + + // If the passed path is absolute, but the base path is not, we + // cannot generate a relative path + if ('' !== $root && '' === $baseRoot) { + throw new InvalidArgumentException(sprintf('The absolute path "%s" cannot be made relative to the relative path "%s". You should provide an absolute base path instead.', $path, $basePath)); + } + + // Fail if the roots of the two paths are different + if ($baseRoot && $root !== $baseRoot) { + throw new InvalidArgumentException(sprintf('The path "%s" cannot be made relative to "%s", because they have different roots ("%s" and "%s").', $path, $basePath, $root, $baseRoot)); + } + + if ('' === $relativeBasePath) { + return $relativePath; + } + + // Build a "../../" prefix with as many "../" parts as necessary + $parts = explode('/', $relativePath); + $baseParts = explode('/', $relativeBasePath); + $dotDotPrefix = ''; + + // Once we found a non-matching part in the prefix, we need to add + // "../" parts for all remaining parts + $match = true; + + foreach ($baseParts as $index => $basePart) { + if ($match && isset($parts[$index]) && $basePart === $parts[$index]) { + unset($parts[$index]); + + continue; + } + + $match = false; + $dotDotPrefix .= '../'; + } + + return rtrim($dotDotPrefix.implode('/', $parts), '/'); + } + + /** + * Returns whether the given path is on the local filesystem. + */ + public static function isLocal(string $path): bool + { + return '' !== $path && false === strpos($path, '://'); + } + + /** + * Returns the longest common base path in canonical form of a set of paths or + * `null` if the paths are on different Windows partitions. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/symfony/css/..' + * ); + * // => /symfony + * ``` + * + * The root is returned if no common base path can be found: + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/puli/css/..' + * ); + * // => / + * ``` + * + * If the paths are located on different Windows partitions, `null` is + * returned. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * 'C:/symfony/css/style.css', + * 'D:/symfony/css/..' + * ); + * // => null + * ``` + */ + public static function getLongestCommonBasePath(string ...$paths): ?string + { + [$bpRoot, $basePath] = self::split(self::canonicalize(reset($paths))); + + for (next($paths); null !== key($paths) && '' !== $basePath; next($paths)) { + [$root, $path] = self::split(self::canonicalize(current($paths))); + + // If we deal with different roots (e.g. C:/ vs. D:/), it's time + // to quit + if ($root !== $bpRoot) { + return null; + } + + // Make the base path shorter until it fits into path + while (true) { + if ('.' === $basePath) { + // No more base paths + $basePath = ''; + + // next path + continue 2; + } + + // Prevent false positives for common prefixes + // see isBasePath() + if (0 === strpos($path.'/', $basePath.'/')) { + // next path + continue 2; + } + + $basePath = \dirname($basePath); + } + } + + return $bpRoot.$basePath; + } + + /** + * Joins two or more path strings into a canonical path. + */ + public static function join(string ...$paths): string + { + $finalPath = null; + $wasScheme = false; + + foreach ($paths as $path) { + if ('' === $path) { + continue; + } + + if (null === $finalPath) { + // For first part we keep slashes, like '/top', 'C:\' or 'phar://' + $finalPath = $path; + $wasScheme = (false !== strpos($path, '://')); + continue; + } + + // Only add slash if previous part didn't end with '/' or '\' + if (!\in_array(substr($finalPath, -1), ['/', '\\'])) { + $finalPath .= '/'; + } + + // If first part included a scheme like 'phar://' we allow \current part to start with '/', otherwise trim + $finalPath .= $wasScheme ? $path : ltrim($path, '/'); + $wasScheme = false; + } + + if (null === $finalPath) { + return ''; + } + + return self::canonicalize($finalPath); + } + + /** + * Returns whether a path is a base path of another path. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * Path::isBasePath('/symfony', '/symfony/css'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony/..'); + * // => false + * + * Path::isBasePath('/symfony', '/puli'); + * // => false + * ``` + */ + public static function isBasePath(string $basePath, string $ofPath): bool + { + $basePath = self::canonicalize($basePath); + $ofPath = self::canonicalize($ofPath); + + // Append slashes to prevent false positives when two paths have + // a common prefix, for example /base/foo and /base/foobar. + // Don't append a slash for the root "/", because then that root + // won't be discovered as common prefix ("//" is not a prefix of + // "/foobar/"). + return 0 === strpos($ofPath.'/', rtrim($basePath, '/').'/'); + } + + /** + * @return string[] + */ + private static function findCanonicalParts(string $root, string $pathWithoutRoot): array + { + $parts = explode('/', $pathWithoutRoot); + + $canonicalParts = []; + + // Collapse "." and "..", if possible + foreach ($parts as $part) { + if ('.' === $part || '' === $part) { + continue; + } + + // Collapse ".." with the previous part, if one exists + // Don't collapse ".." if the previous part is also ".." + if ('..' === $part && \count($canonicalParts) > 0 && '..' !== $canonicalParts[\count($canonicalParts) - 1]) { + array_pop($canonicalParts); + + continue; + } + + // Only add ".." prefixes for relative paths + if ('..' !== $part || '' === $root) { + $canonicalParts[] = $part; + } + } + + return $canonicalParts; + } + + /** + * Splits a canonical path into its root directory and the remainder. + * + * If the path has no root directory, an empty root directory will be + * returned. + * + * If the root directory is a Windows style partition, the resulting root + * will always contain a trailing slash. + * + * list ($root, $path) = Path::split("C:/symfony") + * // => ["C:/", "symfony"] + * + * list ($root, $path) = Path::split("C:") + * // => ["C:/", ""] + * + * @return array{string, string} an array with the root directory and the remaining relative path + */ + private static function split(string $path): array + { + if ('' === $path) { + return ['', '']; + } + + // Remember scheme as part of the root, if any + if (false !== $schemeSeparatorPosition = strpos($path, '://')) { + $root = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $root = ''; + } + + $length = \strlen($path); + + // Remove and remember root directory + if (0 === strpos($path, '/')) { + $root .= '/'; + $path = $length > 1 ? substr($path, 1) : ''; + } elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) { + if (2 === $length) { + // Windows special case: "C:" + $root .= $path.'/'; + $path = ''; + } elseif ('/' === $path[2]) { + // Windows normal case: "C:/".. + $root .= substr($path, 0, 3); + $path = $length > 3 ? substr($path, 3) : ''; + } + } + + return [$root, $path]; + } + + private static function toLower(string $string): string + { + if (false !== $encoding = mb_detect_encoding($string, null, true)) { + return mb_strtolower($string, $encoding); + } + + return strtolower($string); + } + + private function __construct() + { + } +} diff --git a/pandora_console/vendor/symfony/filesystem/README.md b/pandora_console/vendor/symfony/filesystem/README.md new file mode 100644 index 0000000000..f2f6d45f73 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/README.md @@ -0,0 +1,13 @@ +Filesystem Component +==================== + +The Filesystem component provides basic utilities for the filesystem. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/filesystem.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/pandora_console/vendor/symfony/filesystem/composer.json b/pandora_console/vendor/symfony/filesystem/composer.json new file mode 100644 index 0000000000..e756104cd5 --- /dev/null +++ b/pandora_console/vendor/symfony/filesystem/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/filesystem", + "type": "library", + "description": "Provides basic utilities for the filesystem", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/pandora_console/vendor/symfony/polyfill-ctype/Ctype.php b/pandora_console/vendor/symfony/polyfill-ctype/Ctype.php new file mode 100644 index 0000000000..ba75a2c95f --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-ctype/Ctype.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Ctype; + +/** + * Ctype implementation through regex. + * + * @internal + * + * @author Gert de Pagter + */ +final class Ctype +{ + /** + * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. + * + * @see https://php.net/ctype-alnum + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alnum($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is a letter, FALSE otherwise. + * + * @see https://php.net/ctype-alpha + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alpha($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); + } + + /** + * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. + * + * @see https://php.net/ctype-cntrl + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_cntrl($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); + } + + /** + * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. + * + * @see https://php.net/ctype-digit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_digit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. + * + * @see https://php.net/ctype-graph + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_graph($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); + } + + /** + * Returns TRUE if every character in text is a lowercase letter. + * + * @see https://php.net/ctype-lower + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_lower($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); + } + + /** + * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. + * + * @see https://php.net/ctype-print + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_print($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); + } + + /** + * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. + * + * @see https://php.net/ctype-punct + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_punct($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); + } + + /** + * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. + * + * @see https://php.net/ctype-space + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_space($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); + } + + /** + * Returns TRUE if every character in text is an uppercase letter. + * + * @see https://php.net/ctype-upper + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_upper($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); + } + + /** + * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. + * + * @see https://php.net/ctype-xdigit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_xdigit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); + } + + /** + * Converts integers to their char versions according to normal ctype behaviour, if needed. + * + * If an integer between -128 and 255 inclusive is provided, + * it is interpreted as the ASCII value of a single character + * (negative values have 256 added in order to allow characters in the Extended ASCII range). + * Any other integer is interpreted as a string containing the decimal digits of the integer. + * + * @param mixed $int + * @param string $function + * + * @return mixed + */ + private static function convert_int_to_char_for_ctype($int, $function) + { + if (!\is_int($int)) { + return $int; + } + + if ($int < -128 || $int > 255) { + return (string) $int; + } + + if (\PHP_VERSION_ID >= 80100) { + @trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED); + } + + if ($int < 0) { + $int += 256; + } + + return \chr($int); + } +} diff --git a/pandora_console/vendor/symfony/polyfill-ctype/LICENSE b/pandora_console/vendor/symfony/polyfill-ctype/LICENSE new file mode 100644 index 0000000000..3f853aaf35 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-ctype/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2019 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pandora_console/vendor/symfony/polyfill-ctype/README.md b/pandora_console/vendor/symfony/polyfill-ctype/README.md new file mode 100644 index 0000000000..b144d03c3c --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-ctype/README.md @@ -0,0 +1,12 @@ +Symfony Polyfill / Ctype +======================== + +This component provides `ctype_*` functions to users who run php versions without the ctype extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/pandora_console/vendor/symfony/polyfill-ctype/bootstrap.php b/pandora_console/vendor/symfony/polyfill-ctype/bootstrap.php new file mode 100644 index 0000000000..d54524b31b --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-ctype/bootstrap.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('ctype_alnum')) { + function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit($text) { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph($text) { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower($text) { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print($text) { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct($text) { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space($text) { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper($text) { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); } +} diff --git a/pandora_console/vendor/symfony/polyfill-ctype/bootstrap80.php b/pandora_console/vendor/symfony/polyfill-ctype/bootstrap80.php new file mode 100644 index 0000000000..ab2f8611da --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-ctype/bootstrap80.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (!function_exists('ctype_alnum')) { + function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); } +} diff --git a/pandora_console/vendor/symfony/polyfill-ctype/composer.json b/pandora_console/vendor/symfony/polyfill-ctype/composer.json new file mode 100644 index 0000000000..1b3efff572 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-ctype/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/polyfill-ctype", + "type": "library", + "description": "Symfony polyfill for ctype functions", + "keywords": ["polyfill", "compatibility", "portable", "ctype"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/pandora_console/vendor/symfony/polyfill-mbstring/Mbstring.php b/pandora_console/vendor/symfony/polyfill-mbstring/Mbstring.php index b65c54a6b5..bce5c4a84a 100644 --- a/pandora_console/vendor/symfony/polyfill-mbstring/Mbstring.php +++ b/pandora_console/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -80,7 +80,7 @@ final class Mbstring public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { - if (\is_array($fromEncoding) || ($fromEncoding !== null && false !== strpos($fromEncoding, ','))) { + if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) { $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); } else { $fromEncoding = self::getEncoding($fromEncoding); @@ -102,7 +102,7 @@ final class Mbstring $fromEncoding = 'Windows-1252'; } if ('UTF-8' !== $fromEncoding) { - $s = \iconv($fromEncoding, 'UTF-8//IGNORE', $s); + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); } return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); @@ -113,7 +113,7 @@ final class Mbstring $fromEncoding = 'UTF-8'; } - return \iconv($fromEncoding, $toEncoding.'//IGNORE', $s); + return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); } public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars) @@ -130,7 +130,7 @@ final class Mbstring public static function mb_decode_mimeheader($s) { - return \iconv_mime_decode($s, 2, self::$internalEncoding); + return iconv_mime_decode($s, 2, self::$internalEncoding); } public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) @@ -140,7 +140,7 @@ final class Mbstring public static function mb_decode_numericentity($s, $convmap, $encoding = null) { - if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; @@ -150,7 +150,7 @@ final class Mbstring return false; } - if (null !== $encoding && !is_scalar($encoding)) { + if (null !== $encoding && !\is_scalar($encoding)) { trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return ''; // Instead of null (cf. mb_encode_numericentity). @@ -166,10 +166,10 @@ final class Mbstring if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { - $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } $cnt = floor(\count($convmap) / 4) * 4; @@ -195,12 +195,12 @@ final class Mbstring return $s; } - return \iconv('UTF-8', $encoding.'//IGNORE', $s); + return iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) { - if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; @@ -210,13 +210,13 @@ final class Mbstring return false; } - if (null !== $encoding && !is_scalar($encoding)) { + if (null !== $encoding && !\is_scalar($encoding)) { trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; // Instead of '' (cf. mb_decode_numericentity). } - if (null !== $is_hex && !is_scalar($is_hex)) { + if (null !== $is_hex && !\is_scalar($is_hex)) { trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING); return null; @@ -232,10 +232,10 @@ final class Mbstring if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { - $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; @@ -265,7 +265,7 @@ final class Mbstring return $result; } - return \iconv('UTF-8', $encoding.'//IGNORE', $result); + return iconv('UTF-8', $encoding.'//IGNORE', $result); } public static function mb_convert_case($s, $mode, $encoding = null) @@ -280,10 +280,10 @@ final class Mbstring if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { - $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } if (\MB_CASE_TITLE == $mode) { @@ -343,7 +343,7 @@ final class Mbstring return $s; } - return \iconv('UTF-8', $encoding.'//IGNORE', $s); + return iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_internal_encoding($encoding = null) @@ -354,7 +354,7 @@ final class Mbstring $normalizedEncoding = self::getEncoding($encoding); - if ('UTF-8' === $normalizedEncoding || false !== @\iconv($normalizedEncoding, $normalizedEncoding, ' ')) { + if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) { self::$internalEncoding = $normalizedEncoding; return true; @@ -413,7 +413,7 @@ final class Mbstring $encoding = self::$internalEncoding; } - return self::mb_detect_encoding($var, [$encoding]) || false !== @\iconv($encoding, $encoding, $var); + return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var); } public static function mb_detect_encoding($str, $encodingList = null, $strict = false) @@ -488,7 +488,7 @@ final class Mbstring return \strlen($s); } - return @\iconv_strlen($s, $encoding); + return @iconv_strlen($s, $encoding); } public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) @@ -509,7 +509,7 @@ final class Mbstring return 0; } - return \iconv_strpos($haystack, $needle, $offset, $encoding); + return iconv_strpos($haystack, $needle, $offset, $encoding); } public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) @@ -533,7 +533,7 @@ final class Mbstring } $pos = '' !== $needle || 80000 > \PHP_VERSION_ID - ? \iconv_strrpos($haystack, $needle, $encoding) + ? iconv_strrpos($haystack, $needle, $encoding) : self::mb_strlen($haystack, $encoding); return false !== $pos ? $offset + $pos : false; @@ -541,7 +541,7 @@ final class Mbstring public static function mb_str_split($string, $split_length = 1, $encoding = null) { - if (null !== $string && !is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { + if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING); return null; @@ -550,6 +550,7 @@ final class Mbstring if (1 > $split_length = (int) $split_length) { if (80000 > \PHP_VERSION_ID) { trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); + return false; } @@ -568,7 +569,7 @@ final class Mbstring } $rx .= '.{'.$split_length.'})/us'; - return preg_split($rx, $string, null, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); + return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); } $result = []; @@ -617,7 +618,7 @@ final class Mbstring } if ($start < 0) { - $start = \iconv_strlen($s, $encoding) + $start; + $start = iconv_strlen($s, $encoding) + $start; if ($start < 0) { $start = 0; } @@ -626,13 +627,13 @@ final class Mbstring if (null === $length) { $length = 2147483647; } elseif ($length < 0) { - $length = \iconv_strlen($s, $encoding) + $length - $start; + $length = iconv_strlen($s, $encoding) + $length - $start; if ($length < 0) { return ''; } } - return (string) \iconv_substr($s, $start, $length, $encoding); + return (string) iconv_substr($s, $start, $length, $encoding); } public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) @@ -657,7 +658,7 @@ final class Mbstring $pos = strrpos($haystack, $needle); } else { $needle = self::mb_substr($needle, 0, 1, $encoding); - $pos = \iconv_strrpos($haystack, $needle, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); } return self::getSubpart($pos, $part, $haystack, $encoding); @@ -736,12 +737,12 @@ final class Mbstring $encoding = self::getEncoding($encoding); if ('UTF-8' !== $encoding) { - $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); - return ($wide << 1) + \iconv_strlen($s, 'UTF-8'); + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); } public static function mb_substr_count($haystack, $needle, $encoding = null) diff --git a/pandora_console/vendor/symfony/polyfill-mbstring/README.md b/pandora_console/vendor/symfony/polyfill-mbstring/README.md index 4efb599d81..478b40da25 100644 --- a/pandora_console/vendor/symfony/polyfill-mbstring/README.md +++ b/pandora_console/vendor/symfony/polyfill-mbstring/README.md @@ -5,7 +5,7 @@ This component provides a partial, native PHP implementation for the [Mbstring](https://php.net/mbstring) extension. More information can be found in the -[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). License ======= diff --git a/pandora_console/vendor/symfony/polyfill-mbstring/bootstrap80.php b/pandora_console/vendor/symfony/polyfill-mbstring/bootstrap80.php index e0a1eadcc3..82f5ac4d0f 100644 --- a/pandora_console/vendor/symfony/polyfill-mbstring/bootstrap80.php +++ b/pandora_console/vendor/symfony/polyfill-mbstring/bootstrap80.php @@ -122,7 +122,7 @@ if (!function_exists('mb_chr')) { function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); } } if (!function_exists('mb_scrub')) { - function mb_scrub(?string $string, ?string $encoding = null): string { $encoding = ($encoding ?? mb_internal_encoding()); return mb_convert_encoding((string) $string, $encoding, $encoding); } + function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); } } if (!function_exists('mb_str_split')) { function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); } diff --git a/pandora_console/vendor/symfony/polyfill-mbstring/composer.json b/pandora_console/vendor/symfony/polyfill-mbstring/composer.json index 1fa21ca16c..44895536ba 100644 --- a/pandora_console/vendor/symfony/polyfill-mbstring/composer.json +++ b/pandora_console/vendor/symfony/polyfill-mbstring/composer.json @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", diff --git a/pandora_console/vendor/symfony/polyfill-php80/LICENSE b/pandora_console/vendor/symfony/polyfill-php80/LICENSE new file mode 100644 index 0000000000..5593b1d84f --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pandora_console/vendor/symfony/polyfill-php80/Php80.php b/pandora_console/vendor/symfony/polyfill-php80/Php80.php new file mode 100644 index 0000000000..362dd1a959 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/Php80.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Ion Bazan + * @author Nico Oelgart + * @author Nicolas Grekas + * + * @internal + */ +final class Php80 +{ + public static function fdiv(float $dividend, float $divisor): float + { + return @($dividend / $divisor); + } + + public static function get_debug_type($value): string + { + switch (true) { + case null === $value: return 'null'; + case \is_bool($value): return 'bool'; + case \is_string($value): return 'string'; + case \is_array($value): return 'array'; + case \is_int($value): return 'int'; + case \is_float($value): return 'float'; + case \is_object($value): break; + case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; + default: + if (null === $type = @get_resource_type($value)) { + return 'unknown'; + } + + if ('Unknown' === $type) { + $type = 'closed'; + } + + return "resource ($type)"; + } + + $class = \get_class($value); + + if (false === strpos($class, '@')) { + return $class; + } + + return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; + } + + public static function get_resource_id($res): int + { + if (!\is_resource($res) && null === @get_resource_type($res)) { + throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); + } + + return (int) $res; + } + + public static function preg_last_error_msg(): string + { + switch (preg_last_error()) { + case \PREG_INTERNAL_ERROR: + return 'Internal error'; + case \PREG_BAD_UTF8_ERROR: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + case \PREG_BAD_UTF8_OFFSET_ERROR: + return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; + case \PREG_BACKTRACK_LIMIT_ERROR: + return 'Backtrack limit exhausted'; + case \PREG_RECURSION_LIMIT_ERROR: + return 'Recursion limit exhausted'; + case \PREG_JIT_STACKLIMIT_ERROR: + return 'JIT stack limit exhausted'; + case \PREG_NO_ERROR: + return 'No error'; + default: + return 'Unknown error'; + } + } + + public static function str_contains(string $haystack, string $needle): bool + { + return '' === $needle || false !== strpos($haystack, $needle); + } + + public static function str_starts_with(string $haystack, string $needle): bool + { + return 0 === strncmp($haystack, $needle, \strlen($needle)); + } + + public static function str_ends_with(string $haystack, string $needle): bool + { + if ('' === $needle || $needle === $haystack) { + return true; + } + + if ('' === $haystack) { + return false; + } + + $needleLength = \strlen($needle); + + return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); + } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/PhpToken.php b/pandora_console/vendor/symfony/polyfill-php80/PhpToken.php new file mode 100644 index 0000000000..fe6e691056 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/PhpToken.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Fedonyuk Anton + * + * @internal + */ +class PhpToken implements \Stringable +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $text; + + /** + * @var int + */ + public $line; + + /** + * @var int + */ + public $pos; + + public function __construct(int $id, string $text, int $line = -1, int $position = -1) + { + $this->id = $id; + $this->text = $text; + $this->line = $line; + $this->pos = $position; + } + + public function getTokenName(): ?string + { + if ('UNKNOWN' === $name = token_name($this->id)) { + $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; + } + + return $name; + } + + /** + * @param int|string|array $kind + */ + public function is($kind): bool + { + foreach ((array) $kind as $value) { + if (\in_array($value, [$this->id, $this->text], true)) { + return true; + } + } + + return false; + } + + public function isIgnorable(): bool + { + return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true); + } + + public function __toString(): string + { + return (string) $this->text; + } + + /** + * @return static[] + */ + public static function tokenize(string $code, int $flags = 0): array + { + $line = 1; + $position = 0; + $tokens = token_get_all($code, $flags); + foreach ($tokens as $index => $token) { + if (\is_string($token)) { + $id = \ord($token); + $text = $token; + } else { + [$id, $text, $line] = $token; + } + $tokens[$index] = new static($id, $text, $line, $position); + $position += \strlen($text); + } + + return $tokens; + } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/README.md b/pandora_console/vendor/symfony/polyfill-php80/README.md new file mode 100644 index 0000000000..3816c559d5 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/README.md @@ -0,0 +1,25 @@ +Symfony Polyfill / Php80 +======================== + +This component provides features added to PHP 8.0 core: + +- [`Stringable`](https://php.net/stringable) interface +- [`fdiv`](https://php.net/fdiv) +- [`ValueError`](https://php.net/valueerror) class +- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class +- `FILTER_VALIDATE_BOOL` constant +- [`get_debug_type`](https://php.net/get_debug_type) +- [`PhpToken`](https://php.net/phptoken) class +- [`preg_last_error_msg`](https://php.net/preg_last_error_msg) +- [`str_contains`](https://php.net/str_contains) +- [`str_starts_with`](https://php.net/str_starts_with) +- [`str_ends_with`](https://php.net/str_ends_with) +- [`get_resource_id`](https://php.net/get_resource_id) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php new file mode 100644 index 0000000000..2b955423fc --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#[Attribute(Attribute::TARGET_CLASS)] +final class Attribute +{ + public const TARGET_CLASS = 1; + public const TARGET_FUNCTION = 2; + public const TARGET_METHOD = 4; + public const TARGET_PROPERTY = 8; + public const TARGET_CLASS_CONSTANT = 16; + public const TARGET_PARAMETER = 32; + public const TARGET_ALL = 63; + public const IS_REPEATABLE = 64; + + /** @var int */ + public $flags; + + public function __construct(int $flags = self::TARGET_ALL) + { + $this->flags = $flags; + } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php new file mode 100644 index 0000000000..bd1212f6e4 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) { + class PhpToken extends Symfony\Polyfill\Php80\PhpToken + { + } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php new file mode 100644 index 0000000000..7c62d7508b --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + interface Stringable + { + /** + * @return string + */ + public function __toString(); + } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php new file mode 100644 index 0000000000..01c6c6c8ab --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class UnhandledMatchError extends Error + { + } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php new file mode 100644 index 0000000000..783dbc28c7 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class ValueError extends Error + { + } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/bootstrap.php b/pandora_console/vendor/symfony/polyfill-php80/bootstrap.php new file mode 100644 index 0000000000..e5f7dbc1a4 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/bootstrap.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php80 as p; + +if (\PHP_VERSION_ID >= 80000) { + return; +} + +if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { + define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); +} + +if (!function_exists('fdiv')) { + function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } +} +if (!function_exists('preg_last_error_msg')) { + function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } +} +if (!function_exists('str_contains')) { + function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_starts_with')) { + function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_ends_with')) { + function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('get_debug_type')) { + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } +} +if (!function_exists('get_resource_id')) { + function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } +} diff --git a/pandora_console/vendor/symfony/polyfill-php80/composer.json b/pandora_console/vendor/symfony/polyfill-php80/composer.json new file mode 100644 index 0000000000..bd9a3262a9 --- /dev/null +++ b/pandora_console/vendor/symfony/polyfill-php80/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/polyfill-php80", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/pandora_console/vendor/symfony/process/CHANGELOG.md b/pandora_console/vendor/symfony/process/CHANGELOG.md new file mode 100644 index 0000000000..31b9ee6a25 --- /dev/null +++ b/pandora_console/vendor/symfony/process/CHANGELOG.md @@ -0,0 +1,116 @@ +CHANGELOG +========= + +5.2.0 +----- + + * added `Process::setOptions()` to set `Process` specific options + * added option `create_new_console` to allow a subprocess to continue + to run after the main script exited, both on Linux and on Windows + +5.1.0 +----- + + * added `Process::getStartTime()` to retrieve the start time of the process as float + +5.0.0 +----- + + * removed `Process::inheritEnvironmentVariables()` + * removed `PhpProcess::setPhpBinary()` + * `Process` must be instantiated with a command array, use `Process::fromShellCommandline()` when the command should be parsed by the shell + * removed `Process::setCommandLine()` + +4.4.0 +----- + + * deprecated `Process::inheritEnvironmentVariables()`: env variables are always inherited. + * added `Process::getLastOutputTime()` method + +4.2.0 +----- + + * added the `Process::fromShellCommandline()` to run commands in a shell wrapper + * deprecated passing a command as string when creating a `Process` instance + * deprecated the `Process::setCommandline()` and the `PhpProcess::setPhpBinary()` methods + * added the `Process::waitUntil()` method to wait for the process only for a + specific output, then continue the normal execution of your application + +4.1.0 +----- + + * added the `Process::isTtySupported()` method that allows to check for TTY support + * made `PhpExecutableFinder` look for the `PHP_BINARY` env var when searching the php binary + * added the `ProcessSignaledException` class to properly catch signaled process errors + +4.0.0 +----- + + * environment variables will always be inherited + * added a second `array $env = []` argument to the `start()`, `run()`, + `mustRun()`, and `restart()` methods of the `Process` class + * added a second `array $env = []` argument to the `start()` method of the + `PhpProcess` class + * the `ProcessUtils::escapeArgument()` method has been removed + * the `areEnvironmentVariablesInherited()`, `getOptions()`, and `setOptions()` + methods of the `Process` class have been removed + * support for passing `proc_open()` options has been removed + * removed the `ProcessBuilder` class, use the `Process` class instead + * removed the `getEnhanceWindowsCompatibility()` and `setEnhanceWindowsCompatibility()` methods of the `Process` class + * passing a not existing working directory to the constructor of the `Symfony\Component\Process\Process` class is not + supported anymore + +3.4.0 +----- + + * deprecated the ProcessBuilder class + * deprecated calling `Process::start()` without setting a valid working directory beforehand (via `setWorkingDirectory()` or constructor) + +3.3.0 +----- + + * added command line arrays in the `Process` class + * added `$env` argument to `Process::start()`, `run()`, `mustRun()` and `restart()` methods + * deprecated the `ProcessUtils::escapeArgument()` method + * deprecated not inheriting environment variables + * deprecated configuring `proc_open()` options + * deprecated configuring enhanced Windows compatibility + * deprecated configuring enhanced sigchild compatibility + +2.5.0 +----- + + * added support for PTY mode + * added the convenience method "mustRun" + * deprecation: Process::setStdin() is deprecated in favor of Process::setInput() + * deprecation: Process::getStdin() is deprecated in favor of Process::getInput() + * deprecation: Process::setInput() and ProcessBuilder::setInput() do not accept non-scalar types + +2.4.0 +----- + + * added the ability to define an idle timeout + +2.3.0 +----- + + * added ProcessUtils::escapeArgument() to fix the bug in escapeshellarg() function on Windows + * added Process::signal() + * added Process::getPid() + * added support for a TTY mode + +2.2.0 +----- + + * added ProcessBuilder::setArguments() to reset the arguments on a builder + * added a way to retrieve the standard and error output incrementally + * added Process:restart() + +2.1.0 +----- + + * added support for non-blocking processes (start(), wait(), isRunning(), stop()) + * enhanced Windows compatibility + * added Process::getExitCodeText() that returns a string representation for + the exit code returned by the process + * added ProcessBuilder diff --git a/pandora_console/vendor/symfony/process/Exception/ExceptionInterface.php b/pandora_console/vendor/symfony/process/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..bd4a60403b --- /dev/null +++ b/pandora_console/vendor/symfony/process/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * Marker Interface for the Process Component. + * + * @author Johannes M. Schmitt + */ +interface ExceptionInterface extends \Throwable +{ +} diff --git a/pandora_console/vendor/symfony/process/Exception/InvalidArgumentException.php b/pandora_console/vendor/symfony/process/Exception/InvalidArgumentException.php new file mode 100644 index 0000000000..926ee2118b --- /dev/null +++ b/pandora_console/vendor/symfony/process/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * InvalidArgumentException for the Process Component. + * + * @author Romain Neutron + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/pandora_console/vendor/symfony/process/Exception/LogicException.php b/pandora_console/vendor/symfony/process/Exception/LogicException.php new file mode 100644 index 0000000000..be3d490dde --- /dev/null +++ b/pandora_console/vendor/symfony/process/Exception/LogicException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * LogicException for the Process Component. + * + * @author Romain Neutron + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/pandora_console/vendor/symfony/process/Exception/ProcessFailedException.php b/pandora_console/vendor/symfony/process/Exception/ProcessFailedException.php new file mode 100644 index 0000000000..328acfde5e --- /dev/null +++ b/pandora_console/vendor/symfony/process/Exception/ProcessFailedException.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +use Symfony\Component\Process\Process; + +/** + * Exception for failed processes. + * + * @author Johannes M. Schmitt + */ +class ProcessFailedException extends RuntimeException +{ + private $process; + + public function __construct(Process $process) + { + if ($process->isSuccessful()) { + throw new InvalidArgumentException('Expected a failed process, but the given process was successful.'); + } + + $error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s", + $process->getCommandLine(), + $process->getExitCode(), + $process->getExitCodeText(), + $process->getWorkingDirectory() + ); + + if (!$process->isOutputDisabled()) { + $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", + $process->getOutput(), + $process->getErrorOutput() + ); + } + + parent::__construct($error); + + $this->process = $process; + } + + public function getProcess() + { + return $this->process; + } +} diff --git a/pandora_console/vendor/symfony/process/Exception/ProcessSignaledException.php b/pandora_console/vendor/symfony/process/Exception/ProcessSignaledException.php new file mode 100644 index 0000000000..d4d322756f --- /dev/null +++ b/pandora_console/vendor/symfony/process/Exception/ProcessSignaledException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +use Symfony\Component\Process\Process; + +/** + * Exception that is thrown when a process has been signaled. + * + * @author Sullivan Senechal + */ +final class ProcessSignaledException extends RuntimeException +{ + private $process; + + public function __construct(Process $process) + { + $this->process = $process; + + parent::__construct(sprintf('The process has been signaled with signal "%s".', $process->getTermSignal())); + } + + public function getProcess(): Process + { + return $this->process; + } + + public function getSignal(): int + { + return $this->getProcess()->getTermSignal(); + } +} diff --git a/pandora_console/vendor/symfony/process/Exception/ProcessTimedOutException.php b/pandora_console/vendor/symfony/process/Exception/ProcessTimedOutException.php new file mode 100644 index 0000000000..94391a4596 --- /dev/null +++ b/pandora_console/vendor/symfony/process/Exception/ProcessTimedOutException.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +use Symfony\Component\Process\Process; + +/** + * Exception that is thrown when a process times out. + * + * @author Johannes M. Schmitt + */ +class ProcessTimedOutException extends RuntimeException +{ + public const TYPE_GENERAL = 1; + public const TYPE_IDLE = 2; + + private $process; + private $timeoutType; + + public function __construct(Process $process, int $timeoutType) + { + $this->process = $process; + $this->timeoutType = $timeoutType; + + parent::__construct(sprintf( + 'The process "%s" exceeded the timeout of %s seconds.', + $process->getCommandLine(), + $this->getExceededTimeout() + )); + } + + public function getProcess() + { + return $this->process; + } + + public function isGeneralTimeout() + { + return self::TYPE_GENERAL === $this->timeoutType; + } + + public function isIdleTimeout() + { + return self::TYPE_IDLE === $this->timeoutType; + } + + public function getExceededTimeout() + { + switch ($this->timeoutType) { + case self::TYPE_GENERAL: + return $this->process->getTimeout(); + + case self::TYPE_IDLE: + return $this->process->getIdleTimeout(); + + default: + throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); + } + } +} diff --git a/pandora_console/vendor/symfony/process/Exception/RuntimeException.php b/pandora_console/vendor/symfony/process/Exception/RuntimeException.php new file mode 100644 index 0000000000..adead2536b --- /dev/null +++ b/pandora_console/vendor/symfony/process/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * RuntimeException for the Process Component. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/pandora_console/vendor/symfony/process/ExecutableFinder.php b/pandora_console/vendor/symfony/process/ExecutableFinder.php new file mode 100644 index 0000000000..eb8f062924 --- /dev/null +++ b/pandora_console/vendor/symfony/process/ExecutableFinder.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +/** + * Generic executable finder. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class ExecutableFinder +{ + private $suffixes = ['.exe', '.bat', '.cmd', '.com']; + + /** + * Replaces default suffixes of executable. + */ + public function setSuffixes(array $suffixes) + { + $this->suffixes = $suffixes; + } + + /** + * Adds new possible suffix to check for executable. + */ + public function addSuffix(string $suffix) + { + $this->suffixes[] = $suffix; + } + + /** + * Finds an executable by name. + * + * @param string $name The executable name (without the extension) + * @param string|null $default The default to return if no executable is found + * @param array $extraDirs Additional dirs to check into + * + * @return string|null + */ + public function find(string $name, string $default = null, array $extraDirs = []) + { + if (\ini_get('open_basedir')) { + $searchPath = array_merge(explode(\PATH_SEPARATOR, \ini_get('open_basedir')), $extraDirs); + $dirs = []; + foreach ($searchPath as $path) { + // Silencing against https://bugs.php.net/69240 + if (@is_dir($path)) { + $dirs[] = $path; + } else { + if (basename($path) == $name && @is_executable($path)) { + return $path; + } + } + } + } else { + $dirs = array_merge( + explode(\PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')), + $extraDirs + ); + } + + $suffixes = ['']; + if ('\\' === \DIRECTORY_SEPARATOR) { + $pathExt = getenv('PATHEXT'); + $suffixes = array_merge($pathExt ? explode(\PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes); + } + foreach ($suffixes as $suffix) { + foreach ($dirs as $dir) { + if (@is_file($file = $dir.\DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === \DIRECTORY_SEPARATOR || @is_executable($file))) { + return $file; + } + } + } + + return $default; + } +} diff --git a/pandora_console/vendor/symfony/process/InputStream.php b/pandora_console/vendor/symfony/process/InputStream.php new file mode 100644 index 0000000000..240665f32a --- /dev/null +++ b/pandora_console/vendor/symfony/process/InputStream.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\RuntimeException; + +/** + * Provides a way to continuously write to the input of a Process until the InputStream is closed. + * + * @author Nicolas Grekas + * + * @implements \IteratorAggregate + */ +class InputStream implements \IteratorAggregate +{ + /** @var callable|null */ + private $onEmpty = null; + private $input = []; + private $open = true; + + /** + * Sets a callback that is called when the write buffer becomes empty. + */ + public function onEmpty(callable $onEmpty = null) + { + $this->onEmpty = $onEmpty; + } + + /** + * Appends an input to the write buffer. + * + * @param resource|string|int|float|bool|\Traversable|null $input The input to append as scalar, + * stream resource or \Traversable + */ + public function write($input) + { + if (null === $input) { + return; + } + if ($this->isClosed()) { + throw new RuntimeException(sprintf('"%s" is closed.', static::class)); + } + $this->input[] = ProcessUtils::validateInput(__METHOD__, $input); + } + + /** + * Closes the write buffer. + */ + public function close() + { + $this->open = false; + } + + /** + * Tells whether the write buffer is closed or not. + */ + public function isClosed() + { + return !$this->open; + } + + /** + * @return \Traversable + */ + #[\ReturnTypeWillChange] + public function getIterator() + { + $this->open = true; + + while ($this->open || $this->input) { + if (!$this->input) { + yield ''; + continue; + } + $current = array_shift($this->input); + + if ($current instanceof \Iterator) { + yield from $current; + } else { + yield $current; + } + if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) { + $this->write($onEmpty($this)); + } + } + } +} diff --git a/pandora_console/vendor/symfony/process/LICENSE b/pandora_console/vendor/symfony/process/LICENSE new file mode 100644 index 0000000000..88bf75bb4d --- /dev/null +++ b/pandora_console/vendor/symfony/process/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2022 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pandora_console/vendor/symfony/process/PhpExecutableFinder.php b/pandora_console/vendor/symfony/process/PhpExecutableFinder.php new file mode 100644 index 0000000000..998808b66f --- /dev/null +++ b/pandora_console/vendor/symfony/process/PhpExecutableFinder.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +/** + * An executable finder specifically designed for the PHP executable. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class PhpExecutableFinder +{ + private $executableFinder; + + public function __construct() + { + $this->executableFinder = new ExecutableFinder(); + } + + /** + * Finds The PHP executable. + * + * @return string|false + */ + public function find(bool $includeArgs = true) + { + if ($php = getenv('PHP_BINARY')) { + if (!is_executable($php)) { + $command = '\\' === \DIRECTORY_SEPARATOR ? 'where' : 'command -v'; + if ($php = strtok(exec($command.' '.escapeshellarg($php)), \PHP_EOL)) { + if (!is_executable($php)) { + return false; + } + } else { + return false; + } + } + + if (@is_dir($php)) { + return false; + } + + return $php; + } + + $args = $this->findArguments(); + $args = $includeArgs && $args ? ' '.implode(' ', $args) : ''; + + // PHP_BINARY return the current sapi executable + if (\PHP_BINARY && \in_array(\PHP_SAPI, ['cgi-fcgi', 'cli', 'cli-server', 'phpdbg'], true)) { + return \PHP_BINARY.$args; + } + + if ($php = getenv('PHP_PATH')) { + if (!@is_executable($php) || @is_dir($php)) { + return false; + } + + return $php; + } + + if ($php = getenv('PHP_PEAR_PHP_BIN')) { + if (@is_executable($php) && !@is_dir($php)) { + return $php; + } + } + + if (@is_executable($php = \PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php')) && !@is_dir($php)) { + return $php; + } + + $dirs = [\PHP_BINDIR]; + if ('\\' === \DIRECTORY_SEPARATOR) { + $dirs[] = 'C:\xampp\php\\'; + } + + return $this->executableFinder->find('php', false, $dirs); + } + + /** + * Finds the PHP executable arguments. + * + * @return array + */ + public function findArguments() + { + $arguments = []; + if ('phpdbg' === \PHP_SAPI) { + $arguments[] = '-qrr'; + } + + return $arguments; + } +} diff --git a/pandora_console/vendor/symfony/process/PhpProcess.php b/pandora_console/vendor/symfony/process/PhpProcess.php new file mode 100644 index 0000000000..2bc338e5e2 --- /dev/null +++ b/pandora_console/vendor/symfony/process/PhpProcess.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Process\Exception\RuntimeException; + +/** + * PhpProcess runs a PHP script in an independent process. + * + * $p = new PhpProcess(''); + * $p->run(); + * print $p->getOutput()."\n"; + * + * @author Fabien Potencier + */ +class PhpProcess extends Process +{ + /** + * @param string $script The PHP script to run (as a string) + * @param string|null $cwd The working directory or null to use the working dir of the current PHP process + * @param array|null $env The environment variables or null to use the same environment as the current PHP process + * @param int $timeout The timeout in seconds + * @param array|null $php Path to the PHP binary to use with any additional arguments + */ + public function __construct(string $script, string $cwd = null, array $env = null, int $timeout = 60, array $php = null) + { + if (null === $php) { + $executableFinder = new PhpExecutableFinder(); + $php = $executableFinder->find(false); + $php = false === $php ? null : array_merge([$php], $executableFinder->findArguments()); + } + if ('phpdbg' === \PHP_SAPI) { + $file = tempnam(sys_get_temp_dir(), 'dbg'); + file_put_contents($file, $script); + register_shutdown_function('unlink', $file); + $php[] = $file; + $script = null; + } + + parent::__construct($php, $cwd, $env, $script, $timeout); + } + + /** + * {@inheritdoc} + */ + public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + { + throw new LogicException(sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class)); + } + + /** + * {@inheritdoc} + */ + public function start(callable $callback = null, array $env = []) + { + if (null === $this->getCommandLine()) { + throw new RuntimeException('Unable to find the PHP executable.'); + } + + parent::start($callback, $env); + } +} diff --git a/pandora_console/vendor/symfony/process/Pipes/AbstractPipes.php b/pandora_console/vendor/symfony/process/Pipes/AbstractPipes.php new file mode 100644 index 0000000000..656dc03280 --- /dev/null +++ b/pandora_console/vendor/symfony/process/Pipes/AbstractPipes.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Pipes; + +use Symfony\Component\Process\Exception\InvalidArgumentException; + +/** + * @author Romain Neutron + * + * @internal + */ +abstract class AbstractPipes implements PipesInterface +{ + public $pipes = []; + + private $inputBuffer = ''; + private $input; + private $blocked = true; + private $lastError; + + /** + * @param resource|string|int|float|bool|\Iterator|null $input + */ + public function __construct($input) + { + if (\is_resource($input) || $input instanceof \Iterator) { + $this->input = $input; + } elseif (\is_string($input)) { + $this->inputBuffer = $input; + } else { + $this->inputBuffer = (string) $input; + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + foreach ($this->pipes as $pipe) { + if (\is_resource($pipe)) { + fclose($pipe); + } + } + $this->pipes = []; + } + + /** + * Returns true if a system call has been interrupted. + */ + protected function hasSystemCallBeenInterrupted(): bool + { + $lastError = $this->lastError; + $this->lastError = null; + + // stream_select returns false when the `select` system call is interrupted by an incoming signal + return null !== $lastError && false !== stripos($lastError, 'interrupted system call'); + } + + /** + * Unblocks streams. + */ + protected function unblock() + { + if (!$this->blocked) { + return; + } + + foreach ($this->pipes as $pipe) { + stream_set_blocking($pipe, 0); + } + if (\is_resource($this->input)) { + stream_set_blocking($this->input, 0); + } + + $this->blocked = false; + } + + /** + * Writes input to stdin. + * + * @throws InvalidArgumentException When an input iterator yields a non supported value + */ + protected function write(): ?array + { + if (!isset($this->pipes[0])) { + return null; + } + $input = $this->input; + + if ($input instanceof \Iterator) { + if (!$input->valid()) { + $input = null; + } elseif (\is_resource($input = $input->current())) { + stream_set_blocking($input, 0); + } elseif (!isset($this->inputBuffer[0])) { + if (!\is_string($input)) { + if (!\is_scalar($input)) { + throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', get_debug_type($this->input), get_debug_type($input))); + } + $input = (string) $input; + } + $this->inputBuffer = $input; + $this->input->next(); + $input = null; + } else { + $input = null; + } + } + + $r = $e = []; + $w = [$this->pipes[0]]; + + // let's have a look if something changed in streams + if (false === @stream_select($r, $w, $e, 0, 0)) { + return null; + } + + foreach ($w as $stdin) { + if (isset($this->inputBuffer[0])) { + $written = fwrite($stdin, $this->inputBuffer); + $this->inputBuffer = substr($this->inputBuffer, $written); + if (isset($this->inputBuffer[0])) { + return [$this->pipes[0]]; + } + } + + if ($input) { + while (true) { + $data = fread($input, self::CHUNK_SIZE); + if (!isset($data[0])) { + break; + } + $written = fwrite($stdin, $data); + $data = substr($data, $written); + if (isset($data[0])) { + $this->inputBuffer = $data; + + return [$this->pipes[0]]; + } + } + if (feof($input)) { + if ($this->input instanceof \Iterator) { + $this->input->next(); + } else { + $this->input = null; + } + } + } + } + + // no input to read on resource, buffer is empty + if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) { + $this->input = null; + fclose($this->pipes[0]); + unset($this->pipes[0]); + } elseif (!$w) { + return [$this->pipes[0]]; + } + + return null; + } + + /** + * @internal + */ + public function handleError(int $type, string $msg) + { + $this->lastError = $msg; + } +} diff --git a/pandora_console/vendor/symfony/process/Pipes/PipesInterface.php b/pandora_console/vendor/symfony/process/Pipes/PipesInterface.php new file mode 100644 index 0000000000..50eb5c47e1 --- /dev/null +++ b/pandora_console/vendor/symfony/process/Pipes/PipesInterface.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Pipes; + +/** + * PipesInterface manages descriptors and pipes for the use of proc_open. + * + * @author Romain Neutron + * + * @internal + */ +interface PipesInterface +{ + public const CHUNK_SIZE = 16384; + + /** + * Returns an array of descriptors for the use of proc_open. + */ + public function getDescriptors(): array; + + /** + * Returns an array of filenames indexed by their related stream in case these pipes use temporary files. + * + * @return string[] + */ + public function getFiles(): array; + + /** + * Reads data in file handles and pipes. + * + * @param bool $blocking Whether to use blocking calls or not + * @param bool $close Whether to close pipes if they've reached EOF + * + * @return string[] An array of read data indexed by their fd + */ + public function readAndWrite(bool $blocking, bool $close = false): array; + + /** + * Returns if the current state has open file handles or pipes. + */ + public function areOpen(): bool; + + /** + * Returns if pipes are able to read output. + */ + public function haveReadSupport(): bool; + + /** + * Closes file handles and pipes. + */ + public function close(); +} diff --git a/pandora_console/vendor/symfony/process/Pipes/UnixPipes.php b/pandora_console/vendor/symfony/process/Pipes/UnixPipes.php new file mode 100644 index 0000000000..5a0e9d47fe --- /dev/null +++ b/pandora_console/vendor/symfony/process/Pipes/UnixPipes.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Pipes; + +use Symfony\Component\Process\Process; + +/** + * UnixPipes implementation uses unix pipes as handles. + * + * @author Romain Neutron + * + * @internal + */ +class UnixPipes extends AbstractPipes +{ + private $ttyMode; + private $ptyMode; + private $haveReadSupport; + + public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveReadSupport) + { + $this->ttyMode = $ttyMode; + $this->ptyMode = $ptyMode; + $this->haveReadSupport = $haveReadSupport; + + parent::__construct($input); + } + + public function __sleep(): array + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + $this->close(); + } + + /** + * {@inheritdoc} + */ + public function getDescriptors(): array + { + if (!$this->haveReadSupport) { + $nullstream = fopen('/dev/null', 'c'); + + return [ + ['pipe', 'r'], + $nullstream, + $nullstream, + ]; + } + + if ($this->ttyMode) { + return [ + ['file', '/dev/tty', 'r'], + ['file', '/dev/tty', 'w'], + ['file', '/dev/tty', 'w'], + ]; + } + + if ($this->ptyMode && Process::isPtySupported()) { + return [ + ['pty'], + ['pty'], + ['pty'], + ]; + } + + return [ + ['pipe', 'r'], + ['pipe', 'w'], // stdout + ['pipe', 'w'], // stderr + ]; + } + + /** + * {@inheritdoc} + */ + public function getFiles(): array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function readAndWrite(bool $blocking, bool $close = false): array + { + $this->unblock(); + $w = $this->write(); + + $read = $e = []; + $r = $this->pipes; + unset($r[0]); + + // let's have a look if something changed in streams + set_error_handler([$this, 'handleError']); + if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { + restore_error_handler(); + // if a system call has been interrupted, forget about it, let's try again + // otherwise, an error occurred, let's reset pipes + if (!$this->hasSystemCallBeenInterrupted()) { + $this->pipes = []; + } + + return $read; + } + restore_error_handler(); + + foreach ($r as $pipe) { + // prior PHP 5.4 the array passed to stream_select is modified and + // lose key association, we have to find back the key + $read[$type = array_search($pipe, $this->pipes, true)] = ''; + + do { + $data = @fread($pipe, self::CHUNK_SIZE); + $read[$type] .= $data; + } while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1]))); + + if (!isset($read[$type][0])) { + unset($read[$type]); + } + + if ($close && feof($pipe)) { + fclose($pipe); + unset($this->pipes[$type]); + } + } + + return $read; + } + + /** + * {@inheritdoc} + */ + public function haveReadSupport(): bool + { + return $this->haveReadSupport; + } + + /** + * {@inheritdoc} + */ + public function areOpen(): bool + { + return (bool) $this->pipes; + } +} diff --git a/pandora_console/vendor/symfony/process/Pipes/WindowsPipes.php b/pandora_console/vendor/symfony/process/Pipes/WindowsPipes.php new file mode 100644 index 0000000000..bca84f574d --- /dev/null +++ b/pandora_console/vendor/symfony/process/Pipes/WindowsPipes.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Pipes; + +use Symfony\Component\Process\Exception\RuntimeException; +use Symfony\Component\Process\Process; + +/** + * WindowsPipes implementation uses temporary files as handles. + * + * @see https://bugs.php.net/51800 + * @see https://bugs.php.net/65650 + * + * @author Romain Neutron + * + * @internal + */ +class WindowsPipes extends AbstractPipes +{ + private $files = []; + private $fileHandles = []; + private $lockHandles = []; + private $readBytes = [ + Process::STDOUT => 0, + Process::STDERR => 0, + ]; + private $haveReadSupport; + + public function __construct($input, bool $haveReadSupport) + { + $this->haveReadSupport = $haveReadSupport; + + if ($this->haveReadSupport) { + // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. + // Workaround for this problem is to use temporary files instead of pipes on Windows platform. + // + // @see https://bugs.php.net/51800 + $pipes = [ + Process::STDOUT => Process::OUT, + Process::STDERR => Process::ERR, + ]; + $tmpDir = sys_get_temp_dir(); + $lastError = 'unknown reason'; + set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); + for ($i = 0;; ++$i) { + foreach ($pipes as $pipe => $name) { + $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); + + if (!$h = fopen($file.'.lock', 'w')) { + if (file_exists($file.'.lock')) { + continue 2; + } + restore_error_handler(); + throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError); + } + if (!flock($h, \LOCK_EX | \LOCK_NB)) { + continue 2; + } + if (isset($this->lockHandles[$pipe])) { + flock($this->lockHandles[$pipe], \LOCK_UN); + fclose($this->lockHandles[$pipe]); + } + $this->lockHandles[$pipe] = $h; + + if (!($h = fopen($file, 'w')) || !fclose($h) || !$h = fopen($file, 'r')) { + flock($this->lockHandles[$pipe], \LOCK_UN); + fclose($this->lockHandles[$pipe]); + unset($this->lockHandles[$pipe]); + continue 2; + } + $this->fileHandles[$pipe] = $h; + $this->files[$pipe] = $file; + } + break; + } + restore_error_handler(); + } + + parent::__construct($input); + } + + public function __sleep(): array + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + $this->close(); + } + + /** + * {@inheritdoc} + */ + public function getDescriptors(): array + { + if (!$this->haveReadSupport) { + $nullstream = fopen('NUL', 'c'); + + return [ + ['pipe', 'r'], + $nullstream, + $nullstream, + ]; + } + + // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/51800) + // We're not using file handles as it can produce corrupted output https://bugs.php.net/65650 + // So we redirect output within the commandline and pass the nul device to the process + return [ + ['pipe', 'r'], + ['file', 'NUL', 'w'], + ['file', 'NUL', 'w'], + ]; + } + + /** + * {@inheritdoc} + */ + public function getFiles(): array + { + return $this->files; + } + + /** + * {@inheritdoc} + */ + public function readAndWrite(bool $blocking, bool $close = false): array + { + $this->unblock(); + $w = $this->write(); + $read = $r = $e = []; + + if ($blocking) { + if ($w) { + @stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6); + } elseif ($this->fileHandles) { + usleep(Process::TIMEOUT_PRECISION * 1E6); + } + } + foreach ($this->fileHandles as $type => $fileHandle) { + $data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]); + + if (isset($data[0])) { + $this->readBytes[$type] += \strlen($data); + $read[$type] = $data; + } + if ($close) { + ftruncate($fileHandle, 0); + fclose($fileHandle); + flock($this->lockHandles[$type], \LOCK_UN); + fclose($this->lockHandles[$type]); + unset($this->fileHandles[$type], $this->lockHandles[$type]); + } + } + + return $read; + } + + /** + * {@inheritdoc} + */ + public function haveReadSupport(): bool + { + return $this->haveReadSupport; + } + + /** + * {@inheritdoc} + */ + public function areOpen(): bool + { + return $this->pipes && $this->fileHandles; + } + + /** + * {@inheritdoc} + */ + public function close() + { + parent::close(); + foreach ($this->fileHandles as $type => $handle) { + ftruncate($handle, 0); + fclose($handle); + flock($this->lockHandles[$type], \LOCK_UN); + fclose($this->lockHandles[$type]); + } + $this->fileHandles = $this->lockHandles = []; + } +} diff --git a/pandora_console/vendor/symfony/process/Process.php b/pandora_console/vendor/symfony/process/Process.php new file mode 100644 index 0000000000..14e1777465 --- /dev/null +++ b/pandora_console/vendor/symfony/process/Process.php @@ -0,0 +1,1652 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\InvalidArgumentException; +use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Exception\ProcessSignaledException; +use Symfony\Component\Process\Exception\ProcessTimedOutException; +use Symfony\Component\Process\Exception\RuntimeException; +use Symfony\Component\Process\Pipes\PipesInterface; +use Symfony\Component\Process\Pipes\UnixPipes; +use Symfony\Component\Process\Pipes\WindowsPipes; + +/** + * Process is a thin wrapper around proc_* functions to easily + * start independent PHP processes. + * + * @author Fabien Potencier + * @author Romain Neutron + * + * @implements \IteratorAggregate + */ +class Process implements \IteratorAggregate +{ + public const ERR = 'err'; + public const OUT = 'out'; + + public const STATUS_READY = 'ready'; + public const STATUS_STARTED = 'started'; + public const STATUS_TERMINATED = 'terminated'; + + public const STDIN = 0; + public const STDOUT = 1; + public const STDERR = 2; + + // Timeout Precision in seconds. + public const TIMEOUT_PRECISION = 0.2; + + public const ITER_NON_BLOCKING = 1; // By default, iterating over outputs is a blocking call, use this flag to make it non-blocking + public const ITER_KEEP_OUTPUT = 2; // By default, outputs are cleared while iterating, use this flag to keep them in memory + public const ITER_SKIP_OUT = 4; // Use this flag to skip STDOUT while iterating + public const ITER_SKIP_ERR = 8; // Use this flag to skip STDERR while iterating + + private $callback; + private $hasCallback = false; + private $commandline; + private $cwd; + private $env = []; + private $input; + private $starttime; + private $lastOutputTime; + private $timeout; + private $idleTimeout; + private $exitcode; + private $fallbackStatus = []; + private $processInformation; + private $outputDisabled = false; + private $stdout; + private $stderr; + private $process; + private $status = self::STATUS_READY; + private $incrementalOutputOffset = 0; + private $incrementalErrorOutputOffset = 0; + private $tty = false; + private $pty; + private $options = ['suppress_errors' => true, 'bypass_shell' => true]; + + private $useFileHandles = false; + /** @var PipesInterface */ + private $processPipes; + + private $latestSignal; + + private static $sigchild; + + /** + * Exit codes translation table. + * + * User-defined errors must use exit codes in the 64-113 range. + */ + public static $exitCodes = [ + 0 => 'OK', + 1 => 'General error', + 2 => 'Misuse of shell builtins', + + 126 => 'Invoked command cannot execute', + 127 => 'Command not found', + 128 => 'Invalid exit argument', + + // signals + 129 => 'Hangup', + 130 => 'Interrupt', + 131 => 'Quit and dump core', + 132 => 'Illegal instruction', + 133 => 'Trace/breakpoint trap', + 134 => 'Process aborted', + 135 => 'Bus error: "access to undefined portion of memory object"', + 136 => 'Floating point exception: "erroneous arithmetic operation"', + 137 => 'Kill (terminate immediately)', + 138 => 'User-defined 1', + 139 => 'Segmentation violation', + 140 => 'User-defined 2', + 141 => 'Write to pipe with no one reading', + 142 => 'Signal raised by alarm', + 143 => 'Termination (request to terminate)', + // 144 - not defined + 145 => 'Child process terminated, stopped (or continued*)', + 146 => 'Continue if stopped', + 147 => 'Stop executing temporarily', + 148 => 'Terminal stop signal', + 149 => 'Background process attempting to read from tty ("in")', + 150 => 'Background process attempting to write to tty ("out")', + 151 => 'Urgent data available on socket', + 152 => 'CPU time limit exceeded', + 153 => 'File size limit exceeded', + 154 => 'Signal raised by timer counting virtual time: "virtual timer expired"', + 155 => 'Profiling timer expired', + // 156 - not defined + 157 => 'Pollable event', + // 158 - not defined + 159 => 'Bad syscall', + ]; + + /** + * @param array $command The command to run and its arguments listed as separate entries + * @param string|null $cwd The working directory or null to use the working dir of the current PHP process + * @param array|null $env The environment variables or null to use the same environment as the current PHP process + * @param mixed $input The input as stream resource, scalar or \Traversable, or null for no input + * @param int|float|null $timeout The timeout in seconds or null to disable + * + * @throws LogicException When proc_open is not installed + */ + public function __construct(array $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + { + if (!\function_exists('proc_open')) { + throw new LogicException('The Process class relies on proc_open, which is not available on your PHP installation.'); + } + + $this->commandline = $command; + $this->cwd = $cwd; + + // on Windows, if the cwd changed via chdir(), proc_open defaults to the dir where PHP was started + // on Gnu/Linux, PHP builds with --enable-maintainer-zts are also affected + // @see : https://bugs.php.net/51800 + // @see : https://bugs.php.net/50524 + if (null === $this->cwd && (\defined('ZEND_THREAD_SAFE') || '\\' === \DIRECTORY_SEPARATOR)) { + $this->cwd = getcwd(); + } + if (null !== $env) { + $this->setEnv($env); + } + + $this->setInput($input); + $this->setTimeout($timeout); + $this->useFileHandles = '\\' === \DIRECTORY_SEPARATOR; + $this->pty = false; + } + + /** + * Creates a Process instance as a command-line to be run in a shell wrapper. + * + * Command-lines are parsed by the shell of your OS (/bin/sh on Unix-like, cmd.exe on Windows.) + * This allows using e.g. pipes or conditional execution. In this mode, signals are sent to the + * shell wrapper and not to your commands. + * + * In order to inject dynamic values into command-lines, we strongly recommend using placeholders. + * This will save escaping values, which is not portable nor secure anyway: + * + * $process = Process::fromShellCommandline('my_command "${:MY_VAR}"'); + * $process->run(null, ['MY_VAR' => $theValue]); + * + * @param string $command The command line to pass to the shell of the OS + * @param string|null $cwd The working directory or null to use the working dir of the current PHP process + * @param array|null $env The environment variables or null to use the same environment as the current PHP process + * @param mixed $input The input as stream resource, scalar or \Traversable, or null for no input + * @param int|float|null $timeout The timeout in seconds or null to disable + * + * @return static + * + * @throws LogicException When proc_open is not installed + */ + public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) + { + $process = new static([], $cwd, $env, $input, $timeout); + $process->commandline = $command; + + return $process; + } + + /** + * @return array + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + if ($this->options['create_new_console'] ?? false) { + $this->processPipes->close(); + } else { + $this->stop(0); + } + } + + public function __clone() + { + $this->resetProcessData(); + } + + /** + * Runs the process. + * + * The callback receives the type of output (out or err) and + * some bytes from the output in real-time. It allows to have feedback + * from the independent process during execution. + * + * The STDOUT and STDERR are also available after the process is finished + * via the getOutput() and getErrorOutput() methods. + * + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return int The exit status code + * + * @throws RuntimeException When process can't be launched + * @throws RuntimeException When process is already running + * @throws ProcessTimedOutException When process timed out + * @throws ProcessSignaledException When process stopped after receiving signal + * @throws LogicException In case a callback is provided and output has been disabled + * + * @final + */ + public function run(callable $callback = null, array $env = []): int + { + $this->start($callback, $env); + + return $this->wait(); + } + + /** + * Runs the process. + * + * This is identical to run() except that an exception is thrown if the process + * exits with a non-zero exit code. + * + * @return $this + * + * @throws ProcessFailedException if the process didn't terminate successfully + * + * @final + */ + public function mustRun(callable $callback = null, array $env = []): self + { + if (0 !== $this->run($callback, $env)) { + throw new ProcessFailedException($this); + } + + return $this; + } + + /** + * Starts the process and returns after writing the input to STDIN. + * + * This method blocks until all STDIN data is sent to the process then it + * returns while the process runs in the background. + * + * The termination of the process can be awaited with wait(). + * + * The callback receives the type of output (out or err) and some bytes from + * the output in real-time while writing the standard input to the process. + * It allows to have feedback from the independent process during execution. + * + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @throws RuntimeException When process can't be launched + * @throws RuntimeException When process is already running + * @throws LogicException In case a callback is provided and output has been disabled + */ + public function start(callable $callback = null, array $env = []) + { + if ($this->isRunning()) { + throw new RuntimeException('Process is already running.'); + } + + $this->resetProcessData(); + $this->starttime = $this->lastOutputTime = microtime(true); + $this->callback = $this->buildCallback($callback); + $this->hasCallback = null !== $callback; + $descriptors = $this->getDescriptors(); + + if ($this->env) { + $env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->env, $env, 'strcasecmp') : $this->env; + } + + $env += '\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($this->getDefaultEnv(), $env, 'strcasecmp') : $this->getDefaultEnv(); + + if (\is_array($commandline = $this->commandline)) { + $commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline)); + + if ('\\' !== \DIRECTORY_SEPARATOR) { + // exec is mandatory to deal with sending a signal to the process + $commandline = 'exec '.$commandline; + } + } else { + $commandline = $this->replacePlaceholders($commandline, $env); + } + + if ('\\' === \DIRECTORY_SEPARATOR) { + $commandline = $this->prepareWindowsCommandLine($commandline, $env); + } elseif (!$this->useFileHandles && $this->isSigchildEnabled()) { + // last exit code is output on the fourth pipe and caught to work around --enable-sigchild + $descriptors[3] = ['pipe', 'w']; + + // See https://unix.stackexchange.com/questions/71205/background-process-pipe-input + $commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;'; + $commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code'; + + // Workaround for the bug, when PTS functionality is enabled. + // @see : https://bugs.php.net/69442 + $ptsWorkaround = fopen(__FILE__, 'r'); + } + + $envPairs = []; + foreach ($env as $k => $v) { + if (false !== $v && false === \in_array($k, ['argc', 'argv', 'ARGC', 'ARGV'], true)) { + $envPairs[] = $k.'='.$v; + } + } + + if (!is_dir($this->cwd)) { + throw new RuntimeException(sprintf('The provided cwd "%s" does not exist.', $this->cwd)); + } + + $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options); + + if (!\is_resource($this->process)) { + throw new RuntimeException('Unable to launch a new process.'); + } + $this->status = self::STATUS_STARTED; + + if (isset($descriptors[3])) { + $this->fallbackStatus['pid'] = (int) fgets($this->processPipes->pipes[3]); + } + + if ($this->tty) { + return; + } + + $this->updateStatus(false); + $this->checkTimeout(); + } + + /** + * Restarts the process. + * + * Be warned that the process is cloned before being started. + * + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return static + * + * @throws RuntimeException When process can't be launched + * @throws RuntimeException When process is already running + * + * @see start() + * + * @final + */ + public function restart(callable $callback = null, array $env = []): self + { + if ($this->isRunning()) { + throw new RuntimeException('Process is already running.'); + } + + $process = clone $this; + $process->start($callback, $env); + + return $process; + } + + /** + * Waits for the process to terminate. + * + * The callback receives the type of output (out or err) and some bytes + * from the output in real-time while writing the standard input to the process. + * It allows to have feedback from the independent process during execution. + * + * @param callable|null $callback A valid PHP callback + * + * @return int The exitcode of the process + * + * @throws ProcessTimedOutException When process timed out + * @throws ProcessSignaledException When process stopped after receiving signal + * @throws LogicException When process is not yet started + */ + public function wait(callable $callback = null) + { + $this->requireProcessIsStarted(__FUNCTION__); + + $this->updateStatus(false); + + if (null !== $callback) { + if (!$this->processPipes->haveReadSupport()) { + $this->stop(0); + throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::wait".'); + } + $this->callback = $this->buildCallback($callback); + } + + do { + $this->checkTimeout(); + $running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen(); + $this->readPipes($running, '\\' !== \DIRECTORY_SEPARATOR || !$running); + } while ($running); + + while ($this->isRunning()) { + $this->checkTimeout(); + usleep(1000); + } + + if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) { + throw new ProcessSignaledException($this); + } + + return $this->exitcode; + } + + /** + * Waits until the callback returns true. + * + * The callback receives the type of output (out or err) and some bytes + * from the output in real-time while writing the standard input to the process. + * It allows to have feedback from the independent process during execution. + * + * @throws RuntimeException When process timed out + * @throws LogicException When process is not yet started + * @throws ProcessTimedOutException In case the timeout was reached + */ + public function waitUntil(callable $callback): bool + { + $this->requireProcessIsStarted(__FUNCTION__); + $this->updateStatus(false); + + if (!$this->processPipes->haveReadSupport()) { + $this->stop(0); + throw new LogicException('Pass the callback to the "Process::start" method or call enableOutput to use a callback with "Process::waitUntil".'); + } + $callback = $this->buildCallback($callback); + + $ready = false; + while (true) { + $this->checkTimeout(); + $running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen(); + $output = $this->processPipes->readAndWrite($running, '\\' !== \DIRECTORY_SEPARATOR || !$running); + + foreach ($output as $type => $data) { + if (3 !== $type) { + $ready = $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data) || $ready; + } elseif (!isset($this->fallbackStatus['signaled'])) { + $this->fallbackStatus['exitcode'] = (int) $data; + } + } + if ($ready) { + return true; + } + if (!$running) { + return false; + } + + usleep(1000); + } + } + + /** + * Returns the Pid (process identifier), if applicable. + * + * @return int|null The process id if running, null otherwise + */ + public function getPid() + { + return $this->isRunning() ? $this->processInformation['pid'] : null; + } + + /** + * Sends a POSIX signal to the process. + * + * @param int $signal A valid POSIX signal (see https://php.net/pcntl.constants) + * + * @return $this + * + * @throws LogicException In case the process is not running + * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed + * @throws RuntimeException In case of failure + */ + public function signal(int $signal) + { + $this->doSignal($signal, true); + + return $this; + } + + /** + * Disables fetching output and error output from the underlying process. + * + * @return $this + * + * @throws RuntimeException In case the process is already running + * @throws LogicException if an idle timeout is set + */ + public function disableOutput() + { + if ($this->isRunning()) { + throw new RuntimeException('Disabling output while the process is running is not possible.'); + } + if (null !== $this->idleTimeout) { + throw new LogicException('Output cannot be disabled while an idle timeout is set.'); + } + + $this->outputDisabled = true; + + return $this; + } + + /** + * Enables fetching output and error output from the underlying process. + * + * @return $this + * + * @throws RuntimeException In case the process is already running + */ + public function enableOutput() + { + if ($this->isRunning()) { + throw new RuntimeException('Enabling output while the process is running is not possible.'); + } + + $this->outputDisabled = false; + + return $this; + } + + /** + * Returns true in case the output is disabled, false otherwise. + * + * @return bool + */ + public function isOutputDisabled() + { + return $this->outputDisabled; + } + + /** + * Returns the current output of the process (STDOUT). + * + * @return string + * + * @throws LogicException in case the output has been disabled + * @throws LogicException In case the process is not started + */ + public function getOutput() + { + $this->readPipesForOutput(__FUNCTION__); + + if (false === $ret = stream_get_contents($this->stdout, -1, 0)) { + return ''; + } + + return $ret; + } + + /** + * Returns the output incrementally. + * + * In comparison with the getOutput method which always return the whole + * output, this one returns the new output since the last call. + * + * @return string + * + * @throws LogicException in case the output has been disabled + * @throws LogicException In case the process is not started + */ + public function getIncrementalOutput() + { + $this->readPipesForOutput(__FUNCTION__); + + $latest = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset); + $this->incrementalOutputOffset = ftell($this->stdout); + + if (false === $latest) { + return ''; + } + + return $latest; + } + + /** + * Returns an iterator to the output of the process, with the output type as keys (Process::OUT/ERR). + * + * @param int $flags A bit field of Process::ITER_* flags + * + * @throws LogicException in case the output has been disabled + * @throws LogicException In case the process is not started + * + * @return \Generator + */ + #[\ReturnTypeWillChange] + public function getIterator(int $flags = 0) + { + $this->readPipesForOutput(__FUNCTION__, false); + + $clearOutput = !(self::ITER_KEEP_OUTPUT & $flags); + $blocking = !(self::ITER_NON_BLOCKING & $flags); + $yieldOut = !(self::ITER_SKIP_OUT & $flags); + $yieldErr = !(self::ITER_SKIP_ERR & $flags); + + while (null !== $this->callback || ($yieldOut && !feof($this->stdout)) || ($yieldErr && !feof($this->stderr))) { + if ($yieldOut) { + $out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset); + + if (isset($out[0])) { + if ($clearOutput) { + $this->clearOutput(); + } else { + $this->incrementalOutputOffset = ftell($this->stdout); + } + + yield self::OUT => $out; + } + } + + if ($yieldErr) { + $err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset); + + if (isset($err[0])) { + if ($clearOutput) { + $this->clearErrorOutput(); + } else { + $this->incrementalErrorOutputOffset = ftell($this->stderr); + } + + yield self::ERR => $err; + } + } + + if (!$blocking && !isset($out[0]) && !isset($err[0])) { + yield self::OUT => ''; + } + + $this->checkTimeout(); + $this->readPipesForOutput(__FUNCTION__, $blocking); + } + } + + /** + * Clears the process output. + * + * @return $this + */ + public function clearOutput() + { + ftruncate($this->stdout, 0); + fseek($this->stdout, 0); + $this->incrementalOutputOffset = 0; + + return $this; + } + + /** + * Returns the current error output of the process (STDERR). + * + * @return string + * + * @throws LogicException in case the output has been disabled + * @throws LogicException In case the process is not started + */ + public function getErrorOutput() + { + $this->readPipesForOutput(__FUNCTION__); + + if (false === $ret = stream_get_contents($this->stderr, -1, 0)) { + return ''; + } + + return $ret; + } + + /** + * Returns the errorOutput incrementally. + * + * In comparison with the getErrorOutput method which always return the + * whole error output, this one returns the new error output since the last + * call. + * + * @return string + * + * @throws LogicException in case the output has been disabled + * @throws LogicException In case the process is not started + */ + public function getIncrementalErrorOutput() + { + $this->readPipesForOutput(__FUNCTION__); + + $latest = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset); + $this->incrementalErrorOutputOffset = ftell($this->stderr); + + if (false === $latest) { + return ''; + } + + return $latest; + } + + /** + * Clears the process output. + * + * @return $this + */ + public function clearErrorOutput() + { + ftruncate($this->stderr, 0); + fseek($this->stderr, 0); + $this->incrementalErrorOutputOffset = 0; + + return $this; + } + + /** + * Returns the exit code returned by the process. + * + * @return int|null The exit status code, null if the Process is not terminated + */ + public function getExitCode() + { + $this->updateStatus(false); + + return $this->exitcode; + } + + /** + * Returns a string representation for the exit code returned by the process. + * + * This method relies on the Unix exit code status standardization + * and might not be relevant for other operating systems. + * + * @return string|null A string representation for the exit status code, null if the Process is not terminated + * + * @see http://tldp.org/LDP/abs/html/exitcodes.html + * @see http://en.wikipedia.org/wiki/Unix_signal + */ + public function getExitCodeText() + { + if (null === $exitcode = $this->getExitCode()) { + return null; + } + + return self::$exitCodes[$exitcode] ?? 'Unknown error'; + } + + /** + * Checks if the process ended successfully. + * + * @return bool + */ + public function isSuccessful() + { + return 0 === $this->getExitCode(); + } + + /** + * Returns true if the child process has been terminated by an uncaught signal. + * + * It always returns false on Windows. + * + * @return bool + * + * @throws LogicException In case the process is not terminated + */ + public function hasBeenSignaled() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + return $this->processInformation['signaled']; + } + + /** + * Returns the number of the signal that caused the child process to terminate its execution. + * + * It is only meaningful if hasBeenSignaled() returns true. + * + * @return int + * + * @throws RuntimeException In case --enable-sigchild is activated + * @throws LogicException In case the process is not terminated + */ + public function getTermSignal() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + if ($this->isSigchildEnabled() && -1 === $this->processInformation['termsig']) { + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal cannot be retrieved.'); + } + + return $this->processInformation['termsig']; + } + + /** + * Returns true if the child process has been stopped by a signal. + * + * It always returns false on Windows. + * + * @return bool + * + * @throws LogicException In case the process is not terminated + */ + public function hasBeenStopped() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + return $this->processInformation['stopped']; + } + + /** + * Returns the number of the signal that caused the child process to stop its execution. + * + * It is only meaningful if hasBeenStopped() returns true. + * + * @return int + * + * @throws LogicException In case the process is not terminated + */ + public function getStopSignal() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + return $this->processInformation['stopsig']; + } + + /** + * Checks if the process is currently running. + * + * @return bool + */ + public function isRunning() + { + if (self::STATUS_STARTED !== $this->status) { + return false; + } + + $this->updateStatus(false); + + return $this->processInformation['running']; + } + + /** + * Checks if the process has been started with no regard to the current state. + * + * @return bool + */ + public function isStarted() + { + return self::STATUS_READY != $this->status; + } + + /** + * Checks if the process is terminated. + * + * @return bool + */ + public function isTerminated() + { + $this->updateStatus(false); + + return self::STATUS_TERMINATED == $this->status; + } + + /** + * Gets the process status. + * + * The status is one of: ready, started, terminated. + * + * @return string + */ + public function getStatus() + { + $this->updateStatus(false); + + return $this->status; + } + + /** + * Stops the process. + * + * @param int|float $timeout The timeout in seconds + * @param int $signal A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL (9) + * + * @return int|null The exit-code of the process or null if it's not running + */ + public function stop(float $timeout = 10, int $signal = null) + { + $timeoutMicro = microtime(true) + $timeout; + if ($this->isRunning()) { + // given SIGTERM may not be defined and that "proc_terminate" uses the constant value and not the constant itself, we use the same here + $this->doSignal(15, false); + do { + usleep(1000); + } while ($this->isRunning() && microtime(true) < $timeoutMicro); + + if ($this->isRunning()) { + // Avoid exception here: process is supposed to be running, but it might have stopped just + // after this line. In any case, let's silently discard the error, we cannot do anything. + $this->doSignal($signal ?: 9, false); + } + } + + if ($this->isRunning()) { + if (isset($this->fallbackStatus['pid'])) { + unset($this->fallbackStatus['pid']); + + return $this->stop(0, $signal); + } + $this->close(); + } + + return $this->exitcode; + } + + /** + * Adds a line to the STDOUT stream. + * + * @internal + */ + public function addOutput(string $line) + { + $this->lastOutputTime = microtime(true); + + fseek($this->stdout, 0, \SEEK_END); + fwrite($this->stdout, $line); + fseek($this->stdout, $this->incrementalOutputOffset); + } + + /** + * Adds a line to the STDERR stream. + * + * @internal + */ + public function addErrorOutput(string $line) + { + $this->lastOutputTime = microtime(true); + + fseek($this->stderr, 0, \SEEK_END); + fwrite($this->stderr, $line); + fseek($this->stderr, $this->incrementalErrorOutputOffset); + } + + /** + * Gets the last output time in seconds. + */ + public function getLastOutputTime(): ?float + { + return $this->lastOutputTime; + } + + /** + * Gets the command line to be executed. + * + * @return string + */ + public function getCommandLine() + { + return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline; + } + + /** + * Gets the process timeout in seconds (max. runtime). + * + * @return float|null + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Gets the process idle timeout in seconds (max. time since last output). + * + * @return float|null + */ + public function getIdleTimeout() + { + return $this->idleTimeout; + } + + /** + * Sets the process timeout (max. runtime) in seconds. + * + * To disable the timeout, set this value to null. + * + * @return $this + * + * @throws InvalidArgumentException if the timeout is negative + */ + public function setTimeout(?float $timeout) + { + $this->timeout = $this->validateTimeout($timeout); + + return $this; + } + + /** + * Sets the process idle timeout (max. time since last output) in seconds. + * + * To disable the timeout, set this value to null. + * + * @return $this + * + * @throws LogicException if the output is disabled + * @throws InvalidArgumentException if the timeout is negative + */ + public function setIdleTimeout(?float $timeout) + { + if (null !== $timeout && $this->outputDisabled) { + throw new LogicException('Idle timeout cannot be set while the output is disabled.'); + } + + $this->idleTimeout = $this->validateTimeout($timeout); + + return $this; + } + + /** + * Enables or disables the TTY mode. + * + * @return $this + * + * @throws RuntimeException In case the TTY mode is not supported + */ + public function setTty(bool $tty) + { + if ('\\' === \DIRECTORY_SEPARATOR && $tty) { + throw new RuntimeException('TTY mode is not supported on Windows platform.'); + } + + if ($tty && !self::isTtySupported()) { + throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.'); + } + + $this->tty = $tty; + + return $this; + } + + /** + * Checks if the TTY mode is enabled. + * + * @return bool + */ + public function isTty() + { + return $this->tty; + } + + /** + * Sets PTY mode. + * + * @return $this + */ + public function setPty(bool $bool) + { + $this->pty = $bool; + + return $this; + } + + /** + * Returns PTY state. + * + * @return bool + */ + public function isPty() + { + return $this->pty; + } + + /** + * Gets the working directory. + * + * @return string|null + */ + public function getWorkingDirectory() + { + if (null === $this->cwd) { + // getcwd() will return false if any one of the parent directories does not have + // the readable or search mode set, even if the current directory does + return getcwd() ?: null; + } + + return $this->cwd; + } + + /** + * Sets the current working directory. + * + * @return $this + */ + public function setWorkingDirectory(string $cwd) + { + $this->cwd = $cwd; + + return $this; + } + + /** + * Gets the environment variables. + * + * @return array + */ + public function getEnv() + { + return $this->env; + } + + /** + * Sets the environment variables. + * + * @param array $env The new environment variables + * + * @return $this + */ + public function setEnv(array $env) + { + $this->env = $env; + + return $this; + } + + /** + * Gets the Process input. + * + * @return resource|string|\Iterator|null + */ + public function getInput() + { + return $this->input; + } + + /** + * Sets the input. + * + * This content will be passed to the underlying process standard input. + * + * @param string|int|float|bool|resource|\Traversable|null $input The content + * + * @return $this + * + * @throws LogicException In case the process is running + */ + public function setInput($input) + { + if ($this->isRunning()) { + throw new LogicException('Input cannot be set while the process is running.'); + } + + $this->input = ProcessUtils::validateInput(__METHOD__, $input); + + return $this; + } + + /** + * Performs a check between the timeout definition and the time the process started. + * + * In case you run a background process (with the start method), you should + * trigger this method regularly to ensure the process timeout + * + * @throws ProcessTimedOutException In case the timeout was reached + */ + public function checkTimeout() + { + if (self::STATUS_STARTED !== $this->status) { + return; + } + + if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) { + $this->stop(0); + + throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL); + } + + if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) { + $this->stop(0); + + throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE); + } + } + + /** + * @throws LogicException in case process is not started + */ + public function getStartTime(): float + { + if (!$this->isStarted()) { + throw new LogicException('Start time is only available after process start.'); + } + + return $this->starttime; + } + + /** + * Defines options to pass to the underlying proc_open(). + * + * @see https://php.net/proc_open for the options supported by PHP. + * + * Enabling the "create_new_console" option allows a subprocess to continue + * to run after the main process exited, on both Windows and *nix + */ + public function setOptions(array $options) + { + if ($this->isRunning()) { + throw new RuntimeException('Setting options while the process is running is not possible.'); + } + + $defaultOptions = $this->options; + $existingOptions = ['blocking_pipes', 'create_process_group', 'create_new_console']; + + foreach ($options as $key => $value) { + if (!\in_array($key, $existingOptions)) { + $this->options = $defaultOptions; + throw new LogicException(sprintf('Invalid option "%s" passed to "%s()". Supported options are "%s".', $key, __METHOD__, implode('", "', $existingOptions))); + } + $this->options[$key] = $value; + } + } + + /** + * Returns whether TTY is supported on the current operating system. + */ + public static function isTtySupported(): bool + { + static $isTtySupported; + + if (null === $isTtySupported) { + $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes); + } + + return $isTtySupported; + } + + /** + * Returns whether PTY is supported on the current operating system. + * + * @return bool + */ + public static function isPtySupported() + { + static $result; + + if (null !== $result) { + return $result; + } + + if ('\\' === \DIRECTORY_SEPARATOR) { + return $result = false; + } + + return $result = (bool) @proc_open('echo 1 >/dev/null', [['pty'], ['pty'], ['pty']], $pipes); + } + + /** + * Creates the descriptors needed by the proc_open. + */ + private function getDescriptors(): array + { + if ($this->input instanceof \Iterator) { + $this->input->rewind(); + } + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->processPipes = new WindowsPipes($this->input, !$this->outputDisabled || $this->hasCallback); + } else { + $this->processPipes = new UnixPipes($this->isTty(), $this->isPty(), $this->input, !$this->outputDisabled || $this->hasCallback); + } + + return $this->processPipes->getDescriptors(); + } + + /** + * Builds up the callback used by wait(). + * + * The callbacks adds all occurred output to the specific buffer and calls + * the user callback (if present) with the received output. + * + * @param callable|null $callback The user defined PHP callback + * + * @return \Closure + */ + protected function buildCallback(callable $callback = null) + { + if ($this->outputDisabled) { + return function ($type, $data) use ($callback): bool { + return null !== $callback && $callback($type, $data); + }; + } + + $out = self::OUT; + + return function ($type, $data) use ($callback, $out): bool { + if ($out == $type) { + $this->addOutput($data); + } else { + $this->addErrorOutput($data); + } + + return null !== $callback && $callback($type, $data); + }; + } + + /** + * Updates the status of the process, reads pipes. + * + * @param bool $blocking Whether to use a blocking read call + */ + protected function updateStatus(bool $blocking) + { + if (self::STATUS_STARTED !== $this->status) { + return; + } + + $this->processInformation = proc_get_status($this->process); + $running = $this->processInformation['running']; + + $this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running); + + if ($this->fallbackStatus && $this->isSigchildEnabled()) { + $this->processInformation = $this->fallbackStatus + $this->processInformation; + } + + if (!$running) { + $this->close(); + } + } + + /** + * Returns whether PHP has been compiled with the '--enable-sigchild' option or not. + * + * @return bool + */ + protected function isSigchildEnabled() + { + if (null !== self::$sigchild) { + return self::$sigchild; + } + + if (!\function_exists('phpinfo')) { + return self::$sigchild = false; + } + + ob_start(); + phpinfo(\INFO_GENERAL); + + return self::$sigchild = str_contains(ob_get_clean(), '--enable-sigchild'); + } + + /** + * Reads pipes for the freshest output. + * + * @param string $caller The name of the method that needs fresh outputs + * @param bool $blocking Whether to use blocking calls or not + * + * @throws LogicException in case output has been disabled or process is not started + */ + private function readPipesForOutput(string $caller, bool $blocking = false) + { + if ($this->outputDisabled) { + throw new LogicException('Output has been disabled.'); + } + + $this->requireProcessIsStarted($caller); + + $this->updateStatus($blocking); + } + + /** + * Validates and returns the filtered timeout. + * + * @throws InvalidArgumentException if the given timeout is a negative number + */ + private function validateTimeout(?float $timeout): ?float + { + $timeout = (float) $timeout; + + if (0.0 === $timeout) { + $timeout = null; + } elseif ($timeout < 0) { + throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); + } + + return $timeout; + } + + /** + * Reads pipes, executes callback. + * + * @param bool $blocking Whether to use blocking calls or not + * @param bool $close Whether to close file handles or not + */ + private function readPipes(bool $blocking, bool $close) + { + $result = $this->processPipes->readAndWrite($blocking, $close); + + $callback = $this->callback; + foreach ($result as $type => $data) { + if (3 !== $type) { + $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data); + } elseif (!isset($this->fallbackStatus['signaled'])) { + $this->fallbackStatus['exitcode'] = (int) $data; + } + } + } + + /** + * Closes process resource, closes file handles, sets the exitcode. + * + * @return int The exitcode + */ + private function close(): int + { + $this->processPipes->close(); + if (\is_resource($this->process)) { + proc_close($this->process); + } + $this->exitcode = $this->processInformation['exitcode']; + $this->status = self::STATUS_TERMINATED; + + if (-1 === $this->exitcode) { + if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) { + // if process has been signaled, no exitcode but a valid termsig, apply Unix convention + $this->exitcode = 128 + $this->processInformation['termsig']; + } elseif ($this->isSigchildEnabled()) { + $this->processInformation['signaled'] = true; + $this->processInformation['termsig'] = -1; + } + } + + // Free memory from self-reference callback created by buildCallback + // Doing so in other contexts like __destruct or by garbage collector is ineffective + // Now pipes are closed, so the callback is no longer necessary + $this->callback = null; + + return $this->exitcode; + } + + /** + * Resets data related to the latest run of the process. + */ + private function resetProcessData() + { + $this->starttime = null; + $this->callback = null; + $this->exitcode = null; + $this->fallbackStatus = []; + $this->processInformation = null; + $this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+'); + $this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+'); + $this->process = null; + $this->latestSignal = null; + $this->status = self::STATUS_READY; + $this->incrementalOutputOffset = 0; + $this->incrementalErrorOutputOffset = 0; + } + + /** + * Sends a POSIX signal to the process. + * + * @param int $signal A valid POSIX signal (see https://php.net/pcntl.constants) + * @param bool $throwException Whether to throw exception in case signal failed + * + * @throws LogicException In case the process is not running + * @throws RuntimeException In case --enable-sigchild is activated and the process can't be killed + * @throws RuntimeException In case of failure + */ + private function doSignal(int $signal, bool $throwException): bool + { + if (null === $pid = $this->getPid()) { + if ($throwException) { + throw new LogicException('Cannot send signal on a non running process.'); + } + + return false; + } + + if ('\\' === \DIRECTORY_SEPARATOR) { + exec(sprintf('taskkill /F /T /PID %d 2>&1', $pid), $output, $exitCode); + if ($exitCode && $this->isRunning()) { + if ($throwException) { + throw new RuntimeException(sprintf('Unable to kill the process (%s).', implode(' ', $output))); + } + + return false; + } + } else { + if (!$this->isSigchildEnabled()) { + $ok = @proc_terminate($this->process, $signal); + } elseif (\function_exists('posix_kill')) { + $ok = @posix_kill($pid, $signal); + } elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) { + $ok = false === fgets($pipes[2]); + } + if (!$ok) { + if ($throwException) { + throw new RuntimeException(sprintf('Error while sending signal "%s".', $signal)); + } + + return false; + } + } + + $this->latestSignal = $signal; + $this->fallbackStatus['signaled'] = true; + $this->fallbackStatus['exitcode'] = -1; + $this->fallbackStatus['termsig'] = $this->latestSignal; + + return true; + } + + private function prepareWindowsCommandLine(string $cmd, array &$env): string + { + $uid = uniqid('', true); + $varCount = 0; + $varCache = []; + $cmd = preg_replace_callback( + '/"(?:( + [^"%!^]*+ + (?: + (?: !LF! | "(?:\^[%!^])?+" ) + [^"%!^]*+ + )++ + ) | [^"]*+ )"/x', + function ($m) use (&$env, &$varCache, &$varCount, $uid) { + if (!isset($m[1])) { + return $m[0]; + } + if (isset($varCache[$m[0]])) { + return $varCache[$m[0]]; + } + if (str_contains($value = $m[1], "\0")) { + $value = str_replace("\0", '?', $value); + } + if (false === strpbrk($value, "\"%!\n")) { + return '"'.$value.'"'; + } + + $value = str_replace(['!LF!', '"^!"', '"^%"', '"^^"', '""'], ["\n", '!', '%', '^', '"'], $value); + $value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"'; + $var = $uid.++$varCount; + + $env[$var] = $value; + + return $varCache[$m[0]] = '!'.$var.'!'; + }, + $cmd + ); + + $cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')'; + foreach ($this->processPipes->getFiles() as $offset => $filename) { + $cmd .= ' '.$offset.'>"'.$filename.'"'; + } + + return $cmd; + } + + /** + * Ensures the process is running or terminated, throws a LogicException if the process has a not started. + * + * @throws LogicException if the process has not run + */ + private function requireProcessIsStarted(string $functionName) + { + if (!$this->isStarted()) { + throw new LogicException(sprintf('Process must be started before calling "%s()".', $functionName)); + } + } + + /** + * Ensures the process is terminated, throws a LogicException if the process has a status different than "terminated". + * + * @throws LogicException if the process is not yet terminated + */ + private function requireProcessIsTerminated(string $functionName) + { + if (!$this->isTerminated()) { + throw new LogicException(sprintf('Process must be terminated before calling "%s()".', $functionName)); + } + } + + /** + * Escapes a string to be used as a shell argument. + */ + private function escapeArgument(?string $argument): string + { + if ('' === $argument || null === $argument) { + return '""'; + } + if ('\\' !== \DIRECTORY_SEPARATOR) { + return "'".str_replace("'", "'\\''", $argument)."'"; + } + if (str_contains($argument, "\0")) { + $argument = str_replace("\0", '?', $argument); + } + if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) { + return $argument; + } + $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument); + + return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"'; + } + + private function replacePlaceholders(string $commandline, array $env) + { + return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) { + if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) { + throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": ', $matches[1]).$commandline); + } + + return $this->escapeArgument($env[$matches[1]]); + }, $commandline); + } + + private function getDefaultEnv(): array + { + $env = getenv(); + $env = ('\\' === \DIRECTORY_SEPARATOR ? array_intersect_ukey($env, $_SERVER, 'strcasecmp') : array_intersect_key($env, $_SERVER)) ?: $env; + + return $_ENV + ('\\' === \DIRECTORY_SEPARATOR ? array_diff_ukey($env, $_ENV, 'strcasecmp') : $env); + } +} diff --git a/pandora_console/vendor/symfony/process/ProcessUtils.php b/pandora_console/vendor/symfony/process/ProcessUtils.php new file mode 100644 index 0000000000..2a7aff71b6 --- /dev/null +++ b/pandora_console/vendor/symfony/process/ProcessUtils.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\InvalidArgumentException; + +/** + * ProcessUtils is a bunch of utility methods. + * + * This class contains static methods only and is not meant to be instantiated. + * + * @author Martin Hasoň + */ +class ProcessUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Validates and normalizes a Process input. + * + * @param string $caller The name of method call that validates the input + * @param mixed $input The input to validate + * + * @return mixed + * + * @throws InvalidArgumentException In case the input is not valid + */ + public static function validateInput(string $caller, $input) + { + if (null !== $input) { + if (\is_resource($input)) { + return $input; + } + if (\is_string($input)) { + return $input; + } + if (\is_scalar($input)) { + return (string) $input; + } + if ($input instanceof Process) { + return $input->getIterator($input::ITER_SKIP_ERR); + } + if ($input instanceof \Iterator) { + return $input; + } + if ($input instanceof \Traversable) { + return new \IteratorIterator($input); + } + + throw new InvalidArgumentException(sprintf('"%s" only accepts strings, Traversable objects or stream resources.', $caller)); + } + + return $input; + } +} diff --git a/pandora_console/vendor/symfony/process/README.md b/pandora_console/vendor/symfony/process/README.md new file mode 100644 index 0000000000..8777de4a65 --- /dev/null +++ b/pandora_console/vendor/symfony/process/README.md @@ -0,0 +1,28 @@ +Process Component +================= + +The Process component executes commands in sub-processes. + +Sponsor +------- + +The Process component for Symfony 5.4/6.0 is [backed][1] by [SensioLabs][2]. + +As the creator of Symfony, SensioLabs supports companies using Symfony, with an +offering encompassing consultancy, expertise, services, training, and technical +assistance to ensure the success of web application development projects. + +Help Symfony by [sponsoring][3] its development! + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/process.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) + +[1]: https://symfony.com/backers +[2]: https://sensiolabs.com +[3]: https://symfony.com/sponsor diff --git a/pandora_console/vendor/symfony/process/composer.json b/pandora_console/vendor/symfony/process/composer.json new file mode 100644 index 0000000000..1669eba576 --- /dev/null +++ b/pandora_console/vendor/symfony/process/composer.json @@ -0,0 +1,29 @@ +{ + "name": "symfony/process", + "type": "library", + "description": "Executes commands in sub-processes", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Process\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/pandora_console/vendor/symfony/var-dumper/.gitignore b/pandora_console/vendor/symfony/var-dumper/.gitignore new file mode 100644 index 0000000000..5414c2c655 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/.gitignore @@ -0,0 +1,3 @@ +composer.lock +phpunit.xml +vendor/ diff --git a/pandora_console/vendor/symfony/var-dumper/CHANGELOG.md b/pandora_console/vendor/symfony/var-dumper/CHANGELOG.md new file mode 100644 index 0000000000..2d44cad225 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/CHANGELOG.md @@ -0,0 +1,13 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth + * deprecated `MongoCaster` + +2.7.0 +----- + + * deprecated Cloner\Data::getLimitedClone(). Use withMaxDepth, withMaxItemsPerDepth or withRefHandles instead. diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/AmqpCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/AmqpCaster.php new file mode 100644 index 0000000000..dc7a6414fc --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/AmqpCaster.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts Amqp related classes to array representation. + * + * @author Grégoire Pineau + */ +class AmqpCaster +{ + private static $flags = [ + \AMQP_DURABLE => 'AMQP_DURABLE', + \AMQP_PASSIVE => 'AMQP_PASSIVE', + \AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE', + \AMQP_AUTODELETE => 'AMQP_AUTODELETE', + \AMQP_INTERNAL => 'AMQP_INTERNAL', + \AMQP_NOLOCAL => 'AMQP_NOLOCAL', + \AMQP_AUTOACK => 'AMQP_AUTOACK', + \AMQP_IFEMPTY => 'AMQP_IFEMPTY', + \AMQP_IFUNUSED => 'AMQP_IFUNUSED', + \AMQP_MANDATORY => 'AMQP_MANDATORY', + \AMQP_IMMEDIATE => 'AMQP_IMMEDIATE', + \AMQP_MULTIPLE => 'AMQP_MULTIPLE', + \AMQP_NOWAIT => 'AMQP_NOWAIT', + \AMQP_REQUEUE => 'AMQP_REQUEUE', + ]; + + private static $exchangeTypes = [ + \AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT', + \AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT', + \AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC', + \AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS', + ]; + + public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'is_connected' => $c->isConnected(), + ]; + + // Recent version of the extension already expose private properties + if (isset($a["\x00AMQPConnection\x00login"])) { + return $a; + } + + // BC layer in the amqp lib + if (method_exists($c, 'getReadTimeout')) { + $timeout = $c->getReadTimeout(); + } else { + $timeout = $c->getTimeout(); + } + + $a += [ + $prefix.'is_connected' => $c->isConnected(), + $prefix.'login' => $c->getLogin(), + $prefix.'password' => $c->getPassword(), + $prefix.'host' => $c->getHost(), + $prefix.'vhost' => $c->getVhost(), + $prefix.'port' => $c->getPort(), + $prefix.'read_timeout' => $timeout, + ]; + + return $a; + } + + public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'is_connected' => $c->isConnected(), + $prefix.'channel_id' => $c->getChannelId(), + ]; + + // Recent version of the extension already expose private properties + if (isset($a["\x00AMQPChannel\x00connection"])) { + return $a; + } + + $a += [ + $prefix.'connection' => $c->getConnection(), + $prefix.'prefetch_size' => $c->getPrefetchSize(), + $prefix.'prefetch_count' => $c->getPrefetchCount(), + ]; + + return $a; + } + + public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'flags' => self::extractFlags($c->getFlags()), + ]; + + // Recent version of the extension already expose private properties + if (isset($a["\x00AMQPQueue\x00name"])) { + return $a; + } + + $a += [ + $prefix.'connection' => $c->getConnection(), + $prefix.'channel' => $c->getChannel(), + $prefix.'name' => $c->getName(), + $prefix.'arguments' => $c->getArguments(), + ]; + + return $a; + } + + public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'flags' => self::extractFlags($c->getFlags()), + ]; + + $type = isset(self::$exchangeTypes[$c->getType()]) ? new ConstStub(self::$exchangeTypes[$c->getType()], $c->getType()) : $c->getType(); + + // Recent version of the extension already expose private properties + if (isset($a["\x00AMQPExchange\x00name"])) { + $a["\x00AMQPExchange\x00type"] = $type; + + return $a; + } + + $a += [ + $prefix.'connection' => $c->getConnection(), + $prefix.'channel' => $c->getChannel(), + $prefix.'name' => $c->getName(), + $prefix.'type' => $type, + $prefix.'arguments' => $c->getArguments(), + ]; + + return $a; + } + + public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, $isNested, $filter = 0) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode()); + + // Recent version of the extension already expose private properties + if (isset($a["\x00AMQPEnvelope\x00body"])) { + $a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode; + + return $a; + } + + if (!($filter & Caster::EXCLUDE_VERBOSE)) { + $a += [$prefix.'body' => $c->getBody()]; + } + + $a += [ + $prefix.'delivery_tag' => $c->getDeliveryTag(), + $prefix.'is_redelivery' => $c->isRedelivery(), + $prefix.'exchange_name' => $c->getExchangeName(), + $prefix.'routing_key' => $c->getRoutingKey(), + $prefix.'content_type' => $c->getContentType(), + $prefix.'content_encoding' => $c->getContentEncoding(), + $prefix.'headers' => $c->getHeaders(), + $prefix.'delivery_mode' => $deliveryMode, + $prefix.'priority' => $c->getPriority(), + $prefix.'correlation_id' => $c->getCorrelationId(), + $prefix.'reply_to' => $c->getReplyTo(), + $prefix.'expiration' => $c->getExpiration(), + $prefix.'message_id' => $c->getMessageId(), + $prefix.'timestamp' => $c->getTimeStamp(), + $prefix.'type' => $c->getType(), + $prefix.'user_id' => $c->getUserId(), + $prefix.'app_id' => $c->getAppId(), + ]; + + return $a; + } + + private static function extractFlags($flags) + { + $flagsArray = []; + + foreach (self::$flags as $value => $name) { + if ($flags & $value) { + $flagsArray[] = $name; + } + } + + if (!$flagsArray) { + $flagsArray = ['AMQP_NOPARAM']; + } + + return new ConstStub(implode('|', $flagsArray), $flags); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/ArgsStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/ArgsStub.php new file mode 100644 index 0000000000..081fb47e99 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/ArgsStub.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Represents a list of function arguments. + * + * @author Nicolas Grekas + */ +class ArgsStub extends EnumStub +{ + private static $parameters = []; + + public function __construct(array $args, $function, $class) + { + list($variadic, $params) = self::getParameters($function, $class); + + $values = []; + foreach ($args as $k => $v) { + $values[$k] = !is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v; + } + if (null === $params) { + parent::__construct($values, false); + + return; + } + if (\count($values) < \count($params)) { + $params = \array_slice($params, 0, \count($values)); + } elseif (\count($values) > \count($params)) { + $values[] = new EnumStub(array_splice($values, \count($params)), false); + $params[] = $variadic; + } + if (['...'] === $params) { + $this->dumpKeys = false; + $this->value = $values[0]->value; + } else { + $this->value = array_combine($params, $values); + } + } + + private static function getParameters($function, $class) + { + if (isset(self::$parameters[$k = $class.'::'.$function])) { + return self::$parameters[$k]; + } + + try { + $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); + } catch (\ReflectionException $e) { + return [null, null]; + } + + $variadic = '...'; + $params = []; + foreach ($r->getParameters() as $v) { + $k = '$'.$v->name; + if ($v->isPassedByReference()) { + $k = '&'.$k; + } + if (method_exists($v, 'isVariadic') && $v->isVariadic()) { + $variadic .= $k; + } else { + $params[] = $k; + } + } + + return self::$parameters[$k] = [$variadic, $params]; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/Caster.php b/pandora_console/vendor/symfony/var-dumper/Caster/Caster.php new file mode 100644 index 0000000000..a6ebc25bdd --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/Caster.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Helper for filtering out properties in casters. + * + * @author Nicolas Grekas + * + * @final + */ +class Caster +{ + const EXCLUDE_VERBOSE = 1; + const EXCLUDE_VIRTUAL = 2; + const EXCLUDE_DYNAMIC = 4; + const EXCLUDE_PUBLIC = 8; + const EXCLUDE_PROTECTED = 16; + const EXCLUDE_PRIVATE = 32; + const EXCLUDE_NULL = 64; + const EXCLUDE_EMPTY = 128; + const EXCLUDE_NOT_IMPORTANT = 256; + const EXCLUDE_STRICT = 512; + + const PREFIX_VIRTUAL = "\0~\0"; + const PREFIX_DYNAMIC = "\0+\0"; + const PREFIX_PROTECTED = "\0*\0"; + + /** + * Casts objects to arrays and adds the dynamic property prefix. + * + * @param object $obj The object to cast + * @param string $class The class of the object + * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not + * + * @return array The array-cast of the object, with prefixed dynamic properties + */ + public static function castObject($obj, $class, $hasDebugInfo = false, $debugClass = null) + { + if ($class instanceof \ReflectionClass) { + @trigger_error(sprintf('Passing a ReflectionClass to "%s()" is deprecated since Symfony 3.3 and will be unsupported in 4.0. Pass the class name as string instead.', __METHOD__), \E_USER_DEPRECATED); + $hasDebugInfo = $class->hasMethod('__debugInfo'); + $class = $class->name; + } + + if ($hasDebugInfo) { + try { + $debugInfo = $obj->__debugInfo(); + } catch (\Exception $e) { + // ignore failing __debugInfo() + $hasDebugInfo = false; + } + } + + $a = $obj instanceof \Closure ? [] : (array) $obj; + + if ($obj instanceof \__PHP_Incomplete_Class) { + return $a; + } + + if ($a) { + static $publicProperties = []; + if (null === $debugClass) { + if (\PHP_VERSION_ID >= 80000) { + $debugClass = get_debug_type($obj); + } else { + $debugClass = $class; + + if (isset($debugClass[15]) && "\0" === $debugClass[15]) { + $debugClass = (get_parent_class($debugClass) ?: key(class_implements($debugClass)) ?: 'class').'@anonymous'; + } + } + } + + $i = 0; + $prefixedKeys = []; + foreach ($a as $k => $v) { + if (isset($k[0]) ? "\0" !== $k[0] : \PHP_VERSION_ID >= 70200) { + if (!isset($publicProperties[$class])) { + foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) { + $publicProperties[$class][$prop->name] = true; + } + } + if (!isset($publicProperties[$class][$k])) { + $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; + } + } elseif ($debugClass !== $class && 1 === strpos($k, $class)) { + $prefixedKeys[$i] = "\0".$debugClass.strrchr($k, "\0"); + } + ++$i; + } + if ($prefixedKeys) { + $keys = array_keys($a); + foreach ($prefixedKeys as $i => $k) { + $keys[$i] = $k; + } + $a = array_combine($keys, $a); + } + } + + if ($hasDebugInfo && \is_array($debugInfo)) { + foreach ($debugInfo as $k => $v) { + if (!isset($k[0]) || "\0" !== $k[0]) { + if (\array_key_exists(self::PREFIX_DYNAMIC.$k, $a)) { + continue; + } + $k = self::PREFIX_VIRTUAL.$k; + } + + unset($a[$k]); + $a[$k] = $v; + } + } + + return $a; + } + + /** + * Filters out the specified properties. + * + * By default, a single match in the $filter bit field filters properties out, following an "or" logic. + * When EXCLUDE_STRICT is set, an "and" logic is applied: all bits must match for a property to be removed. + * + * @param array $a The array containing the properties to filter + * @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out + * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set + * @param int &$count Set to the number of removed properties + * + * @return array The filtered array + */ + public static function filter(array $a, $filter, array $listedProperties = [], &$count = 0) + { + $count = 0; + + foreach ($a as $k => $v) { + $type = self::EXCLUDE_STRICT & $filter; + + if (null === $v) { + $type |= self::EXCLUDE_NULL & $filter; + $type |= self::EXCLUDE_EMPTY & $filter; + } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { + $type |= self::EXCLUDE_EMPTY & $filter; + } + if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { + $type |= self::EXCLUDE_NOT_IMPORTANT; + } + if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) { + $type |= self::EXCLUDE_VERBOSE; + } + + if (!isset($k[1]) || "\0" !== $k[0]) { + $type |= self::EXCLUDE_PUBLIC & $filter; + } elseif ('~' === $k[1]) { + $type |= self::EXCLUDE_VIRTUAL & $filter; + } elseif ('+' === $k[1]) { + $type |= self::EXCLUDE_DYNAMIC & $filter; + } elseif ('*' === $k[1]) { + $type |= self::EXCLUDE_PROTECTED & $filter; + } else { + $type |= self::EXCLUDE_PRIVATE & $filter; + } + + if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) { + unset($a[$k]); + ++$count; + } + } + + return $a; + } + + public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, $isNested) + { + if (isset($a['__PHP_Incomplete_Class_Name'])) { + $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')'; + unset($a['__PHP_Incomplete_Class_Name']); + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/ClassStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/ClassStub.php new file mode 100644 index 0000000000..1a85098e15 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/ClassStub.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents a PHP class identifier. + * + * @author Nicolas Grekas + */ +class ClassStub extends ConstStub +{ + /** + * @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name + * @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier + */ + public function __construct($identifier, $callable = null) + { + $this->value = $identifier; + + if (0 < $i = strrpos($identifier, '\\')) { + $this->attr['ellipsis'] = \strlen($identifier) - $i; + $this->attr['ellipsis-type'] = 'class'; + $this->attr['ellipsis-tail'] = 1; + } + + try { + if (null !== $callable) { + if ($callable instanceof \Closure) { + $r = new \ReflectionFunction($callable); + } elseif (\is_object($callable)) { + $r = [$callable, '__invoke']; + } elseif (\is_array($callable)) { + $r = $callable; + } elseif (false !== $i = strpos($callable, '::')) { + $r = [substr($callable, 0, $i), substr($callable, 2 + $i)]; + } else { + $r = new \ReflectionFunction($callable); + } + } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) { + $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)]; + } else { + $r = new \ReflectionClass($identifier); + } + + if (\is_array($r)) { + try { + $r = new \ReflectionMethod($r[0], $r[1]); + } catch (\ReflectionException $e) { + $r = new \ReflectionClass($r[0]); + } + } + } catch (\ReflectionException $e) { + return; + } + + if ($f = $r->getFileName()) { + $this->attr['file'] = $f; + $this->attr['line'] = $r->getStartLine(); + } + } + + public static function wrapCallable($callable) + { + if (\is_object($callable) || !\is_callable($callable)) { + return $callable; + } + + if (!\is_array($callable)) { + $callable = new static($callable); + } elseif (\is_string($callable[0])) { + $callable[0] = new static($callable[0]); + } else { + $callable[1] = new static($callable[1], $callable); + } + + return $callable; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/ConstStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/ConstStub.php new file mode 100644 index 0000000000..26c0010b66 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/ConstStub.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Represents a PHP constant and its value. + * + * @author Nicolas Grekas + */ +class ConstStub extends Stub +{ + public function __construct($name, $value) + { + $this->class = $name; + $this->value = $value; + } + + public function __toString() + { + return (string) $this->value; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/CutArrayStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/CutArrayStub.php new file mode 100644 index 0000000000..0e4fb363d2 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/CutArrayStub.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents a cut array. + * + * @author Nicolas Grekas + */ +class CutArrayStub extends CutStub +{ + public $preservedSubset; + + public function __construct(array $value, array $preservedKeys) + { + parent::__construct($value); + + $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys)); + $this->cut -= \count($this->preservedSubset); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/CutStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/CutStub.php new file mode 100644 index 0000000000..690338f542 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/CutStub.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Represents the main properties of a PHP variable, pre-casted by a caster. + * + * @author Nicolas Grekas + */ +class CutStub extends Stub +{ + public function __construct($value) + { + $this->value = $value; + + switch (\gettype($value)) { + case 'object': + $this->type = self::TYPE_OBJECT; + $this->class = \get_class($value); + $this->cut = -1; + break; + + case 'array': + $this->type = self::TYPE_ARRAY; + $this->class = self::ARRAY_ASSOC; + $this->cut = $this->value = \count($value); + break; + + case 'resource': + case 'unknown type': + case 'resource (closed)': + $this->type = self::TYPE_RESOURCE; + $this->handle = (int) $value; + if ('Unknown' === $this->class = @get_resource_type($value)) { + $this->class = 'Closed'; + } + $this->cut = -1; + break; + + case 'string': + $this->type = self::TYPE_STRING; + $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; + $this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8'); + $this->value = ''; + break; + } + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/DOMCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/DOMCaster.php new file mode 100644 index 0000000000..fef3d432a7 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/DOMCaster.php @@ -0,0 +1,302 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts DOM related classes to array representation. + * + * @author Nicolas Grekas + */ +class DOMCaster +{ + private static $errorCodes = [ + \DOM_PHP_ERR => 'DOM_PHP_ERR', + \DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR', + \DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR', + \DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR', + \DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR', + \DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR', + \DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR', + \DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR', + \DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR', + \DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR', + \DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR', + \DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR', + \DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR', + \DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR', + \DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR', + \DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR', + \DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', + ]; + + private static $nodeTypes = [ + \XML_ELEMENT_NODE => 'XML_ELEMENT_NODE', + \XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE', + \XML_TEXT_NODE => 'XML_TEXT_NODE', + \XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE', + \XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE', + \XML_ENTITY_NODE => 'XML_ENTITY_NODE', + \XML_PI_NODE => 'XML_PI_NODE', + \XML_COMMENT_NODE => 'XML_COMMENT_NODE', + \XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE', + \XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE', + \XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE', + \XML_NOTATION_NODE => 'XML_NOTATION_NODE', + \XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE', + \XML_DTD_NODE => 'XML_DTD_NODE', + \XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE', + \XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE', + \XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE', + \XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE', + ]; + + public static function castException(\DOMException $e, array $a, Stub $stub, $isNested) + { + $k = Caster::PREFIX_PROTECTED.'code'; + if (isset($a[$k], self::$errorCodes[$a[$k]])) { + $a[$k] = new ConstStub(self::$errorCodes[$a[$k]], $a[$k]); + } + + return $a; + } + + public static function castLength($dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'length' => $dom->length, + ]; + + return $a; + } + + public static function castImplementation($dom, array $a, Stub $stub, $isNested) + { + $a += [ + Caster::PREFIX_VIRTUAL.'Core' => '1.0', + Caster::PREFIX_VIRTUAL.'XML' => '2.0', + ]; + + return $a; + } + + public static function castNode(\DOMNode $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'nodeName' => $dom->nodeName, + 'nodeValue' => new CutStub($dom->nodeValue), + 'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType), + 'parentNode' => new CutStub($dom->parentNode), + 'childNodes' => $dom->childNodes, + 'firstChild' => new CutStub($dom->firstChild), + 'lastChild' => new CutStub($dom->lastChild), + 'previousSibling' => new CutStub($dom->previousSibling), + 'nextSibling' => new CutStub($dom->nextSibling), + 'attributes' => $dom->attributes, + 'ownerDocument' => new CutStub($dom->ownerDocument), + 'namespaceURI' => $dom->namespaceURI, + 'prefix' => $dom->prefix, + 'localName' => $dom->localName, + 'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI, + 'textContent' => new CutStub($dom->textContent), + ]; + + return $a; + } + + public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'nodeName' => $dom->nodeName, + 'nodeValue' => new CutStub($dom->nodeValue), + 'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType), + 'prefix' => $dom->prefix, + 'localName' => $dom->localName, + 'namespaceURI' => $dom->namespaceURI, + 'ownerDocument' => new CutStub($dom->ownerDocument), + 'parentNode' => new CutStub($dom->parentNode), + ]; + + return $a; + } + + public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, $isNested, $filter = 0) + { + $a += [ + 'doctype' => $dom->doctype, + 'implementation' => $dom->implementation, + 'documentElement' => new CutStub($dom->documentElement), + 'actualEncoding' => $dom->actualEncoding, + 'encoding' => $dom->encoding, + 'xmlEncoding' => $dom->xmlEncoding, + 'standalone' => $dom->standalone, + 'xmlStandalone' => $dom->xmlStandalone, + 'version' => $dom->version, + 'xmlVersion' => $dom->xmlVersion, + 'strictErrorChecking' => $dom->strictErrorChecking, + 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, + 'config' => $dom->config, + 'formatOutput' => $dom->formatOutput, + 'validateOnParse' => $dom->validateOnParse, + 'resolveExternals' => $dom->resolveExternals, + 'preserveWhiteSpace' => $dom->preserveWhiteSpace, + 'recover' => $dom->recover, + 'substituteEntities' => $dom->substituteEntities, + ]; + + if (!($filter & Caster::EXCLUDE_VERBOSE)) { + $formatOutput = $dom->formatOutput; + $dom->formatOutput = true; + $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; + $dom->formatOutput = $formatOutput; + } + + return $a; + } + + public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'data' => $dom->data, + 'length' => $dom->length, + ]; + + return $a; + } + + public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'name' => $dom->name, + 'specified' => $dom->specified, + 'value' => $dom->value, + 'ownerElement' => $dom->ownerElement, + 'schemaTypeInfo' => $dom->schemaTypeInfo, + ]; + + return $a; + } + + public static function castElement(\DOMElement $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'tagName' => $dom->tagName, + 'schemaTypeInfo' => $dom->schemaTypeInfo, + ]; + + return $a; + } + + public static function castText(\DOMText $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'wholeText' => $dom->wholeText, + ]; + + return $a; + } + + public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'typeName' => $dom->typeName, + 'typeNamespace' => $dom->typeNamespace, + ]; + + return $a; + } + + public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'severity' => $dom->severity, + 'message' => $dom->message, + 'type' => $dom->type, + 'relatedException' => $dom->relatedException, + 'related_data' => $dom->related_data, + 'location' => $dom->location, + ]; + + return $a; + } + + public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'lineNumber' => $dom->lineNumber, + 'columnNumber' => $dom->columnNumber, + 'offset' => $dom->offset, + 'relatedNode' => $dom->relatedNode, + 'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri, + ]; + + return $a; + } + + public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'name' => $dom->name, + 'entities' => $dom->entities, + 'notations' => $dom->notations, + 'publicId' => $dom->publicId, + 'systemId' => $dom->systemId, + 'internalSubset' => $dom->internalSubset, + ]; + + return $a; + } + + public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'publicId' => $dom->publicId, + 'systemId' => $dom->systemId, + ]; + + return $a; + } + + public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'publicId' => $dom->publicId, + 'systemId' => $dom->systemId, + 'notationName' => $dom->notationName, + 'actualEncoding' => $dom->actualEncoding, + 'encoding' => $dom->encoding, + 'version' => $dom->version, + ]; + + return $a; + } + + public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'target' => $dom->target, + 'data' => $dom->data, + ]; + + return $a; + } + + public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, $isNested) + { + $a += [ + 'document' => $dom->document, + ]; + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/DateCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/DateCaster.php new file mode 100644 index 0000000000..70f229a0d8 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/DateCaster.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts DateTimeInterface related classes to array representation. + * + * @author Dany Maillard + */ +class DateCaster +{ + public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, $isNested, $filter) + { + $prefix = Caster::PREFIX_VIRTUAL; + $location = $d->getTimezone()->getLocation(); + $fromNow = (new \DateTime())->diff($d); + + $title = $d->format('l, F j, Y') + ."\n".self::formatInterval($fromNow).' from now' + .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') + ; + + unset( + $a[Caster::PREFIX_DYNAMIC.'date'], + $a[Caster::PREFIX_DYNAMIC.'timezone'], + $a[Caster::PREFIX_DYNAMIC.'timezone_type'] + ); + $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); + + $stub->class .= $d->format(' @U'); + + return $a; + } + + public static function castInterval(\DateInterval $interval, array $a, Stub $stub, $isNested, $filter) + { + $now = new \DateTimeImmutable(); + $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); + $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; + + $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; + + return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; + } + + private static function formatInterval(\DateInterval $i) + { + $format = '%R '; + + if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { + $i = date_diff($d = new \DateTime(), date_add(clone $d, $i)); // recalculate carry over points + $format .= 0 < $i->days ? '%ad ' : ''; + } else { + $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); + } + + if (\PHP_VERSION_ID >= 70100 && isset($i->f)) { + $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; + } else { + $format .= $i->h || $i->i || $i->s ? '%H:%I:%S' : ''; + } + + $format = '%R ' === $format ? '0s' : $format; + + return $i->format(rtrim($format)); + } + + public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, $isNested, $filter) + { + $location = $timeZone->getLocation(); + $formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P'); + $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code'], \Locale::getDefault()) : ''; + + $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; + + return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; + } + + public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNested, $filter) + { + if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { // see https://bugs.php.net/71635 + return $a; + } + + $dates = []; + if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/74639 + foreach (clone $p as $i => $d) { + if (3 === $i) { + $now = new \DateTimeImmutable(); + $dates[] = sprintf('%s more', ($end = $p->getEndDate()) + ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) + : $p->recurrences - $i + ); + break; + } + $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d)); + } + } + + $period = sprintf( + 'every %s, from %s (%s) %s', + self::formatInterval($p->getDateInterval()), + self::formatDateTime($p->getStartDate()), + $p->include_start_date ? 'included' : 'excluded', + ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end) : 'recurring '.$p->recurrences.' time/s' + ); + + $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; + + return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; + } + + private static function formatDateTime(\DateTimeInterface $d, $extra = '') + { + return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); + } + + private static function formatSeconds($s, $us) + { + return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/DoctrineCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/DoctrineCaster.php new file mode 100644 index 0000000000..696b87816e --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/DoctrineCaster.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Doctrine\Common\Proxy\Proxy as CommonProxy; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Proxy\Proxy as OrmProxy; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts Doctrine related classes to array representation. + * + * @author Nicolas Grekas + */ +class DoctrineCaster +{ + public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, $isNested) + { + foreach (['__cloner__', '__initializer__'] as $k) { + if (\array_key_exists($k, $a)) { + unset($a[$k]); + ++$stub->cut; + } + } + + return $a; + } + + public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, $isNested) + { + foreach (['_entityPersister', '_identifier'] as $k) { + if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { + unset($a[$k]); + ++$stub->cut; + } + } + + return $a; + } + + public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, $isNested) + { + foreach (['snapshot', 'association', 'typeClass'] as $k) { + if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { + $a[$k] = new CutStub($a[$k]); + } + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/EnumStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/EnumStub.php new file mode 100644 index 0000000000..3cee23eac2 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/EnumStub.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Represents an enumeration of values. + * + * @author Nicolas Grekas + */ +class EnumStub extends Stub +{ + public $dumpKeys = true; + + public function __construct(array $values, $dumpKeys = true) + { + $this->value = $values; + $this->dumpKeys = $dumpKeys; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/ExceptionCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/ExceptionCaster.php new file mode 100644 index 0000000000..62b57402f8 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/ExceptionCaster.php @@ -0,0 +1,349 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\Debug\Exception\SilencedErrorContext; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Exception\ThrowingCasterException; + +/** + * Casts common Exception classes to array representation. + * + * @author Nicolas Grekas + */ +class ExceptionCaster +{ + public static $srcContext = 1; + public static $traceArgs = true; + public static $errorTypes = [ + \E_DEPRECATED => 'E_DEPRECATED', + \E_USER_DEPRECATED => 'E_USER_DEPRECATED', + \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', + \E_ERROR => 'E_ERROR', + \E_WARNING => 'E_WARNING', + \E_PARSE => 'E_PARSE', + \E_NOTICE => 'E_NOTICE', + \E_CORE_ERROR => 'E_CORE_ERROR', + \E_CORE_WARNING => 'E_CORE_WARNING', + \E_COMPILE_ERROR => 'E_COMPILE_ERROR', + \E_COMPILE_WARNING => 'E_COMPILE_WARNING', + \E_USER_ERROR => 'E_USER_ERROR', + \E_USER_WARNING => 'E_USER_WARNING', + \E_USER_NOTICE => 'E_USER_NOTICE', + \E_STRICT => 'E_STRICT', + ]; + + private static $framesCache = []; + + public static function castError(\Error $e, array $a, Stub $stub, $isNested, $filter = 0) + { + return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); + } + + public static function castException(\Exception $e, array $a, Stub $stub, $isNested, $filter = 0) + { + return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); + } + + public static function castErrorException(\ErrorException $e, array $a, Stub $stub, $isNested) + { + if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) { + $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); + } + + return $a; + } + + public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, $isNested) + { + $trace = Caster::PREFIX_VIRTUAL.'trace'; + $prefix = Caster::PREFIX_PROTECTED; + $xPrefix = "\0Exception\0"; + + if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) { + $b = (array) $a[$xPrefix.'previous']; + self::traceUnshift($b[$xPrefix.'trace'], \get_class($a[$xPrefix.'previous']), $b[$prefix.'file'], $b[$prefix.'line']); + $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value)); + } + + unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); + + return $a; + } + + public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, $isNested) + { + $sPrefix = "\0".SilencedErrorContext::class."\0"; + + if (!isset($a[$s = $sPrefix.'severity'])) { + return $a; + } + + if (isset(self::$errorTypes[$a[$s]])) { + $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); + } + + $trace = [[ + 'file' => $a[$sPrefix.'file'], + 'line' => $a[$sPrefix.'line'], + ]]; + + if (isset($a[$sPrefix.'trace'])) { + $trace = array_merge($trace, $a[$sPrefix.'trace']); + } + + unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']); + $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); + + return $a; + } + + public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $isNested) + { + if (!$isNested) { + return $a; + } + $stub->class = ''; + $stub->handle = 0; + $frames = $trace->value; + $prefix = Caster::PREFIX_VIRTUAL; + + $a = []; + $j = \count($frames); + if (0 > $i = $trace->sliceOffset) { + $i = max(0, $j + $i); + } + if (!isset($trace->value[$i])) { + return []; + } + $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; + $frames[] = ['function' => '']; + $collapse = false; + + for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { + $f = $frames[$i]; + $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???'; + + $frame = new FrameStub( + [ + 'object' => isset($f['object']) ? $f['object'] : null, + 'class' => isset($f['class']) ? $f['class'] : null, + 'type' => isset($f['type']) ? $f['type'] : null, + 'function' => isset($f['function']) ? $f['function'] : null, + ] + $frames[$i - 1], + false, + true + ); + $f = self::castFrameStub($frame, [], $frame, true); + if (isset($f[$prefix.'src'])) { + foreach ($f[$prefix.'src']->value as $label => $frame) { + if (0 === strpos($label, "\0~collapse=0")) { + if ($collapse) { + $label = substr_replace($label, '1', 11, 1); + } else { + $collapse = true; + } + } + $label = substr_replace($label, "title=Stack level $j.&", 2, 0); + } + $f = $frames[$i - 1]; + if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) { + $frame->value['arguments'] = new ArgsStub($f['args'], isset($f['function']) ? $f['function'] : null, isset($f['class']) ? $f['class'] : null); + } + } elseif ('???' !== $lastCall) { + $label = new ClassStub($lastCall); + if (isset($label->attr['ellipsis'])) { + $label->attr['ellipsis'] += 2; + $label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()'; + } else { + $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()'; + } + } else { + $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall; + } + $a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame; + + $lastCall = $call; + } + if (null !== $trace->sliceLength) { + $a = \array_slice($a, 0, $trace->sliceLength, true); + } + + return $a; + } + + public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $isNested) + { + if (!$isNested) { + return $a; + } + $f = $frame->value; + $prefix = Caster::PREFIX_VIRTUAL; + + if (isset($f['file'], $f['line'])) { + $cacheKey = $f; + unset($cacheKey['object'], $cacheKey['args']); + $cacheKey[] = self::$srcContext; + $cacheKey = implode('-', $cacheKey); + + if (isset(self::$framesCache[$cacheKey])) { + $a[$prefix.'src'] = self::$framesCache[$cacheKey]; + } else { + if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { + $f['file'] = substr($f['file'], 0, -\strlen($match[0])); + $f['line'] = (int) $match[1]; + } + $caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null; + $src = $f['line']; + $srcKey = $f['file']; + $ellipsis = new LinkStub($srcKey, 0); + $srcAttr = 'collapse='.(int) $ellipsis->inVendor; + $ellipsisTail = isset($ellipsis->attr['ellipsis-tail']) ? $ellipsis->attr['ellipsis-tail'] : 0; + $ellipsis = isset($ellipsis->attr['ellipsis']) ? $ellipsis->attr['ellipsis'] : 0; + + if (file_exists($f['file']) && 0 <= self::$srcContext) { + if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) { + $template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); + + $ellipsis = 0; + $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); + $templateInfo = $template->getDebugInfo(); + if (isset($templateInfo[$f['line']])) { + if (!method_exists($template, 'getSourceContext') || !file_exists($templatePath = $template->getSourceContext()->getPath())) { + $templatePath = null; + } + if ($templateSrc) { + $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath); + $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; + } + } + } + if ($srcKey == $f['file']) { + $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, $caller, 'php', $f['file']); + $srcKey .= ':'.$f['line']; + if ($ellipsis) { + $ellipsis += 1 + \strlen($f['line']); + } + } + $srcAttr .= '&separator= '; + } else { + $srcAttr .= '&separator=:'; + } + $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : ''; + self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]); + } + } + + unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); + if ($frame->inTraceStub) { + unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']); + } + foreach ($a as $k => $v) { + if (!$v) { + unset($a[$k]); + } + } + if ($frame->keepArgs && !empty($f['args'])) { + $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']); + } + + return $a; + } + + private static function filterExceptionArray($xClass, array $a, $xPrefix, $filter) + { + if (isset($a[$xPrefix.'trace'])) { + $trace = $a[$xPrefix.'trace']; + unset($a[$xPrefix.'trace']); // Ensures the trace is always last + } else { + $trace = []; + } + + if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) { + if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { + self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); + } + $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); + } + if (empty($a[$xPrefix.'previous'])) { + unset($a[$xPrefix.'previous']); + } + unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']); + + if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { + $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); + } + + return $a; + } + + private static function traceUnshift(&$trace, $class, $file, $line) + { + if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { + return; + } + array_unshift($trace, [ + 'function' => $class ? 'new '.$class : null, + 'file' => $file, + 'line' => $line, + ]); + } + + private static function extractSource($srcLines, $line, $srcContext, $title, $lang, $file = null) + { + $srcLines = explode("\n", $srcLines); + $src = []; + + for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { + $src[] = (isset($srcLines[$i]) ? $srcLines[$i] : '')."\n"; + } + + $srcLines = []; + $ltrim = 0; + do { + $pad = null; + for ($i = $srcContext << 1; $i >= 0; --$i) { + if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) { + if (null === $pad) { + $pad = $c; + } + if ((' ' !== $c && "\t" !== $c) || $pad !== $c) { + break; + } + } + } + ++$ltrim; + } while (0 > $i && null !== $pad); + + --$ltrim; + + foreach ($src as $i => $c) { + if ($ltrim) { + $c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t"); + } + $c = substr($c, 0, -1); + if ($i !== $srcContext) { + $c = new ConstStub('default', $c); + } else { + $c = new ConstStub($c, $title); + if (null !== $file) { + $c->attr['file'] = $file; + $c->attr['line'] = $line; + } + } + $c->attr['lang'] = $lang; + $srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c; + } + + return new EnumStub($srcLines); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/FrameStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/FrameStub.php new file mode 100644 index 0000000000..1e1194dc85 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/FrameStub.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace(). + * + * @author Nicolas Grekas + */ +class FrameStub extends EnumStub +{ + public $keepArgs; + public $inTraceStub; + + public function __construct(array $frame, $keepArgs = true, $inTraceStub = false) + { + $this->value = $frame; + $this->keepArgs = $keepArgs; + $this->inTraceStub = $inTraceStub; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/LinkStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/LinkStub.php new file mode 100644 index 0000000000..b589b502d4 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/LinkStub.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents a file or a URL. + * + * @author Nicolas Grekas + */ +class LinkStub extends ConstStub +{ + public $inVendor = false; + + private static $vendorRoots; + private static $composerRoots; + + public function __construct($label, $line = 0, $href = null) + { + $this->value = $label; + + if (null === $href) { + $href = $label; + } + if (!\is_string($href)) { + return; + } + if (0 === strpos($href, 'file://')) { + if ($href === $label) { + $label = substr($label, 7); + } + $href = substr($href, 7); + } elseif (false !== strpos($href, '://')) { + $this->attr['href'] = $href; + + return; + } + if (!file_exists($href)) { + return; + } + if ($line) { + $this->attr['line'] = $line; + } + if ($label !== $this->attr['file'] = realpath($href) ?: $href) { + return; + } + if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) { + $this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1; + $this->attr['ellipsis-type'] = 'path'; + $this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0); + } elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) { + $this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2))); + $this->attr['ellipsis-type'] = 'path'; + $this->attr['ellipsis-tail'] = 1; + } + } + + private function getComposerRoot($file, &$inVendor) + { + if (null === self::$vendorRoots) { + self::$vendorRoots = []; + + foreach (get_declared_classes() as $class) { + if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $v = \dirname(\dirname($r->getFileName())); + if (file_exists($v.'/composer/installed.json')) { + self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR; + } + } + } + } + $inVendor = false; + + if (isset(self::$composerRoots[$dir = \dirname($file)])) { + return self::$composerRoots[$dir]; + } + + foreach (self::$vendorRoots as $root) { + if ($inVendor = 0 === strpos($file, $root)) { + return $root; + } + } + + $parent = $dir; + while (!@file_exists($parent.'/composer.json')) { + if (!@file_exists($parent)) { + // open_basedir restriction in effect + break; + } + if ($parent === \dirname($parent)) { + return self::$composerRoots[$dir] = false; + } + + $parent = \dirname($parent); + } + + return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/MongoCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/MongoCaster.php new file mode 100644 index 0000000000..98f1b8e25d --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/MongoCaster.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +@trigger_error('The '.__NAMESPACE__.'\MongoCaster class is deprecated since Symfony 3.4 and will be removed in 4.0.', \E_USER_DEPRECATED); + +/** + * Casts classes from the MongoDb extension to array representation. + * + * @author Nicolas Grekas + * + * @deprecated since version 3.4, to be removed in 4.0. + */ +class MongoCaster +{ + public static function castCursor(\MongoCursorInterface $cursor, array $a, Stub $stub, $isNested) + { + if ($info = $cursor->info()) { + foreach ($info as $k => $v) { + $a[Caster::PREFIX_VIRTUAL.$k] = $v; + } + } + $a[Caster::PREFIX_VIRTUAL.'dead'] = $cursor->dead(); + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/PdoCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/PdoCaster.php new file mode 100644 index 0000000000..8af51829a9 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/PdoCaster.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts PDO related classes to array representation. + * + * @author Nicolas Grekas + */ +class PdoCaster +{ + private static $pdoAttributes = [ + 'CASE' => [ + \PDO::CASE_LOWER => 'LOWER', + \PDO::CASE_NATURAL => 'NATURAL', + \PDO::CASE_UPPER => 'UPPER', + ], + 'ERRMODE' => [ + \PDO::ERRMODE_SILENT => 'SILENT', + \PDO::ERRMODE_WARNING => 'WARNING', + \PDO::ERRMODE_EXCEPTION => 'EXCEPTION', + ], + 'TIMEOUT', + 'PREFETCH', + 'AUTOCOMMIT', + 'PERSISTENT', + 'DRIVER_NAME', + 'SERVER_INFO', + 'ORACLE_NULLS' => [ + \PDO::NULL_NATURAL => 'NATURAL', + \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING', + \PDO::NULL_TO_STRING => 'TO_STRING', + ], + 'CLIENT_VERSION', + 'SERVER_VERSION', + 'STATEMENT_CLASS', + 'EMULATE_PREPARES', + 'CONNECTION_STATUS', + 'STRINGIFY_FETCHES', + 'DEFAULT_FETCH_MODE' => [ + \PDO::FETCH_ASSOC => 'ASSOC', + \PDO::FETCH_BOTH => 'BOTH', + \PDO::FETCH_LAZY => 'LAZY', + \PDO::FETCH_NUM => 'NUM', + \PDO::FETCH_OBJ => 'OBJ', + ], + ]; + + public static function castPdo(\PDO $c, array $a, Stub $stub, $isNested) + { + $attr = []; + $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); + $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + foreach (self::$pdoAttributes as $k => $v) { + if (!isset($k[0])) { + $k = $v; + $v = []; + } + + try { + $attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k)); + if ($v && isset($v[$attr[$k]])) { + $attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]); + } + } catch (\Exception $e) { + } + } + if (isset($attr[$k = 'STATEMENT_CLASS'][1])) { + if ($attr[$k][1]) { + $attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]); + } + $attr[$k][0] = new ClassStub($attr[$k][0]); + } + + $prefix = Caster::PREFIX_VIRTUAL; + $a += [ + $prefix.'inTransaction' => method_exists($c, 'inTransaction'), + $prefix.'errorInfo' => $c->errorInfo(), + $prefix.'attributes' => new EnumStub($attr), + ]; + + if ($a[$prefix.'inTransaction']) { + $a[$prefix.'inTransaction'] = $c->inTransaction(); + } else { + unset($a[$prefix.'inTransaction']); + } + + if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { + unset($a[$prefix.'errorInfo']); + } + + $c->setAttribute(\PDO::ATTR_ERRMODE, $errmode); + + return $a; + } + + public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + $a[$prefix.'errorInfo'] = $c->errorInfo(); + + if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { + unset($a[$prefix.'errorInfo']); + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/PgSqlCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/PgSqlCaster.php new file mode 100644 index 0000000000..fe1f0cc8d9 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/PgSqlCaster.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts pqsql resources to array representation. + * + * @author Nicolas Grekas + */ +class PgSqlCaster +{ + private static $paramCodes = [ + 'server_encoding', + 'client_encoding', + 'is_superuser', + 'session_authorization', + 'DateStyle', + 'TimeZone', + 'IntervalStyle', + 'integer_datetimes', + 'application_name', + 'standard_conforming_strings', + ]; + + private static $transactionStatus = [ + \PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE', + \PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE', + \PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS', + \PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR', + \PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN', + ]; + + private static $resultStatus = [ + \PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY', + \PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK', + \PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK', + \PGSQL_COPY_OUT => 'PGSQL_COPY_OUT', + \PGSQL_COPY_IN => 'PGSQL_COPY_IN', + \PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE', + \PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR', + \PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR', + ]; + + private static $diagCodes = [ + 'severity' => \PGSQL_DIAG_SEVERITY, + 'sqlstate' => \PGSQL_DIAG_SQLSTATE, + 'message' => \PGSQL_DIAG_MESSAGE_PRIMARY, + 'detail' => \PGSQL_DIAG_MESSAGE_DETAIL, + 'hint' => \PGSQL_DIAG_MESSAGE_HINT, + 'statement position' => \PGSQL_DIAG_STATEMENT_POSITION, + 'internal position' => \PGSQL_DIAG_INTERNAL_POSITION, + 'internal query' => \PGSQL_DIAG_INTERNAL_QUERY, + 'context' => \PGSQL_DIAG_CONTEXT, + 'file' => \PGSQL_DIAG_SOURCE_FILE, + 'line' => \PGSQL_DIAG_SOURCE_LINE, + 'function' => \PGSQL_DIAG_SOURCE_FUNCTION, + ]; + + public static function castLargeObject($lo, array $a, Stub $stub, $isNested) + { + $a['seek position'] = pg_lo_tell($lo); + + return $a; + } + + public static function castLink($link, array $a, Stub $stub, $isNested) + { + $a['status'] = pg_connection_status($link); + $a['status'] = new ConstStub(\PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']); + $a['busy'] = pg_connection_busy($link); + + $a['transaction'] = pg_transaction_status($link); + if (isset(self::$transactionStatus[$a['transaction']])) { + $a['transaction'] = new ConstStub(self::$transactionStatus[$a['transaction']], $a['transaction']); + } + + $a['pid'] = pg_get_pid($link); + $a['last error'] = pg_last_error($link); + $a['last notice'] = pg_last_notice($link); + $a['host'] = pg_host($link); + $a['port'] = pg_port($link); + $a['dbname'] = pg_dbname($link); + $a['options'] = pg_options($link); + $a['version'] = pg_version($link); + + foreach (self::$paramCodes as $v) { + if (false !== $s = pg_parameter_status($link, $v)) { + $a['param'][$v] = $s; + } + } + + $a['param']['client_encoding'] = pg_client_encoding($link); + $a['param'] = new EnumStub($a['param']); + + return $a; + } + + public static function castResult($result, array $a, Stub $stub, $isNested) + { + $a['num rows'] = pg_num_rows($result); + $a['status'] = pg_result_status($result); + if (isset(self::$resultStatus[$a['status']])) { + $a['status'] = new ConstStub(self::$resultStatus[$a['status']], $a['status']); + } + $a['command-completion tag'] = pg_result_status($result, \PGSQL_STATUS_STRING); + + if (-1 === $a['num rows']) { + foreach (self::$diagCodes as $k => $v) { + $a['error'][$k] = pg_result_error_field($result, $v); + } + } + + $a['affected rows'] = pg_affected_rows($result); + $a['last OID'] = pg_last_oid($result); + + $fields = pg_num_fields($result); + + for ($i = 0; $i < $fields; ++$i) { + $field = [ + 'name' => pg_field_name($result, $i), + 'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)), + 'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)), + 'nullable' => (bool) pg_field_is_null($result, $i), + 'storage' => pg_field_size($result, $i).' bytes', + 'display' => pg_field_prtlen($result, $i).' chars', + ]; + if (' (OID: )' === $field['table']) { + $field['table'] = null; + } + if ('-1 bytes' === $field['storage']) { + $field['storage'] = 'variable size'; + } elseif ('1 bytes' === $field['storage']) { + $field['storage'] = '1 byte'; + } + if ('1 chars' === $field['display']) { + $field['display'] = '1 char'; + } + $a['fields'][] = new EnumStub($field); + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/RedisCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/RedisCaster.php new file mode 100644 index 0000000000..1e2fb39916 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/RedisCaster.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts Redis class from ext-redis to array representation. + * + * @author Nicolas Grekas + */ +class RedisCaster +{ + private static $serializer = [ + \Redis::SERIALIZER_NONE => 'NONE', + \Redis::SERIALIZER_PHP => 'PHP', + 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY + ]; + + public static function castRedis(\Redis $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + if (\defined('HHVM_VERSION_ID')) { + if (isset($a[Caster::PREFIX_PROTECTED.'serializer'])) { + $ser = $a[Caster::PREFIX_PROTECTED.'serializer']; + $a[Caster::PREFIX_PROTECTED.'serializer'] = isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser; + } + + return $a; + } + + if (!$connected = $c->isConnected()) { + return $a + [ + $prefix.'isConnected' => $connected, + ]; + } + + $ser = $c->getOption(\Redis::OPT_SERIALIZER); + $retry = \defined('Redis::OPT_SCAN') ? $c->getOption(\Redis::OPT_SCAN) : 0; + + return $a + [ + $prefix.'isConnected' => $connected, + $prefix.'host' => $c->getHost(), + $prefix.'port' => $c->getPort(), + $prefix.'auth' => $c->getAuth(), + $prefix.'dbNum' => $c->getDbNum(), + $prefix.'timeout' => $c->getTimeout(), + $prefix.'persistentId' => $c->getPersistentID(), + $prefix.'options' => new EnumStub([ + 'READ_TIMEOUT' => $c->getOption(\Redis::OPT_READ_TIMEOUT), + 'SERIALIZER' => isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser, + 'PREFIX' => $c->getOption(\Redis::OPT_PREFIX), + 'SCAN' => new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry), + ]), + ]; + } + + public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + return $a + [ + $prefix.'hosts' => $c->_hosts(), + $prefix.'function' => ClassStub::wrapCallable($c->_function()), + ]; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/ReflectionCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/ReflectionCaster.php new file mode 100644 index 0000000000..f19886172a --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/ReflectionCaster.php @@ -0,0 +1,345 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts Reflector related classes to array representation. + * + * @author Nicolas Grekas + */ +class ReflectionCaster +{ + private static $extraMap = [ + 'docComment' => 'getDocComment', + 'extension' => 'getExtensionName', + 'isDisabled' => 'isDisabled', + 'isDeprecated' => 'isDeprecated', + 'isInternal' => 'isInternal', + 'isUserDefined' => 'isUserDefined', + 'isGenerator' => 'isGenerator', + 'isVariadic' => 'isVariadic', + ]; + + public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested, $filter = 0) + { + $prefix = Caster::PREFIX_VIRTUAL; + $c = new \ReflectionFunction($c); + + $stub->class = 'Closure'; // HHVM generates unique class names for closures + $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); + + if (false === strpos($c->name, '{closure}')) { + $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name; + unset($a[$prefix.'class']); + } + + if (isset($a[$prefix.'parameters'])) { + foreach ($a[$prefix.'parameters']->value as &$v) { + $param = $v; + $v = new EnumStub([]); + foreach (static::castParameter($param, [], $stub, true) as $k => $param) { + if ("\0" === $k[0]) { + $v->value[substr($k, 3)] = $param; + } + } + unset($v->value['position'], $v->value['isVariadic'], $v->value['byReference'], $v); + } + } + + if (!($filter & Caster::EXCLUDE_VERBOSE) && $f = $c->getFileName()) { + $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); + $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); + } + + $prefix = Caster::PREFIX_DYNAMIC; + unset($a['name'], $a[$prefix.'this'], $a[$prefix.'parameter'], $a[Caster::PREFIX_VIRTUAL.'extra']); + + return $a; + } + + public static function castGenerator(\Generator $c, array $a, Stub $stub, $isNested) + { + if (!class_exists('ReflectionGenerator', false)) { + return $a; + } + + // Cannot create ReflectionGenerator based on a terminated Generator + try { + $reflectionGenerator = new \ReflectionGenerator($c); + } catch (\Exception $e) { + $a[Caster::PREFIX_VIRTUAL.'closed'] = true; + + return $a; + } + + return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested); + } + + public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += [ + $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, + $prefix.'allowsNull' => $c->allowsNull(), + $prefix.'isBuiltin' => $c->isBuiltin(), + ]; + + return $a; + } + + public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + if ($c->getThis()) { + $a[$prefix.'this'] = new CutStub($c->getThis()); + } + $function = $c->getFunction(); + $frame = [ + 'class' => isset($function->class) ? $function->class : null, + 'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null, + 'function' => $function->name, + 'file' => $c->getExecutingFile(), + 'line' => $c->getExecutingLine(), + ]; + if ($trace = $c->getTrace(\DEBUG_BACKTRACE_IGNORE_ARGS)) { + $function = new \ReflectionGenerator($c->getExecutingGenerator()); + array_unshift($trace, [ + 'function' => 'yield', + 'file' => $function->getExecutingFile(), + 'line' => $function->getExecutingLine() - 1, + ]); + $trace[] = $frame; + $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); + } else { + $function = new FrameStub($frame, false, true); + $function = ExceptionCaster::castFrameStub($function, [], $function, true); + $a[$prefix.'executing'] = new EnumStub([ + "\0~separator= \0".$frame['class'].$frame['type'].$frame['function'].'()' => $function[$prefix.'src'], + ]); + } + + $a[Caster::PREFIX_VIRTUAL.'closed'] = false; + + return $a; + } + + public static function castClass(\ReflectionClass $c, array $a, Stub $stub, $isNested, $filter = 0) + { + $prefix = Caster::PREFIX_VIRTUAL; + + if ($n = \Reflection::getModifierNames($c->getModifiers())) { + $a[$prefix.'modifiers'] = implode(' ', $n); + } + + self::addMap($a, $c, [ + 'extends' => 'getParentClass', + 'implements' => 'getInterfaceNames', + 'constants' => 'getConstants', + ]); + + foreach ($c->getProperties() as $n) { + $a[$prefix.'properties'][$n->name] = $n; + } + + foreach ($c->getMethods() as $n) { + $a[$prefix.'methods'][$n->name] = $n; + } + + if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { + self::addExtra($a, $c); + } + + return $a; + } + + public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, $isNested, $filter = 0) + { + $prefix = Caster::PREFIX_VIRTUAL; + + self::addMap($a, $c, [ + 'returnsReference' => 'returnsReference', + 'returnType' => 'getReturnType', + 'class' => 'getClosureScopeClass', + 'this' => 'getClosureThis', + ]); + + if (isset($a[$prefix.'returnType'])) { + $v = $a[$prefix.'returnType']; + $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; + $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); + } + if (isset($a[$prefix.'class'])) { + $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']); + } + if (isset($a[$prefix.'this'])) { + $a[$prefix.'this'] = new CutStub($a[$prefix.'this']); + } + + foreach ($c->getParameters() as $v) { + $k = '$'.$v->name; + if (method_exists($v, 'isVariadic') && $v->isVariadic()) { + $k = '...'.$k; + } + if ($v->isPassedByReference()) { + $k = '&'.$k; + } + $a[$prefix.'parameters'][$k] = $v; + } + if (isset($a[$prefix.'parameters'])) { + $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']); + } + + if ($v = $c->getStaticVariables()) { + foreach ($v as $k => &$v) { + if (\is_object($v)) { + $a[$prefix.'use']['$'.$k] = new CutStub($v); + } else { + $a[$prefix.'use']['$'.$k] = &$v; + } + } + unset($v); + $a[$prefix.'use'] = new EnumStub($a[$prefix.'use']); + } + + if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { + self::addExtra($a, $c); + } + + // Added by HHVM + unset($a[Caster::PREFIX_DYNAMIC.'static']); + + return $a; + } + + public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); + + return $a; + } + + public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + // Added by HHVM + unset($a['info']); + + self::addMap($a, $c, [ + 'position' => 'getPosition', + 'isVariadic' => 'isVariadic', + 'byReference' => 'isPassedByReference', + 'allowsNull' => 'allowsNull', + ]); + + if (method_exists($c, 'getType')) { + if ($v = $c->getType()) { + $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; + } + } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $c, $v)) { + $a[$prefix.'typeHint'] = $v[1]; + } + + if (isset($a[$prefix.'typeHint'])) { + $v = $a[$prefix.'typeHint']; + $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); + } else { + unset($a[$prefix.'allowsNull']); + } + + try { + $a[$prefix.'default'] = $v = $c->getDefaultValue(); + if (method_exists($c, 'isDefaultValueConstant') && $c->isDefaultValueConstant()) { + $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); + } + if (null === $v) { + unset($a[$prefix.'allowsNull']); + } + } catch (\ReflectionException $e) { + if (isset($a[$prefix.'typeHint']) && $c->allowsNull() && !class_exists('ReflectionNamedType', false)) { + $a[$prefix.'default'] = null; + unset($a[$prefix.'allowsNull']); + } + } + + return $a; + } + + public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); + self::addExtra($a, $c); + + return $a; + } + + public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, $isNested) + { + self::addMap($a, $c, [ + 'version' => 'getVersion', + 'dependencies' => 'getDependencies', + 'iniEntries' => 'getIniEntries', + 'isPersistent' => 'isPersistent', + 'isTemporary' => 'isTemporary', + 'constants' => 'getConstants', + 'functions' => 'getFunctions', + 'classes' => 'getClasses', + ]); + + return $a; + } + + public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, $isNested) + { + self::addMap($a, $c, [ + 'version' => 'getVersion', + 'author' => 'getAuthor', + 'copyright' => 'getCopyright', + 'url' => 'getURL', + ]); + + return $a; + } + + private static function addExtra(&$a, \Reflector $c) + { + $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : []; + + if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { + $x['file'] = new LinkStub($m, $c->getStartLine()); + $x['line'] = $c->getStartLine().' to '.$c->getEndLine(); + } + + self::addMap($x, $c, self::$extraMap, ''); + + if ($x) { + $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x); + } + } + + private static function addMap(&$a, \Reflector $c, $map, $prefix = Caster::PREFIX_VIRTUAL) + { + foreach ($map as $k => $m) { + if (\PHP_VERSION_ID >= 80000 && 'isDisabled' === $k) { + continue; + } + + if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { + $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m; + } + } + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/ResourceCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/ResourceCaster.php new file mode 100644 index 0000000000..eb11aee1f0 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/ResourceCaster.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts common resource types to array representation. + * + * @author Nicolas Grekas + */ +class ResourceCaster +{ + /** + * @param \CurlHandle|resource $h + * + * @return array + */ + public static function castCurl($h, array $a, Stub $stub, $isNested) + { + return curl_getinfo($h); + } + + public static function castDba($dba, array $a, Stub $stub, $isNested) + { + $list = dba_list(); + $a['file'] = $list[(int) $dba]; + + return $a; + } + + public static function castProcess($process, array $a, Stub $stub, $isNested) + { + return proc_get_status($process); + } + + public static function castStream($stream, array $a, Stub $stub, $isNested) + { + $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); + if (isset($a['uri'])) { + $a['uri'] = new LinkStub($a['uri']); + } + + return $a; + } + + public static function castStreamContext($stream, array $a, Stub $stub, $isNested) + { + return @stream_context_get_params($stream) ?: $a; + } + + public static function castGd($gd, array $a, Stub $stub, $isNested) + { + $a['size'] = imagesx($gd).'x'.imagesy($gd); + $a['trueColor'] = imageistruecolor($gd); + + return $a; + } + + public static function castMysqlLink($h, array $a, Stub $stub, $isNested) + { + $a['host'] = mysql_get_host_info($h); + $a['protocol'] = mysql_get_proto_info($h); + $a['server'] = mysql_get_server_info($h); + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/SplCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/SplCaster.php new file mode 100644 index 0000000000..360a1a416e --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/SplCaster.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts SPL related classes to array representation. + * + * @author Nicolas Grekas + */ +class SplCaster +{ + private static $splFileObjectFlags = [ + \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', + \SplFileObject::READ_AHEAD => 'READ_AHEAD', + \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', + \SplFileObject::READ_CSV => 'READ_CSV', + ]; + + public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested) + { + return self::castSplArray($c, $a, $stub, $isNested); + } + + public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, $isNested) + { + return self::castSplArray($c, $a, $stub, $isNested); + } + + public static function castHeap(\Iterator $c, array $a, Stub $stub, $isNested) + { + $a += [ + Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), + ]; + + return $a; + } + + public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + $mode = $c->getIteratorMode(); + $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); + + $a += [ + $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode), + $prefix.'dllist' => iterator_to_array($c), + ]; + $c->setIteratorMode($mode); + + return $a; + } + + public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNested) + { + static $map = [ + 'path' => 'getPath', + 'filename' => 'getFilename', + 'basename' => 'getBasename', + 'pathname' => 'getPathname', + 'extension' => 'getExtension', + 'realPath' => 'getRealPath', + 'aTime' => 'getATime', + 'mTime' => 'getMTime', + 'cTime' => 'getCTime', + 'inode' => 'getInode', + 'size' => 'getSize', + 'perms' => 'getPerms', + 'owner' => 'getOwner', + 'group' => 'getGroup', + 'type' => 'getType', + 'writable' => 'isWritable', + 'readable' => 'isReadable', + 'executable' => 'isExecutable', + 'file' => 'isFile', + 'dir' => 'isDir', + 'link' => 'isLink', + 'linkTarget' => 'getLinkTarget', + ]; + + $prefix = Caster::PREFIX_VIRTUAL; + unset($a["\0SplFileInfo\0fileName"]); + unset($a["\0SplFileInfo\0pathName"]); + + if (\PHP_VERSION_ID < 80000) { + if (false === $c->getPathname()) { + $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; + + return $a; + } + } else { + try { + $c->isReadable(); + } catch (\RuntimeException $e) { + if ('Object not initialized' !== $e->getMessage()) { + throw $e; + } + + $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; + + return $a; + } catch (\Error $e) { + if ('Object not initialized' !== $e->getMessage()) { + throw $e; + } + + $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; + + return $a; + } + } + + foreach ($map as $key => $accessor) { + try { + $a[$prefix.$key] = $c->$accessor(); + } catch (\Exception $e) { + } + } + + if (isset($a[$prefix.'realPath'])) { + $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']); + } + + if (isset($a[$prefix.'perms'])) { + $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); + } + + static $mapDate = ['aTime', 'mTime', 'cTime']; + foreach ($mapDate as $key) { + if (isset($a[$prefix.$key])) { + $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); + } + } + + return $a; + } + + public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, $isNested) + { + static $map = [ + 'csvControl' => 'getCsvControl', + 'flags' => 'getFlags', + 'maxLineLen' => 'getMaxLineLen', + 'fstat' => 'fstat', + 'eof' => 'eof', + 'key' => 'key', + ]; + + $prefix = Caster::PREFIX_VIRTUAL; + + foreach ($map as $key => $accessor) { + try { + $a[$prefix.$key] = $c->$accessor(); + } catch (\Exception $e) { + } + } + + if (isset($a[$prefix.'flags'])) { + $flagsArray = []; + foreach (self::$splFileObjectFlags as $value => $name) { + if ($a[$prefix.'flags'] & $value) { + $flagsArray[] = $name; + } + } + $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']); + } + + if (isset($a[$prefix.'fstat'])) { + $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']); + } + + return $a; + } + + public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, $isNested) + { + $storage = []; + unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 + unset($a["\0SplObjectStorage\0storage"]); + + $clone = clone $c; + foreach ($clone as $obj) { + $storage[] = [ + 'object' => $obj, + 'info' => $clone->getInfo(), + ]; + } + + $a += [ + Caster::PREFIX_VIRTUAL.'storage' => $storage, + ]; + + return $a; + } + + public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, $isNested) + { + $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator(); + + return $a; + } + + private static function castSplArray($c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + $flags = $c->getFlags(); + + if (!($flags & \ArrayObject::STD_PROP_LIST)) { + $c->setFlags(\ArrayObject::STD_PROP_LIST); + $a = Caster::castObject($c, \get_class($c), method_exists($c, '__debugInfo'), $stub->class); + $c->setFlags($flags); + } + if (\PHP_VERSION_ID < 70400) { + $a[$prefix.'storage'] = $c->getArrayCopy(); + } + $a += [ + $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST), + $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS), + ]; + if ($c instanceof \ArrayObject) { + $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass()); + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/StubCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/StubCaster.php new file mode 100644 index 0000000000..9927d42610 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/StubCaster.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts a caster's Stub. + * + * @author Nicolas Grekas + */ +class StubCaster +{ + public static function castStub(Stub $c, array $a, Stub $stub, $isNested) + { + if ($isNested) { + $stub->type = $c->type; + $stub->class = $c->class; + $stub->value = $c->value; + $stub->handle = $c->handle; + $stub->cut = $c->cut; + $stub->attr = $c->attr; + + if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) { + $stub->type = Stub::TYPE_STRING; + $stub->class = Stub::STRING_BINARY; + } + + $a = []; + } + + return $a; + } + + public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, $isNested) + { + return $isNested ? $c->preservedSubset : $a; + } + + public static function cutInternals($obj, array $a, Stub $stub, $isNested) + { + if ($isNested) { + $stub->cut += \count($a); + + return []; + } + + return $a; + } + + public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested) + { + if ($isNested) { + $stub->class = $c->dumpKeys ? '' : null; + $stub->handle = 0; + $stub->value = null; + $stub->cut = $c->cut; + $stub->attr = $c->attr; + + $a = []; + + if ($c->value) { + foreach (array_keys($c->value) as $k) { + $keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k; + } + // Preserve references with array_combine() + $a = array_combine($keys, $c->value); + } + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/SymfonyCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/SymfonyCaster.php new file mode 100644 index 0000000000..ae7134f55b --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/SymfonyCaster.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\VarDumper\Cloner\Stub; + +class SymfonyCaster +{ + private static $requestGetters = [ + 'pathInfo' => 'getPathInfo', + 'requestUri' => 'getRequestUri', + 'baseUrl' => 'getBaseUrl', + 'basePath' => 'getBasePath', + 'method' => 'getMethod', + 'format' => 'getRequestFormat', + ]; + + public static function castRequest(Request $request, array $a, Stub $stub, $isNested) + { + $clone = null; + + foreach (self::$requestGetters as $prop => $getter) { + if (null === $a[Caster::PREFIX_PROTECTED.$prop]) { + if (null === $clone) { + $clone = clone $request; + } + $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}(); + } + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/TraceStub.php b/pandora_console/vendor/symfony/var-dumper/Caster/TraceStub.php new file mode 100644 index 0000000000..59548acaee --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/TraceStub.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace(). + * + * @author Nicolas Grekas + */ +class TraceStub extends Stub +{ + public $keepArgs; + public $sliceOffset; + public $sliceLength; + public $numberingOffset; + + public function __construct(array $trace, $keepArgs = true, $sliceOffset = 0, $sliceLength = null, $numberingOffset = 0) + { + $this->value = $trace; + $this->keepArgs = $keepArgs; + $this->sliceOffset = $sliceOffset; + $this->sliceLength = $sliceLength; + $this->numberingOffset = $numberingOffset; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php new file mode 100644 index 0000000000..3ae9ec0ba1 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts XmlReader class to array representation. + * + * @author Baptiste Clavié + */ +class XmlReaderCaster +{ + private static $nodeTypes = [ + \XMLReader::NONE => 'NONE', + \XMLReader::ELEMENT => 'ELEMENT', + \XMLReader::ATTRIBUTE => 'ATTRIBUTE', + \XMLReader::TEXT => 'TEXT', + \XMLReader::CDATA => 'CDATA', + \XMLReader::ENTITY_REF => 'ENTITY_REF', + \XMLReader::ENTITY => 'ENTITY', + \XMLReader::PI => 'PI (Processing Instruction)', + \XMLReader::COMMENT => 'COMMENT', + \XMLReader::DOC => 'DOC', + \XMLReader::DOC_TYPE => 'DOC_TYPE', + \XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT', + \XMLReader::NOTATION => 'NOTATION', + \XMLReader::WHITESPACE => 'WHITESPACE', + \XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', + \XMLReader::END_ELEMENT => 'END_ELEMENT', + \XMLReader::END_ENTITY => 'END_ENTITY', + \XMLReader::XML_DECLARATION => 'XML_DECLARATION', + ]; + + public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, $isNested) + { + $props = Caster::PREFIX_VIRTUAL.'parserProperties'; + $info = [ + 'localName' => $reader->localName, + 'prefix' => $reader->prefix, + 'nodeType' => new ConstStub(self::$nodeTypes[$reader->nodeType], $reader->nodeType), + 'depth' => $reader->depth, + 'isDefault' => $reader->isDefault, + 'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement, + 'xmlLang' => $reader->xmlLang, + 'attributeCount' => $reader->attributeCount, + 'value' => $reader->value, + 'namespaceURI' => $reader->namespaceURI, + 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, + $props => [ + 'LOADDTD' => $reader->getParserProperty(\XMLReader::LOADDTD), + 'DEFAULTATTRS' => $reader->getParserProperty(\XMLReader::DEFAULTATTRS), + 'VALIDATE' => $reader->getParserProperty(\XMLReader::VALIDATE), + 'SUBST_ENTITIES' => $reader->getParserProperty(\XMLReader::SUBST_ENTITIES), + ], + ]; + + if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { + $info[$props] = new EnumStub($info[$props]); + $info[$props]->cut = $count; + } + + $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); + // +2 because hasValue and hasAttributes are always filtered + $stub->cut += $count + 2; + + return $a + $info; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php b/pandora_console/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php new file mode 100644 index 0000000000..99c1486483 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Casts XML resources to array representation. + * + * @author Nicolas Grekas + */ +class XmlResourceCaster +{ + private static $xmlErrors = [ + \XML_ERROR_NONE => 'XML_ERROR_NONE', + \XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY', + \XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX', + \XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS', + \XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN', + \XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN', + \XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR', + \XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH', + \XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE', + \XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT', + \XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF', + \XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY', + \XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF', + \XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY', + \XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF', + \XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF', + \XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF', + \XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI', + \XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING', + \XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING', + \XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION', + \XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING', + ]; + + public static function castXml($h, array $a, Stub $stub, $isNested) + { + $a['current_byte_index'] = xml_get_current_byte_index($h); + $a['current_column_number'] = xml_get_current_column_number($h); + $a['current_line_number'] = xml_get_current_line_number($h); + $a['error_code'] = xml_get_error_code($h); + + if (isset(self::$xmlErrors[$a['error_code']])) { + $a['error_code'] = new ConstStub(self::$xmlErrors[$a['error_code']], $a['error_code']); + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Cloner/AbstractCloner.php b/pandora_console/vendor/symfony/var-dumper/Cloner/AbstractCloner.php new file mode 100644 index 0000000000..76b55b478b --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Cloner/AbstractCloner.php @@ -0,0 +1,336 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Cloner; + +use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Exception\ThrowingCasterException; + +/** + * AbstractCloner implements a generic caster mechanism for objects and resources. + * + * @author Nicolas Grekas + */ +abstract class AbstractCloner implements ClonerInterface +{ + public static $defaultCasters = [ + '__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'], + + 'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], + 'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'], + 'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], + 'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'], + + 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], + 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], + 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], + 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], + 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], + 'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'], + 'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'], + 'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'], + 'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'], + 'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'], + 'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'], + + 'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'], + 'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'], + 'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'], + 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + + 'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], + 'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], + 'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'], + 'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'], + 'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'], + 'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], + 'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'], + 'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'], + 'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'], + 'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'], + 'DOMTypeinfo' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'], + 'DOMDomError' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'], + 'DOMLocator' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'], + 'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'], + 'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'], + 'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'], + 'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'], + 'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'], + + 'XmlReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'], + + 'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'], + 'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'], + 'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'], + 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], + 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], + 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], + 'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'], + 'Symfony\Component\Debug\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], + + 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + 'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], + + 'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'], + 'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'], + + 'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'], + 'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'], + 'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'], + 'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'], + 'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'], + + 'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'], + 'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'], + 'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'], + 'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'], + 'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'], + 'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], + 'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'], + 'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], + 'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'], + + 'MongoCursorInterface' => ['Symfony\Component\VarDumper\Caster\MongoCaster', 'castCursor'], + + 'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], + 'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'], + + 'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'], + 'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'], + 'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'], + 'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'], + + 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], + ':curl' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], + + ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], + ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], + ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], + ':mysql link' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'], + ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'], + ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], + ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], + ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'], + ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'], + ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], + ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], + ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'], + ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], + ]; + + protected $maxItems = 2500; + protected $maxString = -1; + protected $minDepth = 1; + protected $useExt; + + private $casters = []; + private $prevErrorHandler; + private $classInfo = []; + private $filter = 0; + + /** + * @param callable[]|null $casters A map of casters + * + * @see addCasters + */ + public function __construct(array $casters = null) + { + if (null === $casters) { + $casters = static::$defaultCasters; + } + $this->addCasters($casters); + $this->useExt = \extension_loaded('symfony_debug'); + } + + /** + * Adds casters for resources and objects. + * + * Maps resources or objects types to a callback. + * Types are in the key, with a callable caster for value. + * Resource types are to be prefixed with a `:`, + * see e.g. static::$defaultCasters. + * + * @param callable[] $casters A map of casters + */ + public function addCasters(array $casters) + { + foreach ($casters as $type => $callback) { + $this->casters[strtolower($type)][] = \is_string($callback) && false !== strpos($callback, '::') ? explode('::', $callback, 2) : $callback; + } + } + + /** + * Sets the maximum number of items to clone past the minimum depth in nested structures. + * + * @param int $maxItems + */ + public function setMaxItems($maxItems) + { + $this->maxItems = (int) $maxItems; + } + + /** + * Sets the maximum cloned length for strings. + * + * @param int $maxString + */ + public function setMaxString($maxString) + { + $this->maxString = (int) $maxString; + } + + /** + * Sets the minimum tree depth where we are guaranteed to clone all the items. After this + * depth is reached, only setMaxItems items will be cloned. + * + * @param int $minDepth + */ + public function setMinDepth($minDepth) + { + $this->minDepth = (int) $minDepth; + } + + /** + * Clones a PHP variable. + * + * @param mixed $var Any PHP variable + * @param int $filter A bit field of Caster::EXCLUDE_* constants + * + * @return Data The cloned variable represented by a Data object + */ + public function cloneVar($var, $filter = 0) + { + $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) { + if (\E_RECOVERABLE_ERROR === $type || \E_USER_ERROR === $type) { + // Cloner never dies + throw new \ErrorException($msg, 0, $type, $file, $line); + } + + if ($this->prevErrorHandler) { + return \call_user_func($this->prevErrorHandler, $type, $msg, $file, $line, $context); + } + + return false; + }); + $this->filter = $filter; + + if ($gc = gc_enabled()) { + gc_disable(); + } + try { + return new Data($this->doClone($var)); + } finally { + if ($gc) { + gc_enable(); + } + restore_error_handler(); + $this->prevErrorHandler = null; + } + } + + /** + * Effectively clones the PHP variable. + * + * @param mixed $var Any PHP variable + * + * @return array The cloned variable represented in an array + */ + abstract protected function doClone($var); + + /** + * Casts an object to an array representation. + * + * @param Stub $stub The Stub for the casted object + * @param bool $isNested True if the object is nested in the dumped structure + * + * @return array The object casted as array + */ + protected function castObject(Stub $stub, $isNested) + { + $obj = $stub->value; + $class = $stub->class; + + if ((\PHP_VERSION_ID >= 80000 || (isset($class[15]) && "\0" === $class[15])) && false !== strpos($class, "@anonymous\0")) { + $stub->class = \PHP_VERSION_ID < 80000 ? (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous' : get_debug_type($obj); + } + if (isset($this->classInfo[$class])) { + list($i, $parents, $hasDebugInfo) = $this->classInfo[$class]; + } else { + $i = 2; + $parents = [strtolower($class)]; + $hasDebugInfo = method_exists($class, '__debugInfo'); + + foreach (class_parents($class) as $p) { + $parents[] = strtolower($p); + ++$i; + } + foreach (class_implements($class) as $p) { + $parents[] = strtolower($p); + ++$i; + } + $parents[] = '*'; + + $this->classInfo[$class] = [$i, $parents, $hasDebugInfo]; + } + + $a = Caster::castObject($obj, $class, $hasDebugInfo, $stub->class); + + try { + while ($i--) { + if (!empty($this->casters[$p = $parents[$i]])) { + foreach ($this->casters[$p] as $callback) { + $a = $callback($obj, $a, $stub, $isNested, $this->filter); + } + } + } + } catch (\Exception $e) { + $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; + } + + return $a; + } + + /** + * Casts a resource to an array representation. + * + * @param Stub $stub The Stub for the casted resource + * @param bool $isNested True if the object is nested in the dumped structure + * + * @return array The resource casted as array + */ + protected function castResource(Stub $stub, $isNested) + { + $a = []; + $res = $stub->value; + $type = $stub->class; + + try { + if (!empty($this->casters[':'.$type])) { + foreach ($this->casters[':'.$type] as $callback) { + $a = $callback($res, $a, $stub, $isNested, $this->filter); + } + } + } catch (\Exception $e) { + $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; + } + + return $a; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Cloner/ClonerInterface.php b/pandora_console/vendor/symfony/var-dumper/Cloner/ClonerInterface.php new file mode 100644 index 0000000000..7ed287a2dd --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Cloner/ClonerInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Cloner; + +/** + * @author Nicolas Grekas + */ +interface ClonerInterface +{ + /** + * Clones a PHP variable. + * + * @param mixed $var Any PHP variable + * + * @return Data The cloned variable represented by a Data object + */ + public function cloneVar($var); +} diff --git a/pandora_console/vendor/symfony/var-dumper/Cloner/Cursor.php b/pandora_console/vendor/symfony/var-dumper/Cloner/Cursor.php new file mode 100644 index 0000000000..5b0542f6c2 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Cloner/Cursor.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Cloner; + +/** + * Represents the current state of a dumper while dumping. + * + * @author Nicolas Grekas + */ +class Cursor +{ + const HASH_INDEXED = Stub::ARRAY_INDEXED; + const HASH_ASSOC = Stub::ARRAY_ASSOC; + const HASH_OBJECT = Stub::TYPE_OBJECT; + const HASH_RESOURCE = Stub::TYPE_RESOURCE; + + public $depth = 0; + public $refIndex = 0; + public $softRefTo = 0; + public $softRefCount = 0; + public $softRefHandle = 0; + public $hardRefTo = 0; + public $hardRefCount = 0; + public $hardRefHandle = 0; + public $hashType; + public $hashKey; + public $hashKeyIsBinary; + public $hashIndex = 0; + public $hashLength = 0; + public $hashCut = 0; + public $stop = false; + public $attr = []; + public $skipChildren = false; +} diff --git a/pandora_console/vendor/symfony/var-dumper/Cloner/Data.php b/pandora_console/vendor/symfony/var-dumper/Cloner/Data.php new file mode 100644 index 0000000000..3973720794 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Cloner/Data.php @@ -0,0 +1,441 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Cloner; + +use Symfony\Component\VarDumper\Caster\Caster; + +/** + * @author Nicolas Grekas + */ +class Data implements \ArrayAccess, \Countable, \IteratorAggregate +{ + private $data; + private $position = 0; + private $key = 0; + private $maxDepth = 20; + private $maxItemsPerDepth = -1; + private $useRefHandles = -1; + + /** + * @param array $data An array as returned by ClonerInterface::cloneVar() + */ + public function __construct(array $data) + { + $this->data = $data; + } + + /** + * @return string|null The type of the value + */ + public function getType() + { + $item = $this->data[$this->position][$this->key]; + + if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { + $item = $item->value; + } + if (!$item instanceof Stub) { + return \gettype($item); + } + if (Stub::TYPE_STRING === $item->type) { + return 'string'; + } + if (Stub::TYPE_ARRAY === $item->type) { + return 'array'; + } + if (Stub::TYPE_OBJECT === $item->type) { + return $item->class; + } + if (Stub::TYPE_RESOURCE === $item->type) { + return $item->class.' resource'; + } + + return null; + } + + /** + * @param array|bool $recursive Whether values should be resolved recursively or not + * + * @return string|int|float|bool|array|Data[]|null A native representation of the original value + */ + public function getValue($recursive = false) + { + $item = $this->data[$this->position][$this->key]; + + if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { + $item = $item->value; + } + if (!($item = $this->getStub($item)) instanceof Stub) { + return $item; + } + if (Stub::TYPE_STRING === $item->type) { + return $item->value; + } + + $children = $item->position ? $this->data[$item->position] : []; + + foreach ($children as $k => $v) { + if ($recursive && !($v = $this->getStub($v)) instanceof Stub) { + continue; + } + $children[$k] = clone $this; + $children[$k]->key = $k; + $children[$k]->position = $item->position; + + if ($recursive) { + if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) { + $recursive = (array) $recursive; + if (isset($recursive[$v->position])) { + continue; + } + $recursive[$v->position] = true; + } + $children[$k] = $children[$k]->getValue($recursive); + } + } + + return $children; + } + + public function count() + { + return \count($this->getValue()); + } + + public function getIterator() + { + if (!\is_array($value = $this->getValue())) { + throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".', self::class, \gettype($value))); + } + + foreach ($value as $k => $v) { + yield $k => $v; + } + } + + public function __get($key) + { + if (null !== $data = $this->seek($key)) { + $item = $this->getStub($data->data[$data->position][$data->key]); + + return $item instanceof Stub || [] === $item ? $data : $item; + } + + return null; + } + + public function __isset($key) + { + return null !== $this->seek($key); + } + + public function offsetExists($key) + { + return $this->__isset($key); + } + + public function offsetGet($key) + { + return $this->__get($key); + } + + public function offsetSet($key, $value) + { + throw new \BadMethodCallException(self::class.' objects are immutable.'); + } + + public function offsetUnset($key) + { + throw new \BadMethodCallException(self::class.' objects are immutable.'); + } + + public function __toString() + { + $value = $this->getValue(); + + if (!\is_array($value)) { + return (string) $value; + } + + return sprintf('%s (count=%d)', $this->getType(), \count($value)); + } + + /** + * @return array The raw data structure + * + * @deprecated since version 3.3. Use array or object access instead. + */ + public function getRawData() + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the array or object access instead.', __METHOD__)); + + return $this->data; + } + + /** + * Returns a depth limited clone of $this. + * + * @param int $maxDepth The max dumped depth level + * + * @return static + */ + public function withMaxDepth($maxDepth) + { + $data = clone $this; + $data->maxDepth = (int) $maxDepth; + + return $data; + } + + /** + * Limits the number of elements per depth level. + * + * @param int $maxItemsPerDepth The max number of items dumped per depth level + * + * @return static + */ + public function withMaxItemsPerDepth($maxItemsPerDepth) + { + $data = clone $this; + $data->maxItemsPerDepth = (int) $maxItemsPerDepth; + + return $data; + } + + /** + * Enables/disables objects' identifiers tracking. + * + * @param bool $useRefHandles False to hide global ref. handles + * + * @return static + */ + public function withRefHandles($useRefHandles) + { + $data = clone $this; + $data->useRefHandles = $useRefHandles ? -1 : 0; + + return $data; + } + + /** + * Seeks to a specific key in nested data structures. + * + * @param string|int $key The key to seek to + * + * @return static|null Null if the key is not set + */ + public function seek($key) + { + $item = $this->data[$this->position][$this->key]; + + if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { + $item = $item->value; + } + if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) { + return null; + } + $keys = [$key]; + + switch ($item->type) { + case Stub::TYPE_OBJECT: + $keys[] = Caster::PREFIX_DYNAMIC.$key; + $keys[] = Caster::PREFIX_PROTECTED.$key; + $keys[] = Caster::PREFIX_VIRTUAL.$key; + $keys[] = "\0$item->class\0$key"; + // no break + case Stub::TYPE_ARRAY: + case Stub::TYPE_RESOURCE: + break; + default: + return null; + } + + $data = null; + $children = $this->data[$item->position]; + + foreach ($keys as $key) { + if (isset($children[$key]) || \array_key_exists($key, $children)) { + $data = clone $this; + $data->key = $key; + $data->position = $item->position; + break; + } + } + + return $data; + } + + /** + * Dumps data with a DumperInterface dumper. + */ + public function dump(DumperInterface $dumper) + { + $refs = [0]; + $this->dumpItem($dumper, new Cursor(), $refs, $this->data[$this->position][$this->key]); + } + + /** + * Depth-first dumping of items. + * + * @param DumperInterface $dumper The dumper being used for dumping + * @param Cursor $cursor A cursor used for tracking dumper state position + * @param array &$refs A map of all references discovered while dumping + * @param mixed $item A Stub object or the original value being dumped + */ + private function dumpItem($dumper, $cursor, &$refs, $item) + { + $cursor->refIndex = 0; + $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; + $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0; + $firstSeen = true; + + if (!$item instanceof Stub) { + $cursor->attr = []; + $type = \gettype($item); + if ($item && 'array' === $type) { + $item = $this->getStub($item); + } + } elseif (Stub::TYPE_REF === $item->type) { + if ($item->handle) { + if (!isset($refs[$r = $item->handle - (\PHP_INT_MAX >> 1)])) { + $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; + } else { + $firstSeen = false; + } + $cursor->hardRefTo = $refs[$r]; + $cursor->hardRefHandle = $this->useRefHandles & $item->handle; + $cursor->hardRefCount = $item->refCount; + } + $cursor->attr = $item->attr; + $type = $item->class ?: \gettype($item->value); + $item = $this->getStub($item->value); + } + if ($item instanceof Stub) { + if ($item->refCount) { + if (!isset($refs[$r = $item->handle])) { + $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; + } else { + $firstSeen = false; + } + $cursor->softRefTo = $refs[$r]; + } + $cursor->softRefHandle = $this->useRefHandles & $item->handle; + $cursor->softRefCount = $item->refCount; + $cursor->attr = $item->attr; + $cut = $item->cut; + + if ($item->position && $firstSeen) { + $children = $this->data[$item->position]; + + if ($cursor->stop) { + if ($cut >= 0) { + $cut += \count($children); + } + $children = []; + } + } else { + $children = []; + } + switch ($item->type) { + case Stub::TYPE_STRING: + $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut); + break; + + case Stub::TYPE_ARRAY: + $item = clone $item; + $item->type = $item->class; + $item->class = $item->value; + // no break + case Stub::TYPE_OBJECT: + case Stub::TYPE_RESOURCE: + $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth; + $dumper->enterHash($cursor, $item->type, $item->class, $withChildren); + if ($withChildren) { + if ($cursor->skipChildren) { + $withChildren = false; + $cut = -1; + } else { + $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class); + } + } elseif ($children && 0 <= $cut) { + $cut += \count($children); + } + $cursor->skipChildren = false; + $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut); + break; + + default: + throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".', $item->type)); + } + } elseif ('array' === $type) { + $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false); + $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0); + } elseif ('string' === $type) { + $dumper->dumpString($cursor, $item, false, 0); + } else { + $dumper->dumpScalar($cursor, $type, $item); + } + } + + /** + * Dumps children of hash structures. + * + * @param DumperInterface $dumper + * @param Cursor $parentCursor The cursor of the parent hash + * @param array &$refs A map of all references discovered while dumping + * @param array $children The children to dump + * @param int $hashCut The number of items removed from the original hash + * @param string $hashType A Cursor::HASH_* const + * @param bool $dumpKeys Whether keys should be dumped or not + * + * @return int The final number of removed items + */ + private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType, $dumpKeys) + { + $cursor = clone $parentCursor; + ++$cursor->depth; + $cursor->hashType = $hashType; + $cursor->hashIndex = 0; + $cursor->hashLength = \count($children); + $cursor->hashCut = $hashCut; + foreach ($children as $key => $child) { + $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key); + $cursor->hashKey = $dumpKeys ? $key : null; + $this->dumpItem($dumper, $cursor, $refs, $child); + if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) { + $parentCursor->stop = true; + + return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut; + } + } + + return $hashCut; + } + + private function getStub($item) + { + if (!$item || !\is_array($item)) { + return $item; + } + + $stub = new Stub(); + $stub->type = Stub::TYPE_ARRAY; + foreach ($item as $stub->class => $stub->position) { + } + if (isset($item[0])) { + $stub->cut = $item[0]; + } + $stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0); + + return $stub; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Cloner/DumperInterface.php b/pandora_console/vendor/symfony/var-dumper/Cloner/DumperInterface.php new file mode 100644 index 0000000000..912bb52139 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Cloner/DumperInterface.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Cloner; + +/** + * DumperInterface used by Data objects. + * + * @author Nicolas Grekas + */ +interface DumperInterface +{ + /** + * Dumps a scalar value. + * + * @param Cursor $cursor The Cursor position in the dump + * @param string $type The PHP type of the value being dumped + * @param string|int|float|bool $value The scalar value being dumped + */ + public function dumpScalar(Cursor $cursor, $type, $value); + + /** + * Dumps a string. + * + * @param Cursor $cursor The Cursor position in the dump + * @param string $str The string being dumped + * @param bool $bin Whether $str is UTF-8 or binary encoded + * @param int $cut The number of characters $str has been cut by + */ + public function dumpString(Cursor $cursor, $str, $bin, $cut); + + /** + * Dumps while entering an hash. + * + * @param Cursor $cursor The Cursor position in the dump + * @param int $type A Cursor::HASH_* const for the type of hash + * @param string|int $class The object class, resource type or array count + * @param bool $hasChild When the dump of the hash has child item + */ + public function enterHash(Cursor $cursor, $type, $class, $hasChild); + + /** + * Dumps while leaving an hash. + * + * @param Cursor $cursor The Cursor position in the dump + * @param int $type A Cursor::HASH_* const for the type of hash + * @param string|int $class The object class, resource type or array count + * @param bool $hasChild When the dump of the hash has child item + * @param int $cut The number of items the hash has been cut by + */ + public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut); +} diff --git a/pandora_console/vendor/symfony/var-dumper/Cloner/Stub.php b/pandora_console/vendor/symfony/var-dumper/Cloner/Stub.php new file mode 100644 index 0000000000..a56120ce36 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Cloner/Stub.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Cloner; + +/** + * Represents the main properties of a PHP variable. + * + * @author Nicolas Grekas + */ +class Stub +{ + const TYPE_REF = 1; + const TYPE_STRING = 2; + const TYPE_ARRAY = 3; + const TYPE_OBJECT = 4; + const TYPE_RESOURCE = 5; + + const STRING_BINARY = 1; + const STRING_UTF8 = 2; + + const ARRAY_ASSOC = 1; + const ARRAY_INDEXED = 2; + + public $type = self::TYPE_REF; + public $class = ''; + public $value; + public $cut = 0; + public $handle = 0; + public $refCount = 0; + public $position = 0; + public $attr = []; + + private static $defaultProperties = []; + + /** + * @internal + */ + public function __sleep() + { + $properties = []; + + if (!isset(self::$defaultProperties[$c = static::class])) { + self::$defaultProperties[$c] = get_class_vars($c); + + foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) { + unset(self::$defaultProperties[$c][$k]); + } + } + + foreach (self::$defaultProperties[$c] as $k => $v) { + if ($this->$k !== $v) { + $properties[] = $k; + } + } + + return $properties; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Cloner/VarCloner.php b/pandora_console/vendor/symfony/var-dumper/Cloner/VarCloner.php new file mode 100644 index 0000000000..8c4221220e --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Cloner/VarCloner.php @@ -0,0 +1,335 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Cloner; + +/** + * @author Nicolas Grekas + */ +class VarCloner extends AbstractCloner +{ + private static $gid; + private static $hashMask = 0; + private static $hashOffset = 0; + private static $arrayCache = []; + + /** + * {@inheritdoc} + */ + protected function doClone($var) + { + $len = 1; // Length of $queue + $pos = 0; // Number of cloned items past the minimum depth + $refsCounter = 0; // Hard references counter + $queue = [[$var]]; // This breadth-first queue is the return value + $indexedArrays = []; // Map of queue indexes that hold numerically indexed arrays + $hardRefs = []; // Map of original zval hashes to stub objects + $objRefs = []; // Map of original object handles to their stub object counterpart + $objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning + $resRefs = []; // Map of original resource handles to their stub object counterpart + $values = []; // Map of stub objects' hashes to original values + $maxItems = $this->maxItems; + $maxString = $this->maxString; + $minDepth = $this->minDepth; + $currentDepth = 0; // Current tree depth + $currentDepthFinalIndex = 0; // Final $queue index for current tree depth + $minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached + $cookie = (object) []; // Unique object used to detect hard references + $a = null; // Array cast for nested structures + $stub = null; // Stub capturing the main properties of an original item value + // or null if the original value is used directly + + if (!self::$hashMask) { + self::initHashMask(); + self::$gid = md5(dechex(self::$hashMask)); // Unique string used to detect the special $GLOBALS variable + } + $gid = self::$gid; + $hashMask = self::$hashMask; + $hashOffset = self::$hashOffset; + $arrayStub = new Stub(); + $arrayStub->type = Stub::TYPE_ARRAY; + $fromObjCast = false; + + for ($i = 0; $i < $len; ++$i) { + // Detect when we move on to the next tree depth + if ($i > $currentDepthFinalIndex) { + ++$currentDepth; + $currentDepthFinalIndex = $len - 1; + if ($currentDepth >= $minDepth) { + $minimumDepthReached = true; + } + } + + $refs = $vals = $queue[$i]; + if (\PHP_VERSION_ID < 70200 && empty($indexedArrays[$i])) { + // see https://wiki.php.net/rfc/convert_numeric_keys_in_object_array_casts + foreach ($vals as $k => $v) { + if (\is_int($k)) { + continue; + } + foreach ([$k => true] as $gk => $gv) { + } + if ($gk !== $k) { + $fromObjCast = true; + $refs = $vals = array_values($queue[$i]); + break; + } + } + } + foreach ($vals as $k => $v) { + // $v is the original value or a stub object in case of hard references + + if (\PHP_VERSION_ID >= 70400) { + $zvalIsRef = null !== \ReflectionReference::fromArrayElement($vals, $k); + } else { + $refs[$k] = $cookie; + $zvalIsRef = $vals[$k] === $cookie; + } + + if ($zvalIsRef) { + $vals[$k] = &$stub; // Break hard references to make $queue completely + unset($stub); // independent from the original structure + if ($v instanceof Stub && isset($hardRefs[spl_object_hash($v)])) { + $vals[$k] = $refs[$k] = $v; + if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { + ++$v->value->refCount; + } + ++$v->refCount; + continue; + } + $refs[$k] = $vals[$k] = new Stub(); + $refs[$k]->value = $v; + $h = spl_object_hash($refs[$k]); + $hardRefs[$h] = &$refs[$k]; + $values[$h] = $v; + $vals[$k]->handle = ++$refsCounter; + } + // Create $stub when the original value $v can not be used directly + // If $v is a nested structure, put that structure in array $a + switch (true) { + case null === $v: + case \is_bool($v): + case \is_int($v): + case \is_float($v): + continue 2; + case \is_string($v): + if ('' === $v) { + continue 2; + } + if (!preg_match('//u', $v)) { + $stub = new Stub(); + $stub->type = Stub::TYPE_STRING; + $stub->class = Stub::STRING_BINARY; + if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) { + $stub->cut = $cut; + $stub->value = substr($v, 0, -$cut); + } else { + $stub->value = $v; + } + } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) { + $stub = new Stub(); + $stub->type = Stub::TYPE_STRING; + $stub->class = Stub::STRING_UTF8; + $stub->cut = $cut; + $stub->value = mb_substr($v, 0, $maxString, 'UTF-8'); + } else { + continue 2; + } + $a = null; + break; + + case \is_array($v): + if (!$v) { + continue 2; + } + $stub = $arrayStub; + $stub->class = Stub::ARRAY_INDEXED; + + $j = -1; + foreach ($v as $gk => $gv) { + if ($gk !== ++$j) { + $stub->class = Stub::ARRAY_ASSOC; + break; + } + } + $a = $v; + + if (Stub::ARRAY_ASSOC === $stub->class) { + // Copies of $GLOBALS have very strange behavior, + // let's detect them with some black magic + $a[$gid] = true; + + // Happens with copies of $GLOBALS + if (isset($v[$gid])) { + unset($v[$gid]); + $a = []; + foreach ($v as $gk => &$gv) { + $a[$gk] = &$gv; + } + unset($gv); + } else { + $a = $v; + } + } elseif (\PHP_VERSION_ID < 70200) { + $indexedArrays[$len] = true; + } + break; + + case \is_object($v): + case $v instanceof \__PHP_Incomplete_Class: + if (empty($objRefs[$h = $hashMask ^ hexdec(substr(spl_object_hash($v), $hashOffset, \PHP_INT_SIZE))])) { + $stub = new Stub(); + $stub->type = Stub::TYPE_OBJECT; + $stub->class = \get_class($v); + $stub->value = $v; + $stub->handle = $h; + $a = $this->castObject($stub, 0 < $i); + if ($v !== $stub->value) { + if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { + break; + } + $h = $hashMask ^ hexdec(substr(spl_object_hash($stub->value), $hashOffset, \PHP_INT_SIZE)); + $stub->handle = $h; + } + $stub->value = null; + if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { + $stub->cut = \count($a); + $a = null; + } + } + if (empty($objRefs[$h])) { + $objRefs[$h] = $stub; + $objects[] = $v; + } else { + $stub = $objRefs[$h]; + ++$stub->refCount; + $a = null; + } + break; + + default: // resource + if (empty($resRefs[$h = (int) $v])) { + $stub = new Stub(); + $stub->type = Stub::TYPE_RESOURCE; + if ('Unknown' === $stub->class = @get_resource_type($v)) { + $stub->class = 'Closed'; + } + $stub->value = $v; + $stub->handle = $h; + $a = $this->castResource($stub, 0 < $i); + $stub->value = null; + if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { + $stub->cut = \count($a); + $a = null; + } + } + if (empty($resRefs[$h])) { + $resRefs[$h] = $stub; + } else { + $stub = $resRefs[$h]; + ++$stub->refCount; + $a = null; + } + break; + } + + if ($a) { + if (!$minimumDepthReached || 0 > $maxItems) { + $queue[$len] = $a; + $stub->position = $len++; + } elseif ($pos < $maxItems) { + if ($maxItems < $pos += \count($a)) { + $a = \array_slice($a, 0, $maxItems - $pos, true); + if ($stub->cut >= 0) { + $stub->cut += $pos - $maxItems; + } + } + $queue[$len] = $a; + $stub->position = $len++; + } elseif ($stub->cut >= 0) { + $stub->cut += \count($a); + $stub->position = 0; + } + } + + if ($arrayStub === $stub) { + if ($arrayStub->cut) { + $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position]; + $arrayStub->cut = 0; + } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) { + $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position]; + } else { + self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position]; + } + } + + if ($zvalIsRef) { + $refs[$k]->value = $stub; + } else { + $vals[$k] = $stub; + } + } + + if ($fromObjCast) { + $fromObjCast = false; + $refs = $vals; + $vals = []; + $j = -1; + foreach ($queue[$i] as $k => $v) { + foreach ([$k => true] as $gk => $gv) { + } + if ($gk !== $k) { + $vals = (object) $vals; + $vals->{$k} = $refs[++$j]; + $vals = (array) $vals; + } else { + $vals[$k] = $refs[++$j]; + } + } + } + + $queue[$i] = $vals; + } + + foreach ($values as $h => $v) { + $hardRefs[$h] = $v; + } + + return $queue; + } + + private static function initHashMask() + { + $obj = (object) []; + self::$hashOffset = 16 - \PHP_INT_SIZE; + self::$hashMask = -1; + + if (\defined('HHVM_VERSION')) { + self::$hashOffset += 16; + } else { + // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below + $obFuncs = ['ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush']; + foreach (debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { + if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) { + $frame['line'] = 0; + break; + } + } + if (!empty($frame['line'])) { + ob_start(); + debug_zval_dump($obj); + self::$hashMask = (int) substr(ob_get_clean(), 17); + } + } + + self::$hashMask ^= hexdec(substr(spl_object_hash($obj), self::$hashOffset, \PHP_INT_SIZE)); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Dumper/AbstractDumper.php b/pandora_console/vendor/symfony/var-dumper/Dumper/AbstractDumper.php new file mode 100644 index 0000000000..5ea6294f3d --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Dumper/AbstractDumper.php @@ -0,0 +1,213 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper; + +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\DumperInterface; + +/** + * Abstract mechanism for dumping a Data object. + * + * @author Nicolas Grekas + */ +abstract class AbstractDumper implements DataDumperInterface, DumperInterface +{ + const DUMP_LIGHT_ARRAY = 1; + const DUMP_STRING_LENGTH = 2; + const DUMP_COMMA_SEPARATOR = 4; + const DUMP_TRAILING_COMMA = 8; + + public static $defaultOutput = 'php://output'; + + protected $line = ''; + protected $lineDumper; + protected $outputStream; + protected $decimalPoint; // This is locale dependent + protected $indentPad = ' '; + protected $flags; + + private $charset = ''; + + /** + * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput + * @param string|null $charset The default character encoding to use for non-UTF8 strings + * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation + */ + public function __construct($output = null, $charset = null, $flags = 0) + { + $this->flags = (int) $flags; + $this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'); + $this->decimalPoint = localeconv(); + $this->decimalPoint = $this->decimalPoint['decimal_point']; + $this->setOutput($output ?: static::$defaultOutput); + if (!$output && \is_string(static::$defaultOutput)) { + static::$defaultOutput = $this->outputStream; + } + } + + /** + * Sets the output destination of the dumps. + * + * @param callable|resource|string $output A line dumper callable, an opened stream or an output path + * + * @return callable|resource|string The previous output destination + */ + public function setOutput($output) + { + $prev = null !== $this->outputStream ? $this->outputStream : $this->lineDumper; + + if (\is_callable($output)) { + $this->outputStream = null; + $this->lineDumper = $output; + } else { + if (\is_string($output)) { + $output = fopen($output, 'wb'); + } + $this->outputStream = $output; + $this->lineDumper = [$this, 'echoLine']; + } + + return $prev; + } + + /** + * Sets the default character encoding to use for non-UTF8 strings. + * + * @param string $charset The default character encoding to use for non-UTF8 strings + * + * @return string The previous charset + */ + public function setCharset($charset) + { + $prev = $this->charset; + + $charset = strtoupper($charset); + $charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset; + + $this->charset = $charset; + + return $prev; + } + + /** + * Sets the indentation pad string. + * + * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level + * + * @return string The previous indent pad + */ + public function setIndentPad($pad) + { + $prev = $this->indentPad; + $this->indentPad = $pad; + + return $prev; + } + + /** + * Dumps a Data object. + * + * @param Data $data A Data object + * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump + * + * @return string|null The dump as string when $output is true + */ + public function dump(Data $data, $output = null) + { + $this->decimalPoint = localeconv(); + $this->decimalPoint = $this->decimalPoint['decimal_point']; + + if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(\LC_NUMERIC, 0) : null) { + setlocale(\LC_NUMERIC, 'C'); + } + + if ($returnDump = true === $output) { + $output = fopen('php://memory', 'r+b'); + } + if ($output) { + $prevOutput = $this->setOutput($output); + } + try { + $data->dump($this); + $this->dumpLine(-1); + + if ($returnDump) { + $result = stream_get_contents($output, -1, 0); + fclose($output); + + return $result; + } + } finally { + if ($output) { + $this->setOutput($prevOutput); + } + if ($locale) { + setlocale(\LC_NUMERIC, $locale); + } + } + + return null; + } + + /** + * Dumps the current line. + * + * @param int $depth The recursive depth in the dumped structure for the line being dumped, + * or -1 to signal the end-of-dump to the line dumper callable + */ + protected function dumpLine($depth) + { + \call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad); + $this->line = ''; + } + + /** + * Generic line dumper callback. + * + * @param string $line The line to write + * @param int $depth The recursive depth in the dumped structure + * @param string $indentPad The line indent pad + */ + protected function echoLine($line, $depth, $indentPad) + { + if (-1 !== $depth) { + fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); + } + } + + /** + * Converts a non-UTF-8 string to UTF-8. + * + * @param string|null $s The non-UTF-8 string to convert + * + * @return string|null The string converted to UTF-8 + */ + protected function utf8Encode($s) + { + if (null === $s || preg_match('//u', $s)) { + return $s; + } + + if (!\function_exists('iconv')) { + throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); + } + + if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) { + return $c; + } + if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) { + return $c; + } + + return iconv('CP850', 'UTF-8', $s); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Dumper/CliDumper.php b/pandora_console/vendor/symfony/var-dumper/Dumper/CliDumper.php new file mode 100644 index 0000000000..4c48ab7cc8 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Dumper/CliDumper.php @@ -0,0 +1,601 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper; + +use Symfony\Component\VarDumper\Cloner\Cursor; +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * CliDumper dumps variables for command line output. + * + * @author Nicolas Grekas + */ +class CliDumper extends AbstractDumper +{ + public static $defaultColors; + public static $defaultOutput = 'php://stdout'; + + protected $colors; + protected $maxStringWidth = 0; + protected $styles = [ + // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics + 'default' => '0;38;5;208', + 'num' => '1;38;5;38', + 'const' => '1;38;5;208', + 'str' => '1;38;5;113', + 'note' => '38;5;38', + 'ref' => '38;5;247', + 'public' => '', + 'protected' => '', + 'private' => '', + 'meta' => '38;5;170', + 'key' => '38;5;113', + 'index' => '38;5;38', + ]; + + protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/'; + protected static $controlCharsMap = [ + "\t" => '\t', + "\n" => '\n', + "\v" => '\v', + "\f" => '\f', + "\r" => '\r', + "\033" => '\e', + ]; + + protected $collapseNextHash = false; + protected $expandNextHash = false; + + /** + * {@inheritdoc} + */ + public function __construct($output = null, $charset = null, $flags = 0) + { + parent::__construct($output, $charset, $flags); + + if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { + // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI + $this->setStyles([ + 'default' => '31', + 'num' => '1;34', + 'const' => '1;31', + 'str' => '1;32', + 'note' => '34', + 'ref' => '1;30', + 'meta' => '35', + 'key' => '32', + 'index' => '34', + ]); + } + } + + /** + * Enables/disables colored output. + * + * @param bool $colors + */ + public function setColors($colors) + { + $this->colors = (bool) $colors; + } + + /** + * Sets the maximum number of characters per line for dumped strings. + * + * @param int $maxStringWidth + */ + public function setMaxStringWidth($maxStringWidth) + { + $this->maxStringWidth = (int) $maxStringWidth; + } + + /** + * Configures styles. + * + * @param array $styles A map of style names to style definitions + */ + public function setStyles(array $styles) + { + $this->styles = $styles + $this->styles; + } + + /** + * {@inheritdoc} + */ + public function dumpScalar(Cursor $cursor, $type, $value) + { + $this->dumpKey($cursor); + + $style = 'const'; + $attr = $cursor->attr; + + switch ($type) { + case 'default': + $style = 'default'; + break; + + case 'integer': + $style = 'num'; + break; + + case 'double': + $style = 'num'; + + switch (true) { + case \INF === $value: $value = 'INF'; break; + case -\INF === $value: $value = '-INF'; break; + case is_nan($value): $value = 'NAN'; break; + default: + $value = (string) $value; + if (false === strpos($value, $this->decimalPoint)) { + $value .= $this->decimalPoint.'0'; + } + break; + } + break; + + case 'NULL': + $value = 'null'; + break; + + case 'boolean': + $value = $value ? 'true' : 'false'; + break; + + default: + $attr += ['value' => $this->utf8Encode($value)]; + $value = $this->utf8Encode($type); + break; + } + + $this->line .= $this->style($style, $value, $attr); + + $this->endValue($cursor); + } + + /** + * {@inheritdoc} + */ + public function dumpString(Cursor $cursor, $str, $bin, $cut) + { + $this->dumpKey($cursor); + $attr = $cursor->attr; + + if ($bin) { + $str = $this->utf8Encode($str); + } + if ('' === $str) { + $this->line .= '""'; + $this->endValue($cursor); + } else { + $attr += [ + 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, + 'binary' => $bin, + ]; + $str = explode("\n", $str); + if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { + unset($str[1]); + $str[0] .= "\n"; + } + $m = \count($str) - 1; + $i = $lineCut = 0; + + if (self::DUMP_STRING_LENGTH & $this->flags) { + $this->line .= '('.$attr['length'].') '; + } + if ($bin) { + $this->line .= 'b'; + } + + if ($m) { + $this->line .= '"""'; + $this->dumpLine($cursor->depth); + } else { + $this->line .= '"'; + } + + foreach ($str as $str) { + if ($i < $m) { + $str .= "\n"; + } + if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) { + $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8'); + $lineCut = $len - $this->maxStringWidth; + } + if ($m && 0 < $cursor->depth) { + $this->line .= $this->indentPad; + } + if ('' !== $str) { + $this->line .= $this->style('str', $str, $attr); + } + if ($i++ == $m) { + if ($m) { + if ('' !== $str) { + $this->dumpLine($cursor->depth); + if (0 < $cursor->depth) { + $this->line .= $this->indentPad; + } + } + $this->line .= '"""'; + } else { + $this->line .= '"'; + } + if ($cut < 0) { + $this->line .= '…'; + $lineCut = 0; + } elseif ($cut) { + $lineCut += $cut; + } + } + if ($lineCut) { + $this->line .= '…'.$lineCut; + $lineCut = 0; + } + + if ($i > $m) { + $this->endValue($cursor); + } else { + $this->dumpLine($cursor->depth); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function enterHash(Cursor $cursor, $type, $class, $hasChild) + { + if (null === $this->colors) { + $this->colors = $this->supportsColors(); + } + + $this->dumpKey($cursor); + + if ($this->collapseNextHash) { + $cursor->skipChildren = true; + $this->collapseNextHash = $hasChild = false; + } + + $class = $this->utf8Encode($class); + if (Cursor::HASH_OBJECT === $type) { + $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class).' {' : '{'; + } elseif (Cursor::HASH_RESOURCE === $type) { + $prefix = $this->style('note', $class.' resource').($hasChild ? ' {' : ' '); + } else { + $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '['; + } + + if ($cursor->softRefCount || 0 < $cursor->softRefHandle) { + $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); + } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { + $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); + } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { + $prefix = substr($prefix, 0, -1); + } + + $this->line .= $prefix; + + if ($hasChild) { + $this->dumpLine($cursor->depth); + } + } + + /** + * {@inheritdoc} + */ + public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut) + { + $this->dumpEllipsis($cursor, $hasChild, $cut); + $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); + $this->endValue($cursor); + } + + /** + * Dumps an ellipsis for cut children. + * + * @param Cursor $cursor The Cursor position in the dump + * @param bool $hasChild When the dump of the hash has child item + * @param int $cut The number of items the hash has been cut by + */ + protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut) + { + if ($cut) { + $this->line .= ' …'; + if (0 < $cut) { + $this->line .= $cut; + } + if ($hasChild) { + $this->dumpLine($cursor->depth + 1); + } + } + } + + /** + * Dumps a key in a hash structure. + * + * @param Cursor $cursor The Cursor position in the dump + */ + protected function dumpKey(Cursor $cursor) + { + if (null !== $key = $cursor->hashKey) { + if ($cursor->hashKeyIsBinary) { + $key = $this->utf8Encode($key); + } + $attr = ['binary' => $cursor->hashKeyIsBinary]; + $bin = $cursor->hashKeyIsBinary ? 'b' : ''; + $style = 'key'; + switch ($cursor->hashType) { + default: + case Cursor::HASH_INDEXED: + if (self::DUMP_LIGHT_ARRAY & $this->flags) { + break; + } + $style = 'index'; + // no break + case Cursor::HASH_ASSOC: + if (\is_int($key)) { + $this->line .= $this->style($style, $key).' => '; + } else { + $this->line .= $bin.'"'.$this->style($style, $key).'" => '; + } + break; + + case Cursor::HASH_RESOURCE: + $key = "\0~\0".$key; + // no break + case Cursor::HASH_OBJECT: + if (!isset($key[0]) || "\0" !== $key[0]) { + $this->line .= '+'.$bin.$this->style('public', $key).': '; + } elseif (0 < strpos($key, "\0", 1)) { + $key = explode("\0", substr($key, 1), 2); + + switch ($key[0][0]) { + case '+': // User inserted keys + $attr['dynamic'] = true; + $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": '; + break 2; + case '~': + $style = 'meta'; + if (isset($key[0][1])) { + parse_str(substr($key[0], 1), $attr); + $attr += ['binary' => $cursor->hashKeyIsBinary]; + } + break; + case '*': + $style = 'protected'; + $bin = '#'.$bin; + break; + default: + $attr['class'] = $key[0]; + $style = 'private'; + $bin = '-'.$bin; + break; + } + + if (isset($attr['collapse'])) { + if ($attr['collapse']) { + $this->collapseNextHash = true; + } else { + $this->expandNextHash = true; + } + } + + $this->line .= $bin.$this->style($style, $key[1], $attr).(isset($attr['separator']) ? $attr['separator'] : ': '); + } else { + // This case should not happen + $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; + } + break; + } + + if ($cursor->hardRefTo) { + $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; + } + } + } + + /** + * Decorates a value with some style. + * + * @param string $style The type of style being applied + * @param string $value The value being styled + * @param array $attr Optional context information + * + * @return string The value with style decoration + */ + protected function style($style, $value, $attr = []) + { + if (null === $this->colors) { + $this->colors = $this->supportsColors(); + } + + if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { + $prefix = substr($value, 0, -$attr['ellipsis']); + if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && 0 === strpos($prefix, $_SERVER[$pwd])) { + $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd])); + } + if (!empty($attr['ellipsis-tail'])) { + $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']); + $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']); + } else { + $value = substr($value, -$attr['ellipsis']); + } + + return $this->style('default', $prefix).$this->style($style, $value); + } + + $style = $this->styles[$style]; + + $map = static::$controlCharsMap; + $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; + $endCchr = $this->colors ? "\033[m\033[{$style}m" : ''; + $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) { + $s = $startCchr; + $c = $c[$i = 0]; + do { + $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i])); + } while (isset($c[++$i])); + + return $s.$endCchr; + }, $value, -1, $cchrCount); + + if ($this->colors) { + if ($cchrCount && "\033" === $value[0]) { + $value = substr($value, \strlen($startCchr)); + } else { + $value = "\033[{$style}m".$value; + } + if ($cchrCount && $endCchr === substr($value, -\strlen($endCchr))) { + $value = substr($value, 0, -\strlen($endCchr)); + } else { + $value .= "\033[{$this->styles['default']}m"; + } + } + + return $value; + } + + /** + * @return bool Tells if the current output stream supports ANSI colors or not + */ + protected function supportsColors() + { + if ($this->outputStream !== static::$defaultOutput) { + return $this->hasColorSupport($this->outputStream); + } + if (null !== static::$defaultColors) { + return static::$defaultColors; + } + if (isset($_SERVER['argv'][1])) { + $colors = $_SERVER['argv']; + $i = \count($colors); + while (--$i > 0) { + if (isset($colors[$i][5])) { + switch ($colors[$i]) { + case '--ansi': + case '--color': + case '--color=yes': + case '--color=force': + case '--color=always': + return static::$defaultColors = true; + + case '--no-ansi': + case '--color=no': + case '--color=none': + case '--color=never': + return static::$defaultColors = false; + } + } + } + } + + $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; + $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; + + return static::$defaultColors = $this->hasColorSupport($h); + } + + /** + * {@inheritdoc} + */ + protected function dumpLine($depth, $endOfValue = false) + { + if ($this->colors) { + $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line); + } + parent::dumpLine($depth); + } + + protected function endValue(Cursor $cursor) + { + if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { + if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { + $this->line .= ','; + } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) { + $this->line .= ','; + } + } + + $this->dumpLine($cursor->depth, true); + } + + /** + * Returns true if the stream supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @param mixed $stream A CLI output stream + * + * @return bool + */ + private function hasColorSupport($stream) + { + if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { + return false; + } + + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + + if (\DIRECTORY_SEPARATOR === '\\') { + return (\function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($stream)) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + if (\function_exists('stream_isatty')) { + return @stream_isatty($stream); + } + + if (\function_exists('posix_isatty')) { + return @posix_isatty($stream); + } + + $stat = @fstat($stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } + + /** + * Returns true if the Windows terminal supports true color. + * + * Note that this does not check an output stream, but relies on environment + * variables from known implementations, or a PHP and Windows version that + * supports true color. + * + * @return bool + */ + private function isWindowsTrueColor() + { + $result = 183 <= getenv('ANSICON_VER') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM') + || 'Hyper' === getenv('TERM_PROGRAM'); + + if (!$result && \PHP_VERSION_ID >= 70200) { + $version = sprintf( + '%s.%s.%s', + PHP_WINDOWS_VERSION_MAJOR, + PHP_WINDOWS_VERSION_MINOR, + PHP_WINDOWS_VERSION_BUILD + ); + $result = $version >= '10.0.15063'; + } + + return $result; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php b/pandora_console/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php new file mode 100644 index 0000000000..b173bccf38 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper; + +use Symfony\Component\VarDumper\Cloner\Data; + +/** + * DataDumperInterface for dumping Data objects. + * + * @author Nicolas Grekas + */ +interface DataDumperInterface +{ + public function dump(Data $data); +} diff --git a/pandora_console/vendor/symfony/var-dumper/Dumper/HtmlDumper.php b/pandora_console/vendor/symfony/var-dumper/Dumper/HtmlDumper.php new file mode 100644 index 0000000000..ccbdc96f23 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Dumper/HtmlDumper.php @@ -0,0 +1,904 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper; + +use Symfony\Component\VarDumper\Cloner\Cursor; +use Symfony\Component\VarDumper\Cloner\Data; + +/** + * HtmlDumper dumps variables as HTML. + * + * @author Nicolas Grekas + */ +class HtmlDumper extends CliDumper +{ + public static $defaultOutput = 'php://output'; + + protected $dumpHeader; + protected $dumpPrefix = '
';
+    protected $dumpSuffix = '
'; + protected $dumpId = 'sf-dump'; + protected $colors = true; + protected $headerIsDumped = false; + protected $lastDepth = -1; + protected $styles = [ + 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', + 'num' => 'font-weight:bold; color:#1299DA', + 'const' => 'font-weight:bold', + 'str' => 'font-weight:bold; color:#56DB3A', + 'note' => 'color:#1299DA', + 'ref' => 'color:#A0A0A0', + 'public' => 'color:#FFFFFF', + 'protected' => 'color:#FFFFFF', + 'private' => 'color:#FFFFFF', + 'meta' => 'color:#B729D9', + 'key' => 'color:#56DB3A', + 'index' => 'color:#1299DA', + 'ellipsis' => 'color:#FF8400', + ]; + + private $displayOptions = [ + 'maxDepth' => 1, + 'maxStringLength' => 160, + 'fileLinkFormat' => null, + ]; + private $extraDisplayOptions = []; + + /** + * {@inheritdoc} + */ + public function __construct($output = null, $charset = null, $flags = 0) + { + AbstractDumper::__construct($output, $charset, $flags); + $this->dumpId = 'sf-dump-'.mt_rand(); + $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + } + + /** + * {@inheritdoc} + */ + public function setStyles(array $styles) + { + $this->headerIsDumped = false; + $this->styles = $styles + $this->styles; + } + + /** + * Configures display options. + * + * @param array $displayOptions A map of display options to customize the behavior + */ + public function setDisplayOptions(array $displayOptions) + { + $this->headerIsDumped = false; + $this->displayOptions = $displayOptions + $this->displayOptions; + } + + /** + * Sets an HTML header that will be dumped once in the output stream. + * + * @param string $header An HTML string + */ + public function setDumpHeader($header) + { + $this->dumpHeader = $header; + } + + /** + * Sets an HTML prefix and suffix that will encapse every single dump. + * + * @param string $prefix The prepended HTML string + * @param string $suffix The appended HTML string + */ + public function setDumpBoundaries($prefix, $suffix) + { + $this->dumpPrefix = $prefix; + $this->dumpSuffix = $suffix; + } + + /** + * {@inheritdoc} + */ + public function dump(Data $data, $output = null, array $extraDisplayOptions = []) + { + $this->extraDisplayOptions = $extraDisplayOptions; + $result = parent::dump($data, $output); + $this->dumpId = 'sf-dump-'.mt_rand(); + + return $result; + } + + /** + * Dumps the HTML header. + */ + protected function getDumpHeader() + { + $this->headerIsDumped = null !== $this->outputStream ? $this->outputStream : $this->lineDumper; + + if (null !== $this->dumpHeader) { + return $this->dumpHeader; + } + + $line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML' +'.$this->dumpHeader; + } + + /** + * {@inheritdoc} + */ + public function enterHash(Cursor $cursor, $type, $class, $hasChild) + { + parent::enterHash($cursor, $type, $class, false); + + if ($cursor->skipChildren) { + $cursor->skipChildren = false; + $eol = ' class=sf-dump-compact>'; + } elseif ($this->expandNextHash) { + $this->expandNextHash = false; + $eol = ' class=sf-dump-expanded>'; + } else { + $eol = '>'; + } + + if ($hasChild) { + $this->line .= 'refIndex) { + $r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2; + $r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex; + + $this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r); + } + $this->line .= $eol; + $this->dumpLine($cursor->depth); + } + } + + /** + * {@inheritdoc} + */ + public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut) + { + $this->dumpEllipsis($cursor, $hasChild, $cut); + if ($hasChild) { + $this->line .= ''; + } + parent::leaveHash($cursor, $type, $class, $hasChild, 0); + } + + /** + * {@inheritdoc} + */ + protected function style($style, $value, $attr = []) + { + if ('' === $value) { + return ''; + } + + $v = esc($value); + + if ('ref' === $style) { + if (empty($attr['count'])) { + return sprintf('%s', $v); + } + $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); + + return sprintf('%s', $this->dumpId, $r, 1 + $attr['count'], $v); + } + + if ('const' === $style && isset($attr['value'])) { + $style .= sprintf(' title="%s"', esc(is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value']))); + } elseif ('public' === $style) { + $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property'); + } elseif ('str' === $style && 1 < $attr['length']) { + $style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : ''); + } elseif ('note' === $style && false !== $c = strrpos($v, '\\')) { + return sprintf('%s', $v, $style, substr($v, $c + 1)); + } elseif ('protected' === $style) { + $style .= ' title="Protected property"'; + } elseif ('meta' === $style && isset($attr['title'])) { + $style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title']))); + } elseif ('private' === $style) { + $style .= sprintf(' title="Private property defined in class: `%s`"', esc($this->utf8Encode($attr['class']))); + } + $map = static::$controlCharsMap; + + if (isset($attr['ellipsis'])) { + $class = 'sf-dump-ellipsis'; + if (isset($attr['ellipsis-type'])) { + $class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']); + } + $label = esc(substr($value, -$attr['ellipsis'])); + $style = str_replace(' title="', " title=\"$v\n", $style); + $v = sprintf('%s', $class, substr($v, 0, -\strlen($label))); + + if (!empty($attr['ellipsis-tail'])) { + $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']))); + $v .= sprintf('%s%s', substr($label, 0, $tail), substr($label, $tail)); + } else { + $v .= $label; + } + } + + $v = "".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) { + $s = ''; + $c = $c[$i = 0]; + do { + $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i])); + } while (isset($c[++$i])); + + return $s.''; + }, $v).''; + + if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) { + $attr['href'] = $href; + } + if (isset($attr['href'])) { + $target = isset($attr['file']) ? '' : ' target="_blank"'; + $v = sprintf('%s', esc($this->utf8Encode($attr['href'])), $target, $v); + } + if (isset($attr['lang'])) { + $v = sprintf('%s', esc($attr['lang']), $v); + } + + return $v; + } + + /** + * {@inheritdoc} + */ + protected function dumpLine($depth, $endOfValue = false) + { + if (-1 === $this->lastDepth) { + $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; + } + if ($this->headerIsDumped !== (null !== $this->outputStream ? $this->outputStream : $this->lineDumper)) { + $this->line = $this->getDumpHeader().$this->line; + } + + if (-1 === $depth) { + $args = ['"'.$this->dumpId.'"']; + if ($this->extraDisplayOptions) { + $args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT); + } + // Replace is for BC + $this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args)); + } + $this->lastDepth = $depth; + + $this->line = mb_convert_encoding($this->line, 'HTML-ENTITIES', 'UTF-8'); + + if (-1 === $depth) { + AbstractDumper::dumpLine(0); + } + AbstractDumper::dumpLine($depth); + } + + private function getSourceLink($file, $line) + { + $options = $this->extraDisplayOptions + $this->displayOptions; + + if ($fmt = $options['fileLinkFormat']) { + return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); + } + + return false; + } +} + +function esc($str) +{ + return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8'); +} diff --git a/pandora_console/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php b/pandora_console/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php new file mode 100644 index 0000000000..af47753ad5 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Exception; + +/** + * @author Nicolas Grekas + */ +class ThrowingCasterException extends \Exception +{ + /** + * @param \Exception $prev The exception thrown from the caster + */ + public function __construct(\Exception $prev) + { + parent::__construct('Unexpected '.\get_class($prev).' thrown from a caster: '.$prev->getMessage(), 0, $prev); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/LICENSE b/pandora_console/vendor/symfony/var-dumper/LICENSE new file mode 100644 index 0000000000..684fbf94df --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2020 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pandora_console/vendor/symfony/var-dumper/README.md b/pandora_console/vendor/symfony/var-dumper/README.md new file mode 100644 index 0000000000..339f73eba3 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/README.md @@ -0,0 +1,15 @@ +VarDumper Component +=================== + +The VarDumper component provides mechanisms for walking through any arbitrary +PHP variable. It provides a better `dump()` function that you can use instead +of `var_dump`. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/var_dumper/introduction.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/pandora_console/vendor/symfony/var-dumper/Resources/functions/dump.php b/pandora_console/vendor/symfony/var-dumper/Resources/functions/dump.php new file mode 100644 index 0000000000..0e0e4d0435 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Resources/functions/dump.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\VarDumper\VarDumper; + +if (!function_exists('dump')) { + /** + * @author Nicolas Grekas + */ + function dump($var) + { + foreach (func_get_args() as $v) { + VarDumper::dump($v); + } + + if (1 < func_num_args()) { + return func_get_args(); + } + + return $var; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php b/pandora_console/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php new file mode 100644 index 0000000000..b8f12c5e45 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Test; + +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * @author Nicolas Grekas + */ +trait VarDumperTestTrait +{ + public function assertDumpEquals($dump, $data, $filter = 0, $message = '') + { + if (\is_string($filter)) { + @trigger_error(sprintf('The $message argument of the "%s()" method at the 3rd position is deprecated since Symfony 3.4 and will be moved at the 4th position in 4.0.', __METHOD__), \E_USER_DEPRECATED); + $message = $filter; + $filter = 0; + } + + $this->assertSame(rtrim($dump), $this->getDump($data, null, $filter), $message); + } + + public function assertDumpMatchesFormat($dump, $data, $filter = 0, $message = '') + { + if (\is_string($filter)) { + @trigger_error(sprintf('The $message argument of the "%s()" method at the 3rd position is deprecated since Symfony 3.4 and will be moved at the 4th position in 4.0.', __METHOD__), \E_USER_DEPRECATED); + $message = $filter; + $filter = 0; + } + + $this->assertStringMatchesFormat(rtrim($dump), $this->getDump($data, null, $filter), $message); + } + + /** + * @return string|null + */ + protected function getDump($data, $key = null, $filter = 0) + { + $flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0; + $flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0; + + $cloner = new VarCloner(); + $cloner->setMaxItems(-1); + $dumper = new CliDumper(null, null, $flags); + $dumper->setColors(false); + $data = $cloner->cloneVar($data, $filter)->withRefHandles(false); + if (null !== $key && null === $data = $data->seek($key)) { + return null; + } + + return rtrim($dumper->dump($data, true)); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/CasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/CasterTest.php new file mode 100644 index 0000000000..73800e5f9d --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/CasterTest.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +/** + * @author Nicolas Grekas + */ +class CasterTest extends TestCase +{ + use VarDumperTestTrait; + + private $referenceArray = [ + 'null' => null, + 'empty' => false, + 'public' => 'pub', + "\0~\0virtual" => 'virt', + "\0+\0dynamic" => 'dyn', + "\0*\0protected" => 'prot', + "\0Foo\0private" => 'priv', + ]; + + /** + * @dataProvider provideFilter + */ + public function testFilter($filter, $expectedDiff, $listedProperties = null) + { + if (null === $listedProperties) { + $filteredArray = Caster::filter($this->referenceArray, $filter); + } else { + $filteredArray = Caster::filter($this->referenceArray, $filter, $listedProperties); + } + + $this->assertSame($expectedDiff, array_diff_assoc($this->referenceArray, $filteredArray)); + } + + public function provideFilter() + { + return [ + [ + 0, + [], + ], + [ + Caster::EXCLUDE_PUBLIC, + [ + 'null' => null, + 'empty' => false, + 'public' => 'pub', + ], + ], + [ + Caster::EXCLUDE_NULL, + [ + 'null' => null, + ], + ], + [ + Caster::EXCLUDE_EMPTY, + [ + 'null' => null, + 'empty' => false, + ], + ], + [ + Caster::EXCLUDE_VIRTUAL, + [ + "\0~\0virtual" => 'virt', + ], + ], + [ + Caster::EXCLUDE_DYNAMIC, + [ + "\0+\0dynamic" => 'dyn', + ], + ], + [ + Caster::EXCLUDE_PROTECTED, + [ + "\0*\0protected" => 'prot', + ], + ], + [ + Caster::EXCLUDE_PRIVATE, + [ + "\0Foo\0private" => 'priv', + ], + ], + [ + Caster::EXCLUDE_VERBOSE, + [ + 'public' => 'pub', + "\0*\0protected" => 'prot', + ], + ['public', "\0*\0protected"], + ], + [ + Caster::EXCLUDE_NOT_IMPORTANT, + [ + 'null' => null, + 'empty' => false, + "\0~\0virtual" => 'virt', + "\0+\0dynamic" => 'dyn', + "\0Foo\0private" => 'priv', + ], + ['public', "\0*\0protected"], + ], + [ + Caster::EXCLUDE_VIRTUAL | Caster::EXCLUDE_DYNAMIC, + [ + "\0~\0virtual" => 'virt', + "\0+\0dynamic" => 'dyn', + ], + ], + [ + Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_VERBOSE, + $this->referenceArray, + ['public', "\0*\0protected"], + ], + [ + Caster::EXCLUDE_NOT_IMPORTANT | Caster::EXCLUDE_EMPTY, + [ + 'null' => null, + 'empty' => false, + "\0~\0virtual" => 'virt', + "\0+\0dynamic" => 'dyn', + "\0*\0protected" => 'prot', + "\0Foo\0private" => 'priv', + ], + ['public', 'empty'], + ], + [ + Caster::EXCLUDE_VERBOSE | Caster::EXCLUDE_EMPTY | Caster::EXCLUDE_STRICT, + [ + 'empty' => false, + ], + ['public', 'empty'], + ], + ]; + } + + /** + * @requires PHP 7.0 + */ + public function testAnonymousClass() + { + $c = eval('return new class extends stdClass { private $foo = "foo"; };'); + + $this->assertDumpMatchesFormat( + <<<'EOTXT' +stdClass@anonymous { + -foo: "foo" +} +EOTXT + , $c + ); + + $c = eval('return new class { private $foo = "foo"; };'); + + $this->assertDumpMatchesFormat( + <<<'EOTXT' +class@anonymous { + -foo: "foo" +} +EOTXT + , $c + ); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php new file mode 100644 index 0000000000..8c685f6b7b --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php @@ -0,0 +1,461 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Caster\DateCaster; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Symfony\Component\VarDumper\Tests\Fixtures\DateTimeChild; + +/** + * @author Dany Maillard + */ +class DateCasterTest extends TestCase +{ + use VarDumperTestTrait; + + /** + * @dataProvider provideDateTimes + */ + public function testDumpDateTime($time, $timezone, $xDate, $xTimestamp) + { + if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && preg_match('/[-+]\d{2}:\d{2}/', $timezone)) { + $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); + } + + $date = new \DateTime($time, new \DateTimeZone($timezone)); + + $xDump = <<assertDumpEquals($xDump, $date); + } + + /** + * @dataProvider provideDateTimes + */ + public function testCastDateTime($time, $timezone, $xDate, $xTimestamp, $xInfos) + { + if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && preg_match('/[-+]\d{2}:\d{2}/', $timezone)) { + $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); + } + + $stub = new Stub(); + $date = new \DateTime($time, new \DateTimeZone($timezone)); + $cast = DateCaster::castDateTime($date, Caster::castObject($date, \DateTime::class), $stub, false, 0); + + $xDump = << $xDate +] +EODUMP; + + $this->assertDumpEquals($xDump, $cast); + + $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0date"]); + } + + public function provideDateTimes() + { + return [ + ['2017-04-30 00:00:00.000000', 'Europe/Zurich', '2017-04-30 00:00:00.0 Europe/Zurich (+02:00)', 1493503200, 'Sunday, April 30, 2017%Afrom now%ADST On'], + ['2017-12-31 00:00:00.000000', 'Europe/Zurich', '2017-12-31 00:00:00.0 Europe/Zurich (+01:00)', 1514674800, 'Sunday, December 31, 2017%Afrom now%ADST Off'], + ['2017-04-30 00:00:00.000000', '+02:00', '2017-04-30 00:00:00.0 +02:00', 1493503200, 'Sunday, April 30, 2017%Afrom now'], + + ['2017-04-30 00:00:00.100000', '+00:00', '2017-04-30 00:00:00.100 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], + ['2017-04-30 00:00:00.120000', '+00:00', '2017-04-30 00:00:00.120 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], + ['2017-04-30 00:00:00.123000', '+00:00', '2017-04-30 00:00:00.123 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], + ['2017-04-30 00:00:00.123400', '+00:00', '2017-04-30 00:00:00.123400 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], + ['2017-04-30 00:00:00.123450', '+00:00', '2017-04-30 00:00:00.123450 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], + ['2017-04-30 00:00:00.123456', '+00:00', '2017-04-30 00:00:00.123456 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'], + ]; + } + + public function testCastDateTimeWithAdditionalChildProperty() + { + $stub = new Stub(); + $date = new DateTimeChild('2020-02-13 00:00:00.123456', new \DateTimeZone('Europe/Paris')); + $objectCast = Caster::castObject($date, DateTimeChild::class); + $dateCast = DateCaster::castDateTime($date, $objectCast, $stub, false, 0); + + $xDate = '2020-02-13 00:00:00.123456 Europe/Paris (+01:00)'; + $xInfo = 'Thursday, February 13, 2020%Afrom now'; + $xDump = << "foo" + "\\x00~\\x00date" => $xDate +] +EODUMP; + + $this->assertDumpEquals($xDump, $dateCast); + + $xDump = <<assertDumpMatchesFormat($xDump, $dateCast["\0~\0date"]); + } + + /** + * @dataProvider provideIntervals + */ + public function testDumpInterval($intervalSpec, $ms, $invert, $expected) + { + if ($ms && \PHP_VERSION_ID >= 70200 && version_compare(\PHP_VERSION, '7.2.0rc3', '<=')) { + $this->markTestSkipped('Skipped on 7.2 before rc4 because of php bug #75354.'); + } + + $interval = $this->createInterval($intervalSpec, $ms, $invert); + + $xDump = <<assertDumpMatchesFormat($xDump, $interval); + } + + /** + * @dataProvider provideIntervals + */ + public function testDumpIntervalExcludingVerbosity($intervalSpec, $ms, $invert, $expected) + { + if ($ms && \PHP_VERSION_ID >= 70200 && version_compare(\PHP_VERSION, '7.2.0rc3', '<=')) { + $this->markTestSkipped('Skipped on 7.2 before rc4 because of php bug #75354.'); + } + + $interval = $this->createInterval($intervalSpec, $ms, $invert); + + $xDump = <<assertDumpEquals($xDump, $interval, Caster::EXCLUDE_VERBOSE); + } + + /** + * @dataProvider provideIntervals + */ + public function testCastInterval($intervalSpec, $ms, $invert, $xInterval, $xSeconds) + { + if ($ms && \PHP_VERSION_ID >= 70200 && version_compare(\PHP_VERSION, '7.2.0rc3', '<=')) { + $this->markTestSkipped('Skipped on 7.2 before rc4 because of php bug #75354.'); + } + + $interval = $this->createInterval($intervalSpec, $ms, $invert); + $stub = new Stub(); + + $cast = DateCaster::castInterval($interval, ['foo' => 'bar'], $stub, false, Caster::EXCLUDE_VERBOSE); + + $xDump = << $xInterval +] +EODUMP; + + $this->assertDumpEquals($xDump, $cast); + + if (null === $xSeconds) { + return; + } + + $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0interval"]); + } + + public function provideIntervals() + { + $i = new \DateInterval('PT0S'); + $ms = ($withMs = \PHP_VERSION_ID >= 70100 && isset($i->f)) ? '.0' : ''; + + return [ + ['PT0S', 0, 0, '0s', '0s'], + ['PT0S', 0.1, 0, $withMs ? '+ 00:00:00.100' : '0s', '%is'], + ['PT1S', 0, 0, '+ 00:00:01'.$ms, '%is'], + ['PT2M', 0, 0, '+ 00:02:00'.$ms, '%is'], + ['PT3H', 0, 0, '+ 03:00:00'.$ms, '%ss'], + ['P4D', 0, 0, '+ 4d', '%ss'], + ['P5M', 0, 0, '+ 5m', null], + ['P6Y', 0, 0, '+ 6y', null], + ['P1Y2M3DT4H5M6S', 0, 0, '+ 1y 2m 3d 04:05:06'.$ms, null], + ['PT1M60S', 0, 0, '+ 00:02:00'.$ms, null], + ['PT1H60M', 0, 0, '+ 02:00:00'.$ms, null], + ['P1DT24H', 0, 0, '+ 2d', null], + ['P1M32D', 0, 0, '+ 1m 32d', null], + + ['PT0S', 0, 1, '0s', '0s'], + ['PT0S', 0.1, 1, $withMs ? '- 00:00:00.100' : '0s', '%is'], + ['PT1S', 0, 1, '- 00:00:01'.$ms, '%is'], + ['PT2M', 0, 1, '- 00:02:00'.$ms, '%is'], + ['PT3H', 0, 1, '- 03:00:00'.$ms, '%ss'], + ['P4D', 0, 1, '- 4d', '%ss'], + ['P5M', 0, 1, '- 5m', null], + ['P6Y', 0, 1, '- 6y', null], + ['P1Y2M3DT4H5M6S', 0, 1, '- 1y 2m 3d 04:05:06'.$ms, null], + ['PT1M60S', 0, 1, '- 00:02:00'.$ms, null], + ['PT1H60M', 0, 1, '- 02:00:00'.$ms, null], + ['P1DT24H', 0, 1, '- 2d', null], + ['P1M32D', 0, 1, '- 1m 32d', null], + ]; + } + + /** + * @dataProvider provideTimeZones + */ + public function testDumpTimeZone($timezone, $expected) + { + if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && !preg_match('/\w+\/\w+/', $timezone)) { + $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); + } + + $timezone = new \DateTimeZone($timezone); + + $xDump = <<assertDumpMatchesFormat($xDump, $timezone); + } + + /** + * @dataProvider provideTimeZones + */ + public function testDumpTimeZoneExcludingVerbosity($timezone, $expected) + { + if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && !preg_match('/\w+\/\w+/', $timezone)) { + $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); + } + + $timezone = new \DateTimeZone($timezone); + + $xDump = <<assertDumpMatchesFormat($xDump, $timezone, Caster::EXCLUDE_VERBOSE); + } + + /** + * @dataProvider provideTimeZones + */ + public function testCastTimeZone($timezone, $xTimezone, $xRegion) + { + if ((\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) && !preg_match('/\w+\/\w+/', $timezone)) { + $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.'); + } + + $timezone = new \DateTimeZone($timezone); + $stub = new Stub(); + + $cast = DateCaster::castTimeZone($timezone, ['foo' => 'bar'], $stub, false, Caster::EXCLUDE_VERBOSE); + + $xDump = << $xTimezone +] +EODUMP; + + $this->assertDumpMatchesFormat($xDump, $cast); + + $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0timezone"]); + } + + public function provideTimeZones() + { + $xRegion = \extension_loaded('intl') ? '%s' : ''; + + return [ + // type 1 (UTC offset) + ['-12:00', '-12:00', ''], + ['+00:00', '+00:00', ''], + ['+14:00', '+14:00', ''], + + // type 2 (timezone abbreviation) + ['GMT', '+00:00', ''], + ['a', '+01:00', ''], + ['b', '+02:00', ''], + ['z', '+00:00', ''], + + // type 3 (timezone identifier) + ['Africa/Tunis', 'Africa/Tunis (%s:00)', $xRegion], + ['America/Panama', 'America/Panama (%s:00)', $xRegion], + ['Asia/Jerusalem', 'Asia/Jerusalem (%s:00)', $xRegion], + ['Atlantic/Canary', 'Atlantic/Canary (%s:00)', $xRegion], + ['Australia/Perth', 'Australia/Perth (%s:00)', $xRegion], + ['Europe/Zurich', 'Europe/Zurich (%s:00)', $xRegion], + ['Pacific/Tahiti', 'Pacific/Tahiti (%s:00)', $xRegion], + ]; + } + + /** + * @dataProvider providePeriods + */ + public function testDumpPeriod($start, $interval, $end, $options, $expected) + { + if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { + $this->markTestSkipped(); + } + + $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options); + + $xDump = <<assertDumpMatchesFormat($xDump, $p); + } + + /** + * @dataProvider providePeriods + */ + public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDates) + { + if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { + $this->markTestSkipped(); + } + + $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options); + $stub = new Stub(); + + $cast = DateCaster::castPeriod($p, [], $stub, false, 0); + + $xDump = << $xPeriod +] +EODUMP; + + $this->assertDumpEquals($xDump, $cast); + + $xDump = <<assertDumpMatchesFormat($xDump, $cast["\0~\0period"]); + } + + public function providePeriods() + { + $i = new \DateInterval('PT0S'); + $ms = \PHP_VERSION_ID >= 70100 && isset($i->f) ? '.0' : ''; + + $periods = [ + ['2017-01-01', 'P1D', '2017-01-03', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02'], + ['2017-01-01', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01%a2) 2017-01-02'], + + ['2017-01-01', 'P1D', '2017-01-04', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-04 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03'], + ['2017-01-01', 'P1D', 2, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 3 time/s', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03'], + + ['2017-01-01', 'P1D', '2017-01-05', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-05 00:00:00.0', '1) 2017-01-01%a2) 2017-01-02%a1 more'], + ['2017-01-01', 'P1D', 3, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 4 time/s', '1) 2017-01-01%a2) 2017-01-02%a3) 2017-01-03%a1 more'], + + ['2017-01-01', 'P1D', '2017-01-21', 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) to 2017-01-21 00:00:00.0', '1) 2017-01-01%a17 more'], + ['2017-01-01', 'P1D', 19, 0, 'every + 1d, from 2017-01-01 00:00:00.0 (included) recurring 20 time/s', '1) 2017-01-01%a17 more'], + + ['2017-01-01 01:00:00', 'P1D', '2017-01-03 01:00:00', 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) to 2017-01-03 01:00:00.0', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'], + ['2017-01-01 01:00:00', 'P1D', 1, 0, 'every + 1d, from 2017-01-01 01:00:00.0 (included) recurring 2 time/s', '1) 2017-01-01 01:00:00.0%a2) 2017-01-02 01:00:00.0'], + + ['2017-01-01', 'P1DT1H', '2017-01-03', 0, "every + 1d 01:00:00$ms, from 2017-01-01 00:00:00.0 (included) to 2017-01-03 00:00:00.0", '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'], + ['2017-01-01', 'P1DT1H', 1, 0, "every + 1d 01:00:00$ms, from 2017-01-01 00:00:00.0 (included) recurring 2 time/s", '1) 2017-01-01 00:00:00.0%a2) 2017-01-02 01:00:00.0'], + + ['2017-01-01', 'P1D', '2017-01-04', \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) to 2017-01-04 00:00:00.0', '1) 2017-01-02%a2) 2017-01-03'], + ['2017-01-01', 'P1D', 2, \DatePeriod::EXCLUDE_START_DATE, 'every + 1d, from 2017-01-01 00:00:00.0 (excluded) recurring 2 time/s', '1) 2017-01-02%a2) 2017-01-03'], + ]; + + if (\PHP_VERSION_ID < 70107) { + array_walk($periods, function (&$i) { $i[5] = ''; }); + } + + return $periods; + } + + private function createInterval($intervalSpec, $ms, $invert) + { + $interval = new \DateInterval($intervalSpec); + if (\PHP_VERSION_ID >= 70100 && isset($interval->f)) { + $interval->f = $ms; + } + $interval->invert = $invert; + + return $interval; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php new file mode 100644 index 0000000000..738180f5b2 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php @@ -0,0 +1,227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Caster\ExceptionCaster; +use Symfony\Component\VarDumper\Caster\FrameStub; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +class ExceptionCasterTest extends TestCase +{ + use VarDumperTestTrait; + + private function getTestException($msg, &$ref = null) + { + return new \Exception(''.$msg); + } + + protected function tearDown() + { + ExceptionCaster::$srcContext = 1; + ExceptionCaster::$traceArgs = true; + } + + public function testDefaultSettings() + { + $ref = ['foo']; + $e = $this->getTestException('foo', $ref); + + $expectedDump = <<<'EODUMP' +Exception { + #message: "foo" + #code: 0 + #file: "%sExceptionCasterTest.php" + #line: 28 + trace: { + %s%eTests%eCaster%eExceptionCasterTest.php:28 { + › { + › return new \Exception(''.$msg); + › } + } + %s%eTests%eCaster%eExceptionCasterTest.php:40 { …} +%A +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $e); + $this->assertSame(['foo'], $ref); + } + + public function testSeek() + { + $e = $this->getTestException(2); + + $expectedDump = <<<'EODUMP' +{ + %s%eTests%eCaster%eExceptionCasterTest.php:28 { + › { + › return new \Exception(''.$msg); + › } + } + %s%eTests%eCaster%eExceptionCasterTest.php:64 { …} +%A +EODUMP; + + $this->assertStringMatchesFormat($expectedDump, $this->getDump($e, 'trace')); + } + + public function testNoArgs() + { + $e = $this->getTestException(1); + ExceptionCaster::$traceArgs = false; + + $expectedDump = <<<'EODUMP' +Exception { + #message: "1" + #code: 0 + #file: "%sExceptionCasterTest.php" + #line: 28 + trace: { + %sExceptionCasterTest.php:28 { + › { + › return new \Exception(''.$msg); + › } + } + %s%eTests%eCaster%eExceptionCasterTest.php:82 { …} +%A +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $e); + } + + public function testNoSrcContext() + { + $e = $this->getTestException(1); + ExceptionCaster::$srcContext = -1; + + $expectedDump = <<<'EODUMP' +Exception { + #message: "1" + #code: 0 + #file: "%sExceptionCasterTest.php" + #line: 28 + trace: { + %s%eTests%eCaster%eExceptionCasterTest.php:28 + %s%eTests%eCaster%eExceptionCasterTest.php:%d +%A +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $e); + } + + public function testHtmlDump() + { + if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) { + $this->markTestSkipped('A custom file_link_format is defined.'); + } + + $e = $this->getTestException(1); + ExceptionCaster::$srcContext = -1; + + $cloner = new VarCloner(); + $cloner->setMaxItems(1); + $dumper = new HtmlDumper(); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $dump = $dumper->dump($cloner->cloneVar($e)->withRefHandles(false), true); + + $expectedDump = <<<'EODUMP' +Exception { + #message: "1" + #code: 0 + #file: "%s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php" + #line: 28 + trace: { + %s%eVarDumper%eTests%eCaster%eExceptionCasterTest.php:28 + …%d + } +} + +EODUMP; + + $this->assertStringMatchesFormat($expectedDump, $dump); + } + + /** + * @requires function Twig\Template::getSourceContext + */ + public function testFrameWithTwig() + { + require_once \dirname(__DIR__).'/Fixtures/Twig.php'; + + $f = [ + new FrameStub([ + 'file' => \dirname(__DIR__).'/Fixtures/Twig.php', + 'line' => 20, + 'class' => '__TwigTemplate_VarDumperFixture_u75a09', + ]), + new FrameStub([ + 'file' => \dirname(__DIR__).'/Fixtures/Twig.php', + 'line' => 21, + 'class' => '__TwigTemplate_VarDumperFixture_u75a09', + 'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__), + ]), + ]; + + $expectedDump = <<<'EODUMP' +array:2 [ + 0 => { + class: "__TwigTemplate_VarDumperFixture_u75a09" + src: { + %sTwig.php:1 { + › + › foo bar + › twig source + } + } + } + 1 => { + class: "__TwigTemplate_VarDumperFixture_u75a09" + object: __TwigTemplate_VarDumperFixture_u75a09 { + %A + } + src: { + %sExceptionCasterTest.php:2 { + › foo bar + › twig source + › + } + } + } +] + +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $f); + } + + public function testExcludeVerbosity() + { + $e = $this->getTestException('foo'); + + $expectedDump = <<<'EODUMP' +Exception { + #message: "foo" + #code: 0 + #file: "%sExceptionCasterTest.php" + #line: 28 +} +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $e, Caster::EXCLUDE_VERBOSE); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php new file mode 100644 index 0000000000..fca242cfc6 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\PdoCaster; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +/** + * @author Nicolas Grekas + */ +class PdoCasterTest extends TestCase +{ + use VarDumperTestTrait; + + /** + * @requires extension pdo_sqlite + */ + public function testCastPdo() + { + $pdo = new \PDO('sqlite::memory:'); + $pdo->setAttribute(\PDO::ATTR_STATEMENT_CLASS, ['PDOStatement', [$pdo]]); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + $cast = PdoCaster::castPdo($pdo, [], new Stub(), false); + + $this->assertInstanceOf('Symfony\Component\VarDumper\Caster\EnumStub', $cast["\0~\0attributes"]); + + $attr = $cast["\0~\0attributes"] = $cast["\0~\0attributes"]->value; + $this->assertInstanceOf('Symfony\Component\VarDumper\Caster\ConstStub', $attr['CASE']); + $this->assertSame('NATURAL', $attr['CASE']->class); + $this->assertSame('BOTH', $attr['DEFAULT_FETCH_MODE']->class); + + $xDump = <<<'EODUMP' +array:2 [ + "\x00~\x00inTransaction" => false + "\x00~\x00attributes" => array:9 [ + "CASE" => NATURAL + "ERRMODE" => EXCEPTION + "PERSISTENT" => false + "DRIVER_NAME" => "sqlite" + "ORACLE_NULLS" => NATURAL + "CLIENT_VERSION" => "%s" + "SERVER_VERSION" => "%s" + "STATEMENT_CLASS" => array:%d [ + 0 => "PDOStatement"%A + ] + "DEFAULT_FETCH_MODE" => BOTH + ] +] +EODUMP; + + $this->assertDumpMatchesFormat($xDump, $cast); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php new file mode 100644 index 0000000000..ed14494ec8 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +/** + * @author Nicolas Grekas + * @requires extension redis + */ +class RedisCasterTest extends TestCase +{ + use VarDumperTestTrait; + + public function testNotConnected() + { + $redis = new \Redis(); + + if (\defined('HHVM_VERSION_ID')) { + $xCast = <<<'EODUMP' +Redis { + #host: "" +%A +} +EODUMP; + } else { + $xCast = <<<'EODUMP' +Redis { + isConnected: false +} +EODUMP; + } + + $this->assertDumpMatchesFormat($xCast, $redis); + } + + public function testConnected() + { + $redis = new \Redis(); + if (!@$redis->connect('127.0.0.1')) { + $e = error_get_last(); + self::markTestSkipped($e['message']); + } + + if (\defined('HHVM_VERSION_ID')) { + $xCast = <<<'EODUMP' +Redis { + #host: "127.0.0.1" +%A +} +EODUMP; + } else { + $xCast = <<<'EODUMP' +Redis {%A + isConnected: true + host: "127.0.0.1" + port: 6379 + auth: null + dbNum: 0 + timeout: 0.0 + persistentId: null + options: { + READ_TIMEOUT: 0.0 + SERIALIZER: NONE + PREFIX: null + SCAN: NORETRY + } +} +EODUMP; + } + + $this->assertDumpMatchesFormat($xCast, $redis); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php new file mode 100644 index 0000000000..17ad5434be --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo; +use Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass; + +/** + * @author Nicolas Grekas + */ +class ReflectionCasterTest extends TestCase +{ + use VarDumperTestTrait; + + public function testReflectionCaster() + { + $var = new \ReflectionClass('ReflectionClass'); + + $this->assertDumpMatchesFormat( + <<<'EOTXT' +ReflectionClass { + +name: "ReflectionClass" +%Aimplements: array:%d [ + 0 => "Reflector" +%A] + constants: array:3 [ + "IS_IMPLICIT_ABSTRACT" => 16 + "IS_EXPLICIT_ABSTRACT" => %d + "IS_FINAL" => %d + ] + properties: array:%d [ + "name" => ReflectionProperty { +%A +name: "name" + +class: "ReflectionClass" +%A modifiers: "public" + } +%A] + methods: array:%d [ +%A + "__construct" => ReflectionMethod { + +name: "__construct" + +class: "ReflectionClass" +%A parameters: { + $%s: ReflectionParameter { +%A position: 0 +%A +} +EOTXT + , $var + ); + } + + public function testClosureCaster() + { + $a = $b = 123; + $var = function ($x) use ($a, &$b) {}; + + $this->assertDumpMatchesFormat( + <<markTestSkipped('Not for HHVM.'); + } + $var = [ + (new \ReflectionMethod($this, __FUNCTION__))->getClosure($this), + (new \ReflectionMethod(__CLASS__, 'tearDownAfterClass'))->getClosure(), + ]; + + $this->assertDumpMatchesFormat( + << Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest::testFromCallableClosureCaster { + this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …} + file: "%sReflectionCasterTest.php" + line: "%d to %d" + } + 1 => %sTestCase::tearDownAfterClass { + file: "%sTestCase.php" + line: "%d to %d" + } +] +EOTXT + , $var + ); + } + + public function testClosureCasterExcludingVerbosity() + { + $var = function () {}; + + $expectedDump = <<assertDumpEquals($expectedDump, $var, Caster::EXCLUDE_VERBOSE); + } + + public function testReflectionParameter() + { + $var = new \ReflectionParameter(reflectionParameterFixture::class, 0); + + $this->assertDumpMatchesFormat( + <<<'EOTXT' +ReflectionParameter { + +name: "arg1" + position: 0 + typeHint: "Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass" + default: null +} +EOTXT + , $var + ); + } + + /** + * @requires PHP 7.0 + */ + public function testReflectionParameterScalar() + { + $f = eval('return function (int $a) {};'); + $var = new \ReflectionParameter($f, 0); + + $this->assertDumpMatchesFormat( + <<<'EOTXT' +ReflectionParameter { + +name: "a" + position: 0 + typeHint: "int" +} +EOTXT + , $var + ); + } + + /** + * @requires PHP 7.0 + */ + public function testReturnType() + { + $f = eval('return function ():int {};'); + $line = __LINE__ - 1; + + $this->assertDumpMatchesFormat( + <<markTestSkipped('xdebug is active'); + } + + $generator = new GeneratorDemo(); + $generator = $generator->baz(); + + $expectedDump = <<<'EODUMP' +Generator { + this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …} + executing: { + Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() { + %sGeneratorDemo.php:14 { + › { + › yield from bar(); + › } + } + } + } + closed: false +} +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $generator); + + foreach ($generator as $v) { + break; + } + + $expectedDump = <<<'EODUMP' +array:2 [ + 0 => ReflectionGenerator { + this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …} + trace: { + %s%eTests%eFixtures%eGeneratorDemo.php:9 { + › { + › yield 1; + › } + } + %s%eTests%eFixtures%eGeneratorDemo.php:20 { …} + %s%eTests%eFixtures%eGeneratorDemo.php:14 { …} + } + closed: false + } + 1 => Generator { + executing: { + Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() { + %sGeneratorDemo.php:10 { + › yield 1; + › } + › + } + } + } + closed: false + } +] +EODUMP; + + $r = new \ReflectionGenerator($generator); + $this->assertDumpMatchesFormat($expectedDump, [$r, $r->getExecutingGenerator()]); + + foreach ($generator as $v) { + } + + $expectedDump = <<<'EODUMP' +Generator { + closed: true +} +EODUMP; + $this->assertDumpMatchesFormat($expectedDump, $generator); + } +} + +function reflectionParameterFixture(NotLoadableClass $arg1 = null, $arg2) +{ +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php new file mode 100644 index 0000000000..a8c37faf3f --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/SplCasterTest.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +/** + * @author Grégoire Pineau + */ +class SplCasterTest extends TestCase +{ + use VarDumperTestTrait; + + public function getCastFileInfoTests() + { + return [ + [__FILE__, <<<'EOTXT' +SplFileInfo { +%Apath: "%sCaster" + filename: "SplCasterTest.php" + basename: "SplCasterTest.php" + pathname: "%sSplCasterTest.php" + extension: "php" + realPath: "%sSplCasterTest.php" + aTime: %s-%s-%d %d:%d:%d + mTime: %s-%s-%d %d:%d:%d + cTime: %s-%s-%d %d:%d:%d + inode: %i + size: %d + perms: 0%d + owner: %d + group: %d + type: "file" + writable: true + readable: true + executable: false + file: true + dir: false + link: false +%A} +EOTXT + ], + ['https://example.com/about', <<<'EOTXT' +SplFileInfo { +%Apath: "https://example.com" + filename: "about" + basename: "about" + pathname: "https://example.com/about" + extension: "" + realPath: false +%A} +EOTXT + ], + ]; + } + + /** @dataProvider getCastFileInfoTests */ + public function testCastFileInfo($file, $dump) + { + $this->assertDumpMatchesFormat($dump, new \SplFileInfo($file)); + } + + public function testCastFileObject() + { + $var = new \SplFileObject(__FILE__); + $var->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::SKIP_EMPTY); + $dump = <<<'EOTXT' +SplFileObject { +%Apath: "%sCaster" + filename: "SplCasterTest.php" + basename: "SplCasterTest.php" + pathname: "%sSplCasterTest.php" + extension: "php" + realPath: "%sSplCasterTest.php" + aTime: %s-%s-%d %d:%d:%d + mTime: %s-%s-%d %d:%d:%d + cTime: %s-%s-%d %d:%d:%d + inode: %i + size: %d + perms: 0%d + owner: %d + group: %d + type: "file" + writable: true + readable: true + executable: false + file: true + dir: false + link: false +%AcsvControl: array:%d [ + 0 => "," + 1 => """ +%A] + flags: DROP_NEW_LINE|SKIP_EMPTY + maxLineLen: 0 + fstat: array:26 [ + "dev" => %d + "ino" => %i + "nlink" => %d + "rdev" => 0 + "blksize" => %i + "blocks" => %i + …20 + ] + eof: false + key: 0 +} +EOTXT; + $this->assertDumpMatchesFormat($dump, $var); + } + + /** + * @dataProvider provideCastSplDoublyLinkedList + */ + public function testCastSplDoublyLinkedList($modeValue, $modeDump) + { + $var = new \SplDoublyLinkedList(); + $var->setIteratorMode($modeValue); + $dump = <<assertDumpMatchesFormat($dump, $var); + } + + public function provideCastSplDoublyLinkedList() + { + return [ + [\SplDoublyLinkedList::IT_MODE_FIFO, 'IT_MODE_FIFO | IT_MODE_KEEP'], + [\SplDoublyLinkedList::IT_MODE_LIFO, 'IT_MODE_LIFO | IT_MODE_KEEP'], + [\SplDoublyLinkedList::IT_MODE_FIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_FIFO | IT_MODE_DELETE'], + [\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_LIFO | IT_MODE_DELETE'], + ]; + } + + public function testCastObjectStorageIsntModified() + { + $var = new \SplObjectStorage(); + $var->attach(new \stdClass()); + $var->rewind(); + $current = $var->current(); + + $this->assertDumpMatchesFormat('%A', $var); + $this->assertSame($current, $var->current()); + } + + public function testCastObjectStorageDumpsInfo() + { + $var = new \SplObjectStorage(); + $var->attach(new \stdClass(), new \DateTime()); + + $this->assertDumpMatchesFormat('%ADateTime%A', $var); + } + + public function testCastArrayObject() + { + if (\defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM as different internal details.'); + } + $var = new \ArrayObject([123]); + $var->foo = 234; + + $expected = << 123 + ] + flag::STD_PROP_LIST: false + flag::ARRAY_AS_PROPS: false + iteratorClass: "ArrayIterator" +} +EOTXT; + if (\PHP_VERSION_ID < 70400) { + $expected = str_replace('-storage:', 'storage:', $expected); + } + $this->assertDumpEquals($expected, $var); + } + + public function testArrayIterator() + { + if (\defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM as different internal details.'); + } + $var = new MyArrayIterator([234]); + + $expected = << 234 + ] + flag::STD_PROP_LIST: false + flag::ARRAY_AS_PROPS: false +} +EOTXT; + if (\PHP_VERSION_ID < 70400) { + $expected = str_replace('-storage:', 'storage:', $expected); + } + $this->assertDumpEquals($expected, $var); + } + + public function testBadSplFileInfo() + { + $var = new BadSplFileInfo(); + + $expected = <<assertDumpEquals($expected, $var); + } +} + +class MyArrayIterator extends \ArrayIterator +{ + private $foo = 123; +} + +class BadSplFileInfo extends \SplFileInfo +{ + public function __construct() + { + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php new file mode 100644 index 0000000000..945833ebca --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/StubCasterTest.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\ArgsStub; +use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Caster\LinkStub; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Symfony\Component\VarDumper\Tests\Fixtures\FooInterface; + +class StubCasterTest extends TestCase +{ + use VarDumperTestTrait; + + public function testArgsStubWithDefaults($foo = 234, $bar = 456) + { + $args = [new ArgsStub([123], __FUNCTION__, __CLASS__)]; + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + $foo: 123 + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + public function testArgsStubWithExtraArgs($foo = 234) + { + $args = [new ArgsStub([123, 456], __FUNCTION__, __CLASS__)]; + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + $foo: 123 + ...: { + 456 + } + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + public function testArgsStubNoParamWithExtraArgs() + { + $args = [new ArgsStub([123], __FUNCTION__, __CLASS__)]; + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + 123 + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + public function testArgsStubWithClosure() + { + $args = [new ArgsStub([123], '{closure}', null)]; + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + 123 + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + public function testLinkStub() + { + $var = [new LinkStub(__CLASS__, 0, __FILE__)]; + + $cloner = new VarCloner(); + $dumper = new HtmlDumper(); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $dumper->setDisplayOptions(['fileLinkFormat' => '%f:%l']); + $dump = $dumper->dump($cloner->cloneVar($var), true); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => "Symfony\Component\VarDumper\Tests\Caster\StubCasterTest" +] + +EODUMP; + + $this->assertStringMatchesFormat($expectedDump, $dump); + } + + public function testLinkStubWithNoFileLink() + { + $var = [new LinkStub('example.com', 0, 'http://example.com')]; + + $cloner = new VarCloner(); + $dumper = new HtmlDumper(); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $dumper->setDisplayOptions(['fileLinkFormat' => '%f:%l']); + $dump = $dumper->dump($cloner->cloneVar($var), true); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => "example.com" +] + +EODUMP; + + $this->assertStringMatchesFormat($expectedDump, $dump); + } + + public function testClassStub() + { + $var = [new ClassStub('hello', [FooInterface::class, 'foo'])]; + + $cloner = new VarCloner(); + $dumper = new HtmlDumper(); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => "hello" +] + +EODUMP; + + $this->assertStringMatchesFormat($expectedDump, $dump); + } + + public function testClassStubWithNotExistingClass() + { + $var = [new ClassStub(NotExisting::class)]; + + $cloner = new VarCloner(); + $dumper = new HtmlDumper(); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $dump = $dumper->dump($cloner->cloneVar($var), true); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => "Symfony\Component\VarDumper\Tests\Caster\NotExisting" +] + +EODUMP; + + $this->assertStringMatchesFormat($expectedDump, $dump); + } + + public function testClassStubWithNotExistingMethod() + { + $var = [new ClassStub('hello', [FooInterface::class, 'missing'])]; + + $cloner = new VarCloner(); + $dumper = new HtmlDumper(); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $dump = $dumper->dump($cloner->cloneVar($var), true, ['fileLinkFormat' => '%f:%l']); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => "hello" +] + +EODUMP; + + $this->assertStringMatchesFormat($expectedDump, $dump); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php new file mode 100644 index 0000000000..1d7b3f6278 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Caster/XmlReaderCasterTest.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +/** + * @author Baptiste Clavié + */ +class XmlReaderCasterTest extends TestCase +{ + use VarDumperTestTrait; + + /** @var \XmlReader */ + private $reader; + + protected function setUp() + { + $this->reader = new \XmlReader(); + $this->reader->open(__DIR__.'/../Fixtures/xml_reader.xml'); + } + + protected function tearDown() + { + $this->reader->close(); + } + + public function testParserProperty() + { + $this->reader->setParserProperty(\XMLReader::SUBST_ENTITIES, true); + + $expectedDump = <<<'EODUMP' +XMLReader { + +nodeType: NONE + parserProperties: { + SUBST_ENTITIES: true + …3 + } + …12 +} +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $this->reader); + } + + /** + * @dataProvider provideNodes + */ + public function testNodes($seek, $expectedDump) + { + while ($seek--) { + $this->reader->read(); + } + $this->assertDumpMatchesFormat($expectedDump, $this->reader); + } + + public function provideNodes() + { + return [ + [0, <<<'EODUMP' +XMLReader { + +nodeType: NONE + …13 +} +EODUMP + ], + [1, <<<'EODUMP' +XMLReader { + +localName: "foo" + +nodeType: ELEMENT + +baseURI: "%sxml_reader.xml" + …11 +} +EODUMP + ], + [2, <<<'EODUMP' +XMLReader { + +localName: "#text" + +nodeType: SIGNIFICANT_WHITESPACE + +depth: 1 + +value: """ + \n + + """ + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [3, <<<'EODUMP' +XMLReader { + +localName: "bar" + +nodeType: ELEMENT + +depth: 1 + +baseURI: "%sxml_reader.xml" + …10 +} +EODUMP + ], + [4, <<<'EODUMP' +XMLReader { + +localName: "bar" + +nodeType: END_ELEMENT + +depth: 1 + +baseURI: "%sxml_reader.xml" + …10 +} +EODUMP + ], + [6, <<<'EODUMP' +XMLReader { + +localName: "bar" + +nodeType: ELEMENT + +depth: 1 + +isEmptyElement: true + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [9, <<<'EODUMP' +XMLReader { + +localName: "#text" + +nodeType: TEXT + +depth: 2 + +value: "With text" + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [12, <<<'EODUMP' +XMLReader { + +localName: "bar" + +nodeType: ELEMENT + +depth: 1 + +attributeCount: 2 + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [13, <<<'EODUMP' +XMLReader { + +localName: "bar" + +nodeType: END_ELEMENT + +depth: 1 + +baseURI: "%sxml_reader.xml" + …10 +} +EODUMP + ], + [15, <<<'EODUMP' +XMLReader { + +localName: "bar" + +nodeType: ELEMENT + +depth: 1 + +attributeCount: 1 + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [16, <<<'EODUMP' +XMLReader { + +localName: "#text" + +nodeType: SIGNIFICANT_WHITESPACE + +depth: 2 + +value: """ + \n + + """ + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [17, <<<'EODUMP' +XMLReader { + +localName: "baz" + +prefix: "baz" + +nodeType: ELEMENT + +depth: 2 + +namespaceURI: "http://symfony.com" + +baseURI: "%sxml_reader.xml" + …8 +} +EODUMP + ], + [18, <<<'EODUMP' +XMLReader { + +localName: "baz" + +prefix: "baz" + +nodeType: END_ELEMENT + +depth: 2 + +namespaceURI: "http://symfony.com" + +baseURI: "%sxml_reader.xml" + …8 +} +EODUMP + ], + [19, <<<'EODUMP' +XMLReader { + +localName: "#text" + +nodeType: SIGNIFICANT_WHITESPACE + +depth: 2 + +value: """ + \n + + """ + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [21, <<<'EODUMP' +XMLReader { + +localName: "#text" + +nodeType: SIGNIFICANT_WHITESPACE + +depth: 1 + +value: "\n" + +baseURI: "%sxml_reader.xml" + …9 +} +EODUMP + ], + [22, <<<'EODUMP' +XMLReader { + +localName: "foo" + +nodeType: END_ELEMENT + +baseURI: "%sxml_reader.xml" + …11 +} +EODUMP + ], + ]; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Cloner/DataTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Cloner/DataTest.php new file mode 100644 index 0000000000..d4b6c24c11 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Cloner/DataTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Cloner; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\VarCloner; + +class DataTest extends TestCase +{ + public function testBasicData() + { + $values = [1 => 123, 4.5, 'abc', null, false]; + $data = $this->cloneVar($values); + $clonedValues = []; + + $this->assertInstanceOf(Data::class, $data); + $this->assertCount(\count($values), $data); + $this->assertFalse(isset($data->{0})); + $this->assertFalse(isset($data[0])); + + foreach ($data as $k => $v) { + $this->assertTrue(isset($data->{$k})); + $this->assertTrue(isset($data[$k])); + $this->assertSame(\gettype($values[$k]), $data->seek($k)->getType()); + $this->assertSame($values[$k], $data->seek($k)->getValue()); + $this->assertSame($values[$k], $data->{$k}); + $this->assertSame($values[$k], $data[$k]); + $this->assertSame((string) $values[$k], (string) $data->seek($k)); + + $clonedValues[$k] = $v->getValue(); + } + + $this->assertSame($values, $clonedValues); + } + + public function testObject() + { + $data = $this->cloneVar(new \Exception('foo')); + + $this->assertSame('Exception', $data->getType()); + + $this->assertSame('foo', $data->message); + $this->assertSame('foo', $data->{Caster::PREFIX_PROTECTED.'message'}); + + $this->assertSame('foo', $data['message']); + $this->assertSame('foo', $data[Caster::PREFIX_PROTECTED.'message']); + + $this->assertStringMatchesFormat('Exception (count=%d)', (string) $data); + } + + public function testArray() + { + $values = [[], [123]]; + $data = $this->cloneVar($values); + + $this->assertSame($values, $data->getValue(true)); + + $children = $data->getValue(); + + $this->assertIsArray($children); + + $this->assertInstanceOf(Data::class, $children[0]); + $this->assertInstanceOf(Data::class, $children[1]); + + $this->assertEquals($children[0], $data[0]); + $this->assertEquals($children[1], $data[1]); + + $this->assertSame($values[0], $children[0]->getValue(true)); + $this->assertSame($values[1], $children[1]->getValue(true)); + } + + public function testStub() + { + $data = $this->cloneVar([new ClassStub('stdClass')]); + $data = $data[0]; + + $this->assertSame('string', $data->getType()); + $this->assertSame('stdClass', $data->getValue()); + $this->assertSame('stdClass', (string) $data); + } + + public function testHardRefs() + { + $values = [[]]; + $values[1] = &$values[0]; + $values[2][0] = &$values[2]; + + $data = $this->cloneVar($values); + + $this->assertSame([], $data[0]->getValue()); + $this->assertSame([], $data[1]->getValue()); + $this->assertEquals([$data[2]->getValue()], $data[2]->getValue(true)); + + $this->assertSame('array (count=3)', (string) $data); + } + + private function cloneVar($value) + { + $cloner = new VarCloner(); + + return $cloner->cloneVar($value); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php new file mode 100644 index 0000000000..e37482fca9 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php @@ -0,0 +1,505 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Cloner; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Tests\Fixtures\Php74; + +/** + * @author Nicolas Grekas + */ +class VarClonerTest extends TestCase +{ + public function testMaxIntBoundary() + { + $data = [\PHP_INT_MAX => 123]; + + $cloner = new VarCloner(); + $clone = $cloner->cloneVar($data); + + $expected = << Array + ( + [0] => Array + ( + [0] => Array + ( + [1] => 1 + ) + + ) + + [1] => Array + ( + [%s] => 123 + ) + + ) + + [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 + [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 +) + +EOTXT; + $this->assertSame(sprintf($expected, \PHP_INT_MAX), print_r($clone, true)); + } + + public function testClone() + { + $json = json_decode('{"1":{"var":"val"},"2":{"var":"val"}}'); + + $cloner = new VarCloner(); + $clone = $cloner->cloneVar($json); + + $expected = << Array + ( + [0] => Array + ( + [0] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => 4 + [class] => stdClass + [value] => + [cut] => 0 + [handle] => %i + [refCount] => 0 + [position] => 1 + [attr] => Array + ( + ) + + ) + + ) + + [1] => Array + ( + [\000+\0001] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => 4 + [class] => stdClass + [value] => + [cut] => 0 + [handle] => %i + [refCount] => 0 + [position] => 2 + [attr] => Array + ( + ) + + ) + + [\000+\0002] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => 4 + [class] => stdClass + [value] => + [cut] => 0 + [handle] => %i + [refCount] => 0 + [position] => 3 + [attr] => Array + ( + ) + + ) + + ) + + [2] => Array + ( + [\000+\000var] => val + ) + + [3] => Array + ( + [\000+\000var] => val + ) + + ) + + [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 + [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 +) + +EOTXT; + $this->assertStringMatchesFormat($expected, print_r($clone, true)); + } + + public function testLimits() + { + // Level 0: + $data = [ + // Level 1: + [ + // Level 2: + [ + // Level 3: + 'Level 3 Item 0', + 'Level 3 Item 1', + 'Level 3 Item 2', + 'Level 3 Item 3', + ], + [ + 999 => 'Level 3 Item 4', + 'Level 3 Item 5', + 'Level 3 Item 6', + ], + [ + 'Level 3 Item 7', + ], + ], + [ + [ + 'Level 3 Item 8', + ], + 'Level 2 Item 0', + ], + [ + 'Level 2 Item 1', + ], + 'Level 1 Item 0', + [ + // Test setMaxString: + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'SHORT', + ], + ]; + + $cloner = new VarCloner(); + $cloner->setMinDepth(2); + $cloner->setMaxItems(5); + $cloner->setMaxString(20); + $clone = $cloner->cloneVar($data); + + $expected = << Array + ( + [0] => Array + ( + [0] => Array + ( + [2] => 1 + ) + + ) + + [1] => Array + ( + [0] => Array + ( + [2] => 2 + ) + + [1] => Array + ( + [2] => 3 + ) + + [2] => Array + ( + [2] => 4 + ) + + [3] => Level 1 Item 0 + [4] => Array + ( + [2] => 5 + ) + + ) + + [2] => Array + ( + [0] => Array + ( + [2] => 6 + ) + + [1] => Array + ( + [0] => 2 + [1] => 7 + ) + + [2] => Array + ( + [0] => 1 + [2] => 0 + ) + + ) + + [3] => Array + ( + [0] => Array + ( + [0] => 1 + [2] => 0 + ) + + [1] => Level 2 Item 0 + ) + + [4] => Array + ( + [0] => Level 2 Item 1 + ) + + [5] => Array + ( + [0] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => 2 + [class] => 2 + [value] => ABCDEFGHIJKLMNOPQRST + [cut] => 6 + [handle] => 0 + [refCount] => 0 + [position] => 0 + [attr] => Array + ( + ) + + ) + + [1] => SHORT + ) + + [6] => Array + ( + [0] => Level 3 Item 0 + [1] => Level 3 Item 1 + [2] => Level 3 Item 2 + [3] => Level 3 Item 3 + ) + + [7] => Array + ( + [999] => Level 3 Item 4 + ) + + ) + + [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 + [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 +) + +EOTXT; + $this->assertStringMatchesFormat($expected, print_r($clone, true)); + } + + public function testJsonCast() + { + if (2 == ini_get('xdebug.overload_var_dump')) { + $this->markTestSkipped('xdebug is active'); + } + + $data = (array) json_decode('{"1":{}}'); + + $cloner = new VarCloner(); + $clone = $cloner->cloneVar($data); + + $expected = <<<'EOTXT' +object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) { + ["data":"Symfony\Component\VarDumper\Cloner\Data":private]=> + array(2) { + [0]=> + array(1) { + [0]=> + array(1) { + [1]=> + int(1) + } + } + [1]=> + array(1) { + ["1"]=> + object(Symfony\Component\VarDumper\Cloner\Stub)#%i (8) { + ["type"]=> + int(4) + ["class"]=> + string(8) "stdClass" + ["value"]=> + NULL + ["cut"]=> + int(0) + ["handle"]=> + int(%i) + ["refCount"]=> + int(0) + ["position"]=> + int(0) + ["attr"]=> + array(0) { + } + } + } + } + ["position":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(0) + ["key":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(0) + ["maxDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(20) + ["maxItemsPerDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(-1) + ["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(-1) +} + +EOTXT; + ob_start(); + var_dump($clone); + $this->assertStringMatchesFormat(\PHP_VERSION_ID >= 70200 ? str_replace('"1"', '1', $expected) : $expected, ob_get_clean()); + } + + public function testCaster() + { + $cloner = new VarCloner([ + '*' => function ($obj, $array) { + return ['foo' => 123]; + }, + __CLASS__ => function ($obj, $array) { + ++$array['foo']; + + return $array; + }, + ]); + $clone = $cloner->cloneVar($this); + + $expected = << Array + ( + [0] => Array + ( + [0] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => 4 + [class] => %s + [value] => + [cut] => 0 + [handle] => %i + [refCount] => 0 + [position] => 1 + [attr] => Array + ( + ) + + ) + + ) + + [1] => Array + ( + [foo] => 124 + ) + + ) + + [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 + [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 +) + +EOTXT; + $this->assertStringMatchesFormat($expected, print_r($clone, true)); + } + + /** + * @requires PHP 7.4 + */ + public function testPhp74() + { + $data = new Php74(); + + $cloner = new VarCloner(); + $clone = $cloner->cloneVar($data); + + $expected = <<<'EOTXT' +Symfony\Component\VarDumper\Cloner\Data Object +( + [data:Symfony\Component\VarDumper\Cloner\Data:private] => Array + ( + [0] => Array + ( + [0] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => 4 + [class] => Symfony\Component\VarDumper\Tests\Fixtures\Php74 + [value] => + [cut] => 0 + [handle] => %i + [refCount] => 0 + [position] => 1 + [attr] => Array + ( + ) + + ) + + ) + + [1] => Array + ( + [p1] => 123 + [p2] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => 4 + [class] => stdClass + [value] => + [cut] => 0 + [handle] => %i + [refCount] => 0 + [position] => 0 + [attr] => Array + ( + ) + + ) + + ) + + ) + + [position:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [key:Symfony\Component\VarDumper\Cloner\Data:private] => 0 + [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 + [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 +) + +EOTXT; + $this->assertStringMatchesFormat($expected, print_r($clone, true)); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php new file mode 100644 index 0000000000..6fae0843a9 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php @@ -0,0 +1,644 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\AbstractDumper; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; +use Twig\Environment; +use Twig\Loader\FilesystemLoader; + +/** + * @author Nicolas Grekas + */ +class CliDumperTest extends TestCase +{ + use VarDumperTestTrait; + + public function testGet() + { + require __DIR__.'/../Fixtures/dumb-var.php'; + + $dumper = new CliDumper('php://output'); + $dumper->setColors(false); + $cloner = new VarCloner(); + $cloner->addCasters([ + ':stream' => function ($res, $a) { + unset($a['uri'], $a['wrapper_data']); + + return $a; + }, + ]); + $data = $cloner->cloneVar($var); + + ob_start(); + $dumper->dump($data); + $out = ob_get_clean(); + $out = preg_replace('/[ \t]+$/m', '', $out); + $intMax = \PHP_INT_MAX; + $res = (int) $var['res']; + + $r = \defined('HHVM_VERSION') ? '' : '#%d'; + $this->assertStringMatchesFormat( + << 1 + 0 => &1 null + "const" => 1.1 + 1 => true + 2 => false + 3 => NAN + 4 => INF + 5 => -INF + 6 => {$intMax} + "str" => "déjà\\n" + 7 => b"é\\x00" + "[]" => [] + "res" => stream resource {@{$res} +%A wrapper_type: "plainfile" + stream_type: "STDIO" + mode: "r" + unread_bytes: 0 + seekable: true +%A options: [] + } + "obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d + +foo: "foo" + +"bar": "bar" + } + "closure" => Closure {{$r} + class: "Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest" + this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {{$r} …} + parameters: { + \$a: {} + &\$b: { + typeHint: "PDO" + default: null + } + } + file: "%s%eTests%eFixtures%edumb-var.php" + line: "{$var['line']} to {$var['line']}" + } + "line" => {$var['line']} + "nobj" => array:1 [ + 0 => &3 {#%d} + ] + "recurs" => &4 array:1 [ + 0 => &4 array:1 [&4] + ] + 8 => &1 null + "sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d} + "snobj" => &3 {#%d} + "snobj2" => {#%d} + "file" => "{$var['file']}" + b"bin-key-é" => "" +] + +EOTXT + , + $out + ); + } + + /** + * @dataProvider provideDumpWithCommaFlagTests + */ + public function testDumpWithCommaFlag($expected, $flags) + { + $dumper = new CliDumper(null, null, $flags); + $dumper->setColors(false); + $cloner = new VarCloner(); + + $var = [ + 'array' => ['a', 'b'], + 'string' => 'hello', + 'multiline string' => "this\nis\na\multiline\nstring", + ]; + + $dump = $dumper->dump($cloner->cloneVar($var), true); + + $this->assertSame($expected, $dump); + } + + public function testDumpWithCommaFlagsAndExceptionCodeExcerpt() + { + $dumper = new CliDumper(null, null, CliDumper::DUMP_TRAILING_COMMA); + $dumper->setColors(false); + $cloner = new VarCloner(); + + $ex = new \RuntimeException('foo'); + + $dump = $dumper->dump($cloner->cloneVar($ex)->withRefHandles(false), true); + + $this->assertStringMatchesFormat(<<<'EOTXT' +RuntimeException { + #message: "foo" + #code: 0 + #file: "%ACliDumperTest.php" + #line: %d + trace: { + %ACliDumperTest.php:%d { + › + › $ex = new \RuntimeException('foo'); + › + } + %A + } +} + +EOTXT + , $dump); + } + + public function provideDumpWithCommaFlagTests() + { + $expected = <<<'EOTXT' +array:3 [ + "array" => array:2 [ + 0 => "a", + 1 => "b" + ], + "string" => "hello", + "multiline string" => """ + this\n + is\n + a\multiline\n + string + """ +] + +EOTXT; + + yield [$expected, CliDumper::DUMP_COMMA_SEPARATOR]; + + $expected = <<<'EOTXT' +array:3 [ + "array" => array:2 [ + 0 => "a", + 1 => "b", + ], + "string" => "hello", + "multiline string" => """ + this\n + is\n + a\multiline\n + string + """, +] + +EOTXT; + + yield [$expected, CliDumper::DUMP_TRAILING_COMMA]; + } + + /** + * @requires extension xml + * @requires PHP < 8.0 + */ + public function testXmlResource() + { + $var = xml_parser_create(); + + $this->assertDumpMatchesFormat( + <<<'EOTXT' +xml resource { + current_byte_index: %i + current_column_number: %i + current_line_number: 1 + error_code: XML_ERROR_NONE +} +EOTXT + , + $var + ); + } + + public function testJsonCast() + { + $var = (array) json_decode('{"0":{},"1":null}'); + foreach ($var as &$v) { + } + $var[] = &$v; + $var[''] = 2; + + if (\PHP_VERSION_ID >= 70200) { + $this->assertDumpMatchesFormat( + <<<'EOTXT' +array:4 [ + 0 => {} + 1 => &1 null + 2 => &1 null + "" => 2 +] +EOTXT + , + $var + ); + } else { + $this->assertDumpMatchesFormat( + <<<'EOTXT' +array:4 [ + "0" => {} + "1" => &1 null + 0 => &1 null + "" => 2 +] +EOTXT + , + $var + ); + } + } + + public function testObjectCast() + { + $var = (object) [1 => 1]; + $var->{1} = 2; + + if (\PHP_VERSION_ID >= 70200) { + $this->assertDumpMatchesFormat( + <<<'EOTXT' +{ + +"1": 2 +} +EOTXT + , + $var + ); + } else { + $this->assertDumpMatchesFormat( + <<<'EOTXT' +{ + +1: 1 + +"1": 2 +} +EOTXT + , + $var + ); + } + } + + public function testClosedResource() + { + if (\defined('HHVM_VERSION') && HHVM_VERSION_ID < 30600) { + $this->markTestSkipped(); + } + + $var = fopen(__FILE__, 'r'); + fclose($var); + + $dumper = new CliDumper('php://output'); + $dumper->setColors(false); + $cloner = new VarCloner(); + $data = $cloner->cloneVar($var); + + ob_start(); + $dumper->dump($data); + $out = ob_get_clean(); + $res = (int) $var; + + $this->assertStringMatchesFormat( + << 'bar'], + ]; + + $this->assertDumpEquals( + << (3) "foo" + 2 => (3) "bar" + ] +] +EOTXT + , + $var + ); + + putenv('DUMP_LIGHT_ARRAY='); + putenv('DUMP_STRING_LENGTH='); + } + + /** + * @requires function Twig\Template::getSourceContext + */ + public function testThrowingCaster() + { + $out = fopen('php://memory', 'r+b'); + + require_once __DIR__.'/../Fixtures/Twig.php'; + $twig = new \__TwigTemplate_VarDumperFixture_u75a09(new Environment(new FilesystemLoader())); + + $dumper = new CliDumper(); + $dumper->setColors(false); + $cloner = new VarCloner(); + $cloner->addCasters([ + ':stream' => function ($res, $a) { + unset($a['wrapper_data']); + + return $a; + }, + ]); + $cloner->addCasters([ + ':stream' => eval('return function () use ($twig) { + try { + $twig->render([]); + } catch (\Twig\Error\RuntimeError $e) { + throw $e->getPrevious(); + } + };'), + ]); + $ref = (int) $out; + + $data = $cloner->cloneVar($out); + $dumper->dump($data, $out); + $out = stream_get_contents($out, -1, 0); + + $r = \defined('HHVM_VERSION') ? '' : '#%d'; + $this->assertStringMatchesFormat( + << 'foo']; + $var->bar = &$var->foo; + + $dumper = new CliDumper(); + $dumper->setColors(false); + $cloner = new VarCloner(); + + $data = $cloner->cloneVar($var); + $out = $dumper->dump($data, true); + + $r = \defined('HHVM_VERSION') ? '' : '#%d'; + $this->assertStringMatchesFormat( + <<getSpecialVars(); + + $this->assertDumpEquals( + <<<'EOTXT' +array:3 [ + 0 => array:1 [ + 0 => &1 array:1 [ + 0 => &1 array:1 [&1] + ] + ] + 1 => array:1 [ + "GLOBALS" => &2 array:1 [ + "GLOBALS" => &2 array:1 [&2] + ] + ] + 2 => &2 array:1 [&2] +] +EOTXT + , + $var + ); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testGlobalsNoExt() + { + $var = $this->getSpecialVars(); + unset($var[0]); + $out = ''; + + $dumper = new CliDumper(function ($line, $depth) use (&$out) { + if ($depth >= 0) { + $out .= str_repeat(' ', $depth).$line."\n"; + } + }); + $dumper->setColors(false); + $cloner = new VarCloner(); + + $refl = new \ReflectionProperty($cloner, 'useExt'); + $refl->setAccessible(true); + $refl->setValue($cloner, false); + + $data = $cloner->cloneVar($var); + $dumper->dump($data); + + $this->assertSame( + <<<'EOTXT' +array:2 [ + 1 => array:1 [ + "GLOBALS" => &1 array:1 [ + "GLOBALS" => &1 array:1 [&1] + ] + ] + 2 => &1 array:1 [&1] +] + +EOTXT + , + $out + ); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testBuggyRefs() + { + if (\PHP_VERSION_ID >= 50600) { + $this->markTestSkipped('PHP 5.6 fixed refs counting'); + } + + $var = $this->getSpecialVars(); + $var = $var[0]; + + $dumper = new CliDumper(); + $dumper->setColors(false); + $cloner = new VarCloner(); + + $data = $cloner->cloneVar($var)->withMaxDepth(3); + $out = ''; + $dumper->dump($data, function ($line, $depth) use (&$out) { + if ($depth >= 0) { + $out .= str_repeat(' ', $depth).$line."\n"; + } + }); + + $this->assertSame( + <<<'EOTXT' +array:1 [ + 0 => array:1 [ + 0 => array:1 [ + 0 => array:1 [ …1] + ] + ] +] + +EOTXT + , + $out + ); + } + + public function testIncompleteClass() + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', null); + $var = unserialize('O:8:"Foo\Buzz":0:{}'); + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + + $this->assertDumpMatchesFormat( + << 'bar'], + 0, + << "\e[1;38;5;113mbar\e[0;38;5;208m"\e[m +\e[0;38;5;208m]\e[m + +EOTXT + ]; + + yield [[], AbstractDumper::DUMP_LIGHT_ARRAY, "\e[0;38;5;208m[]\e[m\n"]; + + yield [ + ['foo' => 'bar'], + AbstractDumper::DUMP_LIGHT_ARRAY, + << "\e[1;38;5;113mbar\e[0;38;5;208m"\e[m +\e[0;38;5;208m]\e[m + +EOTXT + ]; + + yield [[], 0, "\e[0;38;5;208m[]\e[m\n"]; + } + + /** + * @dataProvider provideDumpArrayWithColor + */ + public function testDumpArrayWithColor($value, $flags, $expectedOut) + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Windows console does not support coloration'); + } + + $out = ''; + $dumper = new CliDumper(function ($line, $depth) use (&$out) { + if ($depth >= 0) { + $out .= str_repeat(' ', $depth).$line."\n"; + } + }, null, $flags); + $dumper->setColors(true); + $cloner = new VarCloner(); + $dumper->dump($cloner->cloneVar($value)); + + $this->assertSame($expectedOut, $out); + } + + private function getSpecialVars() + { + foreach (array_keys($GLOBALS) as $var) { + if ('GLOBALS' !== $var) { + unset($GLOBALS[$var]); + } + } + + $var = function &() { + $var = []; + $var[] = &$var; + + return $var; + }; + + return [$var(), $GLOBALS, &$GLOBALS]; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php new file mode 100644 index 0000000000..7444d4bf58 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/FunctionsTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\VarDumper; + +class FunctionsTest extends TestCase +{ + public function testDumpReturnsFirstArg() + { + $this->setupVarDumper(); + + $var1 = 'a'; + + ob_start(); + $return = dump($var1); + ob_end_clean(); + + $this->assertEquals($var1, $return); + } + + public function testDumpReturnsAllArgsInArray() + { + $this->setupVarDumper(); + + $var1 = 'a'; + $var2 = 'b'; + $var3 = 'c'; + + ob_start(); + $return = dump($var1, $var2, $var3); + ob_end_clean(); + + $this->assertEquals([$var1, $var2, $var3], $return); + } + + protected function setupVarDumper() + { + $cloner = new VarCloner(); + $dumper = new CliDumper('php://output'); + VarDumper::setHandler(function ($var) use ($cloner, $dumper) { + $dumper->dump($cloner->cloneVar($var)); + }); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php new file mode 100644 index 0000000000..d91fda265b --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; + +/** + * @author Nicolas Grekas + */ +class HtmlDumperTest extends TestCase +{ + public function testGet() + { + if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) { + $this->markTestSkipped('A custom file_link_format is defined.'); + } + + require __DIR__.'/../Fixtures/dumb-var.php'; + + $dumper = new HtmlDumper('php://output'); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $cloner = new VarCloner(); + $cloner->addCasters([ + ':stream' => function ($res, $a) { + unset($a['uri'], $a['wrapper_data']); + + return $a; + }, + ]); + $data = $cloner->cloneVar($var); + + ob_start(); + $dumper->dump($data); + $out = ob_get_clean(); + $out = preg_replace('/[ \t]+$/m', '', $out); + $var['file'] = htmlspecialchars($var['file'], \ENT_QUOTES, 'UTF-8'); + $intMax = \PHP_INT_MAX; + preg_match('/sf-dump-\d+/', $out, $dumpId); + $dumpId = $dumpId[0]; + $res = (int) $var['res']; + + $r = \defined('HHVM_VERSION') ? '' : '#%d'; + $this->assertStringMatchesFormat( + <<array:24 [ + "number" => 1 + 0 => &1 null + "const" => 1.1 + 1 => true + 2 => false + 3 => NAN + 4 => INF + 5 => -INF + 6 => {$intMax} + "str" => "d&%s;j&%s;\\n" + 7 => b"&%s;\\x00" + "[]" => [] + "res" => stream resource @{$res} +%A wrapper_type: "plainfile" + stream_type: "STDIO" + mode: "r" + unread_bytes: 0 + seekable: true +%A options: [] + } + "obj" => DumbFoo {#%d + +foo: "foo" + +"bar": "bar" + } + "closure" => Closure {{$r} + class: "Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest" + this: HtmlDumperTest {{$r} &%s;} + parameters: { + \$a: {} + &\$b: { + typeHint: "PDO" + default: null + } + } + file: "%s%eVarDumper%eTests%eFixtures%edumb-var.php" + line: "{$var['line']} to {$var['line']}" + } + "line" => {$var['line']} + "nobj" => array:1 [ + 0 => &3 {#%d} + ] + "recurs" => &4 array:1 [ + 0 => &4 array:1 [&4] + ] + 8 => &1 null + "sobj" => DumbFoo {#%d} + "snobj" => &3 {#%d} + "snobj2" => {#%d} + "file" => "{$var['file']}" + b"bin-key-&%s;" => "" +] + + +EOTXT + , + + $out + ); + } + + public function testCharset() + { + $var = mb_convert_encoding('Словарь', 'CP1251', 'UTF-8'); + + $dumper = new HtmlDumper('php://output', 'CP1251'); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $cloner = new VarCloner(); + + $data = $cloner->cloneVar($var); + $out = $dumper->dump($data, true); + + $this->assertStringMatchesFormat( + <<<'EOTXT' +b"Словарь" + + +EOTXT + , + $out + ); + } + + public function testAppend() + { + $out = fopen('php://memory', 'r+b'); + + $dumper = new HtmlDumper(); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + $cloner = new VarCloner(); + + $dumper->dump($cloner->cloneVar(123), $out); + $dumper->dump($cloner->cloneVar(456), $out); + + $out = stream_get_contents($out, -1, 0); + + $this->assertSame(<<<'EOTXT' +123 + +456 + + +EOTXT + , + $out + ); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/DateTimeChild.php b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/DateTimeChild.php new file mode 100644 index 0000000000..2ea2df6514 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/DateTimeChild.php @@ -0,0 +1,8 @@ +p2 = new \stdClass(); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/Twig.php b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/Twig.php new file mode 100644 index 0000000000..8b84d820fc --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/Twig.php @@ -0,0 +1,38 @@ +parent = false; + $this->blocks = []; + $this->path = $path; + } + + protected function doDisplay(array $context, array $blocks = []) + { + // line 2 + throw new \Exception('Foobar'); + } + + public function getTemplateName() + { + return 'foo.twig'; + } + + public function getDebugInfo() + { + return [20 => 1, 21 => 2]; + } + + public function getSourceContext() + { + return new Twig\Source(" foo bar\n twig source\n\n", 'foo.twig', $this->path ?: __FILE__); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php new file mode 100644 index 0000000000..de51cebc21 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php @@ -0,0 +1,40 @@ +bar = 'bar'; + +$g = fopen(__FILE__, 'r'); + +$var = [ + 'number' => 1, null, + 'const' => 1.1, true, false, NAN, INF, -INF, PHP_INT_MAX, + 'str' => "déjà\n", "\xE9\x00", + '[]' => [], + 'res' => $g, + 'obj' => $foo, + 'closure' => function ($a, \PDO &$b = null) {}, + 'line' => __LINE__ - 1, + 'nobj' => [(object) []], +]; + +$r = []; +$r[] = &$r; + +$var['recurs'] = &$r; +$var[] = &$var[0]; +$var['sobj'] = $var['obj']; +$var['snobj'] = &$var['nobj'][0]; +$var['snobj2'] = $var['nobj'][0]; +$var['file'] = __FILE__; +$var["bin-key-\xE9"] = ''; + +unset($g, $r); diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml new file mode 100644 index 0000000000..740c399fc4 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml @@ -0,0 +1,10 @@ + + + + + With text + + + + + diff --git a/pandora_console/vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php b/pandora_console/vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php new file mode 100644 index 0000000000..18042fb3bf --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Test; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +class VarDumperTestTraitTest extends TestCase +{ + use VarDumperTestTrait; + + public function testItComparesLargeData() + { + $howMany = 700; + $data = array_fill_keys(range(0, $howMany), ['a', 'b', 'c', 'd']); + + $expected = sprintf("array:%d [\n", $howMany + 1); + for ($i = 0; $i <= $howMany; ++$i) { + $expected .= << array:4 [ + 0 => "a" + 1 => "b" + 2 => "c" + 3 => "d" + ]\n +EODUMP; + } + $expected .= "]\n"; + + $this->assertDumpEquals($expected, $data); + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/VarDumper.php b/pandora_console/vendor/symfony/var-dumper/VarDumper.php new file mode 100644 index 0000000000..2ef20c064b --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/VarDumper.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper; + +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; + +// Load the global dump() function +require_once __DIR__.'/Resources/functions/dump.php'; + +/** + * @author Nicolas Grekas + */ +class VarDumper +{ + private static $handler; + + public static function dump($var) + { + if (null === self::$handler) { + $cloner = new VarCloner(); + $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliDumper() : new HtmlDumper(); + self::$handler = function ($var) use ($cloner, $dumper) { + $dumper->dump($cloner->cloneVar($var)); + }; + } + + return \call_user_func(self::$handler, $var); + } + + public static function setHandler(callable $callable = null) + { + $prevHandler = self::$handler; + self::$handler = $callable; + + return $prevHandler; + } +} diff --git a/pandora_console/vendor/symfony/var-dumper/composer.json b/pandora_console/vendor/symfony/var-dumper/composer.json new file mode 100644 index 0000000000..9f2352ee25 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/composer.json @@ -0,0 +1,42 @@ +{ + "name": "symfony/var-dumper", + "type": "library", + "description": "Symfony mechanism for exploring and dumping PHP variables", + "keywords": ["dump", "debug"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "ext-iconv": "*", + "twig/twig": "~1.34|~2.4" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "ext-symfony_debug": "" + }, + "autoload": { + "files": [ "Resources/functions/dump.php" ], + "psr-4": { "Symfony\\Component\\VarDumper\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/pandora_console/vendor/symfony/var-dumper/phpunit.xml.dist b/pandora_console/vendor/symfony/var-dumper/phpunit.xml.dist new file mode 100644 index 0000000000..3243fcd027 --- /dev/null +++ b/pandora_console/vendor/symfony/var-dumper/phpunit.xml.dist @@ -0,0 +1,33 @@ + + + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/pandora_console/views/cluster/edit.php b/pandora_console/views/cluster/edit.php new file mode 100644 index 0000000000..393951f470 --- /dev/null +++ b/pandora_console/views/cluster/edit.php @@ -0,0 +1,129 @@ +operation; + +if ($wizard->id !== null) { + $extra .= '&id='.$wizard->id; +} + +$bc[] = [ + 'link' => $wizard->parentUrl, + 'label' => __('Cluster list'), + 'selected' => false, +]; + +$labels = $wizard->getLabels(); +foreach ($labels as $key => $label) { + $bc[] = [ + 'link' => $wizard->url.(($key >= 0) ? $extra.'&page='.$key : ''), + 'label' => __($label), + 'selected' => ($wizard->page == $key), + ]; +} + +$wizard->prepareBreadcrum($bc); + +$header_str = __(ucfirst($wizard->getOperation())).' '; +$header_str .= (($cluster->name() !== null) ? $cluster->name() : __('cluster ')); +$header_str .= ' » '.__($labels[$wizard->page]); + +// Header. +$buttons = []; + +$main_page = ''; +$main_page .= html_print_image( + 'images/list.png', + true, + [ + 'title' => __('Cluster list'), + 'class' => 'invert_filter', + ] +); +$main_page .= ''; + +$buttons = [ + [ + 'active' => false, + 'text' => $main_page, + ], +]; + +if ($cluster !== null) { + if ($cluster->id() !== null) { + $view = ''; + $view .= html_print_image( + 'images/operation.png', + true, + [ + 'title' => __('View this cluster'), + 'class' => 'invert_filter', + ] + ); + $view .= ''; + + $buttons[] = [ + 'active' => false, + 'text' => $view, + ]; + } +} + +ui_print_page_header( + $header_str, + '', + false, + 'cluster_view', + true, + // Buttons. + $buttons, + false, + '', + GENERIC_SIZE_TEXT, + '', + $wizard->printHeader(true) +); + +// Check if any error ocurred. +if (empty($wizard->errMessages) === false) { + foreach ($wizard->errMessages as $msg) { + ui_print_error_message(__($msg)); + } +} + +if (empty($form) === false) { + // Print form (prepared in ClusterWizard). + HTML::printForm($form, false, ($wizard->page < 6)); +} + +// Print always go back button. +HTML::printForm($wizard->getGoBackForm(), false); diff --git a/pandora_console/views/cluster/list.php b/pandora_console/views/cluster/list.php new file mode 100644 index 0000000000..c72f1752a1 --- /dev/null +++ b/pandora_console/views/cluster/list.php @@ -0,0 +1,132 @@ + 'options', + 'class' => 'action_buttons', + ], + ]; + + $column_names = [ + __('Name'), + __('Description'), + __('Group'), + __('Type'), + __('Nodes'), + __('Status'), + __('Options'), + ]; + + $tableId = 'clusters'; + + // Load datatables user interface. + ui_print_datatable( + [ + 'id' => $tableId, + 'class' => 'info_table', + 'style' => 'width: 100%', + 'columns' => $columns, + 'column_names' => $column_names, + 'ajax_url' => $model->ajaxController, + 'ajax_data' => ['method' => 'draw'], + 'no_sortable_columns' => [-1], + 'order' => [ + 'field' => 'known_status', + 'direction' => 'asc', + ], + 'search_button_class' => 'sub filter float-right', + 'form' => [ + 'inputs' => [ + [ + 'label' => __('Filter group'), + 'name' => 'id_group', + 'returnAllGroup' => true, + 'privilege' => 'AR', + 'type' => 'select_groups', + 'return' => true, + 'size' => '250px', + ], + [ + 'label' => __('Free search'), + 'type' => 'text', + 'class' => 'mw250px', + 'id' => 'free_search', + 'name' => 'free_search', + ], + ], + ], + ] + ); +} catch (Exception $e) { + echo $e->getMessage(); +} + +if (check_acl($config['id_user'], 0, 'AW')) { + HTML::printForm( + [ + 'form' => [ + 'method' => 'POST', + 'action' => ui_get_full_url($model->url.'&op=new'), + ], + 'inputs' => [ + [ + 'class' => 'w100p', + 'arguments' => [ + 'name' => 'submit', + 'label' => __('New cluster'), + 'type' => 'submit', + 'attributes' => 'class="sub next"', + 'return' => true, + ], + ], + ], + ] + ); +} diff --git a/pandora_console/views/cluster/view.php b/pandora_console/views/cluster/view.php new file mode 100644 index 0000000000..1742930ad1 --- /dev/null +++ b/pandora_console/views/cluster/view.php @@ -0,0 +1,453 @@ + $model->url, + 'label' => __('Cluster list'), + 'selected' => false, +]; + +$bc[] = [ + 'link' => $model->url.'&op=view&id='.$cluster->id(), + 'label' => __('Cluster details'), + 'selected' => true, +]; + + +$html->prepareBreadcrum($bc); + +// Header. +$main_page = ''; +$main_page .= html_print_image( + 'images/list.png', + true, + [ + 'title' => __('Cluster list'), + 'class' => 'invert_filter', + ] +); +$main_page .= ''; + +$edit = ''; +$edit .= html_print_image( + 'images/setup.png', + true, + [ + 'title' => __('Edit this cluster'), + 'class' => 'invert_filter', + ] +); +$edit .= ''; + +ui_print_page_header( + __('Cluster details').' » '.$cluster->name(), + '', + false, + // Help link. + 'cluster_view', + true, + // Buttons. + [ + [ + 'active' => false, + 'text' => $main_page, + ],[ + 'active' => false, + 'text' => $edit, + ], + ], + false, + '', + GENERIC_SIZE_TEXT, + '', + $html->printHeader(true) +); + + +if (empty($error) === false) { + echo $error; +} + +if (empty($message) === false) { + echo $message; +} + +if ($critical === true) { + // Print always go back button. + HTML::printForm($model->getGoBackForm(), false); + return; +} + + +/* + * + * All this block has been retrieved from 'estado_generalagente.php' as + * described in issue #5755. + * + */ + + + +/* + * + * + * CLUSTER AGENT DETAILS. + * + */ + +// Prepare information for view. +$alive_animation = agents_get_status_animation( + agents_get_interval_status($cluster->agent()->toArray(), false) +); + + +$agent_name = ui_print_agent_name( + $cluster->agent()->id_agente(), + true, + 500, + 'font-size: medium;font-weight:bold', + true, + '', + '', + false, + false +); +$in_planned_downtime = db_get_sql( + 'SELECT executed FROM tplanned_downtime + INNER JOIN tplanned_downtime_agents + ON tplanned_downtime.id = tplanned_downtime_agents.id_downtime + WHERE tplanned_downtime_agents.id_agent = '.$cluster->agent()->id_agente().' AND tplanned_downtime.executed = 1' +); + +if ($cluster->agent()->disabled()) { + if ($in_planned_downtime) { + $agent_name = ''.$agent_name.ui_print_help_tip(__('Disabled'), true); + } else { + $agent_name = ''.$agent_name.''.ui_print_help_tip(__('Disabled'), true); + } +} else if ($cluster->agent()->quiet()) { + if ($in_planned_downtime) { + $agent_name = "".$agent_name.' '.html_print_image('images/dot_blue.png', true, ['border' => '0', 'title' => __('Quiet'), 'alt' => '']); + } else { + $agent_name = "".$agent_name.' '.html_print_image('images/dot_blue.png', true, ['border' => '0', 'title' => __('Quiet'), 'alt' => '']).''; + } +} else { + $agent_name = $agent_name; +} + +if ($in_planned_downtime && !$cluster->agent()->disabled() && !$cluster->agent()->quiet()) { + $agent_name .= ' '.ui_print_help_tip( + __('Agent in scheduled downtime'), + true, + 'images/minireloj-16.png' + ).''; +} else if (($in_planned_downtime && !$cluster->agent()->disabled()) + || ($in_planned_downtime && !$cluster->agent()->quiet()) +) { + $agent_name .= ' '.ui_print_help_tip( + __('Agent in scheduled 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']) { + $table_agent_header .= ui_print_group_icon( + $cluster->agent()->id_grupo(), + true, + 'groups_small', + 'padding-right: 6px;' + ); +} + +$table_agent_header .= '
'; + +$status_img = agents_detail_view_status_img( + $cluster->agent()->critical_count(), + $cluster->agent()->warning_count(), + $cluster->agent()->unknown_count(), + $cluster->agent()->total_count(), + $cluster->agent()->notinit_count() +); + +$table_agent_header .= '
'.$status_img.'
'; +$table_agent_header .= '  '; +$table_agent_header .= ''.html_print_image( + 'images/target.png', + true, + [ + 'title' => __('Force cluster status calculation'), + 'alt' => '', + 'class' => 'invert_filter', + + ] +).''; +// Fixed width non interactive charts. +$status_chart_width = 180; +$graph_width = 180; + +$table_agent_graph = '
'; +$table_agent_graph .= graph_agent_status( + $cluster->agent()->id_agente(), + $graph_width, + $graph_width, + true, + false, + false, + true +); +$table_agent_graph .= '
'; + +$table_agent_os = '

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

'; + + + +$addresses = agents_get_addresses($cluster->agent()->id_agente()); +$address = agents_get_address($cluster->agent()->id_agente()); + +foreach ($addresses as $k => $add) { + if ($add == $address) { + unset($addresses[$k]); + } +} + +if (empty($address) === false) { + $table_agent_ip = '

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

'; +} + +$table_agent_description = '

'.html_print_image( + 'images/list.png', + true, + [ + 'title' => __('Description'), + 'class' => 'invert_filter', + ] +); +$table_agent_description .= ''; +$table_agent_description .= empty( + $cluster->description() +) ? ''.__('N/A').'' : $cluster->description(); +$table_agent_description .= '

'; + +$table_agent_count_modules = reporting_tiny_stats( + $cluster->agent()->toArray(), + true, + 'agent', + // Useless. + ':', + true +); + +$table_agent_version = '

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

'; + +/* + * + * MAP + * + */ + +$nodes = $cluster->getNodes(); + +$font_size = 20; +$width = '45%'; +$height = '500'; +$node_radius = 40; + +// Generate map. +$map_manager = new NetworkMap( + [ + 'nodes' => $nodes, + 'no_pandora_node' => 1, + 'pure' => 1, + 'map_options' => [ + 'generation_method' => LAYOUT_SPRING1, + 'font_size' => $font_size, + 'node_radius' => $node_radius, + 'height' => $height, + 'width' => '100%', + 'tooltip' => true, + 'size_image' => 50, + 'z_dash' => 0.5, + 'map_filter' => [ + 'node_sep' => 7, + 'node_radius' => 50, + 'x_offs' => 130, + 'y_offs' => -70, + ], + ], + ] +); + + +/* + * + * EVENTS 24h + * + */ + +$table_events = '
'; +$table_events .= '
'; +$table_events .= html_print_image( + 'images/arrow_down_green.png', + true +); +$table_events .= ''; +$table_events .= __('Events (Last 24h)'); +$table_events .= ''; +$table_events .= '
'; +$table_events .= '
'; +$table_events .= graph_graphic_agentevents( + $cluster->agent()->id_agente(), + 95, + 70, + SECONDS_1DAY, + '', + true, + true, + 500 +); +$table_events .= '
'; +$table_events .= '
'; + +?> +
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+ +
+
+ printMap(); ?> +
+
+
+ +
+ +
+ +
+agent()->id_agente(); +require_once $config['homedir'].'/operation/agentes/estado_monitores.php'; +?> +
+ + + [ + 'action' => $model->url.'&op=view&id='.$cluster->id(), + 'method' => 'POST', + ], + 'inputs' => [ + [ + 'arguments' => [ + 'name' => 'submit', + 'label' => __('Reload'), + 'type' => 'submit', + 'attributes' => 'class="sub cancel"', + 'return' => true, + ], + ], + ], + ], + false +); + +echo '
'; + +// Print always go back button. +HTML::printForm($model->getGoBackForm(), false); diff --git a/pandora_plugins/google_sheets/pandora_googlesheet.py b/pandora_plugins/google_sheets/pandora_googlesheet.py new file mode 100644 index 0000000000..a6e599b429 --- /dev/null +++ b/pandora_plugins/google_sheets/pandora_googlesheet.py @@ -0,0 +1,47 @@ +import gspread +import argparse +from oauth2client.service_account import ServiceAccountCredentials +from pprint import pprint + +__author__ = "Alejandro Sánchez Carrion" +__copyright__ = "Copyright 2022, PandoraFMS" +__maintainer__ = "Operations department" +__status__ = "Production" +__version__= '1.0' + +info = f""" +Pandora FMS Google Sheets +Version = {__version__} + +Manual execution + +python3 pandora_googlesheets.py --cred --row --column + +""" + +parser = argparse.ArgumentParser(description= info, formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--cred', help='') +parser.add_argument('--name', help='') +parser.add_argument('--row', help='',type=int) +parser.add_argument('--column', help='',type=int) + +args = parser.parse_args() + +scope = ["https://spreadsheets.google.com/feeds",'https://www.googleapis.com/auth/spreadsheets',"https://www.googleapis.com/auth/drive.file","https://www.googleapis.com/auth/drive"] +creds = ServiceAccountCredentials.from_json_keyfile_name(args.cred, scope) + +client = gspread.authorize(creds) + +sheet = client.open(args.name).sheet1 # Open the spreadhseet + +data = sheet.get_all_records() # Get a list of all records + +if args.row is not None and args.column==None: + row = sheet.row_values(args.row) # Get a specific row + print(row) +elif args.row ==None and args.column is not None: + col = sheet.col_values(args.column) # Get a specific column + print(col) +elif args.row is not None and args.column is not None: + cell = sheet.cell(args.row,args.column).value # Get the value of a specific cell + print(cell) diff --git a/pandora_server/DEBIAN/control b/pandora_server/DEBIAN/control index e035321041..aa073a3664 100644 --- a/pandora_server/DEBIAN/control +++ b/pandora_server/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-server -Version: 7.0NG.767-221216 +Version: 7.0NG.767-230102 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 0a5393a93f..1b88d0ba34 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.767-221216" +pandora_version="7.0NG.767-230102" package_cpan=0 package_pandora=1 diff --git a/pandora_server/bin/pandora_server b/pandora_server/bin/pandora_server index e6ec7ed6dc..45800de908 100755 --- a/pandora_server/bin/pandora_server +++ b/pandora_server/bin/pandora_server @@ -855,6 +855,7 @@ sub main() { # Main loop my $time_ref = time (); + my $thr_time_ref = 0; my $test_remote_interval = ($Config{'keepalive'}/$Config{'server_threshold'}); my $test_remote = 0; while ($RUN == 1) { @@ -870,6 +871,15 @@ sub main() { # Make sure all server threads are running. die("Server thread crashed.") unless (check_server_threads() == 1); + # Monitor server threads. + if (defined($Config{"self_monitoring"}) + && $Config{"self_monitoring"} == 1 + && !is_metaconsole(\%Config) + && time() - $thr_time_ref > $Config{'self_monitoring_interval'}) { + $thr_time_ref = time(); + pandora_thread_monitoring (\%Config, $DBH, \@Servers); + } + db_do ($DBH, "UPDATE tserver SET status = -1 WHERE UNIX_TIMESTAMP(now())-UNIX_TIMESTAMP(keepalive) > 2*server_keepalive diff --git a/pandora_server/lib/PandoraFMS/Config.pm b/pandora_server/lib/PandoraFMS/Config.pm index 736b1c13c5..272ca1bbf6 100644 --- a/pandora_server/lib/PandoraFMS/Config.pm +++ b/pandora_server/lib/PandoraFMS/Config.pm @@ -46,7 +46,7 @@ our @EXPORT = qw( # version: Defines actual version of Pandora Server for this module only my $pandora_version = "7.0NG.767"; -my $pandora_build = "221216"; +my $pandora_build = "230102"; our $VERSION = $pandora_version." ".$pandora_build; # Setup hash diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm index e4bf6eeaae..515c930769 100644 --- a/pandora_server/lib/PandoraFMS/Core.pm +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -98,6 +98,8 @@ Exported Functions: =item * C +=item * C + =item * C =back @@ -258,6 +260,7 @@ our @EXPORT = qw( pandora_group_statistics pandora_server_statistics pandora_self_monitoring + pandora_thread_monitoring pandora_sample_agent pandora_process_policy_queue pandora_sync_agents_integria @@ -280,6 +283,9 @@ our @EXPORT = qw( notification_set_targets notification_get_users notification_get_groups + exec_cluster_aa_module + exec_cluster_ap_module + exec_cluster_status_module ); # Some global variables @@ -6064,6 +6070,71 @@ sub pandora_self_monitoring ($$) { print XMLFILE $xml_output; close (XMLFILE); } + +########################################################################## +=head2 C<< pandora_thread_monitoring (I<$pa_config>, I<$dbh>, I<$servers>) >> + +Generate stats for Pandora FMS threads. + +=cut +########################################################################## + +sub pandora_thread_monitoring ($$$) { + my ($pa_config, $dbh, $servers) = @_; + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime()); + + my $xml_output = ""; + + $xml_output = "{'version'} . "' agent_name='".$pa_config->{'servername'} . "' agent_alias='".$pa_config->{'servername'} . "' interval='".$pa_config->{"self_monitoring_interval"}."' timestamp='".$timestamp."' >"; + foreach my $server (@{$servers}) { + while (my ($tid, $stats) = each(%{$server->getProducerStats()})) { + $xml_output .=" "; + $xml_output .=" " . uc($ServerTypes[$server->{'_server_type'}]) . " Producer Status"; + $xml_output .=" generic_proc"; + $xml_output .=" System"; + $xml_output .=" " . (time() - $stats->{'tstamp'} < 2 * $pa_config->{"self_monitoring_interval"} ? 1 : 0) . ""; + $xml_output .=" "; + + $xml_output .=" "; + $xml_output .=" " . uc($ServerTypes[$server->{'_server_type'}]) . " Producer Processing Rate"; + $xml_output .=" generic_data"; + $xml_output .=" Performance"; + $xml_output .=" " . $stats->{'rate'} . ""; + $xml_output .=" tasks/second"; + $xml_output .=" "; + } + + my $idx = 0; + my $consumer_stats = $server->getConsumerStats(); + foreach my $tid (sort(keys(%{$consumer_stats}))) { + my $stats = $consumer_stats->{$tid}; + + $idx += 1; + $xml_output .=" "; + $xml_output .=" " . uc($ServerTypes[$server->{'_server_type'}]) . " Consumer #$idx Status"; + $xml_output .=" generic_proc"; + $xml_output .=" System"; + $xml_output .=" " . (time() - $stats->{'tstamp'} < 2 * $pa_config->{"self_monitoring_interval"} ? 1 : 0) . ""; + $xml_output .=" "; + + $xml_output .=" "; + $xml_output .=" " . uc($ServerTypes[$server->{'_server_type'}]) . " Consumer #$idx Processing Rate"; + $xml_output .=" generic_data"; + $xml_output .=" Performance"; + $xml_output .=" " . $stats->{'rate'} . ""; + $xml_output .=" tasks/second"; + $xml_output .=" "; + } + } + $xml_output .= ""; + + my $filename = $pa_config->{"incomingdir"}."/".$pa_config->{'servername'}.".threads.".$utimestamp.".data"; + open (XMLFILE, ">", $filename) or die "[FATAL] Could not write to the thread monitoring XML file '$filename'"; + print XMLFILE $xml_output; + close (XMLFILE); +} + ########################################################################## =head2 C<< xml_module_template (I<$module_name>, I<$module_type>, I<$module_data>) >> @@ -7275,6 +7346,175 @@ sub pandora_snmptrapd_still_working ($$) { } } + +################################################################################ +# Execute a cluster status module. +################################################################################ +sub exec_cluster_status_module ($$$$) { + my ($pa_config, $module, $server_id, $dbh) = @_; + + # Execute cluster modules. + my @modules = get_db_rows($dbh, + 'SELECT * + FROM tagente_modulo + WHERE tagente_modulo.id_agente = ? + AND tagente_modulo.disabled != 1 + AND tagente_modulo.tcp_port = 1', + $module->{'id_agente'}); + foreach my $agent_module (@modules) { + # Cluster active-active module. + if ($agent_module->{'prediction_module'} == 6) { + logger ($pa_config, "Executing cluster active-active critical module " . $agent_module->{'nombre'}, 10); + exec_cluster_aa_module($pa_config, $agent_module, $server_id, $dbh); + } + # Cluster active-passive module. + elsif ($agent_module->{'prediction_module'} == 7) { + logger ($pa_config, "Executing cluster active-passive critical module " . $agent_module->{'nombre'}, 10); + exec_cluster_ap_module($pa_config, $agent_module, $server_id, $dbh); + } + } + + # Get the status of cluster modules. + my $data = -1; + @modules = get_db_rows($dbh, + 'SELECT tagente_modulo.id_agente_modulo, tagente_estado.estado + FROM tagente_estado, tagente_modulo + WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND tagente_modulo.disabled != 1 + AND tagente_modulo.tcp_port = 1 + AND tagente_modulo.id_agente = ?', + $module->{'id_agente'}); + foreach my $cluster_module (@modules) { + next if ($cluster_module->{'id_agente_modulo'} == $module->{'id_agente_modulo'}); # Skip the current module. + + # Critical. + if ($cluster_module->{'estado'} == MODULE_NORMAL && $data < 0) { + $data = 0; + } elsif ($cluster_module->{'estado'} == MODULE_WARNING && $data < 1) { + $data = 1; + } elsif (($cluster_module->{'estado'} == MODULE_CRITICAL || $cluster_module->{'estado'} == MODULE_UNKNOWN) && $data < 2) { + $data = 2; + } + } + + # No data. + if ($data < 0) { + pandora_update_module_on_error ($pa_config, $module, $dbh); + return; + } + + # Get the current timestamp. + my $utimestamp = time (); + my $timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + # Update the module. + pandora_process_module($pa_config, {'data' => $data}, '', $module, '', $timestamp, $utimestamp, $server_id, $dbh); + + pandora_update_agent($pa_config, $timestamp, $module->{'id_agente'}, undef, undef, -1, $dbh); +} + +################################################################################ +# Execute a cluster active-active module. +################################################################################ +sub exec_cluster_aa_module ($$$$) { + my ($pa_config, $module, $server_id, $dbh) = @_; + + # Get the cluster item. + my $item = get_db_single_row($dbh, 'SELECT * FROM tcluster_item WHERE id=?', $module->{'custom_integer_2'}); + if (!defined($item)) { + pandora_update_module_on_error ($pa_config, $module, $dbh); + return; + } + + # Get cluster agents and compute the item status. + my ($not_normal, $total) = (0, 0); + my @agents = get_db_rows($dbh, 'SELECT id_agent FROM tcluster_agent WHERE id_cluster = ?', $module->{'custom_integer_1'}); + foreach my $agent_id (@agents) { + my $item_status = get_db_value($dbh, + 'SELECT estado + FROM tagente_estado, tagente_modulo + WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND tagente_modulo.id_agente = ? + AND tagente_modulo.nombre = ?', + $agent_id->{'id_agent'}, $item->{'name'}); + + # Count modules in a status other than normal. + if (!defined($item_status) || $item_status != MODULE_NORMAL) { + $not_normal += 1; + } + + $total += 1; + } + + # No data. + if ($total < 1) { + pandora_update_module_on_error ($pa_config, $module, $dbh); + return; + } + + # Convert $not_normal to a percentage. + $not_normal = 100 * $not_normal / $total; + + # Get the current timestamp. + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + # Update module + pandora_process_module ($pa_config, {'data' => $not_normal}, '', $module, '', $timestamp, $utimestamp, $server_id, $dbh); + + pandora_update_agent ($pa_config, $timestamp, $module->{'id_agente'}, undef, undef, -1, $dbh); +} + +################################################################################ +# Execute a cluster active-pasive module. +################################################################################ +sub exec_cluster_ap_module ($$$$) { + my ($pa_config, $module, $server_id, $dbh) = @_; + + # Get the cluster item. + my $item = get_db_single_row($dbh, 'SELECT * FROM tcluster_item WHERE id=?', $module->{'custom_integer_2'}); + if (!defined($item)) { + pandora_update_module_on_error ($pa_config, $module, $dbh); + return; + } + + # Get cluster agents and compute the item status. + my $data = undef; + my $utimestamp = 0; + my @agents = get_db_rows($dbh, 'SELECT id_agent FROM tcluster_agent WHERE id_cluster = ?', $module->{'custom_integer_1'}); + foreach my $agent_id (@agents) { + my $status = get_db_single_row($dbh, + 'SELECT datos, estado, utimestamp + FROM tagente_estado, tagente_modulo + WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND tagente_modulo.id_agente = ? + AND tagente_modulo.nombre = ?', + $agent_id->{'id_agent'}, $item->{'name'}); + + # Get the most recent data. + if (defined($status) && $status->{'estado'} != MODULE_UNKNOWN && $status->{'utimestamp'} > $utimestamp) { + $utimestamp = $status->{'utimestamp'}; + $data = $status->{'datos'}; + } + } + + # No data. + if ($utimestamp == 0) { + pandora_update_module_on_error ($pa_config, $module, $dbh); + return; + } + + # Get the current timestamp. + $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + # Update the module. + pandora_process_module ($pa_config, {'data' => $data }, '', $module, '', $timestamp, $utimestamp, $server_id, $dbh); + + # Update the agent. + pandora_update_agent ($pa_config, $timestamp, $module->{'id_agente'}, undef, undef, -1, $dbh); +} + # End of function declaration # End of defined Code diff --git a/pandora_server/lib/PandoraFMS/PluginTools.pm b/pandora_server/lib/PandoraFMS/PluginTools.pm index 8d86340a1f..a0302b6d19 100644 --- a/pandora_server/lib/PandoraFMS/PluginTools.pm +++ b/pandora_server/lib/PandoraFMS/PluginTools.pm @@ -34,7 +34,7 @@ our @ISA = qw(Exporter); # version: Defines actual version of Pandora Server for this module only my $pandora_version = "7.0NG.767"; -my $pandora_build = "221216"; +my $pandora_build = "230102"; our $VERSION = $pandora_version." ".$pandora_build; our %EXPORT_TAGS = ( 'all' => [ qw() ] ); diff --git a/pandora_server/lib/PandoraFMS/PredictionServer.pm b/pandora_server/lib/PandoraFMS/PredictionServer.pm index 1fd05730cd..1225ab5b19 100644 --- a/pandora_server/lib/PandoraFMS/PredictionServer.pm +++ b/pandora_server/lib/PandoraFMS/PredictionServer.pm @@ -207,21 +207,21 @@ sub exec_prediction_module ($$$$) { # Cluster status module. if ($agent_module->{'prediction_module'} == 5) { logger ($pa_config, "Executing cluster status module " . $agent_module->{'nombre'}, 10); - enterprise_hook ('exec_cluster_status_module', [$pa_config, $agent_module, $server_id, $dbh]); + exec_cluster_status_module($pa_config, $agent_module, $server_id, $dbh); return; } # Cluster active-active module. if ($agent_module->{'prediction_module'} == 6) { logger ($pa_config, "Executing cluster active-active module " . $agent_module->{'nombre'}, 10); - enterprise_hook ('exec_cluster_aa_module', [$pa_config, $agent_module, $server_id, $dbh]); + exec_cluster_aa_module($pa_config, $agent_module, $server_id, $dbh); return; } # Cluster active-passive module. if ($agent_module->{'prediction_module'} == 7) { logger ($pa_config, "Executing cluster active-passive module " . $agent_module->{'nombre'}, 10); - enterprise_hook ('exec_cluster_ap_module', [$pa_config, $agent_module, $server_id, $dbh]); + exec_cluster_ap_module($pa_config, $agent_module, $server_id, $dbh); return; } diff --git a/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm b/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm index 11026ae0b2..770bc06522 100644 --- a/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm +++ b/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm @@ -89,6 +89,15 @@ sub run ($$$$$) { # Launch consumer threads for (1..$self->getNumThreads ()) { + + # Enable consumer stats + my $consumer_stats = shared_clone({ + 'tstamp' => time(), + 'rate' => 0, + 'rate_count' => 0, + 'rate_tstamp' => time() + }); + my $thr = threads->create ({'exit' => 'thread_only'}, sub { my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_; @@ -98,13 +107,29 @@ sub run ($$$$$) { $sem->up(); exit 0; }; + + # Make consumer stats reachable from the thread + $self->{'_consumer_stats'}->{threads->tid()} = $consumer_stats; + PandoraFMS::ProducerConsumerServer::data_consumer->(@_); }, $self, $task_queue, $pending_tasks, $sem, $task_sem ); return unless defined ($thr); $self->addThread ($thr->tid ()); + + # Make consumer stats reachable from the main program + $self->{'_consumer_stats'}->{$thr->tid()} = $consumer_stats; } + + # Enable producer stats + my $producer_stats = shared_clone({ + 'tstamp' => time(), + 'rate' => 0, + 'rate_count' => 0, + 'rate_tstamp' => time() + }); + # Launch producer thread my $thr = threads->create ({'exit' => 'thread_only'}, sub { @@ -115,11 +140,18 @@ sub run ($$$$$) { $sem->up(); exit 0; }; + + # Make producer stats reachable from the thread + $self->{'_producer_stats'}->{threads->tid()} = $producer_stats; + PandoraFMS::ProducerConsumerServer::data_producer->(@_); }, $self, $task_queue, $pending_tasks, $sem, $task_sem ); return unless defined ($thr); $self->addThread ($thr->tid ()); + + # Make producer stats reachable from the main program + $self->{'_producer_stats'}->{$thr->tid()} = $producer_stats; } ############################################################################### @@ -130,46 +162,50 @@ sub data_producer ($$$$$) { my $pa_config = $self->getConfig (); my $dbh; - eval { - # Connect to the DB - $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'}, - $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); - $self->setDBH ($dbh); + while ($RUN == 1) { + eval { + # Connect to the DB + $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'}, + $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); + $self->setDBH ($dbh); - while ($RUN == 1) { + while ($RUN == 1) { - # Get pending tasks - $self->logThread('[PRODUCER] Queuing tasks.'); - my @tasks = &{$self->{'_producer'}}($self); - - foreach my $task (@tasks) { - $sem->down; + # Get pending tasks + $self->logThread('[PRODUCER] Queuing tasks.'); + my @tasks = &{$self->{'_producer'}}($self); - last if ($RUN == 0); - if (defined $pending_tasks->{$task}) { - $sem->up; - next; - } + foreach my $task (@tasks) { + $sem->down; - # Queue task and signal consumers - $pending_tasks->{$task} = 0; - push (@{$task_queue}, $task); - $task_sem->up; - - $sem->up; + last if ($RUN == 0); + if (defined $pending_tasks->{$task}) { + $sem->up; + next; + } + + # Queue task and signal consumers + $pending_tasks->{$task} = 0; + push (@{$task_queue}, $task); + $task_sem->up; + + $sem->up; + } + + last if ($RUN == 0); + + # Update queue size and thread stats + $self->setQueueSize (scalar @{$task_queue}); + $self->updateProducerStats(scalar(@tasks)); + + threads->yield; + usleep (int(1e6 * $self->getPeriod())); } - - last if ($RUN == 0); - # Update queue size for statistics - $self->setQueueSize (scalar @{$task_queue}); - - threads->yield; - usleep (int(1e6 * $self->getPeriod())); + }; + + if ($@) { + print STDERR $@; } - }; - - if ($@) { - $self->setErrStr ($@); } $task_sem->up($self->getNumThreads ()); @@ -185,40 +221,51 @@ sub data_consumer ($$$$$) { my $pa_config = $self->getConfig (); my $dbh; - eval { - # Connect to the DB - $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'}, - $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); - $self->setDBH ($dbh); + my $sem_timeout = $pa_config->{'self_monitoring_interval'} > 0 ? + $pa_config->{'self_monitoring_interval'} : + 300; + while ($RUN == 1) { + eval { + # Connect to the DB + $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'}, + $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); + $self->setDBH ($dbh); - while ($RUN == 1) { - # Wait for data - $self->logThread('[CONSUMER] Waiting for data.'); - $task_sem->down; + while ($RUN == 1) { + # Wait for data + $self->logThread('[CONSUMER] Waiting for data.'); + while (!$task_sem->down_timed($sem_timeout)) { + $self->updateConsumerStats(0); + } - $sem->down; - last if ($RUN == 0); - my $task = shift (@{$task_queue}); - $sem->up; + last if ($RUN == 0); - # The consumer was waiting for data when the producer exited - last if ($RUN == 0); - - # Execute task - $self->logThread("[CONSUMER] Executing task: $task"); - &{$self->{'_consumer'}}($self, $task); + $sem->down; + my $task = shift (@{$task_queue}); + $sem->up; - # Update task status - $sem->down; - delete ($pending_tasks->{$task}); - $sem->up; + # The consumer was waiting for data when the producer exited + last if ($RUN == 0); + + # Execute task + $self->logThread("[CONSUMER] Executing task: $task"); + &{$self->{'_consumer'}}($self, $task); - threads->yield; + # Update thread stats + $self->updateConsumerStats(1); + + # Update task status + $sem->down; + delete ($pending_tasks->{$task}); + $sem->up; + + threads->yield; + } + }; + + if ($@) { + print STDERR $@; } - }; - - if ($@) { - $self->setErrStr ($@); } db_disconnect ($dbh); diff --git a/pandora_server/lib/PandoraFMS/Server.pm b/pandora_server/lib/PandoraFMS/Server.pm index 41a07b1418..9009c2bbf3 100644 --- a/pandora_server/lib/PandoraFMS/Server.pm +++ b/pandora_server/lib/PandoraFMS/Server.pm @@ -47,7 +47,9 @@ sub new ($$$;$) { _threads => [], _queue_size => 0, _errstr => '', - _period => 0 + _period => 0, + _producer_stats => {}, + _consumer_stats => {}, }; # Share variables that may be set from different threads @@ -176,6 +178,24 @@ sub getServerType ($) { return $self->{'_server_type'}; } +######################################################################################## +# Return consumer stats. +######################################################################################## +sub getConsumerStats ($) { + my $self = shift; + + return $self->{'_consumer_stats'}; +} + +######################################################################################## +# Return producer stats. +######################################################################################## +sub getProducerStats ($) { + my $self = shift; + + return $self->{'_producer_stats'}; +} + ######################################################################################## # Set error string. ######################################################################################## @@ -337,6 +357,52 @@ sub stop ($) { } } +######################################################################################## +# Update stats for the current thread. +######################################################################################## +sub updateStats ($$$) { + my ($self, $dest, $inc) = @_; + my $tid = threads->tid(); + my $curr_time = time(); + + # Stats disabled for this thread. + if (!defined($dest->{$tid})) { + return; + } + + # Update the timestamp and count. + $dest->{$tid}->{'tstamp'} = time(); + $dest->{$tid}->{'rate_count'} += $inc; + + # Compute the processing rate. + my $elapsed = $curr_time - $dest->{$tid}->{'rate_tstamp'}; + if ($elapsed >= $self->{'_pa_config'}->{'self_monitoring_interval'}) { + $dest->{$tid}->{'rate'} = $dest->{$tid}->{'rate_count'} / $elapsed; + $dest->{$tid}->{'rate_count'} = 0; + $dest->{$tid}->{'rate_tstamp'} = $curr_time; + return; + } +} + + +######################################################################################## +# Update producer stats. +######################################################################################## +sub updateProducerStats ($$) { + my ($self, $queued_tasks) = @_; + + $self->updateStats($self->{'_producer_stats'}, $queued_tasks); +} + +######################################################################################## +# Update consumer stats. +######################################################################################## +sub updateConsumerStats ($$) { + my ($self, $processed_tasks) = @_; + + $self->updateStats($self->{'_consumer_stats'}, $processed_tasks); +} + # End of function declaration # End of defined Code diff --git a/pandora_server/pandora_server.redhat.spec b/pandora_server/pandora_server.redhat.spec index 57e384625a..2512055e9e 100644 --- a/pandora_server/pandora_server.redhat.spec +++ b/pandora_server/pandora_server.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_server %define version 7.0NG.767 -%define release 221216 +%define release 230102 Summary: Pandora FMS Server Name: %{name} diff --git a/pandora_server/pandora_server.spec b/pandora_server/pandora_server.spec index ccd8703543..8df2703f2e 100644 --- a/pandora_server/pandora_server.spec +++ b/pandora_server/pandora_server.spec @@ -3,7 +3,7 @@ # %define name pandorafms_server %define version 7.0NG.767 -%define release 221216 +%define release 230102 Summary: Pandora FMS Server Name: %{name} diff --git a/pandora_server/pandora_server_installer b/pandora_server/pandora_server_installer index ce6a4e858e..a4f792ce49 100755 --- a/pandora_server/pandora_server_installer +++ b/pandora_server/pandora_server_installer @@ -9,7 +9,7 @@ # ********************************************************************** PI_VERSION="7.0NG.767" -PI_BUILD="221216" +PI_BUILD="230102" MODE=$1 if [ $# -gt 1 ]; then diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl index 96898aa6fa..ac7ac90011 100755 --- a/pandora_server/util/pandora_db.pl +++ b/pandora_server/util/pandora_db.pl @@ -35,7 +35,7 @@ use PandoraFMS::Config; use PandoraFMS::DB; # version: define current version -my $version = "7.0NG.767 Build 221216"; +my $version = "7.0NG.767 Build 230102"; # Pandora server configuration my %conf; diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl index 42a119a072..202dfa081a 100755 --- 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.767 Build 221216"; +my $version = "7.0NG.767 Build 230102"; # save program name for logging my $progname = basename($0); @@ -1296,7 +1296,6 @@ sub help_screen_line($$$){ sub check_values($) { my ($check) = @_; - use experimental 'smartmatch'; my $arg_cont = 2; my $cont = 0; @@ -1316,7 +1315,7 @@ sub check_values($) { # Check values. if (defined($check->[$cont]->{'values'})) { - if (!($args[$arg_cont] ~~ $check->[$cont]->{'values'})) { + if (!(is_in_array($check->[$cont]->{'values'}, $args[$arg_cont]))) { print "\nError: value `$args[$arg_cont]` is not valid for $check->[$cont]->{'name'}\n"; print "\tAvailable options: \t$check->[$cont]->{'values'}->[0]"; if (defined($check->[$cont]->{'text_extra'}->[0])) { diff --git a/tests/Dockerfile b/tests/Dockerfile index 4a8a114200..f414f3fe03 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -92,6 +92,7 @@ RUN dnf install -y vim wget bzip2 curl && \ libstdc++ \ gettext \ wmic \ + chromium \ cpanminus && \ cpanm Geo::IP && \ mkdir -p /opt/phantomjs/bin && cd /opt/phantomjs/bin && \ diff --git a/visual_console_client/src/Item.ts b/visual_console_client/src/Item.ts index 3d32e8d61e..d8a207d810 100644 --- a/visual_console_client/src/Item.ts +++ b/visual_console_client/src/Item.ts @@ -815,15 +815,15 @@ abstract class VisualConsoleItem { prevProps: Props | null = null, prevMeta: ItemMeta | null = null ): void { - this.updateDomElement(this.childElementRef); - // Move box. if (!prevProps || this.positionChanged(prevProps, this.props)) { this.moveElement(this.props.x, this.props.y); + this.updateDomElement(this.childElementRef); } // Resize box. if (!prevProps || this.sizeChanged(prevProps, this.props)) { this.resizeElement(this.props.width, this.props.height); + this.updateDomElement(this.childElementRef); } // Change label. const oldLabelHtml = this.labelElementRef.innerHTML; @@ -923,6 +923,8 @@ abstract class VisualConsoleItem { } } } + + this.updateDomElement(this.childElementRef); } if (!prevMeta || prevMeta.isSelected !== this.meta.isSelected) { if (this.meta.isSelected) { diff --git a/visual_console_client/src/items/BarsGraph.ts b/visual_console_client/src/items/BarsGraph.ts index 2057b6254e..fa3e96303d 100644 --- a/visual_console_client/src/items/BarsGraph.ts +++ b/visual_console_client/src/items/BarsGraph.ts @@ -72,11 +72,8 @@ export function barsGraphPropsDecoder(data: AnyObject): BarsGraphProps | never { export default class BarsGraph extends Item { protected createDomElement(): HTMLElement { const element = document.createElement("div"); + element.innerHTML = this.props.html; element.className = "bars-graph"; - element.style.backgroundImage = `url(${this.props.html})`; - element.style.backgroundRepeat = "no-repeat"; - element.style.backgroundSize = `${this.props.width}px ${this.props.height}px`; - if ( this.props.agentDisabled === true || this.props.moduleDisabled === true @@ -84,13 +81,23 @@ export default class BarsGraph extends Item { element.style.opacity = "0.2"; } + // 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.style.backgroundImage = `url(${this.props.html})`; - element.style.backgroundRepeat = "no-repeat"; - element.style.backgroundSize = `${this.props.width}px ${this.props.height}px`; + element.innerHTML = this.props.html; if ( this.props.agentDisabled === true || @@ -98,5 +105,13 @@ export default class BarsGraph extends Item { ) { element.style.opacity = "0.2"; } + + // 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) { + eval(scripts[i].innerHTML.trim()); + } + } } } diff --git a/visual_console_client/src/items/DonutGraph.ts b/visual_console_client/src/items/DonutGraph.ts index 6b7ac6b87c..874e0f2ead 100644 --- a/visual_console_client/src/items/DonutGraph.ts +++ b/visual_console_client/src/items/DonutGraph.ts @@ -43,7 +43,7 @@ export function donutGraphPropsDecoder( ? data.html : decodeBase64(data.encodedHtml), legendBackgroundColor: stringIsEmpty(data.legendBackgroundColor) - ? "#000000" + ? "#ffffff" : data.legendBackgroundColor, ...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. @@ -55,6 +55,7 @@ export default class DonutGraph extends Item { const element = document.createElement("div"); element.className = "donut-graph"; element.innerHTML = this.props.html; + element.style.backgroundColor = this.props.legendBackgroundColor; if ( this.props.agentDisabled === true || @@ -66,9 +67,13 @@ export default class DonutGraph extends Item { // 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); + if (scripts[i].src.length === 0) { + setTimeout(() => { + try { + eval(scripts[i].innerHTML.trim()); + } catch (ignored) {} // eslint-disable-line no-empty + }, 0); + } } return element; @@ -76,11 +81,17 @@ export default class DonutGraph extends Item { protected updateDomElement(element: HTMLElement): void { element.innerHTML = this.props.html; + element.style.backgroundColor = this.props.legendBackgroundColor; + + if ( + this.props.agentDisabled === true || + this.props.moduleDisabled === true + ) { + element.style.opacity = "0.2"; + } // 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"); + const scripts = element.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/ModuleGraph.ts b/visual_console_client/src/items/ModuleGraph.ts index 0de3d2fd68..e7f4c2c293 100644 --- a/visual_console_client/src/items/ModuleGraph.ts +++ b/visual_console_client/src/items/ModuleGraph.ts @@ -98,18 +98,6 @@ export default class ModuleGraph extends Item { element.style.opacity = "0.2"; } - // 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++) { @@ -128,16 +116,11 @@ export default class ModuleGraph extends Item { 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(); + if ( + this.props.agentDisabled === true || + this.props.moduleDisabled === true + ) { + element.style.opacity = "0.2"; } // Hack to execute the JS after the HTML is added to the DOM.