From 53417ff97fe4a0e2e2fb0910b7636b40fb6ee362 Mon Sep 17 00:00:00 2001
From: odain
Date: Mon, 6 Oct 2025 15:55:33 +0200
Subject: [PATCH 01/15] poc php-cs-fixer
---
.gitignore | 1 +
.php-cs-fixer.dist.php | 40 +
tools/composer.json | 6 +
tools/composer.lock | 2655 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 2702 insertions(+)
create mode 100644 .php-cs-fixer.dist.php
create mode 100644 tools/composer.json
create mode 100644 tools/composer.lock
diff --git a/.gitignore b/.gitignore
index df991db82c..88d6c976d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
tests/*/vendor/*
+tools/vendor
# all conf but listing prevention
/conf/**
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000000..50bc9768b4
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,40 @@
+exclude('oql')
+ //->in(__DIR__ . '/addons')
+// ->in(__DIR__ . '/application')
+ //->in(__DIR__ . '/core')
+// ->in(__DIR__ . '/datamodels')
+// ->in(__DIR__ . '/dictionaries')
+// ->in(__DIR__ . '/pages')
+// ->in(__DIR__ . '/portal')
+// ->in(__DIR__ . '/setup')
+// ->in(__DIR__ . '/sources')
+// ->in(__DIR__ . '/synchro')
+// ->in(__DIR__ . '/tests')
+ ->in(__DIR__ . '/webservices')
+ ;
+
+$config = new PhpCsFixer\Config();
+return $config->setRiskyAllowed(true)
+ ->setRules([
+ '@PSR12' => true,
+ // We use PSR12 with consistent brace placement.
+ 'curly_braces_position' => [
+ 'functions_opening_brace' => 'same_line',
+ 'classes_opening_brace' => 'same_line',
+ ],
+ // declare(strict_types=1) on the same line as false,
+ 'declare_strict_types' => true,
+ // Keep argument formatting for now.
+ 'method_argument_space' => ['on_multiline' => 'ignore'],
+ 'phpdoc_align' => ['align' => 'left'],
+ 'phpdoc_trim' => true,
+ 'no_empty_phpdoc' => true,
+ 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true],
+ 'no_extra_blank_lines' => true,
+ ])
+ ->setFinder($finder)
+;
\ No newline at end of file
diff --git a/tools/composer.json b/tools/composer.json
new file mode 100644
index 0000000000..b990d13f5b
--- /dev/null
+++ b/tools/composer.json
@@ -0,0 +1,6 @@
+{
+ "require": {
+ "friendsofphp/php-cs-fixer": "^3.10",
+ "phpstan/phpstan": "^2.0"
+ }
+}
diff --git a/tools/composer.lock b/tools/composer.lock
new file mode 100644
index 0000000000..aeb87a424c
--- /dev/null
+++ b/tools/composer.lock
@@ -0,0 +1,2655 @@
+{
+ "_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": "d76c2ddfcd673d9164eff7c412640c4e",
+ "packages": [
+ {
+ "name": "clue/ndjson-react",
+ "version": "v1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/clue/reactphp-ndjson.git",
+ "reference": "392dc165fce93b5bb5c637b67e59619223c931b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0",
+ "reference": "392dc165fce93b5bb5c637b67e59619223c931b0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/stream": "^1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35",
+ "react/event-loop": "^1.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Clue\\React\\NDJson\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ }
+ ],
+ "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.",
+ "homepage": "https://github.com/clue/reactphp-ndjson",
+ "keywords": [
+ "NDJSON",
+ "json",
+ "jsonlines",
+ "newline",
+ "reactphp",
+ "streaming"
+ ],
+ "support": {
+ "issues": "https://github.com/clue/reactphp-ndjson/issues",
+ "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://clue.engineering/support",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-23T10:58:28+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "3.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<1.11.10"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.12 || ^2",
+ "phpstan/phpstan-strict-rules": "^1 || ^2",
+ "phpunit/phpunit": "^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-main": "3.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/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": "2024-11-12T16:29:46+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.11",
+ "symfony/phpunit-bridge": "^3 || ^7"
+ },
+ "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": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-20T19:15:30+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "3.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "shasum": ""
+ },
+ "require": {
+ "composer/pcre": "^1 || ^2 || ^3",
+ "php": "^7.2.5 || ^8.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
+ },
+ "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": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/3.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": "2024-05-06T16:37:16+00:00"
+ },
+ {
+ "name": "evenement/evenement",
+ "version": "v3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9 || ^6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "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/v3.0.2"
+ },
+ "time": "2023-08-08T05:53:35+00:00"
+ },
+ {
+ "name": "fidry/cpu-core-counter",
+ "version": "1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theofidry/cpu-core-counter.git",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678",
+ "reference": "db9508f7b1474469d9d3c53b86f817e344732678",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "fidry/makefile": "^0.2.0",
+ "fidry/php-cs-fixer-config": "^1.1.2",
+ "phpstan/extension-installer": "^1.2.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-deprecation-rules": "^2.0.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^8.5.31 || ^9.5.26",
+ "webmozarts/strict-phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Fidry\\CpuCoreCounter\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Théo FIDRY",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Tiny utility to get the number of CPU cores.",
+ "keywords": [
+ "CPU",
+ "core"
+ ],
+ "support": {
+ "issues": "https://github.com/theofidry/cpu-core-counter/issues",
+ "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theofidry",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-14T07:29:31+00:00"
+ },
+ {
+ "name": "friendsofphp/php-cs-fixer",
+ "version": "v3.87.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
+ "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
+ "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
+ "shasum": ""
+ },
+ "require": {
+ "clue/ndjson-react": "^1.3",
+ "composer/semver": "^3.4",
+ "composer/xdebug-handler": "^3.0.5",
+ "ext-filter": "*",
+ "ext-hash": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "fidry/cpu-core-counter": "^1.3",
+ "php": "^7.4 || ^8.0",
+ "react/child-process": "^0.6.6",
+ "react/event-loop": "^1.5",
+ "react/promise": "^3.3",
+ "react/socket": "^1.16",
+ "react/stream": "^1.4",
+ "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
+ "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0",
+ "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0",
+ "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0",
+ "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0",
+ "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0",
+ "symfony/polyfill-mbstring": "^1.33",
+ "symfony/polyfill-php80": "^1.33",
+ "symfony/polyfill-php81": "^1.33",
+ "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
+ "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
+ },
+ "require-dev": {
+ "facile-it/paraunit": "^1.3.1 || ^2.7",
+ "infection/infection": "^0.29.14",
+ "justinrainbow/json-schema": "^6.5",
+ "keradus/cli-executor": "^2.2",
+ "mikey179/vfsstream": "^1.6.12",
+ "php-coveralls/php-coveralls": "^2.8",
+ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
+ "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
+ "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
+ "symfony/polyfill-php84": "^1.33",
+ "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
+ "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
+ },
+ "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/"
+ },
+ "exclude-from-classmap": [
+ "src/Fixer/Internal/*"
+ ]
+ },
+ "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",
+ "keywords": [
+ "Static code analysis",
+ "fixer",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
+ "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/keradus",
+ "type": "github"
+ }
+ ],
+ "time": "2025-09-10T09:51:40+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.1.27",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "25da374959afa391992792691093550b3098ef1e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/25da374959afa391992792691093550b3098ef1e",
+ "reference": "25da374959afa391992792691093550b3098ef1e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2025-09-17T09:55:13+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "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/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.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/3.0.2"
+ },
+ "time": "2024-09-11T13:17:53+00:00"
+ },
+ {
+ "name": "react/cache",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/cache.git",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
+ "reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "^3.0 || ^2.0 || ^1.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, Promise-based cache interface for ReactPHP",
+ "keywords": [
+ "cache",
+ "caching",
+ "promise",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/cache/issues",
+ "source": "https://github.com/reactphp/cache/tree/v1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2022-11-30T15:59:55+00:00"
+ },
+ {
+ "name": "react/child-process",
+ "version": "v0.6.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/child-process.git",
+ "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159",
+ "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/event-loop": "^1.2",
+ "react/stream": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/socket": "^1.16",
+ "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\ChildProcess\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven library for executing child processes with ReactPHP.",
+ "keywords": [
+ "event-driven",
+ "process",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/child-process/issues",
+ "source": "https://github.com/reactphp/child-process/tree/v0.6.6"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-01-01T16:37:48+00:00"
+ },
+ {
+ "name": "react/dns",
+ "version": "v1.13.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/dns.git",
+ "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+ "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "^1.0 || ^0.6 || ^0.5",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3 || ^2",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Dns\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": [
+ "async",
+ "dns",
+ "dns-resolver",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/dns/issues",
+ "source": "https://github.com/reactphp/dns/tree/v1.13.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-06-13T14:18:03+00:00"
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+ "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "suggest": {
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/event-loop/issues",
+ "source": "https://github.com/reactphp/event-loop/tree/v1.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2023-11-13T13:48:05+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v3.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a",
+ "reference": "23444f53a813a3296c1368bb104793ce8d88f04a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "1.12.28 || 1.4.10",
+ "phpunit/phpunit": "^9.6 || ^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/promise/issues",
+ "source": "https://github.com/reactphp/promise/tree/v3.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2025-08-19T18:57:03+00:00"
+ },
+ {
+ "name": "react/socket",
+ "version": "v1.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/socket.git",
+ "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
+ "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/dns": "^1.13",
+ "react/event-loop": "^1.2",
+ "react/promise": "^3.2 || ^2.6 || ^1.2.1",
+ "react/stream": "^1.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+ "react/async": "^4.3 || ^3.3 || ^2",
+ "react/promise-stream": "^1.4",
+ "react/promise-timer": "^1.11"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": [
+ "Connection",
+ "Socket",
+ "async",
+ "reactphp",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/socket/issues",
+ "source": "https://github.com/reactphp/socket/tree/v1.16.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-07-26T10:38:09+00:00"
+ },
+ {
+ "name": "react/stream",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.2"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/stream/issues",
+ "source": "https://github.com/reactphp/stream/tree/v1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-06-11T12:45:25+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.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",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:53:05+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v7.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
+ "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^7.2"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/stopwatch": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0"
+ },
+ "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",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v7.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-25T06:35:40+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "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.6.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": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v7.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191",
+ "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/event-dispatcher-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/service-contracts": "<2.5"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/stopwatch": "^6.4|^7.0"
+ },
+ "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/v7.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-13T11:49:31+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586",
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "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/v3.6.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": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v7.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd",
+ "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "require-dev": {
+ "symfony/process": "^6.4|^7.0"
+ },
+ "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/v7.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-07T08:17:47+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v7.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe",
+ "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.4|^7.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/v7.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-15T13:41:35+00:00"
+ },
+ {
+ "name": "symfony/options-resolver",
+ "version": "v7.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/options-resolver.git",
+ "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
+ "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^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/v7.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-05T10:16:07+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "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.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
+ "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "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 intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-27T09:58:17+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "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 for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "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.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-23T08:48:59+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "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.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-02T08:10:11+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php81",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php81.git",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "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.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v7.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
+ "reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "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/v7.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-18T09:42:54+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "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/v3.6.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": "2025-04-25T09:37:31+00:00"
+ },
+ {
+ "name": "symfony/stopwatch",
+ "version": "v7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd",
+ "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/service-contracts": "^2.5|^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/v7.3.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": "2025-02-24T10:49:57+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v7.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
+ "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.1",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v7.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-25T06:35:40+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {},
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
+}
From 01cf699ffadd1b0ace2709ad988f2307f774c7f7 Mon Sep 17 00:00:00 2001
From: odain
Date: Mon, 6 Oct 2025 16:18:23 +0200
Subject: [PATCH 02/15] PSR2 for now
---
.php-cs-fixer.dist.php | 38 ++++++++++++--------------------------
1 file changed, 12 insertions(+), 26 deletions(-)
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 50bc9768b4..96e85359b0 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -2,38 +2,24 @@
$finder = PhpCsFixer\Finder::create()
->exclude('oql')
- //->in(__DIR__ . '/addons')
-// ->in(__DIR__ . '/application')
- //->in(__DIR__ . '/core')
-// ->in(__DIR__ . '/datamodels')
-// ->in(__DIR__ . '/dictionaries')
-// ->in(__DIR__ . '/pages')
-// ->in(__DIR__ . '/portal')
-// ->in(__DIR__ . '/setup')
-// ->in(__DIR__ . '/sources')
-// ->in(__DIR__ . '/synchro')
-// ->in(__DIR__ . '/tests')
+ ->in(__DIR__.'/addons')
+ ->in(__DIR__.'/application')
+ ->in(__DIR__.'/core')
+ ->in(__DIR__.'/datamodels')
+ ->in(__DIR__.'/dictionaries')
+ ->in(__DIR__.'/pages')
+ ->in(__DIR__.'/portal')
+ ->in(__DIR__.'/setup')
+ ->in(__DIR__.'/sources')
+ ->in(__DIR__.'/synchro')
+ ->in(__DIR__.'/tests')
->in(__DIR__ . '/webservices')
;
$config = new PhpCsFixer\Config();
return $config->setRiskyAllowed(true)
->setRules([
- '@PSR12' => true,
- // We use PSR12 with consistent brace placement.
- 'curly_braces_position' => [
- 'functions_opening_brace' => 'same_line',
- 'classes_opening_brace' => 'same_line',
- ],
- // declare(strict_types=1) on the same line as false,
- 'declare_strict_types' => true,
- // Keep argument formatting for now.
- 'method_argument_space' => ['on_multiline' => 'ignore'],
- 'phpdoc_align' => ['align' => 'left'],
- 'phpdoc_trim' => true,
- 'no_empty_phpdoc' => true,
- 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true],
+ '@PSR2' => true,
'no_extra_blank_lines' => true,
])
->setFinder($finder)
From c273fbb7002bc2665fc1943d25ebcff51443767a Mon Sep 17 00:00:00 2001
From: odain
Date: Thu, 9 Oct 2025 14:27:59 +0200
Subject: [PATCH 03/15] PSR12 + array short
---
.php-cs-fixer.dist.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 96e85359b0..035c187804 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -19,8 +19,9 @@
$config = new PhpCsFixer\Config();
return $config->setRiskyAllowed(true)
->setRules([
- '@PSR2' => true,
+ '@PSR12' => true,
'no_extra_blank_lines' => true,
+ 'array_syntax' => ['syntax' => 'short'],
])
->setFinder($finder)
;
\ No newline at end of file
From 5769b5a1dd59258e6416725cd92fa860dc4d7d26 Mon Sep 17 00:00:00 2001
From: odain
Date: Thu, 9 Oct 2025 16:46:00 +0200
Subject: [PATCH 04/15] move php-cs-fixer stuff in tests/php-code-style
---
.gitignore | 1 -
.../php-code-style/.php-cs-fixer.dist.php | 0
{tools => tests/php-code-style}/composer.json | 0
{tools => tests/php-code-style}/composer.lock | 0
4 files changed, 1 deletion(-)
rename .php-cs-fixer.dist.php => tests/php-code-style/.php-cs-fixer.dist.php (100%)
rename {tools => tests/php-code-style}/composer.json (100%)
rename {tools => tests/php-code-style}/composer.lock (100%)
diff --git a/.gitignore b/.gitignore
index 88d6c976d6..df991db82c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,7 +20,6 @@
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
tests/*/vendor/*
-tools/vendor
# all conf but listing prevention
/conf/**
diff --git a/.php-cs-fixer.dist.php b/tests/php-code-style/.php-cs-fixer.dist.php
similarity index 100%
rename from .php-cs-fixer.dist.php
rename to tests/php-code-style/.php-cs-fixer.dist.php
diff --git a/tools/composer.json b/tests/php-code-style/composer.json
similarity index 100%
rename from tools/composer.json
rename to tests/php-code-style/composer.json
diff --git a/tools/composer.lock b/tests/php-code-style/composer.lock
similarity index 100%
rename from tools/composer.lock
rename to tests/php-code-style/composer.lock
From d3910ee04d1dc8effdd4b9af1e339e617a4556f9 Mon Sep 17 00:00:00 2001
From: odain
Date: Thu, 9 Oct 2025 16:51:10 +0200
Subject: [PATCH 05/15] add README
---
tests/php-code-style/README.md | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 tests/php-code-style/README.md
diff --git a/tests/php-code-style/README.md b/tests/php-code-style/README.md
new file mode 100644
index 0000000000..15224342b0
--- /dev/null
+++ b/tests/php-code-style/README.md
@@ -0,0 +1,17 @@
+Code formatting tool used by iTop is PHP-CS-Fixer:
+https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/master
+
+
+to check code style issues (no path provided means whole iTop code base):
+
+```
+cd tests/php-code-style/; composer install; cd -
+tests/php-code-style/vendor/bin/php-cs-fixer check [PATH]
+```
+
+to respect iTop code standards and re-format (no path provided means whole iTop code base):
+
+```
+tests/php-code-style/vendor/bin/php-cs-fixer fix [PATH]
+
+```
\ No newline at end of file
From 614fa1d97ed311782abc30a59f8ce66e8a68baa9 Mon Sep 17 00:00:00 2001
From: odain
Date: Tue, 14 Oct 2025 08:43:40 +0200
Subject: [PATCH 06/15] fix php-cs-fixer move into php-code-style
---
tests/php-code-style/.php-cs-fixer.dist.php | 27 ++++++++++++---------
tests/php-code-style/README.md | 4 +--
2 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/tests/php-code-style/.php-cs-fixer.dist.php b/tests/php-code-style/.php-cs-fixer.dist.php
index 035c187804..601ff41cdb 100644
--- a/tests/php-code-style/.php-cs-fixer.dist.php
+++ b/tests/php-code-style/.php-cs-fixer.dist.php
@@ -1,19 +1,22 @@
exclude('oql')
- ->in(__DIR__.'/addons')
- ->in(__DIR__.'/application')
- ->in(__DIR__.'/core')
- ->in(__DIR__.'/datamodels')
- ->in(__DIR__.'/dictionaries')
- ->in(__DIR__.'/pages')
- ->in(__DIR__.'/portal')
- ->in(__DIR__.'/setup')
- ->in(__DIR__.'/sources')
- ->in(__DIR__.'/synchro')
- ->in(__DIR__.'/tests')
- ->in(__DIR__ . '/webservices')
+ ->in($APPROOT.'/addons')
+ ->in($APPROOT.'/application')
+ ->in($APPROOT.'/core')
+ ->in($APPROOT.'/datamodels')
+ ->in($APPROOT.'/dictionaries')
+ ->in($APPROOT.'/pages')
+ ->in($APPROOT.'/portal')
+ ->in($APPROOT.'/setup')
+ ->in($APPROOT.'/sources')
+ ->in($APPROOT.'/synchro')
+ ->in($APPROOT.'/tests')
+ ->in($APPROOT . '/webservices')
;
$config = new PhpCsFixer\Config();
diff --git a/tests/php-code-style/README.md b/tests/php-code-style/README.md
index 15224342b0..cc063439b1 100644
--- a/tests/php-code-style/README.md
+++ b/tests/php-code-style/README.md
@@ -6,12 +6,12 @@ to check code style issues (no path provided means whole iTop code base):
```
cd tests/php-code-style/; composer install; cd -
-tests/php-code-style/vendor/bin/php-cs-fixer check [PATH]
+tests/php-code-style/vendor/bin/php-cs-fixer check --config tests/php-code-style/.php-cs-fixer.dist.php [PATH]
```
to respect iTop code standards and re-format (no path provided means whole iTop code base):
```
-tests/php-code-style/vendor/bin/php-cs-fixer fix [PATH]
+tests/php-code-style/vendor/bin/php-cs-fixer fix --config tests/php-code-style/.php-cs-fixer.dist.php [PATH]
```
\ No newline at end of file
From ffd3544267802af1d325ec08ac24ef56ab5acead Mon Sep 17 00:00:00 2001
From: odain
Date: Tue, 14 Oct 2025 08:44:02 +0200
Subject: [PATCH 07/15] illustrate code reformatting on webservices folder
---
webservices/backoffice.dataloader.php | 160 ++-
webservices/createfrommail.php | 201 ++-
webservices/cron.php | 869 ++++++-------
webservices/export-v2.php | 1053 ++++++++--------
webservices/export.php | 560 ++++-----
webservices/import.php | 1528 +++++++++++------------
webservices/itop.wsdl.php | 41 +-
webservices/itoprest.examples.php | 637 +++++-----
webservices/itopsoap.examples.php | 195 ++-
webservices/itopsoaptypes.class.inc.php | 225 ++--
webservices/rest.php | 408 +++---
webservices/soapserver.php | 111 +-
webservices/status.php | 17 +-
webservices/webservices.basic.php | 493 ++++----
webservices/webservices.class.inc.php | 1064 ++++++++--------
15 files changed, 3514 insertions(+), 4048 deletions(-)
diff --git a/webservices/backoffice.dataloader.php b/webservices/backoffice.dataloader.php
index c73ca047f0..effadf4344 100644
--- a/webservices/backoffice.dataloader.php
+++ b/webservices/backoffice.dataloader.php
@@ -1,4 +1,5 @@
p("No memory limit has been defined in this instance of PHP");
- }
- else
- {
- // Check that the limit will allow us to load the data
- //
- $iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
- if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY))
- {
- if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE)
- {
- $oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
- }
- else
- {
- $oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
- }
- }
- }
+ $sMemoryLimit = trim(ini_get('memory_limit'));
+ if (empty($sMemoryLimit)) {
+ // On some PHP installations, memory_limit does not exist as a PHP setting!
+ // (encountered on a 5.2.0 under Windows)
+ // In that case, ini_set will not work, let's keep track of this and proceed with the data load
+ $oP->p("No memory limit has been defined in this instance of PHP");
+ } else {
+ // Check that the limit will allow us to load the data
+ //
+ $iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
+ if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY)) {
+ if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === false) {
+ $oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
+ } else {
+ $oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
+ }
+ }
+ }
}
-
////////////////////////////////////////////////////////////////////////////////
//
// Main
@@ -87,68 +79,60 @@ function SetMemoryLimit($oP)
$oP = new WebPage("iTop - Backoffice data loader");
-
-try
-{
- // Note: the data model must be loaded first
- $oDataLoader = new XMLDataLoader();
-
- if (empty($sFileName)) {
- throw(new Exception("Missing argument 'file'"));
- }
- if (!file_exists($sFileName)) {
- throw(new Exception("File $sFileName does not exist"));
- }
-
- SetMemoryLimit($oP);
-
-
- // The XMLDataLoader constructor has initialized the DB, let's start a transaction
- CMDBSource::Query('START TRANSACTION');
-
- $oP->p("Starting data load.");
- CMDBObject::SetCurrentChangeFromParams('Initialization WS');
- $oDataLoader->StartSession(CMDBObject::GetCurrentChange());
- $oDataLoader->LoadFile($sFileName);
-
- $oP->p("Ending data load session");
- if ($oDataLoader->EndSession(true /* strict */)) {
- $iCountCreated = $oDataLoader->GetCountCreated();
- CMDBSource::Query('COMMIT');
-
- $oP->p("Data successfully written into the DB: $iCountCreated objects created");
- } else {
- CMDBSource::Query('ROLLBACK');
- $oP->p("Some issues have been encountered, changes will not be recorded, please review the source data");
- $aErrors = $oDataLoader->GetErrors();
- if (count($aErrors) > 0) {
- $oP->p('Errors ('.count($aErrors).')');
- foreach ($aErrors as $sMsg) {
- $oP->p(' * '.$sMsg);
- }
- }
- $aWarnings = $oDataLoader->GetWarnings();
- if (count($aWarnings) > 0)
- {
- $oP->p('Warnings ('.count($aWarnings).')');
- foreach ($aWarnings as $sMsg)
- {
- $oP->p(' * '.$sMsg);
- }
- }
- }
-
-}
-catch(Exception $e)
-{
- $oP->p("An error happened while loading the data: ".$e->getMessage());
- $oP->p("Aborting (no data written)...");
- CMDBSource::Query('ROLLBACK');
+try {
+ // Note: the data model must be loaded first
+ $oDataLoader = new XMLDataLoader();
+
+ if (empty($sFileName)) {
+ throw(new Exception("Missing argument 'file'"));
+ }
+ if (!file_exists($sFileName)) {
+ throw(new Exception("File $sFileName does not exist"));
+ }
+
+ SetMemoryLimit($oP);
+
+ // The XMLDataLoader constructor has initialized the DB, let's start a transaction
+ CMDBSource::Query('START TRANSACTION');
+
+ $oP->p("Starting data load.");
+ CMDBObject::SetCurrentChangeFromParams('Initialization WS');
+ $oDataLoader->StartSession(CMDBObject::GetCurrentChange());
+ $oDataLoader->LoadFile($sFileName);
+
+ $oP->p("Ending data load session");
+ if ($oDataLoader->EndSession(true /* strict */)) {
+ $iCountCreated = $oDataLoader->GetCountCreated();
+ CMDBSource::Query('COMMIT');
+
+ $oP->p("Data successfully written into the DB: $iCountCreated objects created");
+ } else {
+ CMDBSource::Query('ROLLBACK');
+ $oP->p("Some issues have been encountered, changes will not be recorded, please review the source data");
+ $aErrors = $oDataLoader->GetErrors();
+ if (count($aErrors) > 0) {
+ $oP->p('Errors ('.count($aErrors).')');
+ foreach ($aErrors as $sMsg) {
+ $oP->p(' * '.$sMsg);
+ }
+ }
+ $aWarnings = $oDataLoader->GetWarnings();
+ if (count($aWarnings) > 0) {
+ $oP->p('Warnings ('.count($aWarnings).')');
+ foreach ($aWarnings as $sMsg) {
+ $oP->p(' * '.$sMsg);
+ }
+ }
+ }
+
+} catch (Exception $e) {
+ $oP->p("An error happened while loading the data: ".$e->getMessage());
+ $oP->p("Aborting (no data written)...");
+ CMDBSource::Query('ROLLBACK');
}
-if (function_exists('memory_get_peak_usage'))
-{
- $oP->p("Information: memory peak usage: ".memory_get_peak_usage());
+if (function_exists('memory_get_peak_usage')) {
+ $oP->p("Information: memory peak usage: ".memory_get_peak_usage());
}
$oP->Output();
diff --git a/webservices/createfrommail.php b/webservices/createfrommail.php
index a83488754a..35edac0f50 100644
--- a/webservices/createfrommail.php
+++ b/webservices/createfrommail.php
@@ -41,14 +41,13 @@
function GetSender($aHeaders)
{
- $aResult = array('name' => '', 'email' => '');
- $aResult['name'] = $aHeaders['From'];
- $aMatches = array();
- if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches))
- {
- $aResult['email'] = $aMatches[1].'@'.$aMatches[2];
- }
- return $aResult;
+ $aResult = ['name' => '', 'email' => ''];
+ $aResult['name'] = $aHeaders['From'];
+ $aMatches = [];
+ if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches)) {
+ $aResult['email'] = $aMatches[1].'@'.$aMatches[2];
+ }
+ return $aResult;
}
/**
@@ -60,44 +59,38 @@ function GetSender($aHeaders)
*/
function CreateTicket($sSenderEmail, $sSubject, $sBody)
{
- $oTicket = null;
- try
- {
- $oContactSearch = new DBObjectSearch('Contact'); // Can be either a Person or a Team, but must be a valid Contact
- $oContactSearch->AddCondition('email', $sSenderEmail, '=');
- $oSet = new DBObjectSet($oContactSearch);
- if ($oSet->Count() == 1)
- {
- $oContact = $oSet->Fetch();
- $oOrganization = MetaModel::GetObject('Organization', $oContact->Get('org_id'));
- $oTicket = new UserRequest;
- $oTicket->Set('title', $sSubject);
- $oTicket->Set('description', $sBody);
- $oTicket->Set('org_id', $oOrganization->GetKey());
- $oTicket->Set('caller_id', $oContact->GetKey());
- $oTicket->Set('impact', DEFAULT_IMPACT);
- $oTicket->Set('urgency', DEFAULT_URGENCY);
- $oTicket->Set('product', DEFAULT_PRODUCT);
- $oTicket->Set('service_id', DEFAULT_SERVICE_ID); // Can be replaced by a search for a valid service for this 'org_id'
- $oTicket->Set('servicesubcategory_id', DEFAULT_SUBSERVICE_ID); // Same as above...
- $oTicket->Set('workgroup_id', DEFAULT_WORKGROUP_ID); // Same as above...
+ $oTicket = null;
+ try {
+ $oContactSearch = new DBObjectSearch('Contact'); // Can be either a Person or a Team, but must be a valid Contact
+ $oContactSearch->AddCondition('email', $sSenderEmail, '=');
+ $oSet = new DBObjectSet($oContactSearch);
+ if ($oSet->Count() == 1) {
+ $oContact = $oSet->Fetch();
+ $oOrganization = MetaModel::GetObject('Organization', $oContact->Get('org_id'));
+ $oTicket = new UserRequest();
+ $oTicket->Set('title', $sSubject);
+ $oTicket->Set('description', $sBody);
+ $oTicket->Set('org_id', $oOrganization->GetKey());
+ $oTicket->Set('caller_id', $oContact->GetKey());
+ $oTicket->Set('impact', DEFAULT_IMPACT);
+ $oTicket->Set('urgency', DEFAULT_URGENCY);
+ $oTicket->Set('product', DEFAULT_PRODUCT);
+ $oTicket->Set('service_id', DEFAULT_SERVICE_ID); // Can be replaced by a search for a valid service for this 'org_id'
+ $oTicket->Set('servicesubcategory_id', DEFAULT_SUBSERVICE_ID); // Same as above...
+ $oTicket->Set('workgroup_id', DEFAULT_WORKGROUP_ID); // Same as above...
- // Record the change information about the object
- $sUserString = $oContact->GetName().', submitted by email';
- CMDBObject::SetTrackInfo($sUserString);
- $oTicket->DBInsert();
- }
- else
- {
- echo "No contact found in iTop having the email: $sSenderEmail, email message ignored.\n";
- }
- }
- catch(Exception $e)
- {
- echo "Error: exception ".$e->getMessage();
- $oTicket = null;
- }
- return $oTicket;
+ // Record the change information about the object
+ $sUserString = $oContact->GetName().', submitted by email';
+ CMDBObject::SetTrackInfo($sUserString);
+ $oTicket->DBInsert();
+ } else {
+ echo "No contact found in iTop having the email: $sSenderEmail, email message ignored.\n";
+ }
+ } catch (Exception $e) {
+ echo "Error: exception ".$e->getMessage();
+ $oTicket = null;
+ }
+ return $oTicket;
}
/**
* Main program
@@ -111,72 +104,62 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
// Note: it is expected that the sender of the email exists a valid contact as a 'Contact'
// in iTop (identified by her/his email address), otherwise the ticket creation will fail
$iNbMessages = $oPop3->numMsg();
-for($index = 1; $index <= $iNbMessages; $index++)
-{
- $params['include_bodies'] = true;
- $params['decode_bodies'] = true;
- $params['decode_headers'] = true;
- $params['crlf'] = "\r\n";
- $aHeaders = $oPop3->getParsedHeaders($index);
- $aSender = GetSender($aHeaders);
- $oDecoder = new Mail_mimeDecode( $oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index) );
- $oStructure = $oDecoder->decode($params);
- $sSubject = $aHeaders['Subject'];
- // Search for the text/plain body part
- $iPartIndex = 0;
- $bFound = false;
- $sTextBody = '';
- //echo "\n";
- //print_r($oStructure);
- //echo " \n";
- if (!isset($oStructure->parts) || count($oStructure->parts) == 0)
- {
- $sTextBody = $oStructure->body;
- }
- else
- {
- // Find the first "part" of the body which is in text/plain
- while( ($iPartIndex < count($oStructure->parts)) && (!$bFound) )
- {
- //echo "Reading part $iPartIndex
\n";
- if ( ($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
- ($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain') )
- {
- $sTextBody = $oStructure->parts[$iPartIndex]->body;
- $bFound = true;
- //echo "Plain text found ! ($sTextBody)
\n";
- }
- $iPartIndex++;
- }
- // Try again but this time look for an HTML part
- if (!$bFound)
- {
- while( ($iPartIndex < count($oStructure->parts)) && (!$bFound) )
- {
- //echo "Reading part $iPartIndex
\n";
- if ( ($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
- ($oStructure->parts[$iPartIndex]->ctype_secondary == 'html') )
- {
- $sTextBody = $oStructure->parts[$iPartIndex]->body;
- $bFound = true;
- //echo "HTML text found ! (".htmlentities($sTextBody, ENT_QUOTES, 'UTF-8').")
\n";
- }
- $iPartIndex++;
- }
- }
- }
+for ($index = 1; $index <= $iNbMessages; $index++) {
+ $params['include_bodies'] = true;
+ $params['decode_bodies'] = true;
+ $params['decode_headers'] = true;
+ $params['crlf'] = "\r\n";
+ $aHeaders = $oPop3->getParsedHeaders($index);
+ $aSender = GetSender($aHeaders);
+ $oDecoder = new Mail_mimeDecode($oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index));
+ $oStructure = $oDecoder->decode($params);
+ $sSubject = $aHeaders['Subject'];
+ // Search for the text/plain body part
+ $iPartIndex = 0;
+ $bFound = false;
+ $sTextBody = '';
+ //echo "\n";
+ //print_r($oStructure);
+ //echo " \n";
+ if (!isset($oStructure->parts) || count($oStructure->parts) == 0) {
+ $sTextBody = $oStructure->body;
+ } else {
+ // Find the first "part" of the body which is in text/plain
+ while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
+ //echo "Reading part $iPartIndex
\n";
+ if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
+ ($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain')) {
+ $sTextBody = $oStructure->parts[$iPartIndex]->body;
+ $bFound = true;
+ //echo "Plain text found ! ($sTextBody)
\n";
+ }
+ $iPartIndex++;
+ }
+ // Try again but this time look for an HTML part
+ if (!$bFound) {
+ while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
+ //echo "Reading part $iPartIndex
\n";
+ if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
+ ($oStructure->parts[$iPartIndex]->ctype_secondary == 'html')) {
+ $sTextBody = $oStructure->parts[$iPartIndex]->body;
+ $bFound = true;
+ //echo "HTML text found ! (".htmlentities($sTextBody, ENT_QUOTES, 'UTF-8').")
\n";
+ }
+ $iPartIndex++;
+ }
+ }
+ }
- // Bug: depending on the email, the email address could be found in :
- // email => 'john.foo@combodo.com'
- // name => 'john foo
+ // Bug: depending on the email, the email address could be found in :
+ // email => 'john.foo@combodo.com'
+ // name => 'john foo
- $oTicket = CreateTicket($aSender['email'], $sSubject, $sTextBody);
- if ($oTicket != null)
- {
- // Ticket created, delete the email
- $oPop3->deleteMsg($index);
- echo "Ticket: ".$oTicket->GetName()." created.\n";
- }
+ $oTicket = CreateTicket($aSender['email'], $sSubject, $sTextBody);
+ if ($oTicket != null) {
+ // Ticket created, delete the email
+ $oPop3->deleteMsg($index);
+ echo "Ticket: ".$oTicket->GetName()." created.\n";
+ }
}
$oPop3->disconnect();
?>
diff --git a/webservices/cron.php b/webservices/cron.php
index f51a46f64c..56cd8d066d 100644
--- a/webservices/cron.php
+++ b/webservices/cron.php
@@ -1,4 +1,5 @@
p("ERROR: Missing argument '$sParam'\n");
- UsageAndExit($oP);
- }
-
- return trim($sValue);
+ $sValue = utils::ReadParam($sParam, null, true, $sSanitizationFilter);
+ if (is_null($sValue)) {
+ $oP->p("ERROR: Missing argument '$sParam'\n");
+ UsageAndExit($oP);
+ }
+
+ return trim($sValue);
}
function UsageAndExit($oP)
{
- $bModeCLI = ($oP instanceof CLIPage);
-
- if ($bModeCLI)
- {
- $oP->p("USAGE:\n");
- $oP->p("php cron.php --auth_user= --auth_pwd= [--param_file=] [--verbose=1] [--debug=1] [--status_only=1]\n");
- }
- else
- {
- $oP->p("Optional parameters: verbose, param_file, status_only\n");
- }
- $oP->output();
- exit(EXIT_CODE_FATAL);
+ $bModeCLI = ($oP instanceof CLIPage);
+
+ if ($bModeCLI) {
+ $oP->p("USAGE:\n");
+ $oP->p("php cron.php --auth_user= --auth_pwd= [--param_file=] [--verbose=1] [--debug=1] [--status_only=1]\n");
+ } else {
+ $oP->p("Optional parameters: verbose, param_file, status_only\n");
+ }
+ $oP->output();
+ exit(EXIT_CODE_FATAL);
}
/**
@@ -92,102 +87,84 @@ function UsageAndExit($oP)
*/
function RunTask(BackgroundTask $oTask, $iTimeLimit)
{
- $TaskClass = $oTask->Get('class_name');
- $oProcess = new $TaskClass;
- $oRefClass = new ReflectionClass(get_class($oProcess));
- $oDateStarted = new DateTime();
- $oDatePlanned = new DateTime($oTask->Get('next_run_date'));
- $fStart = microtime(true);
- $oCtx = new ContextTag('CRON:Task:'.$TaskClass);
-
- $sMessage = '';
- $oExceptionToThrow = null;
- try
- {
- // Record (when starting) that this task was started, just in case it crashes during the execution
- $oTask->Set('latest_run_date', $oDateStarted->format('Y-m-d H:i:s'));
- // Record the current user running the cron
- $oTask->Set('system_user', utils::GetCurrentUserName());
- $oTask->Set('running', 1);
- $oTask->DBUpdate();
- // Time in seconds allowed to the task
- $iCurrTimeLimit = $iTimeLimit;
- // Compute allowed time
- if ($oRefClass->implementsInterface('iScheduledProcess') === false)
- {
- // Periodic task, allow only X times ($iMaxTaskExecutionTime) its periodicity (GetPeriodicity())
- $iMaxTaskExecutionTime = MetaModel::GetConfig()->Get('cron_task_max_execution_time');
- $iTaskLimit = time() + $oProcess->GetPeriodicity() * $iMaxTaskExecutionTime;
- // If our proposed time limit is less than cron limit, and cron_task_max_execution_time is > 0
- if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0)
- {
- $iCurrTimeLimit = $iTaskLimit;
- }
- }
- $sMessage = $oProcess->Process($iCurrTimeLimit);
- $oTask->Set('running', 0);
- }
- catch (MySQLHasGoneAwayException $e)
- {
- throw $e;
- }
- catch (ProcessFatalException $e)
- {
- $oExceptionToThrow = $e;
- }
- catch (Exception $e) // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
- {
- if ($oTask->IsDebug())
- {
- $sMessage = 'Processing failed with message: '. $e->getMessage() . '. ' . $e->getTraceAsString();
- }
- else
- {
- $sMessage = 'Processing failed with message: '. $e->getMessage();
- }
- }
- $fDuration = microtime(true) - $fStart;
- if ($oTask->Get('total_exec_count') == 0)
- {
- // First execution
- $oTask->Set('first_run_date', $oDateStarted->format('Y-m-d H:i:s'));
- }
- $oTask->ComputeDurations($fDuration); // does increment the counter and compute statistics
-
- // Update the timestamp since we want to be able to re-order the tasks based on the time they finished
- $oDateEnded = new DateTime();
- $oTask->Set('latest_run_date', $oDateEnded->format('Y-m-d H:i:s'));
-
- if ($oRefClass->implementsInterface('iScheduledProcess'))
- {
- // Schedules process do repeat at specific moments
- $oPlannedStart = $oProcess->GetNextOccurrence();
- }
- else
- {
- // Background processes do repeat periodically
- $oPlannedStart = clone $oDatePlanned;
- // Let's schedule from the previous planned date of execution to avoid shift
- $oPlannedStart->modify($oProcess->GetPeriodicity().' seconds');
- $oEnd = new DateTime();
- while ($oPlannedStart->format('U') < $oEnd->format('U'))
- {
- // Next planned start is already in the past, increase it again by a period
- $oPlannedStart = $oPlannedStart->modify('+'.$oProcess->GetPeriodicity().' seconds');
- }
- }
-
- $oTask->Set('next_run_date', $oPlannedStart->format('Y-m-d H:i:s'));
- $oTask->DBUpdate();
-
- if ($oExceptionToThrow)
- {
- throw $oExceptionToThrow;
- }
-
- unset($oCtx);
-
- return $sMessage;
+ $TaskClass = $oTask->Get('class_name');
+ $oProcess = new $TaskClass();
+ $oRefClass = new ReflectionClass(get_class($oProcess));
+ $oDateStarted = new DateTime();
+ $oDatePlanned = new DateTime($oTask->Get('next_run_date'));
+ $fStart = microtime(true);
+ $oCtx = new ContextTag('CRON:Task:'.$TaskClass);
+
+ $sMessage = '';
+ $oExceptionToThrow = null;
+ try {
+ // Record (when starting) that this task was started, just in case it crashes during the execution
+ $oTask->Set('latest_run_date', $oDateStarted->format('Y-m-d H:i:s'));
+ // Record the current user running the cron
+ $oTask->Set('system_user', utils::GetCurrentUserName());
+ $oTask->Set('running', 1);
+ $oTask->DBUpdate();
+ // Time in seconds allowed to the task
+ $iCurrTimeLimit = $iTimeLimit;
+ // Compute allowed time
+ if ($oRefClass->implementsInterface('iScheduledProcess') === false) {
+ // Periodic task, allow only X times ($iMaxTaskExecutionTime) its periodicity (GetPeriodicity())
+ $iMaxTaskExecutionTime = MetaModel::GetConfig()->Get('cron_task_max_execution_time');
+ $iTaskLimit = time() + $oProcess->GetPeriodicity() * $iMaxTaskExecutionTime;
+ // If our proposed time limit is less than cron limit, and cron_task_max_execution_time is > 0
+ if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0) {
+ $iCurrTimeLimit = $iTaskLimit;
+ }
+ }
+ $sMessage = $oProcess->Process($iCurrTimeLimit);
+ $oTask->Set('running', 0);
+ } catch (MySQLHasGoneAwayException $e) {
+ throw $e;
+ } catch (ProcessFatalException $e) {
+ $oExceptionToThrow = $e;
+ } catch (Exception $e) { // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
+ if ($oTask->IsDebug()) {
+ $sMessage = 'Processing failed with message: '. $e->getMessage() . '. ' . $e->getTraceAsString();
+ } else {
+ $sMessage = 'Processing failed with message: '. $e->getMessage();
+ }
+ }
+ $fDuration = microtime(true) - $fStart;
+ if ($oTask->Get('total_exec_count') == 0) {
+ // First execution
+ $oTask->Set('first_run_date', $oDateStarted->format('Y-m-d H:i:s'));
+ }
+ $oTask->ComputeDurations($fDuration); // does increment the counter and compute statistics
+
+ // Update the timestamp since we want to be able to re-order the tasks based on the time they finished
+ $oDateEnded = new DateTime();
+ $oTask->Set('latest_run_date', $oDateEnded->format('Y-m-d H:i:s'));
+
+ if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ // Schedules process do repeat at specific moments
+ $oPlannedStart = $oProcess->GetNextOccurrence();
+ } else {
+ // Background processes do repeat periodically
+ $oPlannedStart = clone $oDatePlanned;
+ // Let's schedule from the previous planned date of execution to avoid shift
+ $oPlannedStart->modify($oProcess->GetPeriodicity().' seconds');
+ $oEnd = new DateTime();
+ while ($oPlannedStart->format('U') < $oEnd->format('U')) {
+ // Next planned start is already in the past, increase it again by a period
+ $oPlannedStart = $oPlannedStart->modify('+'.$oProcess->GetPeriodicity().' seconds');
+ }
+ }
+
+ $oTask->Set('next_run_date', $oPlannedStart->format('Y-m-d H:i:s'));
+ $oTask->DBUpdate();
+
+ if ($oExceptionToThrow) {
+ throw $oExceptionToThrow;
+ }
+
+ unset($oCtx);
+
+ return $sMessage;
}
/**
@@ -207,147 +184,128 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
* @throws \OQLException
* @throws \ReflectionException
*/
-function CronExec($oP, $bVerbose, $bDebug=false)
+function CronExec($oP, $bVerbose, $bDebug = false)
{
- $iStarted = time();
- $iMaxDuration = MetaModel::GetConfig()->Get('cron_max_execution_time');
- $iTimeLimit = $iStarted + $iMaxDuration;
- $iCronSleep = MetaModel::GetConfig()->Get('cron_sleep');
-
- if ($bVerbose)
- {
- $oP->p("Planned duration = $iMaxDuration seconds");
- $oP->p("Loop pause = $iCronSleep seconds");
- }
-
- ReSyncProcesses($oP, $bVerbose, $bDebug);
-
- while (time() < $iTimeLimit)
- {
- CheckMaintenanceMode($oP);
-
- $oNow = new DateTime();
- $sNow = $oNow->format('Y-m-d H:i:s');
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oSearch->AddCondition('next_run_date', $sNow, '<=');
- $oSearch->AddCondition('status', 'active');
- $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
- $bWorkDone = false;
-
- if ($oTasks->CountExceeds(0))
- {
- $bWorkDone = true;
- $aTasks = array();
- if ($bVerbose)
- {
- $sCount = $oTasks->Count();
- $oP->p("$sCount Tasks planned to run now ($sNow):");
- $oP->p('+---------------------------+---------+---------------------+---------------------+');
- $oP->p('| Task Class | Status | Last Run | Next Run |');
- $oP->p('+---------------------------+---------+---------------------+---------------------+');
- }
- while ($oTask = $oTasks->Fetch())
- {
- $aTasks[$oTask->Get('class_name')] = $oTask;
- if ($bVerbose)
- {
- $sTaskName = $oTask->Get('class_name');
- $sStatus = $oTask->Get('status');
- $sLastRunDate = $oTask->Get('latest_run_date');
- $sNextRunDate = $oTask->Get('next_run_date');
- $oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s |', $sTaskName, $sStatus, $sLastRunDate, $sNextRunDate));
- }
- }
- if ($bVerbose)
- {
- $oP->p('+---------------------------+---------+---------------------+---------------------+');
- }
- $aRunTasks = [];
- foreach ($aTasks as $oTask)
- {
- $sTaskClass = $oTask->Get('class_name');
- $aRunTasks[] = $sTaskClass;
-
- // N°3219 for each process will use a specific CMDBChange object with a specific track info
- // Any BackgroundProcess can overrides this as needed
- CMDBObject::SetCurrentChangeFromParams("Background task ($sTaskClass)");
-
- // Run the task and record its next run time
- if ($bVerbose)
- {
- $oNow = new DateTime();
- $oP->p(">> === ".$oNow->format('Y-m-d H:i:s').sprintf(" Starting:%-'=49s", ' '.$sTaskClass.' '));
- }
- try
- {
- $sMessage = RunTask($aTasks[$sTaskClass], $iTimeLimit);
- } catch (MySQLHasGoneAwayException $e)
- {
- $oP->p("ERROR : 'MySQL has gone away' thrown when processing $sTaskClass (error_code=".$e->getCode().")");
- exit(EXIT_CODE_FATAL);
- } catch (ProcessFatalException $e)
- {
- $oP->p("ERROR : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().")");
- IssueLog::Error("Cron.php error : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().')');
- }
- if ($bVerbose)
- {
- if (!empty($sMessage))
- {
- $oP->p("$sTaskClass: $sMessage");
- }
- $oEnd = new DateTime();
- $sNextRunDate = $oTask->Get('next_run_date');
- $oP->p("<< === ".$oEnd->format('Y-m-d H:i:s').sprintf(" End of: %-'=42s", ' '.$sTaskClass.' ')." Next: $sNextRunDate");
- }
- if (time() > $iTimeLimit)
- {
- break 2;
- }
- CheckMaintenanceMode($oP);
- }
-
- // Tasks to run later
- if ($bVerbose)
- {
- $oP->p('--');
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oSearch->AddCondition('next_run_date', $sNow, '>');
- $oSearch->AddCondition('status', 'active');
- $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
- while ($oTask = $oTasks->Fetch())
- {
- if (!in_array($oTask->Get('class_name'), $aRunTasks))
- {
- $oP->p(sprintf("-- Skipping task: %-'-40s", $oTask->Get('class_name').' ')." until: ".$oTask->Get('next_run_date'));
- }
- }
- }
- }
-
- if ($bVerbose && $bWorkDone)
- {
- $oP->p("Sleeping...\n");
- }
- sleep($iCronSleep);
- }
- if ($bVerbose)
- {
- $oP->p('');
- DisplayStatus($oP, ['next_run_date' => true]);
- $oP->p("Reached normal execution time limit (exceeded by ".(time() - $iTimeLimit)."s)");
- }
+ $iStarted = time();
+ $iMaxDuration = MetaModel::GetConfig()->Get('cron_max_execution_time');
+ $iTimeLimit = $iStarted + $iMaxDuration;
+ $iCronSleep = MetaModel::GetConfig()->Get('cron_sleep');
+
+ if ($bVerbose) {
+ $oP->p("Planned duration = $iMaxDuration seconds");
+ $oP->p("Loop pause = $iCronSleep seconds");
+ }
+
+ ReSyncProcesses($oP, $bVerbose, $bDebug);
+
+ while (time() < $iTimeLimit) {
+ CheckMaintenanceMode($oP);
+
+ $oNow = new DateTime();
+ $sNow = $oNow->format('Y-m-d H:i:s');
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oSearch->AddCondition('next_run_date', $sNow, '<=');
+ $oSearch->AddCondition('status', 'active');
+ $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
+ $bWorkDone = false;
+
+ if ($oTasks->CountExceeds(0)) {
+ $bWorkDone = true;
+ $aTasks = [];
+ if ($bVerbose) {
+ $sCount = $oTasks->Count();
+ $oP->p("$sCount Tasks planned to run now ($sNow):");
+ $oP->p('+---------------------------+---------+---------------------+---------------------+');
+ $oP->p('| Task Class | Status | Last Run | Next Run |');
+ $oP->p('+---------------------------+---------+---------------------+---------------------+');
+ }
+ while ($oTask = $oTasks->Fetch()) {
+ $aTasks[$oTask->Get('class_name')] = $oTask;
+ if ($bVerbose) {
+ $sTaskName = $oTask->Get('class_name');
+ $sStatus = $oTask->Get('status');
+ $sLastRunDate = $oTask->Get('latest_run_date');
+ $sNextRunDate = $oTask->Get('next_run_date');
+ $oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s |', $sTaskName, $sStatus, $sLastRunDate, $sNextRunDate));
+ }
+ }
+ if ($bVerbose) {
+ $oP->p('+---------------------------+---------+---------------------+---------------------+');
+ }
+ $aRunTasks = [];
+ foreach ($aTasks as $oTask) {
+ $sTaskClass = $oTask->Get('class_name');
+ $aRunTasks[] = $sTaskClass;
+
+ // N°3219 for each process will use a specific CMDBChange object with a specific track info
+ // Any BackgroundProcess can overrides this as needed
+ CMDBObject::SetCurrentChangeFromParams("Background task ($sTaskClass)");
+
+ // Run the task and record its next run time
+ if ($bVerbose) {
+ $oNow = new DateTime();
+ $oP->p(">> === ".$oNow->format('Y-m-d H:i:s').sprintf(" Starting:%-'=49s", ' '.$sTaskClass.' '));
+ }
+ try {
+ $sMessage = RunTask($aTasks[$sTaskClass], $iTimeLimit);
+ } catch (MySQLHasGoneAwayException $e) {
+ $oP->p("ERROR : 'MySQL has gone away' thrown when processing $sTaskClass (error_code=".$e->getCode().")");
+ exit(EXIT_CODE_FATAL);
+ } catch (ProcessFatalException $e) {
+ $oP->p("ERROR : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().")");
+ IssueLog::Error("Cron.php error : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().')');
+ }
+ if ($bVerbose) {
+ if (!empty($sMessage)) {
+ $oP->p("$sTaskClass: $sMessage");
+ }
+ $oEnd = new DateTime();
+ $sNextRunDate = $oTask->Get('next_run_date');
+ $oP->p("<< === ".$oEnd->format('Y-m-d H:i:s').sprintf(" End of: %-'=42s", ' '.$sTaskClass.' ')." Next: $sNextRunDate");
+ }
+ if (time() > $iTimeLimit) {
+ break 2;
+ }
+ CheckMaintenanceMode($oP);
+ }
+
+ // Tasks to run later
+ if ($bVerbose) {
+ $oP->p('--');
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oSearch->AddCondition('next_run_date', $sNow, '>');
+ $oSearch->AddCondition('status', 'active');
+ $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
+ while ($oTask = $oTasks->Fetch()) {
+ if (!in_array($oTask->Get('class_name'), $aRunTasks)) {
+ $oP->p(sprintf("-- Skipping task: %-'-40s", $oTask->Get('class_name').' ')." until: ".$oTask->Get('next_run_date'));
+ }
+ }
+ }
+ }
+
+ if ($bVerbose && $bWorkDone) {
+ $oP->p("Sleeping...\n");
+ }
+ sleep($iCronSleep);
+ }
+ if ($bVerbose) {
+ $oP->p('');
+ DisplayStatus($oP, ['next_run_date' => true]);
+ $oP->p("Reached normal execution time limit (exceeded by ".(time() - $iTimeLimit)."s)");
+ }
}
/**
* @param WebPage $oP
*/
-function CheckMaintenanceMode(Page $oP) {
-// Verify files instead of reloading the full config each time
- if (file_exists(MAINTENANCE_MODE_FILE) || file_exists(READONLY_MODE_FILE)) {
- $oP->p("Maintenance detected, exiting");
- exit(EXIT_CODE_ERROR);
- }
+function CheckMaintenanceMode(Page $oP)
+{
+ // Verify files instead of reloading the full config each time
+ if (file_exists(MAINTENANCE_MODE_FILE) || file_exists(READONLY_MODE_FILE)) {
+ $oP->p("Maintenance detected, exiting");
+ exit(EXIT_CODE_ERROR);
+ }
}
/**
@@ -362,23 +320,29 @@ function CheckMaintenanceMode(Page $oP) {
*/
function DisplayStatus($oP, $aTaskOrderBy = [])
{
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oTasks = new DBObjectSet($oSearch, $aTaskOrderBy);
- $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
- $oP->p('| Task Class | Status | Last Run | Next Run | Nb Run | Avg. Dur. |');
- $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
- while ($oTask = $oTasks->Fetch())
- {
- $sTaskName = $oTask->Get('class_name');
- $sStatus = $oTask->Get('status');
- $sLastRunDate = $oTask->Get('latest_run_date');
- $sNextRunDate = $oTask->Get('next_run_date');
- $iNbRun = (int)$oTask->Get('total_exec_count');
- $sAverageRunTime = $oTask->Get('average_run_duration');
- $oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |', $sTaskName, $sStatus,
- $sLastRunDate, $sNextRunDate, $iNbRun, $sAverageRunTime));
- }
- $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oTasks = new DBObjectSet($oSearch, $aTaskOrderBy);
+ $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
+ $oP->p('| Task Class | Status | Last Run | Next Run | Nb Run | Avg. Dur. |');
+ $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
+ while ($oTask = $oTasks->Fetch()) {
+ $sTaskName = $oTask->Get('class_name');
+ $sStatus = $oTask->Get('status');
+ $sLastRunDate = $oTask->Get('latest_run_date');
+ $sNextRunDate = $oTask->Get('next_run_date');
+ $iNbRun = (int)$oTask->Get('total_exec_count');
+ $sAverageRunTime = $oTask->Get('average_run_duration');
+ $oP->p(sprintf(
+ '| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |',
+ $sTaskName,
+ $sStatus,
+ $sLastRunDate,
+ $sNextRunDate,
+ $iNbRun,
+ $sAverageRunTime
+ ));
+ }
+ $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
}
/**
@@ -397,99 +361,83 @@ function DisplayStatus($oP, $aTaskOrderBy = [])
*/
function ReSyncProcesses($oP, $bVerbose, $bDebug)
{
- // Enumerate classes implementing BackgroundProcess
- //
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oTasks = new DBObjectSet($oSearch);
- $aTasks = array();
- while ($oTask = $oTasks->Fetch())
- {
- $aTasks[$oTask->Get('class_name')] = $oTask;
- }
- $oNow = new DateTime();
-
- $aProcesses = array();
- foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass)
- {
- $oProcess = new $sTaskClass;
- $aProcesses[$sTaskClass] = $oProcess;
-
- // Create missing entry if needed
- if (!array_key_exists($sTaskClass, $aTasks))
- {
- // New entry, let's create a new BackgroundTask record, and plan the first execution
- $oTask = new BackgroundTask();
- $oTask->SetDebug($bDebug);
- $oTask->Set('class_name', $sTaskClass);
- $oTask->Set('total_exec_count', 0);
- $oTask->Set('min_run_duration', 99999.999);
- $oTask->Set('max_run_duration', 0);
- $oTask->Set('average_run_duration', 0);
- $oRefClass = new ReflectionClass($sTaskClass);
- if ($oRefClass->implementsInterface('iScheduledProcess'))
- {
- $oNextOcc = $oProcess->GetNextOccurrence();
- $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
- }
- else
- {
- // Background processes do start asap, i.e. "now"
- $oTask->Set('next_run_date', $oNow->format('Y-m-d H:i:s'));
- }
- if ($bVerbose)
- {
- $oP->p('Creating record for: '.$sTaskClass);
- $oP->p('First execution planned at: '.$oTask->Get('next_run_date'));
- }
- $oTask->DBInsert();
- }
- else
- {
- /** @var \BackgroundTask $oTask */
- $oTask = $aTasks[$sTaskClass];
- if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00')
- {
- // check for rescheduled tasks
- $oRefClass = new ReflectionClass($sTaskClass);
- if ($oRefClass->implementsInterface('iScheduledProcess'))
- {
- $oNextOcc = $oProcess->GetNextOccurrence();
- $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
- $oTask->DBUpdate();
- }
- }
- // Reactivate task if necessary
- if ($oTask->Get('status') == 'removed')
- {
- $oTask->Set('status', 'active');
- $oTask->DBUpdate();
- }
- // task having a real class to execute
- unset($aTasks[$sTaskClass]);
- }
- }
-
- // Remove all the tasks not having a valid class
- foreach ($aTasks as $oTask)
- {
- $sTaskClass = $oTask->Get('class_name');
- if (!class_exists($sTaskClass))
- {
- $oTask->Set('status', 'removed');
- $oTask->DBUpdate();
- }
- }
-
- if ($bVerbose)
- {
- $aDisplayProcesses = array();
- foreach ($aProcesses as $oExecInstance)
- {
- $aDisplayProcesses[] = get_class($oExecInstance);
- }
- $sDisplayProcesses = implode(', ', $aDisplayProcesses);
- $oP->p("Background processes: ".$sDisplayProcesses);
- }
+ // Enumerate classes implementing BackgroundProcess
+ //
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oTasks = new DBObjectSet($oSearch);
+ $aTasks = [];
+ while ($oTask = $oTasks->Fetch()) {
+ $aTasks[$oTask->Get('class_name')] = $oTask;
+ }
+ $oNow = new DateTime();
+
+ $aProcesses = [];
+ foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass) {
+ $oProcess = new $sTaskClass();
+ $aProcesses[$sTaskClass] = $oProcess;
+
+ // Create missing entry if needed
+ if (!array_key_exists($sTaskClass, $aTasks)) {
+ // New entry, let's create a new BackgroundTask record, and plan the first execution
+ $oTask = new BackgroundTask();
+ $oTask->SetDebug($bDebug);
+ $oTask->Set('class_name', $sTaskClass);
+ $oTask->Set('total_exec_count', 0);
+ $oTask->Set('min_run_duration', 99999.999);
+ $oTask->Set('max_run_duration', 0);
+ $oTask->Set('average_run_duration', 0);
+ $oRefClass = new ReflectionClass($sTaskClass);
+ if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ $oNextOcc = $oProcess->GetNextOccurrence();
+ $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
+ } else {
+ // Background processes do start asap, i.e. "now"
+ $oTask->Set('next_run_date', $oNow->format('Y-m-d H:i:s'));
+ }
+ if ($bVerbose) {
+ $oP->p('Creating record for: '.$sTaskClass);
+ $oP->p('First execution planned at: '.$oTask->Get('next_run_date'));
+ }
+ $oTask->DBInsert();
+ } else {
+ /** @var \BackgroundTask $oTask */
+ $oTask = $aTasks[$sTaskClass];
+ if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00') {
+ // check for rescheduled tasks
+ $oRefClass = new ReflectionClass($sTaskClass);
+ if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ $oNextOcc = $oProcess->GetNextOccurrence();
+ $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
+ $oTask->DBUpdate();
+ }
+ }
+ // Reactivate task if necessary
+ if ($oTask->Get('status') == 'removed') {
+ $oTask->Set('status', 'active');
+ $oTask->DBUpdate();
+ }
+ // task having a real class to execute
+ unset($aTasks[$sTaskClass]);
+ }
+ }
+
+ // Remove all the tasks not having a valid class
+ foreach ($aTasks as $oTask) {
+ $sTaskClass = $oTask->Get('class_name');
+ if (!class_exists($sTaskClass)) {
+ $oTask->Set('status', 'removed');
+ $oTask->DBUpdate();
+ }
+ }
+
+ if ($bVerbose) {
+ $aDisplayProcesses = [];
+ foreach ($aProcesses as $oExecInstance) {
+ $aDisplayProcesses[] = get_class($oExecInstance);
+ }
+ $sDisplayProcesses = implode(', ', $aDisplayProcesses);
+ $oP->p("Background processes: ".$sDisplayProcesses);
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -500,117 +448,86 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
set_time_limit(0); // Some background actions may really take long to finish (like backup)
$bIsModeCLI = utils::IsModeCLI();
-if ($bIsModeCLI)
-{
- $oP = new CLIPage("iTop - cron");
+if ($bIsModeCLI) {
+ $oP = new CLIPage("iTop - cron");
- SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
-}
-else
-{
- $oP = new WebPage("iTop - cron");
+ SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
+} else {
+ $oP = new WebPage("iTop - cron");
}
-try
-{
- utils::UseParamFile();
-
- $bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */);
- $bDebug = utils::ReadParam('debug', false, true /* Allow CLI */);
-
- if ($bIsModeCLI)
- {
- // Next steps:
- // specific arguments: 'csv file'
- //
- $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
- $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
- {
- UserRights::Login($sAuthUser); // Login & set the user's language
- }
- else
- {
- $oP->p("Access wrong credentials ('$sAuthUser')");
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
- }
- else
- {
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
- LoginWebPage::DoLogin(); // Check user rights and prompt if needed
- }
-
- if (!UserRights::IsAdministrator())
- {
- $oP->p("Access restricted to administrators");
- $oP->Output();
- exit(EXIT_CODE_ERROR);
- }
-
-
- if (utils::ReadParam('status_only', false, true /* Allow CLI */))
- {
- // Display status and exit
- DisplayStatus($oP);
- exit(0);
- }
-
- require_once(APPROOT.'core/mutex.class.inc.php');
- $oP->p("Starting: ".time().' ('.date('Y-m-d H:i:s').')');
-}
-catch (Exception $e)
-{
- $oP->p("Error: ".$e->GetMessage());
- $oP->output();
- exit(EXIT_CODE_FATAL);
+try {
+ utils::UseParamFile();
+
+ $bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */);
+ $bDebug = utils::ReadParam('debug', false, true /* Allow CLI */);
+
+ if ($bIsModeCLI) {
+ // Next steps:
+ // specific arguments: 'csv file'
+ //
+ $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
+ $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ $oP->p("Access wrong credentials ('$sAuthUser')");
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
+ } else {
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
+ }
+
+ if (!UserRights::IsAdministrator()) {
+ $oP->p("Access restricted to administrators");
+ $oP->Output();
+ exit(EXIT_CODE_ERROR);
+ }
+
+ if (utils::ReadParam('status_only', false, true /* Allow CLI */)) {
+ // Display status and exit
+ DisplayStatus($oP);
+ exit(0);
+ }
+
+ require_once(APPROOT.'core/mutex.class.inc.php');
+ $oP->p("Starting: ".time().' ('.date('Y-m-d H:i:s').')');
+} catch (Exception $e) {
+ $oP->p("Error: ".$e->GetMessage());
+ $oP->output();
+ exit(EXIT_CODE_FATAL);
}
-try
-{
- $oMutex = new iTopMutex('cron');
- if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
- {
- $oP->p("A maintenance is ongoing");
- }
- else
- {
- if ($oMutex->TryLock())
- {
- CronExec($oP, $bVerbose, $bDebug);
- }
- else
- {
- // Exit silently
- $oP->p("Already running...");
- }
- }
-}
-catch (Exception $e)
-{
- $oP->p("ERROR: '".$e->getMessage()."'");
- if ($bDebug)
- {
- // Might contain verb parameters such a password...
- $oP->p($e->getTraceAsString());
- }
-}
-finally
-{
- try
- {
- $oMutex->Unlock();
- }
- catch (Exception $e)
- {
- $oP->p("ERROR: '".$e->getMessage()."'");
- if ($bDebug)
- {
- // Might contain verb parameters such a password...
- $oP->p($e->getTraceAsString());
- }
- }
+try {
+ $oMutex = new iTopMutex('cron');
+ if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) {
+ $oP->p("A maintenance is ongoing");
+ } else {
+ if ($oMutex->TryLock()) {
+ CronExec($oP, $bVerbose, $bDebug);
+ } else {
+ // Exit silently
+ $oP->p("Already running...");
+ }
+ }
+} catch (Exception $e) {
+ $oP->p("ERROR: '".$e->getMessage()."'");
+ if ($bDebug) {
+ // Might contain verb parameters such a password...
+ $oP->p($e->getTraceAsString());
+ }
+} finally {
+ try {
+ $oMutex->Unlock();
+ } catch (Exception $e) {
+ $oP->p("ERROR: '".$e->getMessage()."'");
+ if ($bDebug) {
+ // Might contain verb parameters such a password...
+ $oP->p($e->getTraceAsString());
+ }
+ }
}
$oP->p("Exiting: ".time().' ('.date('Y-m-d H:i:s').')');
diff --git a/webservices/export-v2.php b/webservices/export-v2.php
index a902bf0442..e505ca6c34 100644
--- a/webservices/export-v2.php
+++ b/webservices/export-v2.php
@@ -1,4 +1,5 @@
p('ERROR: '.utils::HtmlEntities($sErrorMessage));
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
- else
- {
- $oP = new WebPage("iTop - Export");
- $oP->add_http_headers();
- $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
+ if (utils::IsModeCLI()) {
+ $oP = new CLIPage("iTop - Export");
+ $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ } else {
+ $oP = new WebPage("iTop - Export");
+ $oP->add_http_headers();
+ $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
}
function ReportErrorAndUsage($sErrorMessage)
{
- if (utils::IsModeCLI())
- {
- $oP = new CLIPage("iTop - Export");
- $oP->p('ERROR: '.$sErrorMessage);
- Usage($oP);
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
- else {
- $oP = new WebPage("iTop - Export");
- $oP->add_http_headers();
- $oP->p('ERROR: '.$sErrorMessage);
- Usage($oP);
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
+ if (utils::IsModeCLI()) {
+ $oP = new CLIPage("iTop - Export");
+ $oP->p('ERROR: '.$sErrorMessage);
+ Usage($oP);
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ } else {
+ $oP = new WebPage("iTop - Export");
+ $oP->add_http_headers();
+ $oP->p('ERROR: '.$sErrorMessage);
+ Usage($oP);
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
}
function Usage(Page $oP)
{
- if (Utils::IsModeCLI())
- {
- $oP->p('Usage: php '.basename(__FILE__).' --auth_user= --auth_pwd= --expression= --query= [--arg_xxx=] [--no_localize=0|1] [--format=] [--format-options...]');
- $oP->p("Parameters:");
- $oP->p(" * auth_user: the iTop user account for authentication");
- $oP->p(" * auth_pwd: the password of the iTop user account");
- }
- else
- {
- $oP->p("Parameters:");
- }
- $oP->p(" * expression: an OQL expression (e.g. SELECT Contact WHERE name LIKE 'm%')");
- $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
- if (Utils::IsModeCLI())
- {
- $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
- }
- else
- {
- $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
- }
- $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- $oP->p(" * format: (optional, default is html) the desired output format. Can be one of '".implode("', '", array_keys($aSupportedFormats))."'");
- foreach($aSupportedFormats as $sFormatCode => $sLabel)
- {
- $oExporter = BulkExport::FindExporter($sFormatCode);
- if ($oExporter !== null)
- {
- if (!Utils::IsModeCLI())
- {
- $oP->add(' ');
- }
- $oExporter->DisplayUsage($oP);
- if (!Utils::IsModeCLI())
- {
- $oP->add('');
- }
- }
- }
- //if (!Utils::IsModeCLI())
- //{
- // $oP->add('');
- //}
+ if (Utils::IsModeCLI()) {
+ $oP->p('Usage: php '.basename(__FILE__).' --auth_user= --auth_pwd= --expression= --query= [--arg_xxx=] [--no_localize=0|1] [--format=] [--format-options...]');
+ $oP->p("Parameters:");
+ $oP->p(" * auth_user: the iTop user account for authentication");
+ $oP->p(" * auth_pwd: the password of the iTop user account");
+ } else {
+ $oP->p("Parameters:");
+ }
+ $oP->p(" * expression: an OQL expression (e.g. SELECT Contact WHERE name LIKE 'm%')");
+ $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
+ if (Utils::IsModeCLI()) {
+ $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
+ } else {
+ $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
+ }
+ $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ $oP->p(" * format: (optional, default is html) the desired output format. Can be one of '".implode("', '", array_keys($aSupportedFormats))."'");
+ foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
+ $oExporter = BulkExport::FindExporter($sFormatCode);
+ if ($oExporter !== null) {
+ if (!Utils::IsModeCLI()) {
+ $oP->add(' ');
+ }
+ $oExporter->DisplayUsage($oP);
+ if (!Utils::IsModeCLI()) {
+ $oP->add('');
+ }
+ }
+ }
+ //if (!Utils::IsModeCLI())
+ //{
+ // $oP->add('');
+ //}
}
function DisplayExpressionForm(WebPage $oP, $sAction, $sExpression = '', $sExceptionMessage = '', $oForm = null)
{
- $oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:ScopeDefinition'));
- if ($oForm == null) {
- $oForm = FormUIBlockFactory::MakeStandard('export-form');
- $oForm->SetAction($sAction);
- $oP->AddSubBlock($oForm);
- }
- $oForm->AddSubBlock($oPanel);
-
- $oPanel->AddSubBlock(InputUIBlockFactory::MakeForHidden('interactive', '1'));
-
- $oFieldQuery = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelOQLExpression').' ');
- $oTextArea = new TextArea('expression', utils::EscapeHtml($sExpression), "textarea_oql", 70, 8);
- $oTextArea->SetPlaceholder(Dict::S('Core:BulkExportQueryPlaceholder'));
- $oTextArea->AddCSSClasses(["ibo-input-text", "ibo-query-oql", "ibo-is-code"]);
- $oFieldQuery->AddSubBlock($oTextArea);
- $oPanel->AddSubBlock($oFieldQuery);
- if (!empty($sExceptionMessage)) {
- $oAlert = AlertUIBlockFactory::MakeForFailure($sExceptionMessage);
- $oAlert->SetIsCollapsible(false);
- $oPanel->AddSubBlock($oAlert);
- }
-
- $oFieldPhraseBook = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelPhrasebookEntry').' ');
- $oSelect = SelectUIBlockFactory::MakeForSelect('query', "select_phrasebook");
- $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption("", Dict::S('UI:SelectOne'), false));
-
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL');
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- while ($oQuery = $oQueries->Fetch()) {
- $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($oQuery->GetKey(), $oQuery->Get('name'), false));
- }
- $oFieldPhraseBook->AddSubBlock($oSelect);
- $oPanel->AddSubBlock($oFieldPhraseBook);
-
- $oPanel->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), "", "", true, "next-btn"));
- $oP->p(''.Dict::S('Core:BulkExportCanRunNonInteractive').' ');
- $oP->p(''.Dict::S('Core:BulkExportLegacyExport').' ');
- $sJSEmptyOQL = json_encode(Dict::S('Core:BulkExportMessageEmptyOQL'));
- $sJSEmptyQueryId = json_encode(Dict::S('Core:BulkExportMessageEmptyPhrasebookEntry'));
-
- $oP->add_ready_script(
- <<SetAction($sAction);
+ $oP->AddSubBlock($oForm);
+ }
+ $oForm->AddSubBlock($oPanel);
+
+ $oPanel->AddSubBlock(InputUIBlockFactory::MakeForHidden('interactive', '1'));
+
+ $oFieldQuery = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelOQLExpression').' ');
+ $oTextArea = new TextArea('expression', utils::EscapeHtml($sExpression), "textarea_oql", 70, 8);
+ $oTextArea->SetPlaceholder(Dict::S('Core:BulkExportQueryPlaceholder'));
+ $oTextArea->AddCSSClasses(["ibo-input-text", "ibo-query-oql", "ibo-is-code"]);
+ $oFieldQuery->AddSubBlock($oTextArea);
+ $oPanel->AddSubBlock($oFieldQuery);
+ if (!empty($sExceptionMessage)) {
+ $oAlert = AlertUIBlockFactory::MakeForFailure($sExceptionMessage);
+ $oAlert->SetIsCollapsible(false);
+ $oPanel->AddSubBlock($oAlert);
+ }
+
+ $oFieldPhraseBook = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelPhrasebookEntry').' ');
+ $oSelect = SelectUIBlockFactory::MakeForSelect('query', "select_phrasebook");
+ $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption("", Dict::S('UI:SelectOne'), false));
+
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL');
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ while ($oQuery = $oQueries->Fetch()) {
+ $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($oQuery->GetKey(), $oQuery->Get('name'), false));
+ }
+ $oFieldPhraseBook->AddSubBlock($oSelect);
+ $oPanel->AddSubBlock($oFieldPhraseBook);
+
+ $oPanel->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), "", "", true, "next-btn"));
+ $oP->p(''.Dict::S('Core:BulkExportCanRunNonInteractive').' ');
+ $oP->p(''.Dict::S('Core:BulkExportLegacyExport').' ');
+ $sJSEmptyOQL = json_encode(Dict::S('Core:BulkExportMessageEmptyOQL'));
+ $sJSEmptyQueryId = json_encode(Dict::S('Core:BulkExportMessageEmptyPhrasebookEntry'));
+
+ $oP->add_ready_script(
+ <<add_script(DateTimeFormat::GetJSSQLToCustomFormat());
- $sJSDefaultDateTimeFormat = json_encode((string)AttributeDateTime::GetFormat());
- $oP->add_script(
- <<add_script(DateTimeFormat::GetJSSQLToCustomFormat());
+ $sJSDefaultDateTimeFormat = json_encode((string)AttributeDateTime::GetFormat());
+ $oP->add_script(
+ <<LinkScriptFromAppRoot('js/tabularfieldsselector.js');
- $oP->LinkScriptFromAppRoot('js/jquery.dragtable.js');
- $oP->LinkStylesheetFromAppRoot('css/dragtable.css');
-
- $oForm = FormUIBlockFactory::MakeStandard("export-form");
- $oForm->SetAction($sAction);
- $oForm->AddDataAttribute("state", "not-yet-started");
- $oP->AddSubBlock($oForm);
-
- $bExpressionIsValid = true;
- $sExpressionError = '';
- if (($sExpression === null) && ($sQueryId === null)) {
- $bExpressionIsValid = false;
- } else if ($sExpression !== '') {
- try {
- $oExportSearch = DBObjectSearch::FromOQL($sExpression);
- $oExportSearch->UpdateContextFromUser();
- }
- catch (OQLException $e) {
- $bExpressionIsValid = false;
- $sExpressionError = $e->getMessage();
- }
- }
-
- if (!$bExpressionIsValid) {
- DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError,$oForm);
-
- return;
- }
-
- if ($sExpression !== '') {
- $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("expression", $sExpression));
- $oExportSearch = DBObjectSearch::FromOQL($sExpression);
- $oExportSearch->UpdateContextFromUser();
- } else {
- $oQuery = MetaModel::GetObject('QueryOQL', $sQueryId);
- $oExportSearch = DBObjectSearch::FromOQL($oQuery->Get('oql'));
- $oExportSearch->UpdateContextFromUser();
- $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("query", $sQueryId));
- }
- $aFormPartsByFormat = array();
- $aAllFormParts = array();
- if ($sFormat == null) {
- // No specific format chosen
- $sDefaultFormat = utils::ReadParam('format', 'xlsx');
-
-
- $oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("format", Dict::S('Core:BulkExport:ExportFormatPrompt'), "format_selector");
- $oSelect->SetIsLabelBefore(true);
- $oForm->AddSubBlock($oSelect);
-
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- asort($aSupportedFormats);
- foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
- $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sFormatCode, $sLabel, ($sFormatCode == $sDefaultFormat)));
- $oExporter = BulkExport::FindExporter($sFormatCode);
- $oExporter->SetObjectList($oExportSearch);
- $aParts = $oExporter->EnumFormParts();
- foreach ($aParts as $sPartId => $void) {
- $aAllFormParts[$sPartId] = $oExporter;
- }
- $aFormPartsByFormat[$sFormatCode] = array_keys($aParts);
- }
-
- } else {
- // One specific format was chosen
- $oSelect = InputUIBlockFactory::MakeForHidden("format", utils::EscapeHtml($sFormat));
- $oForm->AddSubBlock($oSelect);
-
- $oExporter = BulkExport::FindExporter($sFormat, $oExportSearch);
- $aParts = $oExporter->EnumFormParts();
- foreach ($aParts as $sPartId => $void) {
- $aAllFormParts[$sPartId] = $oExporter;
- }
- $aFormPartsByFormat[$sFormat] = array_keys($aAllFormParts);
- }
- foreach ($aAllFormParts as $sPartId => $oExport) {
- $UIContentBlock = UIContentBlockUIBlockFactory::MakeStandard('form_part_'.$sPartId)->AddCSSClass('form_part');
- $oForm->AddSubBlock($UIContentBlock);
- $UIContentBlock->AddSubBlock($oExport->GetFormPart($oP, $sPartId));
- }
- //end of form
- $oBlockExport = UIContentBlockUIBlockFactory::MakeStandard("export-feedback")->SetIsHidden(true);
- $oBlockExport->AddSubBlock(new Html(''.Dict::S('ExcelExport:PreparingExport').'
'));
- $oBlockExport->AddSubBlock(new Html(''));
- $oP->AddSubBlock($oBlockExport);
- if ($sFormat == null) {//if it's global export
- $oP->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction('export', Dict::S('UI:Button:Export'), 'export', false, 'export-btn'));
- }
- $oBlockResult = UIContentBlockUIBlockFactory::MakeStandard("export_text_result")->SetIsHidden(true);
- $oBlockResult->AddSubBlock(new Html(Dict::S('Core:BulkExport:ExportResult')));
-
- $oTextArea = new TextArea('export_content', '', 'export_content');
- $oTextArea->AddCSSClass('ibo-input-text--export');
- $oBlockResult->AddSubBlock($oTextArea);
- $oP->AddSubBlock($oBlockResult);
-
- $sJSParts = json_encode($aFormPartsByFormat);
- $oP->add_ready_script(
- <<LinkScriptFromAppRoot('js/tabularfieldsselector.js');
+ $oP->LinkScriptFromAppRoot('js/jquery.dragtable.js');
+ $oP->LinkStylesheetFromAppRoot('css/dragtable.css');
+
+ $oForm = FormUIBlockFactory::MakeStandard("export-form");
+ $oForm->SetAction($sAction);
+ $oForm->AddDataAttribute("state", "not-yet-started");
+ $oP->AddSubBlock($oForm);
+
+ $bExpressionIsValid = true;
+ $sExpressionError = '';
+ if (($sExpression === null) && ($sQueryId === null)) {
+ $bExpressionIsValid = false;
+ } elseif ($sExpression !== '') {
+ try {
+ $oExportSearch = DBObjectSearch::FromOQL($sExpression);
+ $oExportSearch->UpdateContextFromUser();
+ } catch (OQLException $e) {
+ $bExpressionIsValid = false;
+ $sExpressionError = $e->getMessage();
+ }
+ }
+
+ if (!$bExpressionIsValid) {
+ DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError, $oForm);
+
+ return;
+ }
+
+ if ($sExpression !== '') {
+ $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("expression", $sExpression));
+ $oExportSearch = DBObjectSearch::FromOQL($sExpression);
+ $oExportSearch->UpdateContextFromUser();
+ } else {
+ $oQuery = MetaModel::GetObject('QueryOQL', $sQueryId);
+ $oExportSearch = DBObjectSearch::FromOQL($oQuery->Get('oql'));
+ $oExportSearch->UpdateContextFromUser();
+ $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("query", $sQueryId));
+ }
+ $aFormPartsByFormat = [];
+ $aAllFormParts = [];
+ if ($sFormat == null) {
+ // No specific format chosen
+ $sDefaultFormat = utils::ReadParam('format', 'xlsx');
+
+ $oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("format", Dict::S('Core:BulkExport:ExportFormatPrompt'), "format_selector");
+ $oSelect->SetIsLabelBefore(true);
+ $oForm->AddSubBlock($oSelect);
+
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ asort($aSupportedFormats);
+ foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
+ $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sFormatCode, $sLabel, ($sFormatCode == $sDefaultFormat)));
+ $oExporter = BulkExport::FindExporter($sFormatCode);
+ $oExporter->SetObjectList($oExportSearch);
+ $aParts = $oExporter->EnumFormParts();
+ foreach ($aParts as $sPartId => $void) {
+ $aAllFormParts[$sPartId] = $oExporter;
+ }
+ $aFormPartsByFormat[$sFormatCode] = array_keys($aParts);
+ }
+
+ } else {
+ // One specific format was chosen
+ $oSelect = InputUIBlockFactory::MakeForHidden("format", utils::EscapeHtml($sFormat));
+ $oForm->AddSubBlock($oSelect);
+
+ $oExporter = BulkExport::FindExporter($sFormat, $oExportSearch);
+ $aParts = $oExporter->EnumFormParts();
+ foreach ($aParts as $sPartId => $void) {
+ $aAllFormParts[$sPartId] = $oExporter;
+ }
+ $aFormPartsByFormat[$sFormat] = array_keys($aAllFormParts);
+ }
+ foreach ($aAllFormParts as $sPartId => $oExport) {
+ $UIContentBlock = UIContentBlockUIBlockFactory::MakeStandard('form_part_'.$sPartId)->AddCSSClass('form_part');
+ $oForm->AddSubBlock($UIContentBlock);
+ $UIContentBlock->AddSubBlock($oExport->GetFormPart($oP, $sPartId));
+ }
+ //end of form
+ $oBlockExport = UIContentBlockUIBlockFactory::MakeStandard("export-feedback")->SetIsHidden(true);
+ $oBlockExport->AddSubBlock(new Html(''.Dict::S('ExcelExport:PreparingExport').'
'));
+ $oBlockExport->AddSubBlock(new Html(''));
+ $oP->AddSubBlock($oBlockExport);
+ if ($sFormat == null) {//if it's global export
+ $oP->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction('export', Dict::S('UI:Button:Export'), 'export', false, 'export-btn'));
+ }
+ $oBlockResult = UIContentBlockUIBlockFactory::MakeStandard("export_text_result")->SetIsHidden(true);
+ $oBlockResult->AddSubBlock(new Html(Dict::S('Core:BulkExport:ExportResult')));
+
+ $oTextArea = new TextArea('export_content', '', 'export_content');
+ $oTextArea->AddCSSClass('ibo-input-text--export');
+ $oBlockResult->AddSubBlock($oTextArea);
+ $oP->AddSubBlock($oBlockResult);
+
+ $sJSParts = json_encode($aFormPartsByFormat);
+ $oP->add_ready_script(
+ <<add('');
- $oP->add_ready_script(
- <<
add('');
+ $oP->add_ready_script(
+ <<SetBreadCrumbEntry('ui-tool-export', Dict::S('Menu:ExportMenu'), Dict::S('Menu:ExportMenu+'), '', 'fas fa-file-export', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
- }
-
- if ($sExpression === null) {
- // No expression supplied, let's check if phrasebook entry is given
- if ($sQueryId !== null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- } else {
- ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
- }
- } else {
- if (utils::IsModeCLI()) {
- Usage($oP);
- ReportErrorAndExit("No expression or query phrasebook identifier supplied.");
- } else {
- // form to enter an OQL query or pick a query phrasebook identifier
- DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
- $oP->output();
- exit;
- }
- }
- }
-
-
- if ($sFormat !== null) {
- $oExporter = BulkExport::FindExporter($sFormat);
- if ($oExporter === null) {
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
- } else {
- DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
- }
- } else {
- DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
- }
- if ($sMode == 'dialog') {
- $oP->add('
');
- }
- $oP->output();
+ );
+ } else {
+ $oP = new iTopWebPage('iTop Export');
+ $oP->SetBreadCrumbEntry('ui-tool-export', Dict::S('Menu:ExportMenu'), Dict::S('Menu:ExportMenu+'), '', 'fas fa-file-export', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
+ }
+
+ if ($sExpression === null) {
+ // No expression supplied, let's check if phrasebook entry is given
+ if ($sQueryId !== null) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ } else {
+ ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
+ }
+ } else {
+ if (utils::IsModeCLI()) {
+ Usage($oP);
+ ReportErrorAndExit("No expression or query phrasebook identifier supplied.");
+ } else {
+ // form to enter an OQL query or pick a query phrasebook identifier
+ DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
+ $oP->output();
+ exit;
+ }
+ }
+ }
+
+ if ($sFormat !== null) {
+ $oExporter = BulkExport::FindExporter($sFormat);
+ if ($oExporter === null) {
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
+ } else {
+ DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
+ }
+ } else {
+ DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
+ }
+ if ($sMode == 'dialog') {
+ $oP->add(' ');
+ }
+ $oP->output();
}
/**
@@ -439,117 +422,103 @@ function InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode)
*/
function CheckParameters($sExpression, $sQueryId, $sFormat)
{
- $oExporter = null;
- $oQuery = null;
-
- if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
- ReportErrorAndExit("The user account is not authorized to access the archives");
- }
-
- if (($sExpression === null) && ($sQueryId === null)) {
- ReportErrorAndUsage("Missing parameter. The parameter 'expression' or 'query' must be specified.");
- }
-
- // Either $sExpression or $sQueryId must be specified
- if ($sExpression === null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- } else {
- ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
- }
- }
- if ($sFormat === null) {
- ReportErrorAndUsage("Missing parameter 'format'.");
- }
-
- // Check if the supplied query is valid (and all the parameters are supplied
- try {
- $oSearch = DBObjectSearch::FromOQL($sExpression);
- $oSearch->UpdateContextFromUser();
- $aArgs = array();
- foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
- $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
- if (!is_null($value)) {
- $aArgs[$sParam] = $value;
- } else {
- throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
- }
- }
- $oSearch->SetInternalParams($aArgs);
-
- $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
- $oExporter = BulkExport::FindExporter($sFormat, $oSearch);
- if ($oExporter == null)
- {
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
- }
- }
- catch(MissingQueryArgument $e)
- {
- $oSearch = null;
- ReportErrorAndUsage("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
- }
- catch(OQLException $e)
- {
- $oSearch = null;
- ReportErrorAndExit("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
- }
- catch(Exception $e)
- {
- $oSearch = null;
- ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
- }
-
- // update last export information if check parameters ok
- if($oQuery != null){
- $oQuery->UpdateLastExportInformation();
- }
-
- $oExporter->SetFormat($sFormat);
- $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
- $oExporter->SetObjectList($oSearch);
- $oExporter->ReadParameters();
-
- return $oExporter;
+ $oExporter = null;
+ $oQuery = null;
+
+ if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
+ ReportErrorAndExit("The user account is not authorized to access the archives");
+ }
+
+ if (($sExpression === null) && ($sQueryId === null)) {
+ ReportErrorAndUsage("Missing parameter. The parameter 'expression' or 'query' must be specified.");
+ }
+
+ // Either $sExpression or $sQueryId must be specified
+ if ($sExpression === null) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ } else {
+ ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
+ }
+ }
+ if ($sFormat === null) {
+ ReportErrorAndUsage("Missing parameter 'format'.");
+ }
+
+ // Check if the supplied query is valid (and all the parameters are supplied
+ try {
+ $oSearch = DBObjectSearch::FromOQL($sExpression);
+ $oSearch->UpdateContextFromUser();
+ $aArgs = [];
+ foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
+ $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
+ if (!is_null($value)) {
+ $aArgs[$sParam] = $value;
+ } else {
+ throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
+ }
+ }
+ $oSearch->SetInternalParams($aArgs);
+
+ $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
+ $oExporter = BulkExport::FindExporter($sFormat, $oSearch);
+ if ($oExporter == null) {
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
+ }
+ } catch (MissingQueryArgument $e) {
+ $oSearch = null;
+ ReportErrorAndUsage("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (OQLException $e) {
+ $oSearch = null;
+ ReportErrorAndExit("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (Exception $e) {
+ $oSearch = null;
+ ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
+ }
+
+ // update last export information if check parameters ok
+ if ($oQuery != null) {
+ $oQuery->UpdateLastExportInformation();
+ }
+
+ $oExporter->SetFormat($sFormat);
+ $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
+ $oExporter->SetObjectList($oSearch);
+ $oExporter->ReadParameters();
+
+ return $oExporter;
}
function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
{
- $oExporter->SetHttpHeaders($oP);
- $exportResult = $oExporter->GetHeader();
- $aStatus = array();
- do
- {
- $exportResult .= $oExporter->GetNextChunk($aStatus);
- }
- while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
-
- if ($aStatus['code'] == 'error')
- {
- $oExporter->Cleanup();
- ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
- }
- else
- {
- $exportResult .= $oExporter->GetFooter();
- $sMimeType = $oExporter->GetMimeType();
- if (substr($sMimeType, 0, 5) == 'text/')
- {
- $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet());
- }
- $oP->SetContentType($sMimeType);
- $oP->SetContentDisposition('attachment', $oExporter->GetDownloadFileName());
- $oP->add($exportResult);
- $oExporter->Cleanup();
- }
+ $oExporter->SetHttpHeaders($oP);
+ $exportResult = $oExporter->GetHeader();
+ $aStatus = [];
+ do {
+ $exportResult .= $oExporter->GetNextChunk($aStatus);
+ } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'error') {
+ $oExporter->Cleanup();
+ ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
+ } else {
+ $exportResult .= $oExporter->GetFooter();
+ $sMimeType = $oExporter->GetMimeType();
+ if (substr($sMimeType, 0, 5) == 'text/') {
+ $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet());
+ }
+ $oP->SetContentType($sMimeType);
+ $oP->SetContentDisposition('attachment', $oExporter->GetDownloadFileName());
+ $oP->add($exportResult);
+ $oExporter->Cleanup();
+ }
}
-
/////////////////////////////////////////////////////////////////////////////
//
// Command Line mode
@@ -562,116 +531,103 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
$oCtx = new ContextTag(ContextTag::TAG_EXPORT);
if (utils::IsModeCLI()) {
- SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('iTop - Export'));
-
- try {
- // Do this before loging, in order to allow setting user credentials from within the file
- utils::UseParamFile();
- }
- catch (Exception $e) {
- echo "Error: ".utils::HtmlEntities($e->getMessage())." \n";
- exit(EXIT_CODE_FATAL);
- }
-
- $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
- $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
- if ($sAuthUser == null) {
- ReportErrorAndUsage("Missing parameter '--auth_user'");
- }
- if ($sAuthPwd == null) {
- ReportErrorAndUsage("Missing parameter '--auth_pwd'");
- }
-
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
- UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
- ReportErrorAndExit("Access restricted or wrong credentials for user '$sAuthUser'");
- }
-
- $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
- $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
- $bLocalize = (utils::ReadParam('no_localize', 0) != 1);
- if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
- ReportErrorAndExit("The user account is not authorized to access the archives");
- }
-
- if (($sExpression == null) && ($sQueryId == null)) {
- ReportErrorAndUsage("Missing parameter. At least one of '--expression' or '--query' must be specified.");
- }
-
- if ($sExpression === null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- } else {
- ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
- }
- }
- try {
- $oSearch = DBObjectSearch::FromOQL($sExpression);
- $oSearch->UpdateContextFromUser();
- $aArgs = array();
- foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
- $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
- if (!is_null($value)) {
- $aArgs[$sParam] = $value;
- } else {
- throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
- }
- }
- $oSearch->SetInternalParams($aArgs);
-
- $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
- $oExporter = BulkExport::FindExporter($sFormat);
- if ($oExporter == null)
- {
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
- }
-
- $oExporter->SetFormat($sFormat);
- $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
- $oExporter->SetObjectList($oSearch);
- $oExporter->ReadParameters();
-
- $exportResult = $oExporter->GetHeader();
- $aStatus = array();
-
- do
- {
- $exportResult .= $oExporter->GetNextChunk($aStatus);
- }
- while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
-
- if ($aStatus['code'] == 'error')
- {
- ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
- }
- else
- {
- $exportResult .= $oExporter->GetFooter();
- echo $exportResult;
- }
- $oExporter->Cleanup();
-
- }
- catch(MissingQueryArgument $e)
- {
- ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
- }
- catch(OQLException $e)
- {
- ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
- }
- catch(Exception $e)
- {
- ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
- }
-
- exit;
+ SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('iTop - Export'));
+
+ try {
+ // Do this before loging, in order to allow setting user credentials from within the file
+ utils::UseParamFile();
+ } catch (Exception $e) {
+ echo "Error: ".utils::HtmlEntities($e->getMessage())." \n";
+ exit(EXIT_CODE_FATAL);
+ }
+
+ $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
+ $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
+ if ($sAuthUser == null) {
+ ReportErrorAndUsage("Missing parameter '--auth_user'");
+ }
+ if ($sAuthPwd == null) {
+ ReportErrorAndUsage("Missing parameter '--auth_pwd'");
+ }
+
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ ReportErrorAndExit("Access restricted or wrong credentials for user '$sAuthUser'");
+ }
+
+ $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
+ $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
+ $bLocalize = (utils::ReadParam('no_localize', 0) != 1);
+ if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
+ ReportErrorAndExit("The user account is not authorized to access the archives");
+ }
+
+ if (($sExpression == null) && ($sQueryId == null)) {
+ ReportErrorAndUsage("Missing parameter. At least one of '--expression' or '--query' must be specified.");
+ }
+
+ if ($sExpression === null) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ } else {
+ ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
+ }
+ }
+ try {
+ $oSearch = DBObjectSearch::FromOQL($sExpression);
+ $oSearch->UpdateContextFromUser();
+ $aArgs = [];
+ foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
+ $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
+ if (!is_null($value)) {
+ $aArgs[$sParam] = $value;
+ } else {
+ throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
+ }
+ }
+ $oSearch->SetInternalParams($aArgs);
+
+ $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
+ $oExporter = BulkExport::FindExporter($sFormat);
+ if ($oExporter == null) {
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
+ }
+
+ $oExporter->SetFormat($sFormat);
+ $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
+ $oExporter->SetObjectList($oSearch);
+ $oExporter->ReadParameters();
+
+ $exportResult = $oExporter->GetHeader();
+ $aStatus = [];
+
+ do {
+ $exportResult .= $oExporter->GetNextChunk($aStatus);
+ } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'error') {
+ ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
+ } else {
+ $exportResult .= $oExporter->GetFooter();
+ echo $exportResult;
+ }
+ $oExporter->Cleanup();
+
+ } catch (MissingQueryArgument $e) {
+ ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (OQLException $e) {
+ ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (Exception $e) {
+ ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
+ }
+
+ exit;
}
/////////////////////////////////////////////////////////////////////////////
@@ -680,59 +636,56 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
//
/////////////////////////////////////////////////////////////////////////////
-try
-{
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
-
- // Main parameters
- $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
- $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
- $sFormat = utils::ReadParam('format', null, true /* Allow CLI */);
- $sFileName = utils::ReadParam('filename', '', true, 'string');
- $bInteractive = utils::ReadParam('interactive', false);
- $sMode = utils::ReadParam('mode', '');
-
- LoginWebPage::DoLogin(); // Check user rights and prompt if needed
-
- ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
-
- if ($bInteractive) {
- InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode);
- } else {
- $oExporter = CheckParameters($sExpression, $sQueryId, $sFormat);
- $sMimeType = $oExporter->GetMimeType();
- if ($sMimeType == 'text/html') {
- // Note: Using NiceWebPage only for HTML export as it includes JS scripts & files, which makes no sense in other export formats. More over, it breaks Excel spreadsheet import.
- if ($oExporter instanceof HTMLBulkExport) {
- $oP = new NiceWebPage('iTop export');
- $oP->add_http_headers();
- $oP->add_ready_script("$('table.listResults').tablesorter({widgets: ['MyZebra']});");
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
- } else {
- $oP = new WebPage('iTop export');
- $oP->add_http_headers();
- $oP->add_style("table br { mso-data-placement:same-cell; }"); // Trick for Excel: keep line breaks inside the same cell !
- }
- $oP->add_style("body { overflow: auto; }");
- } else {
- $oP = new DownloadPage('iTop export');
- $oP->SetContentType($oExporter->GetMimeType());
- }
- DoExport($oP, $oExporter, false);
- $oP->output();
- }
-}
-catch (BulkExportMissingParameterException $e) {
- $oP = new AjaxPage('iTop Export');
- $oP->add(utils::HtmlEntities($e->getMessage()));
- Usage($oP);
- $oP->output();
-}
-catch (Exception $e) {
- $oP = new WebPage('iTop Export');
- $oP->add_http_headers();
- $oP->add('Error: '.utils::HtmlEntities($e->getMessage()));
- IssueLog::Error(utils::HtmlEntities($e->getMessage())."\n".$e->getTraceAsString());
- $oP->output();
+try {
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+
+ // Main parameters
+ $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
+ $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
+ $sFormat = utils::ReadParam('format', null, true /* Allow CLI */);
+ $sFileName = utils::ReadParam('filename', '', true, 'string');
+ $bInteractive = utils::ReadParam('interactive', false);
+ $sMode = utils::ReadParam('mode', '');
+
+ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
+
+ ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
+
+ if ($bInteractive) {
+ InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode);
+ } else {
+ $oExporter = CheckParameters($sExpression, $sQueryId, $sFormat);
+ $sMimeType = $oExporter->GetMimeType();
+ if ($sMimeType == 'text/html') {
+ // Note: Using NiceWebPage only for HTML export as it includes JS scripts & files, which makes no sense in other export formats. More over, it breaks Excel spreadsheet import.
+ if ($oExporter instanceof HTMLBulkExport) {
+ $oP = new NiceWebPage('iTop export');
+ $oP->add_http_headers();
+ $oP->add_ready_script("$('table.listResults').tablesorter({widgets: ['MyZebra']});");
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
+ } else {
+ $oP = new WebPage('iTop export');
+ $oP->add_http_headers();
+ $oP->add_style("table br { mso-data-placement:same-cell; }"); // Trick for Excel: keep line breaks inside the same cell !
+ }
+ $oP->add_style("body { overflow: auto; }");
+ } else {
+ $oP = new DownloadPage('iTop export');
+ $oP->SetContentType($oExporter->GetMimeType());
+ }
+ DoExport($oP, $oExporter, false);
+ $oP->output();
+ }
+} catch (BulkExportMissingParameterException $e) {
+ $oP = new AjaxPage('iTop Export');
+ $oP->add(utils::HtmlEntities($e->getMessage()));
+ Usage($oP);
+ $oP->output();
+} catch (Exception $e) {
+ $oP = new WebPage('iTop Export');
+ $oP->add_http_headers();
+ $oP->add('Error: '.utils::HtmlEntities($e->getMessage()));
+ IssueLog::Error(utils::HtmlEntities($e->getMessage())."\n".$e->getTraceAsString());
+ $oP->output();
}
diff --git a/webservices/export.php b/webservices/export.php
index 3a43ce4089..c35d4f1f3c 100644
--- a/webservices/export.php
+++ b/webservices/export.php
@@ -1,4 +1,5 @@
GetMessage()." \n";
- exit(EXIT_CODE_FATAL);
+try {
+ // Do this before loging, in order to allow setting user credentials from within the file
+ utils::UseParamFile();
+} catch (Exception $e) {
+ echo "Error: ".$e->GetMessage()." \n";
+ exit(EXIT_CODE_FATAL);
}
/**
* @since 3.1.0 N°6047
*/
$oCtx = new ContextTag(ContextTag::TAG_EXPORT);
-if (utils::IsModeCLI())
-{
- $oP = new CLIPage("iTop - Export");
- SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
-
- $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
- $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
-
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
- {
- UserRights::Login($sAuthUser); // Login & set the user's language
- }
- else
- {
- $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
-}
-else
-{
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
- LoginWebPage::DoLogin(); // Check user rights and prompt if needed
+if (utils::IsModeCLI()) {
+ $oP = new CLIPage("iTop - Export");
+ SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
+
+ $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
+ $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
+
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
+} else {
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
}
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
-
$oAppContext = new ApplicationContext();
$iActiveNodeId = utils::ReadParam('menu', -1);
$currentOrganization = utils::ReadParam('org_id', '');
-if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive())
-{
- $oP = new CLIPage("iTop - Export");
- $oP->p("The user account is not authorized to access the archives");
- $oP->output();
- exit(EXIT_CODE_ERROR);
+if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
+ $oP = new CLIPage("iTop - Export");
+ $oP->p("The user account is not authorized to access the archives");
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
}
$bLocalize = (utils::ReadParam('no_localize', 0) != 1);
@@ -108,287 +95,242 @@
$oQuery = null;
-if (strlen($sExpression) == 0)
-{
- $sQueryId = trim(utils::ReadParam('query', '', true /* Allow CLI */, 'raw_data'));
- if (strlen($sQueryId) > 0)
- {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0)
- {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- if (strlen($sFields) == 0)
- {
- $sFields = trim($oQuery->Get('fields'));
- }
- }
- }
+if (strlen($sExpression) == 0) {
+ $sQueryId = trim(utils::ReadParam('query', '', true /* Allow CLI */, 'raw_data'));
+ if (strlen($sQueryId) > 0) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ if (strlen($sFields) == 0) {
+ $sFields = trim($oQuery->Get('fields'));
+ }
+ }
+ }
}
$sFormat = strtolower(utils::ReadParam('format', 'html', true /* Allow CLI */));
-
$aFields = explode(',', $sFields);
// Clean the list of columns (empty it if every string is empty)
-foreach($aFields as $index => $sField)
-{
- $aFields[$index] = trim($sField);
- if(strlen($aFields[$index]) == 0)
- {
- unset($aFields[$index]);
- }
+foreach ($aFields as $index => $sField) {
+ $aFields[$index] = trim($sField);
+ if (strlen($aFields[$index]) == 0) {
+ unset($aFields[$index]);
+ }
}
$oP = null;
-if (!empty($sExpression))
-{
- try
- {
- $oFilter = DBObjectSearch::FromOQL($sExpression);
-
- // Check and adjust column names
- //
- $aAliasToFields = array();
- foreach($aFields as $index => $sField)
- {
- if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches))
- {
- $sClassAlias = $aMatches[1];
- $sAttCode = $aMatches[2];
- }
- else
- {
- $sClassAlias = $oFilter->GetClassAlias();
- $sAttCode = $sField;
- // Disambiguate the class alias
- $aFields[$index] = $sClassAlias.'.'.$sAttCode;
- }
- $aAliasToFields[$sClassAlias][] = $sAttCode;
-
- $sClass = $oFilter->GetClassName($sClassAlias);
- if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
- {
- throw new CoreException("Invalid field specification $sField: $sAttCode is not a valid attribute for $sClass");
- }
- $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
- if ($oAttDef instanceof AttributeSubItem)
- {
- $aAliasToFields[$sClassAlias][] = $oAttDef->GetParentAttCode();
- }
- else if($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName())
- {
- $sKeyAttCode = $oAttDef->GetKeyAttCode();
- $aAliasToFields[$sClassAlias][] = $sKeyAttCode;
- }
- }
-
- // Read query parameters
- //
- $aArgs = array();
- foreach($oFilter->GetQueryParams() as $sParam => $foo)
- {
- $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
- if (!is_null($value))
- {
- $aArgs[$sParam] = $value;
- }
- }
- $oFilter->SetInternalParams($aArgs);
- foreach ($oFilter->GetSelectedClasses() as $sAlias => $sClass)
- {
- if ((UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) && UR_ALLOWED_YES) == 0)
- {
- throw new Exception("The current user does not have permission for exporting data of class $sClass");
- }
- }
-
- // update last export information if check parameters ok
- if($oQuery != null){
- $oQuery->UpdateLastExportInformation();
- }
-
- if ($oFilter)
- {
- $oSet = new CMDBObjectSet($oFilter, array(), $aArgs);
- $oSet->OptimizeColumnLoad($aAliasToFields);
- switch($sFormat)
- {
- case 'html':
- $oP = new NiceWebPage("iTop - Export");
- $oP->add_style('body { overflow: auto; }'); // Show scroll bars if needed
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
-
- // Integration within MS-Excel web queries + HTTPS + IIS:
- // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
- // Then the fix is to force the reset of header values Pragma and Cache-control
- header("Cache-control:", true);
- header("Pragma:", true);
-
- // The HTML output is made for pages located in the /pages/ folder
- // since this page is in a different folder, let's adjust the HTML 'base' attribute
- // to make the relative hyperlinks in the page work
- $sUrl = utils::GetAbsoluteUrlAppRoot();
- $oP->set_base($sUrl.'pages/');
-
- if (count($aFields) > 0) {
- $iSearch = array_search('id', $aFields);
- if ($iSearch !== false) {
- $bViewLink = true;
- unset($aFields[$iSearch]);
- } else {
- $bViewLink = false;
- }
- $sFields = implode(',', $aFields);
- $aExtraParams = array(
- 'menu' => false,
- 'toolkit_menu' => false,
- 'display_limit' => false,
- 'localize_values' => $bLocalize,
- 'zlist' => false,
- 'extra_fields' => $sFields,
- 'view_link' => $bViewLink,
- );
- } else {
- $aExtraParams = array(
- 'menu' => false,
- 'toolkit_menu' => false,
- 'display_limit' => false,
- 'localize_values' => $bLocalize,
- 'zlist' => 'details',
- );
- }
-
- $oResultBlock = new DisplayBlock($oFilter, 'list', false, $aExtraParams);
- $oResultBlock->Display($oP, 'expresult');
- break;
-
- case 'csv':
- $oP = new CSVPage("iTop - Export");
- $sFields = implode(',', $aFields);
- $sCharset = utils::ReadParam('charset', MetaModel::GetConfig()->Get('csv_file_default_charset'), true /* Allow CLI */, 'raw_data');
- $sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize), $sCharset);
- if ($sCharset == 'UTF-8')
- {
- $sOutputData = UTF8_BOM.$sCSVData;
- }
- else
- {
- $sOutputData = $sCSVData;
- }
- if ($sFileName == '')
- {
- // Plain text => Firefox will NOT propose to download the file
- $oP->add_header("Content-type: text/plain; charset=$sCharset");
- }
- else
- {
- $oP->add_header("Content-type: text/csv; charset=$sCharset");
- }
- $oP->add($sOutputData);
- break;
-
- case 'spreadsheet':
- $oP = new WebPage("iTop - Export for spreadsheet");
-
- // Integration within MS-Excel web queries + HTTPS + IIS:
- // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
- // Then the fix is to force the reset of header values Pragma and Cache-control
- header("Pragma:", true);
- header("Cache-control:", true);
-
- $sFields = implode(',', $aFields);
- $oP->add_style('table br {mso-data-placement:same-cell;}'); // Trick for Excel: keep line breaks inside the same cell !
- cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize));
- break;
-
- case 'xml':
- $oP = new XMLPage("iTop - Export", true /* passthrough */);
- cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, array('localize_values' => $bLocalize));
- break;
-
- case 'xlsx':
- $oP = new AjaxPage('');
- $oExporter = new ExcelExporter();
- $oExporter->SetObjectList($oFilter);
-
- // Run the export by chunk of 1000 objects to limit memory usage
- $oExporter->SetChunkSize(1000);
- do
- {
- $aStatus = $oExporter->Run(); // process one chunk
- }
- while( ($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
-
- if ($aStatus['code'] == 'done')
- {
- $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
- $oP->SetContentDisposition('attachment', $oFilter->GetClass().'.xlsx');
- $oP->add(file_get_contents($oExporter->GetExcelFilePath()));
- $oExporter->Cleanup();
- }
- else
- {
- $oP->add('Error, xlsx export failed: '.$aStatus['message']);
- }
- break;
-
- default:
- $oP = new WebPage("iTop - Export");
- $oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
- }
- }
- }
- catch(Exception $e)
- {
- $oP = new WebPage("iTop - Export");
- $oP->p("Error the query can not be executed.");
- if ($e instanceof CoreException)
- {
- $oP->p($e->GetHtmlDesc());
- }
- else
- {
- $oP->p($e->getMessage());
- }
- }
+if (!empty($sExpression)) {
+ try {
+ $oFilter = DBObjectSearch::FromOQL($sExpression);
+
+ // Check and adjust column names
+ //
+ $aAliasToFields = [];
+ foreach ($aFields as $index => $sField) {
+ if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches)) {
+ $sClassAlias = $aMatches[1];
+ $sAttCode = $aMatches[2];
+ } else {
+ $sClassAlias = $oFilter->GetClassAlias();
+ $sAttCode = $sField;
+ // Disambiguate the class alias
+ $aFields[$index] = $sClassAlias.'.'.$sAttCode;
+ }
+ $aAliasToFields[$sClassAlias][] = $sAttCode;
+
+ $sClass = $oFilter->GetClassName($sClassAlias);
+ if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
+ throw new CoreException("Invalid field specification $sField: $sAttCode is not a valid attribute for $sClass");
+ }
+ $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+ if ($oAttDef instanceof AttributeSubItem) {
+ $aAliasToFields[$sClassAlias][] = $oAttDef->GetParentAttCode();
+ } elseif ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) {
+ $sKeyAttCode = $oAttDef->GetKeyAttCode();
+ $aAliasToFields[$sClassAlias][] = $sKeyAttCode;
+ }
+ }
+
+ // Read query parameters
+ //
+ $aArgs = [];
+ foreach ($oFilter->GetQueryParams() as $sParam => $foo) {
+ $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
+ if (!is_null($value)) {
+ $aArgs[$sParam] = $value;
+ }
+ }
+ $oFilter->SetInternalParams($aArgs);
+ foreach ($oFilter->GetSelectedClasses() as $sAlias => $sClass) {
+ if ((UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) && UR_ALLOWED_YES) == 0) {
+ throw new Exception("The current user does not have permission for exporting data of class $sClass");
+ }
+ }
+
+ // update last export information if check parameters ok
+ if ($oQuery != null) {
+ $oQuery->UpdateLastExportInformation();
+ }
+
+ if ($oFilter) {
+ $oSet = new CMDBObjectSet($oFilter, [], $aArgs);
+ $oSet->OptimizeColumnLoad($aAliasToFields);
+ switch ($sFormat) {
+ case 'html':
+ $oP = new NiceWebPage("iTop - Export");
+ $oP->add_style('body { overflow: auto; }'); // Show scroll bars if needed
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
+
+ // Integration within MS-Excel web queries + HTTPS + IIS:
+ // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
+ // Then the fix is to force the reset of header values Pragma and Cache-control
+ header("Cache-control:", true);
+ header("Pragma:", true);
+
+ // The HTML output is made for pages located in the /pages/ folder
+ // since this page is in a different folder, let's adjust the HTML 'base' attribute
+ // to make the relative hyperlinks in the page work
+ $sUrl = utils::GetAbsoluteUrlAppRoot();
+ $oP->set_base($sUrl.'pages/');
+
+ if (count($aFields) > 0) {
+ $iSearch = array_search('id', $aFields);
+ if ($iSearch !== false) {
+ $bViewLink = true;
+ unset($aFields[$iSearch]);
+ } else {
+ $bViewLink = false;
+ }
+ $sFields = implode(',', $aFields);
+ $aExtraParams = [
+ 'menu' => false,
+ 'toolkit_menu' => false,
+ 'display_limit' => false,
+ 'localize_values' => $bLocalize,
+ 'zlist' => false,
+ 'extra_fields' => $sFields,
+ 'view_link' => $bViewLink,
+ ];
+ } else {
+ $aExtraParams = [
+ 'menu' => false,
+ 'toolkit_menu' => false,
+ 'display_limit' => false,
+ 'localize_values' => $bLocalize,
+ 'zlist' => 'details',
+ ];
+ }
+
+ $oResultBlock = new DisplayBlock($oFilter, 'list', false, $aExtraParams);
+ $oResultBlock->Display($oP, 'expresult');
+ break;
+
+ case 'csv':
+ $oP = new CSVPage("iTop - Export");
+ $sFields = implode(',', $aFields);
+ $sCharset = utils::ReadParam('charset', MetaModel::GetConfig()->Get('csv_file_default_charset'), true /* Allow CLI */, 'raw_data');
+ $sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize], $sCharset);
+ if ($sCharset == 'UTF-8') {
+ $sOutputData = UTF8_BOM.$sCSVData;
+ } else {
+ $sOutputData = $sCSVData;
+ }
+ if ($sFileName == '') {
+ // Plain text => Firefox will NOT propose to download the file
+ $oP->add_header("Content-type: text/plain; charset=$sCharset");
+ } else {
+ $oP->add_header("Content-type: text/csv; charset=$sCharset");
+ }
+ $oP->add($sOutputData);
+ break;
+
+ case 'spreadsheet':
+ $oP = new WebPage("iTop - Export for spreadsheet");
+
+ // Integration within MS-Excel web queries + HTTPS + IIS:
+ // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
+ // Then the fix is to force the reset of header values Pragma and Cache-control
+ header("Pragma:", true);
+ header("Cache-control:", true);
+
+ $sFields = implode(',', $aFields);
+ $oP->add_style('table br {mso-data-placement:same-cell;}'); // Trick for Excel: keep line breaks inside the same cell !
+ cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize]);
+ break;
+
+ case 'xml':
+ $oP = new XMLPage("iTop - Export", true /* passthrough */);
+ cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, ['localize_values' => $bLocalize]);
+ break;
+
+ case 'xlsx':
+ $oP = new AjaxPage('');
+ $oExporter = new ExcelExporter();
+ $oExporter->SetObjectList($oFilter);
+
+ // Run the export by chunk of 1000 objects to limit memory usage
+ $oExporter->SetChunkSize(1000);
+ do {
+ $aStatus = $oExporter->Run(); // process one chunk
+ } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'done') {
+ $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ $oP->SetContentDisposition('attachment', $oFilter->GetClass().'.xlsx');
+ $oP->add(file_get_contents($oExporter->GetExcelFilePath()));
+ $oExporter->Cleanup();
+ } else {
+ $oP->add('Error, xlsx export failed: '.$aStatus['message']);
+ }
+ break;
+
+ default:
+ $oP = new WebPage("iTop - Export");
+ $oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
+ }
+ }
+ } catch (Exception $e) {
+ $oP = new WebPage("iTop - Export");
+ $oP->p("Error the query can not be executed.");
+ if ($e instanceof CoreException) {
+ $oP->p($e->GetHtmlDesc());
+ } else {
+ $oP->p($e->getMessage());
+ }
+ }
}
-if (!$oP)
-{
- // Display a short message about how to use this page
- $bModeCLI = utils::IsModeCLI();
- if ($bModeCLI)
- {
- $oP = new CLIPage("iTop - Export");
- }
- else
- {
- $oP = new WebPage("iTop - Export");
- }
- $oP->p("General purpose export page.");
- $oP->p("Parameters:");
- $oP->p(" * expression: an OQL expression (URL encoded if needed)");
- $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
- if (Utils::IsModeCLI()) {
- $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
- } else {
- $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
- }
- $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
- $oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv', 'xlsx' or 'xml'");
- $oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes, or alias.attcode) separated by a comma");
- $oP->p(" * fields_advanced: (optional, no effect on XML/HTML formats ; ignored is fields is specified) If set to 1, the default list of fields will include the external keys and their reconciliation keys");
- $oP->p(" * filename: (optional, no effect in CLI mode) if set then the results will be downloaded as a file");
+if (!$oP) {
+ // Display a short message about how to use this page
+ $bModeCLI = utils::IsModeCLI();
+ if ($bModeCLI) {
+ $oP = new CLIPage("iTop - Export");
+ } else {
+ $oP = new WebPage("iTop - Export");
+ }
+ $oP->p("General purpose export page.");
+ $oP->p("Parameters:");
+ $oP->p(" * expression: an OQL expression (URL encoded if needed)");
+ $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
+ if (Utils::IsModeCLI()) {
+ $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
+ } else {
+ $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
+ }
+ $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
+ $oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv', 'xlsx' or 'xml'");
+ $oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes, or alias.attcode) separated by a comma");
+ $oP->p(" * fields_advanced: (optional, no effect on XML/HTML formats ; ignored is fields is specified) If set to 1, the default list of fields will include the external keys and their reconciliation keys");
+ $oP->p(" * filename: (optional, no effect in CLI mode) if set then the results will be downloaded as a file");
}
-if ($sFileName != '')
-{
- $oP->add_header('Content-Disposition: attachment; filename="'.$sFileName.'"');
+if ($sFileName != '') {
+ $oP->add_header('Content-Disposition: attachment; filename="'.$sFileName.'"');
}
$oP->TrashUnexpectedOutput();
$oP->output();
-?>
diff --git a/webservices/import.php b/webservices/import.php
index dc009b8409..2c757fc945 100644
--- a/webservices/import.php
+++ b/webservices/import.php
@@ -1,4 +1,5 @@
array
- (
- 'mandatory' => true,
- 'modes' => 'cli',
- 'default' => null,
- 'description' => 'login (must have enough rights to create objects of the given class)',
- ),
- 'auth_pwd' => array
- (
- 'mandatory' => true,
- 'modes' => 'cli',
- 'default' => null,
- 'description' => 'password',
- ),
- 'class' => array
- (
- 'mandatory' => true,
- 'modes' => 'http,cli',
- 'default' => null,
- 'description' => 'class of loaded objects',
- ),
- 'csvdata' => array
- (
- 'mandatory' => true,
- 'modes' => 'http',
- 'default' => null,
- 'description' => 'data',
- ),
- 'csvfile' => array
- (
- 'mandatory' => true,
- 'modes' => 'cli',
- 'default' => '',
- 'description' => 'local data file, replaces csvdata if specified',
- ),
- 'charset' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'Character set encoding of the CSV data: UTF-8, ISO-8859-1, WINDOWS-1251, WINDOWS-1252, ISO-8859-15, If blank, then the charset is set to config(csv_file_default_charset)',
- ),
- 'date_format' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'Input date format (used both for dates and datetimes) - Examples: Y-m-d H:i:s, d/m/Y H:i:s (Europe) - no transformation is applied if the argument is omitted. (note: old format specification using %Y %m %d is also supported for backward compatibility)',
- ),
- 'separator' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => ',',
- 'description' => 'column separator in CSV data (1 char, or \'tab\')',
- ),
- 'qualifier' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '"',
- 'description' => 'test qualifier in CSV data',
- ),
- 'output' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => 'summary',
- 'description' => '[retcode] to return the count of lines in error, [summary] to return a concise report, [details] to get a detailed report (each line listed)',
- ),
+$aPageParams =
+[
+ 'auth_user' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'cli',
+ 'default' => null,
+ 'description' => 'login (must have enough rights to create objects of the given class)',
+ ],
+ 'auth_pwd' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'cli',
+ 'default' => null,
+ 'description' => 'password',
+ ],
+ 'class' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'http,cli',
+ 'default' => null,
+ 'description' => 'class of loaded objects',
+ ],
+ 'csvdata' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'http',
+ 'default' => null,
+ 'description' => 'data',
+ ],
+ 'csvfile' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'cli',
+ 'default' => '',
+ 'description' => 'local data file, replaces csvdata if specified',
+ ],
+ 'charset' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'Character set encoding of the CSV data: UTF-8, ISO-8859-1, WINDOWS-1251, WINDOWS-1252, ISO-8859-15, If blank, then the charset is set to config(csv_file_default_charset)',
+ ],
+ 'date_format' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'Input date format (used both for dates and datetimes) - Examples: Y-m-d H:i:s, d/m/Y H:i:s (Europe) - no transformation is applied if the argument is omitted. (note: old format specification using %Y %m %d is also supported for backward compatibility)',
+ ],
+ 'separator' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => ',',
+ 'description' => 'column separator in CSV data (1 char, or \'tab\')',
+ ],
+ 'qualifier' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '"',
+ 'description' => 'test qualifier in CSV data',
+ ],
+ 'output' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => 'summary',
+ 'description' => '[retcode] to return the count of lines in error, [summary] to return a concise report, [details] to get a detailed report (each line listed)',
+ ],
/*
- 'reportlevel' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => 'errors|warnings|created|changed|unchanged',
- 'description' => 'combination of flags to limit the detailed output',
- ),
+ 'reportlevel' => array
+ (
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => 'errors|warnings|created|changed|unchanged',
+ 'description' => 'combination of flags to limit the detailed output',
+ ),
*/
- 'reconciliationkeys' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'name of the columns used to identify existing objects and update them, or create a new one',
- ),
- 'simulate' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '0',
- 'description' => 'If set to 1, then the load will not be executed, but the expected report will be produced',
- ),
- 'comment' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'Comment to be added into the change log',
- ),
- 'no_localize' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '0',
- 'description' => 'If set to 0, then header and values are supposed to be localized in the language of the logged in user. Set to 1 to use internal attribute codes and values (enums)',
- ),
-);
+ 'reconciliationkeys' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'name of the columns used to identify existing objects and update them, or create a new one',
+ ],
+ 'simulate' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '0',
+ 'description' => 'If set to 1, then the load will not be executed, but the expected report will be produced',
+ ],
+ 'comment' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'Comment to be added into the change log',
+ ],
+ 'no_localize' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '0',
+ 'description' => 'If set to 0, then header and values are supposed to be localized in the language of the logged in user. Set to 1 to use internal attribute codes and values (enums)',
+ ],
+];
function UsageAndExit($oP)
{
- global $aPageParams;
- $bModeCLI = utils::IsModeCLI();
-
- $oP->p("USAGE:\n");
- foreach($aPageParams as $sParam => $aParamData)
- {
- $aModes = explode(',', $aParamData['modes']);
- if ($bModeCLI)
- {
- if (in_array('cli', $aModes))
- {
- $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
- $oP->p("$sParam = $sDesc");
- }
- }
- else
- {
- if (in_array('http', $aModes))
- {
- $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
- $oP->p("$sParam = $sDesc");
- }
- }
- }
- $oP->output();
- exit;
+ global $aPageParams;
+ $bModeCLI = utils::IsModeCLI();
+
+ $oP->p("USAGE:\n");
+ foreach ($aPageParams as $sParam => $aParamData) {
+ $aModes = explode(',', $aParamData['modes']);
+ if ($bModeCLI) {
+ if (in_array('cli', $aModes)) {
+ $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
+ $oP->p("$sParam = $sDesc");
+ }
+ } else {
+ if (in_array('http', $aModes)) {
+ $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
+ $oP->p("$sParam = $sDesc");
+ }
+ }
+ }
+ $oP->output();
+ exit;
}
-
function ReadParam($oP, $sParam, $sSanitizationFilter = 'parameter')
{
- global $aPageParams;
- assert(isset($aPageParams[$sParam]));
- assert(!$aPageParams[$sParam]['mandatory']);
- $sValue = utils::ReadParam($sParam, $aPageParams[$sParam]['default'], true /* Allow CLI */, $sSanitizationFilter);
- return trim($sValue);
+ global $aPageParams;
+ assert(isset($aPageParams[$sParam]));
+ assert(!$aPageParams[$sParam]['mandatory']);
+ $sValue = utils::ReadParam($sParam, $aPageParams[$sParam]['default'], true /* Allow CLI */, $sSanitizationFilter);
+ return trim($sValue);
}
function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
{
- global $aPageParams;
- assert(isset($aPageParams[$sParam]));
- assert($aPageParams[$sParam]['mandatory']);
-
- $sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter);
- if (is_null($sValue))
- {
- $oP->p("ERROR: Missing argument '$sParam'\n");
- UsageAndExit($oP);
- }
- return trim($sValue);
+ global $aPageParams;
+ assert(isset($aPageParams[$sParam]));
+ assert($aPageParams[$sParam]['mandatory']);
+
+ $sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter);
+ if (is_null($sValue)) {
+ $oP->p("ERROR: Missing argument '$sParam'\n");
+ UsageAndExit($oP);
+ }
+ return trim($sValue);
}
/////////////////////////////////
@@ -213,60 +205,47 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
* @since 3.1.0 N°6047
*/
$oCtx = new ContextTag(ContextTag::TAG_IMPORT);
-if (utils::IsModeCLI())
-{
- $oP = new CLIPage("iTop - Bulk import");
- SetupUtils::CheckPhpAndExtensionsForCli($oP, -2);
-}
-else
-{
- $oP = new CSVPage("iTop - Bulk import");
+if (utils::IsModeCLI()) {
+ $oP = new CLIPage("iTop - Bulk import");
+ SetupUtils::CheckPhpAndExtensionsForCli($oP, -2);
+} else {
+ $oP = new CSVPage("iTop - Bulk import");
}
-try
-{
- utils::UseParamFile();
-}
-catch(Exception $e)
-{
- $oP->p("Error: ".$e->GetMessage());
- $oP->output();
- exit(-2);
+try {
+ utils::UseParamFile();
+} catch (Exception $e) {
+ $oP->p("Error: ".$e->GetMessage());
+ $oP->output();
+ exit(-2);
}
-if (utils::IsModeCLI())
-{
- // Next steps:
- // specific arguments: 'csvfile'
- //
- $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
- $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
- $sCsvFile = ReadMandatoryParam($oP, 'csvfile', 'raw_data');
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
- {
- UserRights::Login($sAuthUser); // Login & set the user's language
- }
- else
- {
- $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
- $oP->output();
- exit(-1);
- }
-
- if (!is_readable($sCsvFile))
- {
- $oP->p("Input file could not be found or could not be read: '$sCsvFile'");
- $oP->output();
- exit(-1);
- }
- $sCSVData = file_get_contents($sCsvFile);
+if (utils::IsModeCLI()) {
+ // Next steps:
+ // specific arguments: 'csvfile'
+ //
+ $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
+ $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
+ $sCsvFile = ReadMandatoryParam($oP, 'csvfile', 'raw_data');
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
+ $oP->output();
+ exit(-1);
+ }
-}
-else
-{
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+ if (!is_readable($sCsvFile)) {
+ $oP->p("Input file could not be found or could not be read: '$sCsvFile'");
+ $oP->output();
+ exit(-1);
+ }
+ $sCSVData = file_get_contents($sCsvFile);
+
+} else {
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::ResetSession(true);
- $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
+ $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
if ($iRet !== LoginWebPage::EXIT_CODE_OK) {
switch ($iRet) {
case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
@@ -296,637 +275,530 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
exit -1;
}
- $sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data');
+ $sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data');
}
+try {
+ $aWarnings = [];
+
+ //////////////////////////////////////////////////
+ //
+ // Read parameters
+ //
+ $sClass = ReadMandatoryParam($oP, 'class', 'raw_data'); // do not filter as a valid class, we want to produce the report "wrong class" ourselves
+ $sSep = ReadParam($oP, 'separator', 'raw_data');
+ $sQualifier = ReadParam($oP, 'qualifier', 'raw_data');
+ $sCharSet = ReadParam($oP, 'charset', 'raw_data');
+ $sDateFormat = ReadParam($oP, 'date_format', 'raw_data');
+ if (strpos($sDateFormat, '%') !== false) {
+ $sDateFormat = utils::DateTimeFormatToPHP($sDateFormat);
+ }
+ $sOutput = ReadParam($oP, 'output', 'string');
+ $sReconcKeys = ReadParam($oP, 'reconciliationkeys', 'raw_data');
+ $sSimulate = ReadParam($oP, 'simulate');
+ $sComment = ReadParam($oP, 'comment', 'raw_data');
+ $bLocalize = (ReadParam($oP, 'no_localize') != 1);
+
+ if (strtolower(trim($sSep)) == 'tab') {
+ $sSep = "\t";
+ }
-try
-{
- $aWarnings = array();
-
- //////////////////////////////////////////////////
- //
- // Read parameters
- //
- $sClass = ReadMandatoryParam($oP, 'class', 'raw_data'); // do not filter as a valid class, we want to produce the report "wrong class" ourselves
- $sSep = ReadParam($oP, 'separator', 'raw_data');
- $sQualifier = ReadParam($oP, 'qualifier', 'raw_data');
- $sCharSet = ReadParam($oP, 'charset', 'raw_data');
- $sDateFormat = ReadParam($oP, 'date_format', 'raw_data');
- if (strpos($sDateFormat, '%') !== false)
- {
- $sDateFormat = utils::DateTimeFormatToPHP($sDateFormat);
- }
- $sOutput = ReadParam($oP, 'output', 'string');
- $sReconcKeys = ReadParam($oP, 'reconciliationkeys', 'raw_data');
- $sSimulate = ReadParam($oP, 'simulate');
- $sComment = ReadParam($oP, 'comment', 'raw_data');
- $bLocalize = (ReadParam($oP, 'no_localize') != 1);
-
- if (strtolower(trim($sSep)) == 'tab')
- {
- $sSep = "\t";
- }
-
- //////////////////////////////////////////////////
- //
- // Check parameters format/consistency
- //
- if (strlen($sCSVData) == 0)
- {
- throw new BulkLoadException("Missing data - at least one line is expected");
- }
-
- if (!MetaModel::IsValidClass($sClass))
- {
- throw new BulkLoadException("Unknown class: '$sClass'");
- }
-
- if (strlen($sSep) > 1)
- {
- throw new BulkLoadException("Separator is limited to one character, found '$sSep'");
- }
-
- if (strlen($sQualifier) > 1)
- {
- throw new BulkLoadException("Text qualifier is limited to one character, found '$sQualifier'");
- }
-
- if (!in_array($sOutput, array('retcode', 'summary', 'details')))
- {
- throw new BulkLoadException("Unknown output format: '$sOutput'");
- }
-
- if (strlen($sDateFormat) == 0)
- {
- $sDateFormat = null;
- }
-
- if ($sCharSet == '')
- {
- $sCharSet = MetaModel::GetConfig()->Get('csv_file_default_charset');
- }
-
- if ($sSimulate == '1')
- {
- $bSimulate = true;
- }
- else
- {
- $bSimulate = false;
- }
-
- if (($sOutput == "summary") || ($sOutput == 'details'))
- {
- $oP->add_comment("Output format: ".$sOutput);
- $oP->add_comment("Class: ".$sClass);
- $oP->add_comment("Separator: ".$sSep);
- $oP->add_comment("Qualifier: ".$sQualifier);
- $oP->add_comment("Charset Encoding:".$sCharSet);
- if (($sDateFormat !== null) && (strlen($sDateFormat) > 0))
- {
- $oP->add_comment("Date and time format: '$sDateFormat'");
- $oDateTimeFormat = new DateTimeFormat($sDateFormat);
- $sDateOnlyFormat = $oDateTimeFormat->ToDateFormat();
- $oP->add_comment("Date format: '$sDateOnlyFormat'");
- }
- else
- {
- $oP->add_comment("Date format: ");
- }
- $oP->add_comment("Localize: ".($bLocalize?'yes':'no'));
- $oP->add_comment("Data Size: ".strlen($sCSVData));
- }
- //////////////////////////////////////////////////
- //
- // Security
- //
- if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY))
- {
- throw new SecurityException(Dict::Format('UI:Error:BulkModifyNotAllowedOn_Class', $sClass));
- }
-
- //////////////////////////////////////////////////
- //
- // Create an index of the known column names (in lower case)
- // If data is localized, an array of => array of (several leads to ambiguity)
- // Otherwise an array of => array of (1 element by construction)
- //
- // Examples (localized in french):
- // 'lieu' => 'location_id'
- // 'lieu->name' => 'location_id->name'
- //
- // Note: it may happen that an external field has the same label as the external key
- // in that case, we consider that the external key has precedence
- //
- $aKnownColumnNames = ['id'=>['id']];
- foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
- {
- if ($bLocalize)
- {
- $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCode));
- }
- else
- {
- $sColName = strtolower($sAttCode);
- }
- if (!$oAttDef->IsExternalField() || !array_key_exists($sColName, $aKnownColumnNames))
- {
- $aKnownColumnNames[$sColName][] = $sAttCode;
- }
- if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
- {
- $sRemoteClass = $oAttDef->GetTargetClass();
- foreach(MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef)
- {
- $sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
- if ($bLocalize)
- {
- $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCodeEx));
- }
- else
- {
- $sColName = strtolower($sAttCodeEx);
- }
- if (!array_key_exists($sColName, $aKnownColumnNames))
- {
- $aKnownColumnNames[$sColName][] = $sAttCodeEx;
- }
- }
- }
- }
-
- //print_r($aKnownColumnNames);
- //print_r(array_keys($aKnownColumnNames));
- //exit;
-
- //////////////////////////////////////////////////
- //
- // Parse first line, check attributes, analyse the request
- //
- if ($sCharSet == 'UTF-8')
- {
- // Remove the BOM if any
- if (substr($sCSVData, 0, 3) == UTF8_BOM)
- {
- $sCSVData = substr($sCSVData, 3);
- }
- // Clean the input
- // Todo: warn the user if some characters are lost/substituted
- $sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
- }
- else
- {
- $sUTF8Data = iconv($sCharSet, 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
- }
- $oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier);
-
- // Limitation: as the attribute list is in the first line, we can not match external key by a third-party attribute
- $aRawFieldList = $oCSVParser->ListFields();
- $iColCount = count($aRawFieldList);
-
- // Translate into internal names
- $aFieldList = [];
- foreach($aRawFieldList as $iFieldId => $sFieldName)
- {
- $sFieldName = trim($sFieldName);
- $aMatches = array();
- if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches))
- {
- // Ignore any trailing "star" (*) that simply indicates a mandatory field
- $sFieldName = $aMatches[1];
- }
- else if (preg_match('/^(.+)\*->(.+)$/', $sFieldName, $aMatches))
- {
- // Remove any trailing "star" character before the arrow (->)
- // A star character at the end can be used to indicate a mandatory field
- $sFieldName = $aMatches[1].'->'.$aMatches[2];
- }
- if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames))
- {
- $aColumns = $aKnownColumnNames[strtolower($sFieldName)];
- if (count($aColumns) > 1)
- {
- $aCompetitors = array();
- foreach ($aColumns as $sAttCodeEx)
- {
- $aCompetitors[] = $sAttCodeEx;
- }
- $aWarnings[] = "Input column '$sFieldName' is ambiguous. Could be related to ".implode (' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
- }
- $aFieldList[$iFieldId] = $aColumns[0];
- }
- else
- {
- // Protect against XSS injection
- $sSafeName = str_replace(array('"', '<', '>'), '', $sFieldName);
- throw new BulkLoadException("Unknown column: '$sSafeName'. Possible columns: ".implode(', ', array_keys($aKnownColumnNames)));
- }
- }
- // Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
-
- $aAttList = array();
- $aExtKeys = array();
- foreach($aFieldList as $iFieldId => $sFieldName)
- {
- $aMatches = array();
- if (preg_match('/^(.+)->(.+)$/', trim($sFieldName), $aMatches))
- {
- // The column has been specified as "extkey->attcode"
- //
- $sExtKeyAttCode = $aMatches[1];
- $sRemoteAttCode = $aMatches[2];
- if (!MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode))
- {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown attribute '$sExtKeyAttCode' (class: '$sClass')");
- }
- $oAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
- if (!$oAtt->IsExternalKey())
- {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Not an external key '$sExtKeyAttCode' (class: '$sClass')");
- }
- $sTargetClass = $oAtt->GetTargetClass();
- if (!MetaModel::IsValidAttCode($sTargetClass, $sRemoteAttCode))
- {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown attribute '$sRemoteAttCode' (key: '$sExtKeyAttCode', class: '$sTargetClass')");
- }
- $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
- }
- elseif ($sFieldName == 'id')
- {
- $aAttList[$sFieldName] = $iFieldId;
- }
- else
- {
- // The column has been specified as "attcode"
- //
- if (!MetaModel::IsValidAttCode($sClass, $sFieldName))
- {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown attribute '$sFieldName' (class: '$sClass')");
- }
- $oAtt = MetaModel::GetAttributeDef($sClass, $sFieldName);
- if ($oAtt->IsExternalKey())
- {
- $aExtKeys[$sFieldName]['id'] = $iFieldId;
- $aAttList[$sFieldName] = $iFieldId;
- }
- elseif ($oAtt->IsExternalField())
- {
- $sExtKeyAttCode = $oAtt->GetKeyAttCode();
- $sRemoteAttCode = $oAtt->GetExtAttCode();
- $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
- }
- else
- {
- $aAttList[$sFieldName] = $iFieldId;
- }
- }
- }
-
- // Make sure there are some reconciliation keys
- //
- if (empty($sReconcKeys))
- {
- $aReconcSpec = array();
- // Base reconciliation scheme on the default one
- // The reconciliation attributes not present in the data will be ignored
- foreach(MetaModel::GetReconcKeys($sClass) as $sReconcKeyAttCode)
- {
- if (in_array($sReconcKeyAttCode, $aFieldList))
- {
- if ($bLocalize)
- {
- $aReconcSpec[] = MetaModel::GetLabel($sClass, $sReconcKeyAttCode);
- }
- else
- {
- $aReconcSpec[] = $sReconcKeyAttCode;
- }
- }
- }
- if (count($aReconcSpec) == 0)
- {
- throw new BulkLoadException("No reconciliation scheme could be defined, please add a column corresponding to one defined reconciliation key (class: '$sClass', reconciliation:".implode(',', MetaModel::GetReconcKeys($sClass)).")");
- }
- $sReconcKeys = implode(',', $aReconcSpec);
- }
-
- // Interpret the list of reconciliation keys
- //
- $aFinalReconcilKeys = array();
- $aReconcilKeysReport = array();
- foreach (explode(',', $sReconcKeys) as $sReconcKey)
- {
- $sReconcKey = trim($sReconcKey);
- if (empty($sReconcKey)) continue; // skip empty spec
-
- if (array_key_exists(strtolower($sReconcKey), $aKnownColumnNames))
- {
- // Translate from a translated name to codes
- $aColumns = $aKnownColumnNames[strtolower($sReconcKey)];
- if (count($aColumns) > 1)
- {
- $aCompetitors = array();
- foreach ($aColumns as $sAttCodeEx)
- {
- $aCompetitors[] = $sAttCodeEx;
- }
- $aWarnings[] = "Reconciliation key '$sReconcKey' is ambiguous. Could be related to ".implode (' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
- }
- $sReconcKey = $aColumns[0];
- }
- else
- {
- // Protect against XSS injection
- $sSafeName = str_replace(array('"', '<', '>'), '', $sReconcKey);
- throw new BulkLoadException("Unknown reconciliation key: '$sSafeName'");
- }
-
- // Check that the reconciliation key is either a given column, or an external key
- if (!in_array($sReconcKey, $aFieldList))
- {
- if (!array_key_exists($sReconcKey, $aExtKeys))
- {
- // Protect against XSS injection
- $sSafeName = str_replace(array('"', '<', '>'), '', $sReconcKey);
- throw new BulkLoadException("Reconciliation key not found in the input columns: '$sSafeName'");
- }
- }
-
- if (preg_match('/^(.+)->(.+)$/', trim($sReconcKey), $aMatches))
- {
- // The column has been specified as "extkey->attcode"
- //
- $sExtKeyAttCode = $aMatches[1];
- $sRemoteAttCode = $aMatches[2];
-
- $aFinalReconcilKeys[] = $sExtKeyAttCode;
- $aReconcilKeysReport[$sExtKeyAttCode][] = $sRemoteAttCode;
- }
- else
- {
- if (!MetaModel::IsValidAttCode($sClass, $sReconcKey) && $sReconcKey != 'id')
- {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown reconciliation attribute '$sReconcKey' (class: '$sClass')");
- }
- if ($sReconcKey == 'id') {
- $aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey] = array();
- } else {
- $oAtt = MetaModel::GetAttributeDef($sClass, $sReconcKey);
- if ($oAtt->IsExternalKey()) {
- $aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey][] = 'id';
- } elseif ($oAtt->IsExternalField()) {
- $sReconcAttCode = $oAtt->GetKeyAttCode();
- $sReconcKeyReport = "$sReconcAttCode ($sReconcKey)";
-
- $aFinalReconcilKeys[] = $sReconcAttCode;
- $aReconcilKeysReport[$sReconcAttCode][] = $sReconcKeyReport;
- } else {
- $aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey] = array();
- }
- }
- }
- }
-
- //////////////////////////////////////////////////
- //
- // Go for parsing and interpretation
- //
-
- $aData = $oCSVParser->ToArray();
- $iLineCount = count($aData);
-
- if (($sOutput == "summary") || ($sOutput == 'details'))
- {
- $oP->add_comment("Data Lines: ".$iLineCount);
- $oP->add_comment("Simulate: ".($bSimulate ? '1' : '0'));
- $oP->add_comment("Columns: ".implode(', ', $aFieldList));
-
- $aReconciliationReport = array();
- foreach($aReconcilKeysReport as $sKey => $aKeyDetails)
- {
- if (count($aKeyDetails) > 0)
- {
- $aReconciliationReport[] = $sKey.' ('.implode(',', $aKeyDetails).')';
- }
- else
- {
- $aReconciliationReport[] = $sKey;
- }
- }
- $oP->add_comment("Reconciliation Keys: ".implode(', ', $aReconciliationReport));
-
- foreach ($aWarnings as $sWarning)
- {
- $oP->add_comment("Warning: ".$sWarning);
- }
- }
-
- $oBulk = new BulkChange(
- $sClass,
- $aData,
- $aAttList,
- $aExtKeys,
- $aFinalReconcilKeys,
- null, // synchro scope
- null, // on delete
- $sDateFormat,
- $bLocalize
- );
-
- if ($bSimulate)
- {
- $oMyChange = null;
- }
- else
- {
- if (strlen($sComment) > 0) {
- $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV) - '.$sComment;
- } else {
- $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV)';
- }
- CMDBObject::SetCurrentChangeFromParams($sMoreInfo, 'csv-import.php');
- $oMyChange = CMDBObject::GetCurrentChange();
- }
-
- $aRes = $oBulk->Process($oMyChange);
-
- //////////////////////////////////////////////////
- //
- // Compute statistics
- //
- $iCountErrors = 0;
- $iCountWarnings = 0;
- $iCountCreations = 0;
- $iCountUpdates = 0;
- $iCountUnchanged = 0;
- foreach($aRes as $iRow => $aRowData)
- {
- $bWritten = false;
-
- $oStatus = $aRowData["__STATUS__"];
- switch(get_class($oStatus))
- {
- case 'RowStatus_NoChange':
- $iCountUnchanged++;
- break;
- case 'RowStatus_Modify':
- $iCountUpdates++;
- $bWritten = true;
- break;
- case 'RowStatus_NewObj':
- $iCountCreations++;
- $bWritten = true;
- break;
- case 'RowStatus_Issue':
- $iCountErrors++;
- break;
- }
-
- if ($bWritten)
- {
- // Something has been done, still there may be some issues to report
- foreach($aRowData as $key => $value)
- {
- if (!is_object($value)) continue;
-
- switch (get_class($value))
- {
- case 'CellStatus_Void':
- case 'CellStatus_Modify':
- break;
- case 'CellStatus_Issue':
- case 'CellStatus_SearchIssue':
- case 'CellStatus_NullIssue':
- case 'CellStatus_Ambiguous':
- $iCountWarnings++;
- break;
- }
- }
- }
- }
-
- //////////////////////////////////////////////////
- //
- // Summary of settings and results
- //
- if ($sOutput == 'retcode')
- {
- $oP->add($iCountErrors);
- }
-
- if (($sOutput == "summary") || ($sOutput == 'details'))
- {
- $oP->add_comment("Change tracking comment: ".$sComment);
- $oP->add_comment("Issues: ".$iCountErrors);
- $oP->add_comment("Warnings: ".$iCountWarnings);
- $oP->add_comment("Created: ".$iCountCreations);
- $oP->add_comment("Updated: ".$iCountUpdates);
- $oP->add_comment("Unchanged: ".$iCountUnchanged);
- }
-
-
- if ($sOutput == 'details')
- {
- // Setup result presentation
- //
- $aDisplayConfig = array();
- $aDisplayConfig["__LINE__"] = array("label"=>"Line", "description"=>"");
- $aDisplayConfig["__STATUS__"] = array("label"=>"Status", "description"=>"");
- $aDisplayConfig["__OBJECT_CLASS__"] = array("label"=>"Object Class", "description"=>"");
- $aDisplayConfig["__OBJECT_ID__"] = array("label"=>"Object Id", "description"=>"");
- foreach($aExtKeys as $sExtKeyAttCode => $aRemoteAtt)
- {
- $sLabel = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode)->GetLabel();
- $aDisplayConfig["$sExtKeyAttCode"] = array("label"=>$sExtKeyAttCode, "description"=>$sLabel." - ext key");
- }
- foreach($aFinalReconcilKeys as $iCol => $sAttCode)
- {
- // $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
- // $aDisplayConfig["$iCol"] = array("label"=>"$sLabel", "description"=>"");
- }
- foreach ($aAttList as $sAttCode => $iCol)
- {
- if ($sAttCode == 'id')
- {
- $sLabel = Dict::S('UI:CSVImport:idField');
-
- $aDisplayConfig["$iCol"] = array("label"=>$sAttCode, "description"=>$sLabel);
- }
- else
- {
- $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
- $aDisplayConfig["$iCol"] = array("label"=>$sAttCode, "description"=>$sLabel);
- }
- }
-
- $aResultDisp = array(); // to be displayed
- foreach($aRes as $iRow => $aRowData)
- {
- $aRowDisp = array();
- $aRowDisp["__LINE__"] = $iRow;
- if (is_object($aRowData["__STATUS__"]))
- {
- $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription();
- }
- else
- {
- $aRowDisp["__STATUS__"] = "*No status available*";
- }
- if (isset($aRowData["finalclass"]) && isset($aRowData["id"]))
- {
- $aRowDisp["__OBJECT_CLASS__"] = $aRowData["finalclass"];
- $aRowDisp["__OBJECT_ID__"] = $aRowData["id"]->GetCLIValue();
- }
- else
- {
- $aRowDisp["__OBJECT_CLASS__"] = "n/a";
- $aRowDisp["__OBJECT_ID__"] = "n/a";
- }
- foreach($aRowData as $key => $value)
- {
- $sKey = (string) $key;
-
- if ($sKey == '__STATUS__') continue;
- //__ERRORS__ used by tests only
- if ($sKey == '__ERRORS__') continue;
- if ($sKey == 'finalclass') continue;
- if ($sKey == 'id') continue;
-
- if (is_object($value))
- {
- $aRowDisp["$sKey"] = $value->GetCLIValueAndDescription();
- }
- else
- {
- $aRowDisp["$sKey"] = $value;
- }
- }
- $aResultDisp[$iRow] = $aRowDisp;
- }
- $oP->table($aDisplayConfig, $aResultDisp);
- }
-}
-catch(BulkLoadException $e)
-{
- $oP->add_comment($e->getMessage());
-}
-catch(SecurityException $e)
-{
- $oP->add_comment($e->getMessage());
-}
-catch(Exception $e)
-{
- $oP->add_comment((string)$e);
+ //////////////////////////////////////////////////
+ //
+ // Check parameters format/consistency
+ //
+ if (strlen($sCSVData) == 0) {
+ throw new BulkLoadException("Missing data - at least one line is expected");
+ }
+
+ if (!MetaModel::IsValidClass($sClass)) {
+ throw new BulkLoadException("Unknown class: '$sClass'");
+ }
+
+ if (strlen($sSep) > 1) {
+ throw new BulkLoadException("Separator is limited to one character, found '$sSep'");
+ }
+
+ if (strlen($sQualifier) > 1) {
+ throw new BulkLoadException("Text qualifier is limited to one character, found '$sQualifier'");
+ }
+
+ if (!in_array($sOutput, ['retcode', 'summary', 'details'])) {
+ throw new BulkLoadException("Unknown output format: '$sOutput'");
+ }
+
+ if (strlen($sDateFormat) == 0) {
+ $sDateFormat = null;
+ }
+
+ if ($sCharSet == '') {
+ $sCharSet = MetaModel::GetConfig()->Get('csv_file_default_charset');
+ }
+
+ if ($sSimulate == '1') {
+ $bSimulate = true;
+ } else {
+ $bSimulate = false;
+ }
+
+ if (($sOutput == "summary") || ($sOutput == 'details')) {
+ $oP->add_comment("Output format: ".$sOutput);
+ $oP->add_comment("Class: ".$sClass);
+ $oP->add_comment("Separator: ".$sSep);
+ $oP->add_comment("Qualifier: ".$sQualifier);
+ $oP->add_comment("Charset Encoding:".$sCharSet);
+ if (($sDateFormat !== null) && (strlen($sDateFormat) > 0)) {
+ $oP->add_comment("Date and time format: '$sDateFormat'");
+ $oDateTimeFormat = new DateTimeFormat($sDateFormat);
+ $sDateOnlyFormat = $oDateTimeFormat->ToDateFormat();
+ $oP->add_comment("Date format: '$sDateOnlyFormat'");
+ } else {
+ $oP->add_comment("Date format: ");
+ }
+ $oP->add_comment("Localize: ".($bLocalize ? 'yes' : 'no'));
+ $oP->add_comment("Data Size: ".strlen($sCSVData));
+ }
+ //////////////////////////////////////////////////
+ //
+ // Security
+ //
+ if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY)) {
+ throw new SecurityException(Dict::Format('UI:Error:BulkModifyNotAllowedOn_Class', $sClass));
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // Create an index of the known column names (in lower case)
+ // If data is localized, an array of => array of (several leads to ambiguity)
+ // Otherwise an array of => array of (1 element by construction)
+ //
+ // Examples (localized in french):
+ // 'lieu' => 'location_id'
+ // 'lieu->name' => 'location_id->name'
+ //
+ // Note: it may happen that an external field has the same label as the external key
+ // in that case, we consider that the external key has precedence
+ //
+ $aKnownColumnNames = ['id' => ['id']];
+ foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
+ if ($bLocalize) {
+ $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCode));
+ } else {
+ $sColName = strtolower($sAttCode);
+ }
+ if (!$oAttDef->IsExternalField() || !array_key_exists($sColName, $aKnownColumnNames)) {
+ $aKnownColumnNames[$sColName][] = $sAttCode;
+ }
+ if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
+ $sRemoteClass = $oAttDef->GetTargetClass();
+ foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
+ $sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
+ if ($bLocalize) {
+ $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCodeEx));
+ } else {
+ $sColName = strtolower($sAttCodeEx);
+ }
+ if (!array_key_exists($sColName, $aKnownColumnNames)) {
+ $aKnownColumnNames[$sColName][] = $sAttCodeEx;
+ }
+ }
+ }
+ }
+
+ //print_r($aKnownColumnNames);
+ //print_r(array_keys($aKnownColumnNames));
+ //exit;
+
+ //////////////////////////////////////////////////
+ //
+ // Parse first line, check attributes, analyse the request
+ //
+ if ($sCharSet == 'UTF-8') {
+ // Remove the BOM if any
+ if (substr($sCSVData, 0, 3) == UTF8_BOM) {
+ $sCSVData = substr($sCSVData, 3);
+ }
+ // Clean the input
+ // Todo: warn the user if some characters are lost/substituted
+ $sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
+ } else {
+ $sUTF8Data = iconv($sCharSet, 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
+ }
+ $oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier);
+
+ // Limitation: as the attribute list is in the first line, we can not match external key by a third-party attribute
+ $aRawFieldList = $oCSVParser->ListFields();
+ $iColCount = count($aRawFieldList);
+
+ // Translate into internal names
+ $aFieldList = [];
+ foreach ($aRawFieldList as $iFieldId => $sFieldName) {
+ $sFieldName = trim($sFieldName);
+ $aMatches = [];
+ if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches)) {
+ // Ignore any trailing "star" (*) that simply indicates a mandatory field
+ $sFieldName = $aMatches[1];
+ } elseif (preg_match('/^(.+)\*->(.+)$/', $sFieldName, $aMatches)) {
+ // Remove any trailing "star" character before the arrow (->)
+ // A star character at the end can be used to indicate a mandatory field
+ $sFieldName = $aMatches[1].'->'.$aMatches[2];
+ }
+ if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames)) {
+ $aColumns = $aKnownColumnNames[strtolower($sFieldName)];
+ if (count($aColumns) > 1) {
+ $aCompetitors = [];
+ foreach ($aColumns as $sAttCodeEx) {
+ $aCompetitors[] = $sAttCodeEx;
+ }
+ $aWarnings[] = "Input column '$sFieldName' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
+ }
+ $aFieldList[$iFieldId] = $aColumns[0];
+ } else {
+ // Protect against XSS injection
+ $sSafeName = str_replace(['"', '<', '>'], '', $sFieldName);
+ throw new BulkLoadException("Unknown column: '$sSafeName'. Possible columns: ".implode(', ', array_keys($aKnownColumnNames)));
+ }
+ }
+ // Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
+
+ $aAttList = [];
+ $aExtKeys = [];
+ foreach ($aFieldList as $iFieldId => $sFieldName) {
+ $aMatches = [];
+ if (preg_match('/^(.+)->(.+)$/', trim($sFieldName), $aMatches)) {
+ // The column has been specified as "extkey->attcode"
+ //
+ $sExtKeyAttCode = $aMatches[1];
+ $sRemoteAttCode = $aMatches[2];
+ if (!MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode)) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown attribute '$sExtKeyAttCode' (class: '$sClass')");
+ }
+ $oAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
+ if (!$oAtt->IsExternalKey()) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Not an external key '$sExtKeyAttCode' (class: '$sClass')");
+ }
+ $sTargetClass = $oAtt->GetTargetClass();
+ if (!MetaModel::IsValidAttCode($sTargetClass, $sRemoteAttCode)) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown attribute '$sRemoteAttCode' (key: '$sExtKeyAttCode', class: '$sTargetClass')");
+ }
+ $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
+ } elseif ($sFieldName == 'id') {
+ $aAttList[$sFieldName] = $iFieldId;
+ } else {
+ // The column has been specified as "attcode"
+ //
+ if (!MetaModel::IsValidAttCode($sClass, $sFieldName)) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown attribute '$sFieldName' (class: '$sClass')");
+ }
+ $oAtt = MetaModel::GetAttributeDef($sClass, $sFieldName);
+ if ($oAtt->IsExternalKey()) {
+ $aExtKeys[$sFieldName]['id'] = $iFieldId;
+ $aAttList[$sFieldName] = $iFieldId;
+ } elseif ($oAtt->IsExternalField()) {
+ $sExtKeyAttCode = $oAtt->GetKeyAttCode();
+ $sRemoteAttCode = $oAtt->GetExtAttCode();
+ $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
+ } else {
+ $aAttList[$sFieldName] = $iFieldId;
+ }
+ }
+ }
+
+ // Make sure there are some reconciliation keys
+ //
+ if (empty($sReconcKeys)) {
+ $aReconcSpec = [];
+ // Base reconciliation scheme on the default one
+ // The reconciliation attributes not present in the data will be ignored
+ foreach (MetaModel::GetReconcKeys($sClass) as $sReconcKeyAttCode) {
+ if (in_array($sReconcKeyAttCode, $aFieldList)) {
+ if ($bLocalize) {
+ $aReconcSpec[] = MetaModel::GetLabel($sClass, $sReconcKeyAttCode);
+ } else {
+ $aReconcSpec[] = $sReconcKeyAttCode;
+ }
+ }
+ }
+ if (count($aReconcSpec) == 0) {
+ throw new BulkLoadException("No reconciliation scheme could be defined, please add a column corresponding to one defined reconciliation key (class: '$sClass', reconciliation:".implode(',', MetaModel::GetReconcKeys($sClass)).")");
+ }
+ $sReconcKeys = implode(',', $aReconcSpec);
+ }
+
+ // Interpret the list of reconciliation keys
+ //
+ $aFinalReconcilKeys = [];
+ $aReconcilKeysReport = [];
+ foreach (explode(',', $sReconcKeys) as $sReconcKey) {
+ $sReconcKey = trim($sReconcKey);
+ if (empty($sReconcKey)) {
+ continue;
+ } // skip empty spec
+
+ if (array_key_exists(strtolower($sReconcKey), $aKnownColumnNames)) {
+ // Translate from a translated name to codes
+ $aColumns = $aKnownColumnNames[strtolower($sReconcKey)];
+ if (count($aColumns) > 1) {
+ $aCompetitors = [];
+ foreach ($aColumns as $sAttCodeEx) {
+ $aCompetitors[] = $sAttCodeEx;
+ }
+ $aWarnings[] = "Reconciliation key '$sReconcKey' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
+ }
+ $sReconcKey = $aColumns[0];
+ } else {
+ // Protect against XSS injection
+ $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
+ throw new BulkLoadException("Unknown reconciliation key: '$sSafeName'");
+ }
+
+ // Check that the reconciliation key is either a given column, or an external key
+ if (!in_array($sReconcKey, $aFieldList)) {
+ if (!array_key_exists($sReconcKey, $aExtKeys)) {
+ // Protect against XSS injection
+ $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
+ throw new BulkLoadException("Reconciliation key not found in the input columns: '$sSafeName'");
+ }
+ }
+
+ if (preg_match('/^(.+)->(.+)$/', trim($sReconcKey), $aMatches)) {
+ // The column has been specified as "extkey->attcode"
+ //
+ $sExtKeyAttCode = $aMatches[1];
+ $sRemoteAttCode = $aMatches[2];
+
+ $aFinalReconcilKeys[] = $sExtKeyAttCode;
+ $aReconcilKeysReport[$sExtKeyAttCode][] = $sRemoteAttCode;
+ } else {
+ if (!MetaModel::IsValidAttCode($sClass, $sReconcKey) && $sReconcKey != 'id') {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown reconciliation attribute '$sReconcKey' (class: '$sClass')");
+ }
+ if ($sReconcKey == 'id') {
+ $aFinalReconcilKeys[] = $sReconcKey;
+ $aReconcilKeysReport[$sReconcKey] = [];
+ } else {
+ $oAtt = MetaModel::GetAttributeDef($sClass, $sReconcKey);
+ if ($oAtt->IsExternalKey()) {
+ $aFinalReconcilKeys[] = $sReconcKey;
+ $aReconcilKeysReport[$sReconcKey][] = 'id';
+ } elseif ($oAtt->IsExternalField()) {
+ $sReconcAttCode = $oAtt->GetKeyAttCode();
+ $sReconcKeyReport = "$sReconcAttCode ($sReconcKey)";
+
+ $aFinalReconcilKeys[] = $sReconcAttCode;
+ $aReconcilKeysReport[$sReconcAttCode][] = $sReconcKeyReport;
+ } else {
+ $aFinalReconcilKeys[] = $sReconcKey;
+ $aReconcilKeysReport[$sReconcKey] = [];
+ }
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // Go for parsing and interpretation
+ //
+
+ $aData = $oCSVParser->ToArray();
+ $iLineCount = count($aData);
+
+ if (($sOutput == "summary") || ($sOutput == 'details')) {
+ $oP->add_comment("Data Lines: ".$iLineCount);
+ $oP->add_comment("Simulate: ".($bSimulate ? '1' : '0'));
+ $oP->add_comment("Columns: ".implode(', ', $aFieldList));
+
+ $aReconciliationReport = [];
+ foreach ($aReconcilKeysReport as $sKey => $aKeyDetails) {
+ if (count($aKeyDetails) > 0) {
+ $aReconciliationReport[] = $sKey.' ('.implode(',', $aKeyDetails).')';
+ } else {
+ $aReconciliationReport[] = $sKey;
+ }
+ }
+ $oP->add_comment("Reconciliation Keys: ".implode(', ', $aReconciliationReport));
+
+ foreach ($aWarnings as $sWarning) {
+ $oP->add_comment("Warning: ".$sWarning);
+ }
+ }
+
+ $oBulk = new BulkChange(
+ $sClass,
+ $aData,
+ $aAttList,
+ $aExtKeys,
+ $aFinalReconcilKeys,
+ null, // synchro scope
+ null, // on delete
+ $sDateFormat,
+ $bLocalize
+ );
+
+ if ($bSimulate) {
+ $oMyChange = null;
+ } else {
+ if (strlen($sComment) > 0) {
+ $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV) - '.$sComment;
+ } else {
+ $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV)';
+ }
+ CMDBObject::SetCurrentChangeFromParams($sMoreInfo, 'csv-import.php');
+ $oMyChange = CMDBObject::GetCurrentChange();
+ }
+
+ $aRes = $oBulk->Process($oMyChange);
+
+ //////////////////////////////////////////////////
+ //
+ // Compute statistics
+ //
+ $iCountErrors = 0;
+ $iCountWarnings = 0;
+ $iCountCreations = 0;
+ $iCountUpdates = 0;
+ $iCountUnchanged = 0;
+ foreach ($aRes as $iRow => $aRowData) {
+ $bWritten = false;
+
+ $oStatus = $aRowData["__STATUS__"];
+ switch (get_class($oStatus)) {
+ case 'RowStatus_NoChange':
+ $iCountUnchanged++;
+ break;
+ case 'RowStatus_Modify':
+ $iCountUpdates++;
+ $bWritten = true;
+ break;
+ case 'RowStatus_NewObj':
+ $iCountCreations++;
+ $bWritten = true;
+ break;
+ case 'RowStatus_Issue':
+ $iCountErrors++;
+ break;
+ }
+
+ if ($bWritten) {
+ // Something has been done, still there may be some issues to report
+ foreach ($aRowData as $key => $value) {
+ if (!is_object($value)) {
+ continue;
+ }
+
+ switch (get_class($value)) {
+ case 'CellStatus_Void':
+ case 'CellStatus_Modify':
+ break;
+ case 'CellStatus_Issue':
+ case 'CellStatus_SearchIssue':
+ case 'CellStatus_NullIssue':
+ case 'CellStatus_Ambiguous':
+ $iCountWarnings++;
+ break;
+ }
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // Summary of settings and results
+ //
+ if ($sOutput == 'retcode') {
+ $oP->add($iCountErrors);
+ }
+
+ if (($sOutput == "summary") || ($sOutput == 'details')) {
+ $oP->add_comment("Change tracking comment: ".$sComment);
+ $oP->add_comment("Issues: ".$iCountErrors);
+ $oP->add_comment("Warnings: ".$iCountWarnings);
+ $oP->add_comment("Created: ".$iCountCreations);
+ $oP->add_comment("Updated: ".$iCountUpdates);
+ $oP->add_comment("Unchanged: ".$iCountUnchanged);
+ }
+
+ if ($sOutput == 'details') {
+ // Setup result presentation
+ //
+ $aDisplayConfig = [];
+ $aDisplayConfig["__LINE__"] = ["label" => "Line", "description" => ""];
+ $aDisplayConfig["__STATUS__"] = ["label" => "Status", "description" => ""];
+ $aDisplayConfig["__OBJECT_CLASS__"] = ["label" => "Object Class", "description" => ""];
+ $aDisplayConfig["__OBJECT_ID__"] = ["label" => "Object Id", "description" => ""];
+ foreach ($aExtKeys as $sExtKeyAttCode => $aRemoteAtt) {
+ $sLabel = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode)->GetLabel();
+ $aDisplayConfig["$sExtKeyAttCode"] = ["label" => $sExtKeyAttCode, "description" => $sLabel." - ext key"];
+ }
+ foreach ($aFinalReconcilKeys as $iCol => $sAttCode) {
+ // $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
+ // $aDisplayConfig["$iCol"] = array("label"=>"$sLabel", "description"=>"");
+ }
+ foreach ($aAttList as $sAttCode => $iCol) {
+ if ($sAttCode == 'id') {
+ $sLabel = Dict::S('UI:CSVImport:idField');
+
+ $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
+ } else {
+ $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
+ $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
+ }
+ }
+
+ $aResultDisp = []; // to be displayed
+ foreach ($aRes as $iRow => $aRowData) {
+ $aRowDisp = [];
+ $aRowDisp["__LINE__"] = $iRow;
+ if (is_object($aRowData["__STATUS__"])) {
+ $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription();
+ } else {
+ $aRowDisp["__STATUS__"] = "*No status available*";
+ }
+ if (isset($aRowData["finalclass"]) && isset($aRowData["id"])) {
+ $aRowDisp["__OBJECT_CLASS__"] = $aRowData["finalclass"];
+ $aRowDisp["__OBJECT_ID__"] = $aRowData["id"]->GetCLIValue();
+ } else {
+ $aRowDisp["__OBJECT_CLASS__"] = "n/a";
+ $aRowDisp["__OBJECT_ID__"] = "n/a";
+ }
+ foreach ($aRowData as $key => $value) {
+ $sKey = (string) $key;
+
+ if ($sKey == '__STATUS__') {
+ continue;
+ }
+ //__ERRORS__ used by tests only
+ if ($sKey == '__ERRORS__') {
+ continue;
+ }
+ if ($sKey == 'finalclass') {
+ continue;
+ }
+ if ($sKey == 'id') {
+ continue;
+ }
+
+ if (is_object($value)) {
+ $aRowDisp["$sKey"] = $value->GetCLIValueAndDescription();
+ } else {
+ $aRowDisp["$sKey"] = $value;
+ }
+ }
+ $aResultDisp[$iRow] = $aRowDisp;
+ }
+ $oP->table($aDisplayConfig, $aResultDisp);
+ }
+} catch (BulkLoadException $e) {
+ $oP->add_comment($e->getMessage());
+} catch (SecurityException $e) {
+ $oP->add_comment($e->getMessage());
+} catch (Exception $e) {
+ $oP->add_comment((string)$e);
}
$oP->output();
-?>
diff --git a/webservices/itop.wsdl.php b/webservices/itop.wsdl.php
index 5b9b37b1f9..d818859889 100644
--- a/webservices/itop.wsdl.php
+++ b/webservices/itop.wsdl.php
@@ -1,4 +1,5 @@
diff --git a/webservices/itoprest.examples.php b/webservices/itoprest.examples.php
index e14988c0c3..58d02c63d1 100644
--- a/webservices/itoprest.examples.php
+++ b/webservices/itoprest.examples.php
@@ -1,9 +1,10 @@
-
/**
- * Shows a usage of the SOAP queries
+ * Shows a usage of the SOAP queries
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -36,120 +36,101 @@
* @return bool|false|string
* @throws \Exception
*/
-function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
+function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = [])
{
- // $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
+ // $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
- if (function_exists('curl_init'))
- {
- // If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
- // For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
- // by setting the SSLVERSION to 3 as done below.
- $aHTTPHeaders = array();
- if ($sOptionnalHeaders !== null)
- {
- $aHeaders = explode("\n", $sOptionnalHeaders);
- // N°3267 - Webservices: Fix optional headers not being taken into account
- // See https://www.php.net/curl_setopt CURLOPT_HTTPHEADER
- $aHTTPHeaders = array();
- foreach($aHeaders as $sHeaderString)
- {
- $aHTTPHeaders[] = trim($sHeaderString);
- }
- }
- // Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
- $aOptions = array(
- CURLOPT_RETURNTRANSFER => true, // return the content of the request
- CURLOPT_HEADER => false, // don't return the headers in the output
- CURLOPT_FOLLOWLOCATION => true, // follow redirects
- CURLOPT_ENCODING => "", // handle all encodings
- CURLOPT_USERAGENT => "spider", // who am i
- CURLOPT_AUTOREFERER => true, // set referer on redirect
- CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
- CURLOPT_TIMEOUT => 120, // timeout on response
- CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
- CURLOPT_SSL_VERIFYHOST => 0, // Disabled SSL Cert checks
- CURLOPT_SSL_VERIFYPEER => 0, // Disabled SSL Cert checks
- // SSLV3 (CURL_SSLVERSION_SSLv3 = 3) is now considered as obsolete/dangerous: http://disablessl3.com/#why
- // but it used to be a MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
- // CURLOPT_SSLVERSION => 3,
- CURLOPT_POST => count($aData),
- CURLOPT_POSTFIELDS => http_build_query($aData),
- CURLOPT_HTTPHEADER => $aHTTPHeaders,
- );
- $aAllOptions = $aCurlOptions + $aOptions;
- $ch = curl_init($sUrl);
- curl_setopt_array($ch, $aAllOptions);
- $response = curl_exec($ch);
- $iErr = curl_errno($ch);
- $sErrMsg = curl_error( $ch );
- if ($iErr !== 0)
- {
- throw new Exception("Problem opening URL: $sUrl, $sErrMsg");
- }
- if (is_array($aResponseHeaders))
- {
- $aHeaders = curl_getinfo($ch);
- foreach($aHeaders as $sCode => $sValue)
- {
- $sName = str_replace(' ' , '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
- $aResponseHeaders[$sName] = $sValue;
- }
- }
- curl_close( $ch );
- }
- else
- {
- // cURL is not available let's try with streams and fopen...
+ if (function_exists('curl_init')) {
+ // If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
+ // For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
+ // by setting the SSLVERSION to 3 as done below.
+ $aHTTPHeaders = [];
+ if ($sOptionnalHeaders !== null) {
+ $aHeaders = explode("\n", $sOptionnalHeaders);
+ // N°3267 - Webservices: Fix optional headers not being taken into account
+ // See https://www.php.net/curl_setopt CURLOPT_HTTPHEADER
+ $aHTTPHeaders = [];
+ foreach ($aHeaders as $sHeaderString) {
+ $aHTTPHeaders[] = trim($sHeaderString);
+ }
+ }
+ // Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
+ $aOptions = [
+ CURLOPT_RETURNTRANSFER => true, // return the content of the request
+ CURLOPT_HEADER => false, // don't return the headers in the output
+ CURLOPT_FOLLOWLOCATION => true, // follow redirects
+ CURLOPT_ENCODING => "", // handle all encodings
+ CURLOPT_USERAGENT => "spider", // who am i
+ CURLOPT_AUTOREFERER => true, // set referer on redirect
+ CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
+ CURLOPT_TIMEOUT => 120, // timeout on response
+ CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
+ CURLOPT_SSL_VERIFYHOST => 0, // Disabled SSL Cert checks
+ CURLOPT_SSL_VERIFYPEER => 0, // Disabled SSL Cert checks
+ // SSLV3 (CURL_SSLVERSION_SSLv3 = 3) is now considered as obsolete/dangerous: http://disablessl3.com/#why
+ // but it used to be a MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
+ // CURLOPT_SSLVERSION => 3,
+ CURLOPT_POST => count($aData),
+ CURLOPT_POSTFIELDS => http_build_query($aData),
+ CURLOPT_HTTPHEADER => $aHTTPHeaders,
+ ];
+ $aAllOptions = $aCurlOptions + $aOptions;
+ $ch = curl_init($sUrl);
+ curl_setopt_array($ch, $aAllOptions);
+ $response = curl_exec($ch);
+ $iErr = curl_errno($ch);
+ $sErrMsg = curl_error($ch);
+ if ($iErr !== 0) {
+ throw new Exception("Problem opening URL: $sUrl, $sErrMsg");
+ }
+ if (is_array($aResponseHeaders)) {
+ $aHeaders = curl_getinfo($ch);
+ foreach ($aHeaders as $sCode => $sValue) {
+ $sName = str_replace(' ', '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
+ $aResponseHeaders[$sName] = $sValue;
+ }
+ }
+ curl_close($ch);
+ } else {
+ // cURL is not available let's try with streams and fopen...
- $sData = http_build_query($aData);
- $aParams = array('http' => array(
- 'method' => 'POST',
- 'content' => $sData,
- 'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
- ));
- if ($sOptionnalHeaders !== null)
- {
- $aParams['http']['header'] .= $sOptionnalHeaders;
- }
- $ctx = stream_context_create($aParams);
+ $sData = http_build_query($aData);
+ $aParams = ['http' => [
+ 'method' => 'POST',
+ 'content' => $sData,
+ 'header' => "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
+ ]];
+ if ($sOptionnalHeaders !== null) {
+ $aParams['http']['header'] .= $sOptionnalHeaders;
+ }
+ $ctx = stream_context_create($aParams);
- $fp = @fopen($sUrl, 'rb', false, $ctx);
- if (!$fp)
- {
- global $php_errormsg;
- if (isset($php_errormsg))
- {
- throw new Exception("Wrong URL: $sUrl, $php_errormsg");
- }
- elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl'))
- {
- throw new Exception("Cannot connect to $sUrl: missing module 'openssl'");
- }
- else
- {
- throw new Exception("Wrong URL: $sUrl");
- }
- }
- $response = @stream_get_contents($fp);
- if ($response === false)
- {
- throw new Exception("Problem reading data from $sUrl, $php_errormsg");
- }
- if (is_array($aResponseHeaders))
- {
- $aMeta = stream_get_meta_data($fp);
- $aHeaders = $aMeta['wrapper_data'];
- foreach($aHeaders as $sHeaderString)
- {
- if(preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches))
- {
- $aResponseHeaders[$aMatches[1]] = trim($aMatches[2]);
- }
- }
- }
- }
- return $response;
+ $fp = @fopen($sUrl, 'rb', false, $ctx);
+ if (!$fp) {
+ global $php_errormsg;
+ if (isset($php_errormsg)) {
+ throw new Exception("Wrong URL: $sUrl, $php_errormsg");
+ } elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl')) {
+ throw new Exception("Cannot connect to $sUrl: missing module 'openssl'");
+ } else {
+ throw new Exception("Wrong URL: $sUrl");
+ }
+ }
+ $response = @stream_get_contents($fp);
+ if ($response === false) {
+ throw new Exception("Problem reading data from $sUrl, $php_errormsg");
+ }
+ if (is_array($aResponseHeaders)) {
+ $aMeta = stream_get_meta_data($fp);
+ $aHeaders = $aMeta['wrapper_data'];
+ foreach ($aHeaders as $sHeaderString) {
+ if (preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches)) {
+ $aResponseHeaders[$aMatches[1]] = trim($aMatches[2]);
+ }
+ }
+ }
+ }
+ return $response;
}
////////////////////////////////////////////////////////////////////////////////
@@ -161,235 +142,223 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
// Define the operations to perform (one operation per call the rest service)
//
-$aOperations = array(
- array(
- 'operation' => 'list_operations', // operation code
- ),
- array(
- 'operation' => 'core/create', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => array(
- 'org_id' => "SELECT Organization WHERE name = 'Demo'",
- 'caller_id' => array('name' => 'monet', 'first_name' => 'claude'),
- 'title' => 'issue blah',
- 'description' => 'something happened'
- ),
- ),
- array(
- 'operation' => 'core/update', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest WHERE id=1',
- 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => array(
- 'title' => 'Issue #'.rand(0, 100),
- 'contacts_list' => array(
- array(
- 'role' => 'fireman #'.rand(0, 100),
- 'contact_id' => array('finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'),
- ),
- ),
- ),
- ),
- // Rewrite the full CaseLog on an existing UserRequest with id=1, setting date and user (optional)
- array(
- 'operation' => 'core/update',
- 'comment' => 'Synchronization from Client A', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest WHERE id=1',
- 'output_fields' => 'id, friendlyname, title',
- 'fields' => array(
- 'public_log' => array(
- 'items' => array(
- 0 => array(
- 'date' => '2001-02-01 23:59:59', //Allow to set the date of a true event, an alarm for eg.
- 'user_login' => 'Alarm monitoring', //Free text
- 'user_id' => 0, //0 is required for the user_login to be taken into account
- 'message' => 'This is 1st entry as an HTML formatted text',
- ),
- 1 => array(
- 'date' => '2001-02-02 00:00:00', //If ommitted set automatically.
- 'user_login' => 'Alarm monitoring', //user=id=0 is missing so will be ignored
- 'message' => 'Second entry in text format:
+$aOperations = [
+ [
+ 'operation' => 'list_operations', // operation code
+ ],
+ [
+ 'operation' => 'core/create', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'org_id' => "SELECT Organization WHERE name = 'Demo'",
+ 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
+ 'title' => 'issue blah',
+ 'description' => 'something happened'
+ ],
+ ],
+ [
+ 'operation' => 'core/update', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest WHERE id=1',
+ 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'title' => 'Issue #'.rand(0, 100),
+ 'contacts_list' => [
+ [
+ 'role' => 'fireman #'.rand(0, 100),
+ 'contact_id' => ['finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'],
+ ],
+ ],
+ ],
+ ],
+ // Rewrite the full CaseLog on an existing UserRequest with id=1, setting date and user (optional)
+ [
+ 'operation' => 'core/update',
+ 'comment' => 'Synchronization from Client A', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest WHERE id=1',
+ 'output_fields' => 'id, friendlyname, title',
+ 'fields' => [
+ 'public_log' => [
+ 'items' => [
+ 0 => [
+ 'date' => '2001-02-01 23:59:59', //Allow to set the date of a true event, an alarm for eg.
+ 'user_login' => 'Alarm monitoring', //Free text
+ 'user_id' => 0, //0 is required for the user_login to be taken into account
+ 'message' => 'This is 1st entry as an HTML formatted text',
+ ],
+ 1 => [
+ 'date' => '2001-02-02 00:00:00', //If ommitted set automatically.
+ 'user_login' => 'Alarm monitoring', //user=id=0 is missing so will be ignored
+ 'message' => 'Second entry in text format:
with new line, but format not specified, so treated as HTML!, user_id=0 missing, so user_login ignored',
- ),
- ),
- ),
- ),
- ),
- // Add a Text entry in the HTML CaseLog of the UserRequest with id=1, setting date and user (optional)
- array(
- 'operation' => 'core/update', // operation code
- 'comment' => 'Synchronization from Alarm Monitoring', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 1, // object id or OQL
- 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
- // Example of adding an entry into a CaseLog on an existing UserRequest
- 'fields' => array(
- 'public_log' => array(
- 'add_item' => array(
- 'user_login' => 'New Entry', //Free text
- 'user_id' => 0, //0 is required for the user_login to be taken into account
- 'format' => 'text', //If ommitted, source is expected to be HTML
- 'message' => 'This text is not HTML formatted with 3 lines:
+ ],
+ ],
+ ],
+ ],
+ ],
+ // Add a Text entry in the HTML CaseLog of the UserRequest with id=1, setting date and user (optional)
+ [
+ 'operation' => 'core/update', // operation code
+ 'comment' => 'Synchronization from Alarm Monitoring', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 1, // object id or OQL
+ 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
+ // Example of adding an entry into a CaseLog on an existing UserRequest
+ 'fields' => [
+ 'public_log' => [
+ 'add_item' => [
+ 'user_login' => 'New Entry', //Free text
+ 'user_id' => 0, //0 is required for the user_login to be taken into account
+ 'format' => 'text', //If ommitted, source is expected to be HTML
+ 'message' => 'This text is not HTML formatted with 3 lines:
new line
3rd and last line',
- ),
- ),
- ),
- ),
- array(
- 'operation' => 'core/get', // operation code
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest',
- 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
- ),
- array(
- 'operation' => 'core/delete', // operation code
- 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest WHERE org_id = 2',
- 'simulate' => true,
- ),
- array(
- 'operation' => 'core/apply_stimulus', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 1,
- 'stimulus' => 'ev_assign',
- // Values to set
- 'fields' => array(
- 'team_id' => 15, // Helpdesk
- 'agent_id' => 9 // Jules Verne
- ),
- 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
- ),
- array(
- 'operation' => 'core/get_related', // operation code
- 'class' => 'Server',
- 'key' => 'SELECT Server',
- 'relation' => 'impacts', // relation code
- 'depth' => 4, // max recursion depth
- ),
-);
-$aOperations = array(
- array(
- 'operation' => 'core/create', // operation code
- 'comment' => 'Automatic creation of attachment blah blah...', // comment recorded in the change tracking log
- 'class' => 'Attachment',
- 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => array(
- 'item_class' => 'UserRequest',
- 'item_id' => 1,
- 'item_org_id' => 3,
- 'contents' => array(
- 'data' => 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVChTfZHRDYMwDESzQ2fqhHx3C3ao+MkW/WlnaFxfzk7sEnE6JHJ+NgaKZN2zLHVN2ssfkae0Da7FQ5PRk/ve4Hcx19Ie6CEGuh/6vMgNhwanHVUNbt73lUDbYJ+6pg8b3+m2RehsVPdMXyvQY+OVkB+Rrv64lUjb3nq+aCA6v4leRqtfaIgimr53atBy9PlfUhoh3fFCNDmErv9FWR6ylBL5AREbmHBnFj5lAAAAAElFTkSuQmCC',
- 'filename' => 'myself.png',
- 'mimetype' => 'image/png'
- ),
- ),
- ),
- array(
- 'operation' => 'core/get', // operation code
- 'class' => 'Attachment',
- 'key' => 'SELECT Attachment',
- 'output_fields' => '*',
- )
-);
-$aOperations = array(
- array(
- 'operation' => 'core/update', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'Server',
- 'key' => 'SELECT Server WHERE name="Server1"',
- 'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => array(
- 'description' => 'Issue #'.time(),
- ),
- ),
-);
-$aOperations = array(
- array(
- 'operation' => 'core/create', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => array(
- 'org_id' => "SELECT Organization WHERE name = 'Demo'",
- 'caller_id' => array('name' => 'monet', 'first_name' => 'claude'),
- 'title' => 'issue blah',
- 'description' => 'something happened'
- ),
- ),
-);
-$aXXXOperations = array(
- array(
- 'operation' => 'core/check_credentials', // operation code
- 'user' => 'admin',
- 'password' => 'admin',
- ),
-);
-$aDeleteOperations = array(
- array(
- 'operation' => 'core/delete', // operation code
- 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
- 'class' => 'Server',
- 'key' => 'SELECT Server',
- 'simulate' => false,
- ),
-);
+ ],
+ ],
+ ],
+ ],
+ [
+ 'operation' => 'core/get', // operation code
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest',
+ 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
+ ],
+ [
+ 'operation' => 'core/delete', // operation code
+ 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest WHERE org_id = 2',
+ 'simulate' => true,
+ ],
+ [
+ 'operation' => 'core/apply_stimulus', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 1,
+ 'stimulus' => 'ev_assign',
+ // Values to set
+ 'fields' => [
+ 'team_id' => 15, // Helpdesk
+ 'agent_id' => 9 // Jules Verne
+ ],
+ 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
+ ],
+ [
+ 'operation' => 'core/get_related', // operation code
+ 'class' => 'Server',
+ 'key' => 'SELECT Server',
+ 'relation' => 'impacts', // relation code
+ 'depth' => 4, // max recursion depth
+ ],
+];
+$aOperations = [
+ [
+ 'operation' => 'core/create', // operation code
+ 'comment' => 'Automatic creation of attachment blah blah...', // comment recorded in the change tracking log
+ 'class' => 'Attachment',
+ 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'item_class' => 'UserRequest',
+ 'item_id' => 1,
+ 'item_org_id' => 3,
+ 'contents' => [
+ 'data' => 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVChTfZHRDYMwDESzQ2fqhHx3C3ao+MkW/WlnaFxfzk7sEnE6JHJ+NgaKZN2zLHVN2ssfkae0Da7FQ5PRk/ve4Hcx19Ie6CEGuh/6vMgNhwanHVUNbt73lUDbYJ+6pg8b3+m2RehsVPdMXyvQY+OVkB+Rrv64lUjb3nq+aCA6v4leRqtfaIgimr53atBy9PlfUhoh3fFCNDmErv9FWR6ylBL5AREbmHBnFj5lAAAAAElFTkSuQmCC',
+ 'filename' => 'myself.png',
+ 'mimetype' => 'image/png'
+ ],
+ ],
+ ],
+ [
+ 'operation' => 'core/get', // operation code
+ 'class' => 'Attachment',
+ 'key' => 'SELECT Attachment',
+ 'output_fields' => '*',
+ ]
+];
+$aOperations = [
+ [
+ 'operation' => 'core/update', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'Server',
+ 'key' => 'SELECT Server WHERE name="Server1"',
+ 'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'description' => 'Issue #'.time(),
+ ],
+ ],
+];
+$aOperations = [
+ [
+ 'operation' => 'core/create', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'org_id' => "SELECT Organization WHERE name = 'Demo'",
+ 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
+ 'title' => 'issue blah',
+ 'description' => 'something happened'
+ ],
+ ],
+];
+$aXXXOperations = [
+ [
+ 'operation' => 'core/check_credentials', // operation code
+ 'user' => 'admin',
+ 'password' => 'admin',
+ ],
+];
+$aDeleteOperations = [
+ [
+ 'operation' => 'core/delete', // operation code
+ 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
+ 'class' => 'Server',
+ 'key' => 'SELECT Server',
+ 'simulate' => false,
+ ],
+];
-if (false)
-{
- echo "Please edit the sample script and configure the server URL";
- exit;
-}
-else
-{
- $sUrl = "https://localhost/itop/webservices/rest.php?version=1.3";
+if (false) {
+ echo "Please edit the sample script and configure the server URL";
+ exit;
+} else {
+ $sUrl = "https://localhost/itop/webservices/rest.php?version=1.3";
}
-$aData = array();
+$aData = [];
$aData['auth_user'] = 'rest';
$aData['auth_pwd'] = 'rest';
+foreach ($aOperations as $iOp => $aOperation) {
+ echo "======================================\n";
+ echo "Operation #$iOp: ".$aOperation['operation']."\n";
+ $aData['json_data'] = json_encode($aOperation);
-foreach ($aOperations as $iOp => $aOperation)
-{
- echo "======================================\n";
- echo "Operation #$iOp: ".$aOperation['operation']."\n";
- $aData['json_data'] = json_encode($aOperation);
-
- echo "--------------------------------------\n";
- echo "Input:\n";
- print_r($aOperation);
- $aResults = null;
- try
- {
- $response = DoPostRequest($sUrl, $aData);
- $aResults = json_decode($response);
- }
- catch (Exception $e)
- {
- $response = $e->getMessage();
- }
- if ($aResults)
- {
- echo "--------------------------------------\n";
- echo "Reply:\n";
- print_r($aResults);
- }
- else
- {
- echo "ERROR rest.php replied:\n";
- echo $response;
- }
+ echo "--------------------------------------\n";
+ echo "Input:\n";
+ print_r($aOperation);
+ $aResults = null;
+ try {
+ $response = DoPostRequest($sUrl, $aData);
+ $aResults = json_decode($response);
+ } catch (Exception $e) {
+ $response = $e->getMessage();
+ }
+ if ($aResults) {
+ echo "--------------------------------------\n";
+ echo "Reply:\n";
+ print_r($aResults);
+ } else {
+ echo "ERROR rest.php replied:\n";
+ echo $response;
+ }
}
-
diff --git a/webservices/itopsoap.examples.php b/webservices/itopsoap.examples.php
index 5ed1bdc24c..b869dde628 100644
--- a/webservices/itopsoap.examples.php
+++ b/webservices/itopsoap.examples.php
@@ -1,9 +1,10 @@
-
/**
- * Shows a usage of the SOAP queries
+ * Shows a usage of the SOAP queries
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -31,115 +31,102 @@
$aSOAPMapping = SOAPMapping::GetMapping();
-ini_set("soap.wsdl_cache_enabled","0");
+ini_set("soap.wsdl_cache_enabled", "0");
$oSoapClient = new SoapClient(
- $sWsdlUri,
- array(
- 'trace' => 1,
- 'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php
- )
+ $sWsdlUri,
+ [
+ 'trace' => 1,
+ 'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php
+ ]
);
-try
-{
- // The most simple service, returning a string
- //
- $sServerVersion = $oSoapClient->GetVersion();
- echo "GetVersion() returned $sServerVersion
";
+try {
+ // The most simple service, returning a string
+ //
+ $sServerVersion = $oSoapClient->GetVersion();
+ echo "GetVersion() returned $sServerVersion
";
- // More complex ones, returning a SOAPResult structure
- // (run the page to know more about the returned data)
- //
- $oRes = $oSoapClient->CreateIncidentTicket
- (
- 'admin', /* login */
- 'admin', /* password */
- 'Email server down', /* title */
- 'HW found shutdown', /* description */
- null, /* caller */
- new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* customer */
- new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'NW Management'))), /* service */
- new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Troubleshooting'))), /* service subcategory */
- '', /* product */
- new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'NW support'))), /* workgroup */
- array(
- new SOAPLinkCreationSpec(
- 'Device',
- array(new SOAPSearchCondition('name', 'switch01')),
- array()
- ),
- new SOAPLinkCreationSpec(
- 'Server',
- array(new SOAPSearchCondition('name', 'dbserver1.demo.com')),
- array()
- ),
- ), /* impacted cis */
- '1', /* impact */
- '1' /* urgency */
- );
+ // More complex ones, returning a SOAPResult structure
+ // (run the page to know more about the returned data)
+ //
+ $oRes = $oSoapClient->CreateIncidentTicket(
+ 'admin', /* login */
+ 'admin', /* password */
+ 'Email server down', /* title */
+ 'HW found shutdown', /* description */
+ null, /* caller */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Demo')]), /* customer */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW Management')]), /* service */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Troubleshooting')]), /* service subcategory */
+ '', /* product */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW support')]), /* workgroup */
+ [
+ new SOAPLinkCreationSpec(
+ 'Device',
+ [new SOAPSearchCondition('name', 'switch01')],
+ []
+ ),
+ new SOAPLinkCreationSpec(
+ 'Server',
+ [new SOAPSearchCondition('name', 'dbserver1.demo.com')],
+ []
+ ),
+ ], /* impacted cis */
+ '1', /* impact */
+ '1' /* urgency */
+ );
- echo "CreateIncidentTicket() returned:\n";
- echo "
\n";
- print_r($oRes);
- echo " \n";
- echo "
\n";
+ echo "CreateIncidentTicket() returned:\n";
+ echo "
\n";
+ print_r($oRes);
+ echo " \n";
+ echo "\n";
- $oRes = $oSoapClient->SearchObjects
- (
- 'admin', /* login */
- 'admin', /* password */
- 'SELECT URP_Profiles' /* oql */
- );
+ $oRes = $oSoapClient->SearchObjects(
+ 'admin', /* login */
+ 'admin', /* password */
+ 'SELECT URP_Profiles' /* oql */
+ );
- echo "SearchObjects() returned:\n";
- if ($oRes->status)
- {
- $aResults = $oRes->result;
+ echo "
SearchObjects() returned:\n";
+ if ($oRes->status) {
+ $aResults = $oRes->result;
- echo "
\n";
+ echo "\n";
- // Header made after the first line
- echo "\n";
- foreach ($aResults[0]->values as $aKeyValuePair)
- {
- echo " ".$aKeyValuePair->key." \n";
- }
- echo " \n";
+ // Header made after the first line
+ echo "\n";
+ foreach ($aResults[0]->values as $aKeyValuePair) {
+ echo " ".$aKeyValuePair->key." \n";
+ }
+ echo " \n";
- foreach ($aResults as $iRow => $aData)
- {
- echo "\n";
- foreach ($aData->values as $aKeyValuePair)
- {
- echo " ".$aKeyValuePair->value." \n";
- }
- echo " \n";
- }
- echo "
\n";
- }
- else
- {
- $aErrors = array();
- foreach ($oRes->errors->messages as $oMessage)
- {
- $aErrors[] = $oMessage->text;
- }
- $sErrorMsg = implode(', ', $aErrors);
- echo "SearchObjects() failed with message: $sErrorMsg
\n";
- //echo "\n";
- //print_r($oRes);
- //echo " \n";
- }
- echo "\n";
-}
-catch(SoapFault $e)
-{
- echo "SoapFault Exception: {$e->getMessage()} \n";
- echo "Request \n";
- echo "\n";
- echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
- echo " ";
- echo "Response ";
- echo $oSoapClient->__getLastResponse()."\n";
+ foreach ($aResults as $iRow => $aData) {
+ echo "\n";
+ foreach ($aData->values as $aKeyValuePair) {
+ echo " ".$aKeyValuePair->value." \n";
+ }
+ echo " \n";
+ }
+ echo "
\n";
+ } else {
+ $aErrors = [];
+ foreach ($oRes->errors->messages as $oMessage) {
+ $aErrors[] = $oMessage->text;
+ }
+ $sErrorMsg = implode(', ', $aErrors);
+ echo "SearchObjects() failed with message: $sErrorMsg
\n";
+ //echo "\n";
+ //print_r($oRes);
+ //echo " \n";
+ }
+ echo "\n";
+} catch (SoapFault $e) {
+ echo "SoapFault Exception: {$e->getMessage()} \n";
+ echo "Request \n";
+ echo "\n";
+ echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
+ echo " ";
+ echo "Response ";
+ echo $oSoapClient->__getLastResponse()."\n";
}
-?>
diff --git a/webservices/itopsoaptypes.class.inc.php b/webservices/itopsoaptypes.class.inc.php
index 6145d85fae..ce1ca86a59 100644
--- a/webservices/itopsoaptypes.class.inc.php
+++ b/webservices/itopsoaptypes.class.inc.php
@@ -1,9 +1,10 @@
-
/**
* Declarations required for the WSDL
*
@@ -24,165 +24,158 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
-
// Note: the attributes must have the same names (case sensitive) as in the WSDL specification
//
class SOAPSearchCondition
{
- public $attcode; // string
- public $value; // mixed
-
- public function __construct($sAttCode, $value)
- {
- $this->attcode = $sAttCode;
- $this->value = $value;
- }
+ public $attcode; // string
+ public $value; // mixed
+
+ public function __construct($sAttCode, $value)
+ {
+ $this->attcode = $sAttCode;
+ $this->value = $value;
+ }
}
-
class SOAPExternalKeySearch
{
- public $conditions; // array of SOAPSearchCondition
-
- public function __construct($aConditions = null)
- {
- $this->conditions = $aConditions;
- }
-
- public function IsVoid()
- {
- if (is_null($this->conditions)) return true;
- if (count($this->conditions) == 0) return true;
- }
+ public $conditions; // array of SOAPSearchCondition
+
+ public function __construct($aConditions = null)
+ {
+ $this->conditions = $aConditions;
+ }
+
+ public function IsVoid()
+ {
+ if (is_null($this->conditions)) {
+ return true;
+ }
+ if (count($this->conditions) == 0) {
+ return true;
+ }
+ }
}
-
class SOAPAttributeValue
{
- public $attcode; // string
- public $value; // mixed
-
- public function __construct($sAttCode, $value)
- {
- $this->attcode = $sAttCode;
- $this->value = $value;
- }
+ public $attcode; // string
+ public $value; // mixed
+
+ public function __construct($sAttCode, $value)
+ {
+ $this->attcode = $sAttCode;
+ $this->value = $value;
+ }
}
-
class SOAPLinkCreationSpec
{
- public $class;
- public $conditions; // array of SOAPSearchCondition
- public $attributes; // array of SOAPAttributeValue
-
- public function __construct($sClass, $aConditions, $aAttributes)
- {
- $this->class = $sClass;
- $this->conditions = $aConditions;
- $this->attributes = $aAttributes;
- }
+ public $class;
+ public $conditions; // array of SOAPSearchCondition
+ public $attributes; // array of SOAPAttributeValue
+
+ public function __construct($sClass, $aConditions, $aAttributes)
+ {
+ $this->class = $sClass;
+ $this->conditions = $aConditions;
+ $this->attributes = $aAttributes;
+ }
}
-
class SOAPLogMessage
{
- public $text; // string
+ public $text; // string
- public function __construct($sText)
- {
- $this->text = $sText;
- }
+ public function __construct($sText)
+ {
+ $this->text = $sText;
+ }
}
-
class SOAPResultLog
{
- public $messages; // array of SOAPLogMessage
+ public $messages; // array of SOAPLogMessage
- public function __construct($aMessages)
- {
- $this->messages = $aMessages;
- }
+ public function __construct($aMessages)
+ {
+ $this->messages = $aMessages;
+ }
}
-
class SOAPKeyValue
{
- public $key; // string
- public $value; // string
-
- public function __construct($sKey, $sValue)
- {
- $this->key = $sKey;
- $this->value = $sValue;
- }
+ public $key; // string
+ public $value; // string
+
+ public function __construct($sKey, $sValue)
+ {
+ $this->key = $sKey;
+ $this->value = $sValue;
+ }
}
class SOAPResultMessage
{
- public $label; // string
- public $values; // array of SOAPKeyValue
-
- public function __construct($sLabel, $aValues)
- {
- $this->label = $sLabel;
- $this->values = $aValues;
- }
+ public $label; // string
+ public $values; // array of SOAPKeyValue
+
+ public function __construct($sLabel, $aValues)
+ {
+ $this->label = $sLabel;
+ $this->values = $aValues;
+ }
}
-
class SOAPResult
{
- public $status; // boolean
- public $result; // array of SOAPResultMessage
- public $errors; // array of SOAPResultLog
- public $warnings; // array of SOAPResultLog
- public $infos; // array of SOAPResultLog
-
- public function __construct($bStatus, $aResult, $aErrors, $aWarnings, $aInfos)
- {
- $this->status = $bStatus;
- $this->result = $aResult;
- $this->errors = $aErrors;
- $this->warnings = $aWarnings;
- $this->infos = $aInfos;
- }
+ public $status; // boolean
+ public $result; // array of SOAPResultMessage
+ public $errors; // array of SOAPResultLog
+ public $warnings; // array of SOAPResultLog
+ public $infos; // array of SOAPResultLog
+
+ public function __construct($bStatus, $aResult, $aErrors, $aWarnings, $aInfos)
+ {
+ $this->status = $bStatus;
+ $this->result = $aResult;
+ $this->errors = $aErrors;
+ $this->warnings = $aWarnings;
+ $this->infos = $aInfos;
+ }
}
class SOAPSimpleResult
{
- public $status; // boolean
- public $message; // string
-
- public function __construct($bStatus, $sMessage)
- {
- $this->status = $bStatus;
- $this->message = $sMessage;
- }
+ public $status; // boolean
+ public $message; // string
+
+ public function __construct($bStatus, $sMessage)
+ {
+ $this->status = $bStatus;
+ $this->message = $sMessage;
+ }
}
-
class SOAPMapping
{
- static function GetMapping()
- {
- $aSOAPMapping = array(
- 'SearchCondition' => 'SOAPSearchCondition',
- 'ExternalKeySearch' => 'SOAPExternalKeySearch',
- 'AttributeValue' => 'SOAPAttributeValue',
- 'LinkCreationSpec' => 'SOAPLinkCreationSpec',
- 'KeyValue' => 'SOAPKeyValue',
- 'LogMessage' => 'SOAPLogMessage',
- 'ResultLog' => 'SOAPResultLog',
- 'ResultData' => 'SOAPKeyValue',
- 'ResultMessage' => 'SOAPResultMessage',
- 'Result' => 'SOAPResult',
- 'SimpleResult' => 'SOAPSimpleResult',
- );
- return $aSOAPMapping;
- }
+ public static function GetMapping()
+ {
+ $aSOAPMapping = [
+ 'SearchCondition' => 'SOAPSearchCondition',
+ 'ExternalKeySearch' => 'SOAPExternalKeySearch',
+ 'AttributeValue' => 'SOAPAttributeValue',
+ 'LinkCreationSpec' => 'SOAPLinkCreationSpec',
+ 'KeyValue' => 'SOAPKeyValue',
+ 'LogMessage' => 'SOAPLogMessage',
+ 'ResultLog' => 'SOAPResultLog',
+ 'ResultData' => 'SOAPKeyValue',
+ 'ResultMessage' => 'SOAPResultMessage',
+ 'Result' => 'SOAPResult',
+ 'SimpleResult' => 'SOAPSimpleResult',
+ ];
+ return $aSOAPMapping;
+ }
}
-
-?>
diff --git a/webservices/rest.php b/webservices/rest.php
index 70f08cb0e2..c16b8df703 100644
--- a/webservices/rest.php
+++ b/webservices/rest.php
@@ -1,4 +1,5 @@
operations[] = array(
- 'verb' => $sVerb,
- 'description' => $sDescription,
- 'extension' => $sServiceProviderClass,
- );
- }
+ public $version;
+ public $operations;
+
+ public function AddOperation($sVerb, $sDescription, $sServiceProviderClass)
+ {
+ $this->operations[] = [
+ 'verb' => $sVerb,
+ 'description' => $sDescription,
+ 'extension' => $sServiceProviderClass,
+ ];
+ }
}
if (!function_exists('json_last_error_msg')) {
- function json_last_error_msg() {
- static $ERRORS = array(
- JSON_ERROR_NONE => 'No error',
- JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
- JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
- JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
- JSON_ERROR_SYNTAX => 'Syntax error',
- JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
- );
-
- $error = json_last_error();
- return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
- }
+ function json_last_error_msg()
+ {
+ static $ERRORS = [
+ JSON_ERROR_NONE => 'No error',
+ JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
+ JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
+ JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
+ JSON_ERROR_SYNTAX => 'Syntax error',
+ JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
+ ];
+
+ $error = json_last_error();
+ return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -74,206 +75,169 @@ function json_last_error_msg() {
//read json_data parameter via as a string (standard behaviour)
$sJsonString = utils::ReadParam('json_data', null, false, 'raw_data');
-if (empty($sJsonString)){
- //N °3455: read json_data parameter via a file passed by http protocol
- if(isset($_FILES['json_data']['tmp_name']))
- {
- $sTmpFilePath = $_FILES['json_data']['tmp_name'];
- if (is_file($sTmpFilePath)){
- $sValue = file_get_contents($sTmpFilePath);
- unlink($sTmpFilePath);
- if (! empty($sValue)){
- $sJsonString = utils::Sanitize($sValue, null, 'raw_data');
- }
- }
- }
+if (empty($sJsonString)) {
+ //N °3455: read json_data parameter via a file passed by http protocol
+ if (isset($_FILES['json_data']['tmp_name'])) {
+ $sTmpFilePath = $_FILES['json_data']['tmp_name'];
+ if (is_file($sTmpFilePath)) {
+ $sValue = file_get_contents($sTmpFilePath);
+ unlink($sTmpFilePath);
+ if (! empty($sValue)) {
+ $sJsonString = utils::Sanitize($sValue, null, 'raw_data');
+ }
+ }
+ }
}
$sProvider = '';
$oKPI = new ExecutionKPI();
-try
-{
- utils::UseParamFile();
-
- $oKPI->ComputeAndReport('Data model loaded');
+try {
+ utils::UseParamFile();
+
+ $oKPI->ComputeAndReport('Data model loaded');
// N°6358 - force credentials for REST calls
LoginWebPage::ResetSession(true);
- $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
+ $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
$oKPI->ComputeAndReport('User login');
- if ($iRet == LoginWebPage::EXIT_CODE_OK)
- {
- // Extra validation of the profile
- if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User'))
- {
- // Web services access is limited to the users with the profile REST Web Services
- $iRet = LoginWebPage::EXIT_CODE_NOTAUTHORIZED;
- }
- }
- if ($iRet != LoginWebPage::EXIT_CODE_OK)
- {
- switch($iRet)
- {
- case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
- throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
- break;
-
- case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
- throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
- break;
-
- case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
- throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
- break;
-
- case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
- throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
- break;
-
- case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
- throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
- break;
-
- default:
- throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
- }
- }
-
- if ($sVersion == null)
- {
- throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
- }
-
- if ($sJsonString == null)
- {
- throw new Exception("Missing parameter 'json_data'", RestResult::MISSING_JSON);
- }
-
- if (is_string($sJsonString))
- {
- $aJsonData = @json_decode($sJsonString);
+ if ($iRet == LoginWebPage::EXIT_CODE_OK) {
+ // Extra validation of the profile
+ if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User')) {
+ // Web services access is limited to the users with the profile REST Web Services
+ $iRet = LoginWebPage::EXIT_CODE_NOTAUTHORIZED;
+ }
}
- elseif(is_array($sJsonString))
- {
+ if ($iRet != LoginWebPage::EXIT_CODE_OK) {
+ switch ($iRet) {
+ case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
+ throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
+ break;
+
+ case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
+ throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
+ break;
+
+ case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
+ throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
+ break;
+
+ case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
+ throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
+ break;
+
+ case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
+ throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
+ break;
+
+ default:
+ throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
+ }
+ }
+
+ if ($sVersion == null) {
+ throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
+ }
+
+ if ($sJsonString == null) {
+ throw new Exception("Missing parameter 'json_data'", RestResult::MISSING_JSON);
+ }
+
+ if (is_string($sJsonString)) {
+ $aJsonData = @json_decode($sJsonString);
+ } elseif (is_array($sJsonString)) {
$aJsonData = (object) $sJsonString;
$sJsonString = json_encode($aJsonData);
- }
- else
- {
+ } else {
$aJsonData = null;
}
- if ($aJsonData == null)
- {
+ if ($aJsonData == null) {
throw new Exception('Parameter json_data is not a valid JSON structure', RestResult::INVALID_JSON);
}
- $oKPI->ComputeAndReport('Parameters validated');
-
-
- /** @var iRestServiceProvider[] $aProviders */
- $oKPI = new ExecutionKPI();
- $aProviders = array();
- foreach(get_declared_classes() as $sPHPClass)
- {
- $oRefClass = new ReflectionClass($sPHPClass);
- if ($oRefClass->implementsInterface('iRestServiceProvider'))
- {
- $aProviders[] = new $sPHPClass;
- }
- }
-
- $aOpToRestService = array(); // verb => $oRestServiceProvider
- /** @var iRestServiceProvider $oRestSP */
- foreach ($aProviders as $oRestSP)
- {
- $aOperations = $oRestSP->ListOperations($sVersion);
- foreach ($aOperations as $aOpData)
- {
- $aOpToRestService[$aOpData['verb']] = array
- (
- 'service_provider' => $oRestSP,
- 'description' => $aOpData['description'],
- );
- }
- }
- $oKPI->ComputeAndReport('iRestServiceProvider loaded with operations');
-
- if (count($aOpToRestService) == 0)
- {
- throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
- }
-
-
- $sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
- if ($sOperation == 'list_operations')
- {
- $oResult = new RestResultListOperations();
- $oResult->message = "Operations: ".count($aOpToRestService);
- $oResult->version = $sVersion;
- foreach ($aOpToRestService as $sVerb => $aOpData)
- {
- $oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
- }
- }
- else
- {
- if (!array_key_exists($sOperation, $aOpToRestService))
- {
- throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
- }
- /** @var iRestServiceProvider $oRS */
- $oRS = $aOpToRestService[$sOperation]['service_provider'];
- $sProvider = get_class($oRS);
-
- if ($oRS instanceof iRestInputSanitizer) {
- $sSanitizedJsonInput = $oRS->SanitizeJsonInput($sJsonString);
- }
-
- CMDBObject::SetTrackOrigin('webservice-rest');
- $oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
- }
- $oKPI->ComputeAndReport('Operation finished');
-}
-catch(Exception $e)
-{
- $oResult = new RestResult();
- if ($e->GetCode() == 0)
- {
- $oResult->code = RestResult::INTERNAL_ERROR;
- }
- else
- {
- $oResult->code = $e->GetCode();
- }
- $oResult->message = "Error: ".$e->GetMessage();
- $oKPI->ComputeAndReport('Exception catched');
+ $oKPI->ComputeAndReport('Parameters validated');
+
+ /** @var iRestServiceProvider[] $aProviders */
+ $oKPI = new ExecutionKPI();
+ $aProviders = [];
+ foreach (get_declared_classes() as $sPHPClass) {
+ $oRefClass = new ReflectionClass($sPHPClass);
+ if ($oRefClass->implementsInterface('iRestServiceProvider')) {
+ $aProviders[] = new $sPHPClass();
+ }
+ }
+
+ $aOpToRestService = []; // verb => $oRestServiceProvider
+ /** @var iRestServiceProvider $oRestSP */
+ foreach ($aProviders as $oRestSP) {
+ $aOperations = $oRestSP->ListOperations($sVersion);
+ foreach ($aOperations as $aOpData) {
+ $aOpToRestService[$aOpData['verb']] =
+ [
+ 'service_provider' => $oRestSP,
+ 'description' => $aOpData['description'],
+ ];
+ }
+ }
+ $oKPI->ComputeAndReport('iRestServiceProvider loaded with operations');
+
+ if (count($aOpToRestService) == 0) {
+ throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
+ }
+
+ $sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
+ if ($sOperation == 'list_operations') {
+ $oResult = new RestResultListOperations();
+ $oResult->message = "Operations: ".count($aOpToRestService);
+ $oResult->version = $sVersion;
+ foreach ($aOpToRestService as $sVerb => $aOpData) {
+ $oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
+ }
+ } else {
+ if (!array_key_exists($sOperation, $aOpToRestService)) {
+ throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
+ }
+ /** @var iRestServiceProvider $oRS */
+ $oRS = $aOpToRestService[$sOperation]['service_provider'];
+ $sProvider = get_class($oRS);
+
+ if ($oRS instanceof iRestInputSanitizer) {
+ $sSanitizedJsonInput = $oRS->SanitizeJsonInput($sJsonString);
+ }
+
+ CMDBObject::SetTrackOrigin('webservice-rest');
+ $oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
+ }
+ $oKPI->ComputeAndReport('Operation finished');
+} catch (Exception $e) {
+ $oResult = new RestResult();
+ if ($e->GetCode() == 0) {
+ $oResult->code = RestResult::INTERNAL_ERROR;
+ } else {
+ $oResult->code = $e->GetCode();
+ }
+ $oResult->message = "Error: ".$e->GetMessage();
+ $oKPI->ComputeAndReport('Exception catched');
}
// Output the results
//
$sResponse = json_encode($oResult);
-
-if ($sResponse === false)
-{
- $oJsonIssue = new RestResult();
- $oJsonIssue->code = RestResult::INTERNAL_ERROR;
- $oJsonIssue->message = 'json encoding failed with message: '.json_last_error_msg().'. Full response structure for debugging purposes (print_r+bin2hex): '.bin2hex(print_r($oResult, true));
- $sResponse = json_encode($oJsonIssue);
+if ($sResponse === false) {
+ $oJsonIssue = new RestResult();
+ $oJsonIssue->code = RestResult::INTERNAL_ERROR;
+ $oJsonIssue->message = 'json encoding failed with message: '.json_last_error_msg().'. Full response structure for debugging purposes (print_r+bin2hex): '.bin2hex(print_r($oResult, true));
+ $sResponse = json_encode($oJsonIssue);
}
-
$sCallback = utils::ReadParam('callback', null);
-if ($sCallback == null)
-{
- $oP = new JsonPage();
-}
-else
-{
- $oP = new JsonPPage($sCallback);
+if ($sCallback == null) {
+ $oP = new JsonPage();
+} else {
+ $oP = new JsonPPage($sCallback);
}
$oP->add_header('Access-Control-Allow-Origin: *');
$oP->SetData(json_decode($sResponse, true));
@@ -283,31 +247,29 @@ function json_last_error_msg() {
// Log usage
//
-if (MetaModel::GetConfig()->Get('log_rest_service'))
-{
- $oLog = new EventRestService();
- $oLog->SetTrim('userinfo', UserRights::GetUser());
- $oLog->Set('version', $sVersion);
- $oLog->Set('operation', $sOperation);
+if (MetaModel::GetConfig()->Get('log_rest_service')) {
+ $oLog = new EventRestService();
+ $oLog->SetTrim('userinfo', UserRights::GetUser());
+ $oLog->Set('version', $sVersion);
+ $oLog->Set('operation', $sOperation);
$oLog->SetTrim('json_input', $sSanitizedJsonInput ?? $sJsonString);
- $oLog->Set('provider', $sProvider);
- $sMessage = $oResult->message;
- if (empty($oResult->message))
- {
- $sMessage = 'Ok';
- }
- $oLog->SetTrim('message', $sMessage);
- $oLog->Set('code', $oResult->code);
- $oResult->SanitizeContent();
- $iUnescapeSlashAndUnicode = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
- $sJsonOuputWithPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode | JSON_PRETTY_PRINT);
- $sJsonOutputWithoutPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode);
- !StringFitsInLogField( $sJsonOuputWithPrettyPrinting) ?
- $oLog->SetTrim('json_output', $sJsonOutputWithoutPrettyPrinting) : // too long, we don't make it pretty
- $oLog->SetTrim('json_output', $sJsonOuputWithPrettyPrinting);
-
- $oLog->DBInsertNoReload();
+ $oLog->Set('provider', $sProvider);
+ $sMessage = $oResult->message;
+ if (empty($oResult->message)) {
+ $sMessage = 'Ok';
+ }
+ $oLog->SetTrim('message', $sMessage);
+ $oLog->Set('code', $oResult->code);
+ $oResult->SanitizeContent();
+ $iUnescapeSlashAndUnicode = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
+ $sJsonOuputWithPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode | JSON_PRETTY_PRINT);
+ $sJsonOutputWithoutPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode);
+ !StringFitsInLogField($sJsonOuputWithPrettyPrinting) ?
+ $oLog->SetTrim('json_output', $sJsonOutputWithoutPrettyPrinting) : // too long, we don't make it pretty
+ $oLog->SetTrim('json_output', $sJsonOuputWithPrettyPrinting);
+
+ $oLog->DBInsertNoReload();
}
/**
@@ -315,5 +277,5 @@ function json_last_error_msg() {
*/
function StringFitsInLogField(string $sLog): bool
{
- return mb_strlen($sLog) <= 16383; // hardcoded value, see N°8260
-}
\ No newline at end of file
+ return mb_strlen($sLog) <= 16383; // hardcoded value, see N°8260
+}
diff --git a/webservices/soapserver.php b/webservices/soapserver.php
index 2d9b49ec46..e0b01ac4ab 100644
--- a/webservices/soapserver.php
+++ b/webservices/soapserver.php
@@ -1,4 +1,5 @@
$aSOAPMapping
- )
+$oSoapServer = new SoapServer(
+ $sWsdlUri,
+ [
+ 'classmap' => $aSOAPMapping
+ ]
);
// $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION);
-if (!empty($sServiceCategory))
-{
- $sServiceClass = $sServiceCategory;
- if (!class_exists($sServiceClass))
- {
- // not a valid class name (not a PHP class at all)
- throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not a PHP class");
- }
- elseif (!is_subclass_of($sServiceClass, 'WebServicesBase'))
- {
- // not a valid class name (not deriving from WebServicesBase)
- throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not derived from WebServicesBase");
- }
- else
- {
- $oSoapServer->setClass($sServiceClass, null);
- }
-}
-else
-{
- $oSoapServer->setClass('BasicServices', null);
+if (!empty($sServiceCategory)) {
+ $sServiceClass = $sServiceCategory;
+ if (!class_exists($sServiceClass)) {
+ // not a valid class name (not a PHP class at all)
+ throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not a PHP class");
+ } elseif (!is_subclass_of($sServiceClass, 'WebServicesBase')) {
+ // not a valid class name (not deriving from WebServicesBase)
+ throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not derived from WebServicesBase");
+ } else {
+ $oSoapServer->setClass($sServiceClass, null);
+ }
+} else {
+ $oSoapServer->setClass('BasicServices', null);
}
-if ($_SERVER["REQUEST_METHOD"] == "POST")
-{
- CMDBObject::SetTrackOrigin('webservice-soap');
- $oSoapServer->handle();
-}
-else
-{
- echo "This SOAP server can handle the following functions: ";
- $aFunctions = $oSoapServer->getFunctions();
- echo "\n";
- foreach($aFunctions as $sFunc)
- {
- if ($sFunc == 'GetWSDLContents') continue;
+if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ CMDBObject::SetTrackOrigin('webservice-soap');
+ $oSoapServer->handle();
+} else {
+ echo "This SOAP server can handle the following functions: ";
+ $aFunctions = $oSoapServer->getFunctions();
+ echo "\n";
+ foreach ($aFunctions as $sFunc) {
+ if ($sFunc == 'GetWSDLContents') {
+ continue;
+ }
- echo "$sFunc \n";
- }
- echo " \n";
- echo "Here the WSDL file
";
+ echo "
$sFunc \n";
+ }
+ echo " \n";
+ echo "Here the WSDL file
";
- echo "You may also want to try the following service categories: ";
- echo "
\n";
- foreach(get_declared_classes() as $sPHPClass)
- {
- if (is_subclass_of($sPHPClass, 'WebServicesBase'))
- {
- $sServiceCategory = $sPHPClass;
- $sSoapServerUri = utils::GetAbsoluteUrlAppRoot().'webservices/soapserver.php';
- $sSoapServerUri .= "?service_category=$sServiceCategory";
- echo "$sServiceCategory \n";
- }
- }
- echo " \n";
+ echo "You may also want to try the following service categories: ";
+ echo "\n";
+ foreach (get_declared_classes() as $sPHPClass) {
+ if (is_subclass_of($sPHPClass, 'WebServicesBase')) {
+ $sServiceCategory = $sPHPClass;
+ $sSoapServerUri = utils::GetAbsoluteUrlAppRoot().'webservices/soapserver.php';
+ $sSoapServerUri .= "?service_category=$sServiceCategory";
+ echo "$sServiceCategory \n";
+ }
+ }
+ echo " \n";
}
-?>
diff --git a/webservices/status.php b/webservices/status.php
index 636ab4ec64..55f297519d 100644
--- a/webservices/status.php
+++ b/webservices/status.php
@@ -6,16 +6,13 @@
use Combodo\iTop\Application\Status\Status;
//Do check Status
-try
-{
- new Status();
- $aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
-}
-catch (Exception $e)
-{
- $iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
- $aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
- http_response_code(500);
+try {
+ new Status();
+ $aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
+} catch (Exception $e) {
+ $iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
+ $aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
+ http_response_code(500);
}
//Set headers, based on webservices/rest.php
diff --git a/webservices/webservices.basic.php b/webservices/webservices.basic.php
index bf8c364474..336ace0713 100644
--- a/webservices/webservices.basic.php
+++ b/webservices/webservices.basic.php
@@ -1,9 +1,10 @@
-
/**
* Implementation of iTop SOAP services
*
@@ -26,267 +26,234 @@
require_once(APPROOT.'/webservices/webservices.class.inc.php');
-
class BasicServices extends WebServicesBase
{
- static protected function GetWSDLFilePath()
- {
- return APPROOT.'/webservices/itop.wsdl.tpl';
- }
-
- /**
- * Get the server version (TODO: get it dynamically, where ?)
- *
- * @return string WebServiceResult
- */
- static public function GetVersion()
- {
- if (ITOP_REVISION == 'svn')
- {
- $sVersionString = ITOP_VERSION.' [dev]';
- }
- else
- {
- // This is a build made from SVN, let display the full information
- $sVersionString = ITOP_VERSION_FULL." ".ITOP_BUILD_DATE;
- }
-
- return $sVersionString;
- }
-
- public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
- {
- if (!UserRights::CheckCredentials($sLogin, $sPassword))
- {
- $oRes = new WebServiceResultFailedLogin($sLogin);
- $this->LogUsage(__FUNCTION__, $oRes);
-
- return $oRes->ToSoapStructure();
- }
- UserRights::Login($sLogin);
-
- $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
- $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
- $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
- $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
- $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
-
- $aImpactedCIs = array();
- if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
- foreach($aSOAPImpactedCIs as $oImpactedCIs)
- {
- $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
- }
-
- $oRes = $this->_CreateResponseTicket
- (
- 'UserRequest',
- $sTitle,
- $sDescription,
- $aCallerDesc,
- $aCustomerDesc,
- $aServiceDesc,
- $aServiceSubcategoryDesc,
- $sProduct,
- $aWorkgroupDesc,
- $aImpactedCIs,
- $sImpact,
- $sUrgency
- );
- return $oRes->ToSoapStructure();
- }
-
- public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
- {
- if (!UserRights::CheckCredentials($sLogin, $sPassword))
- {
- $oRes = new WebServiceResultFailedLogin($sLogin);
- $this->LogUsage(__FUNCTION__, $oRes);
-
- return $oRes->ToSoapStructure();
- }
- UserRights::Login($sLogin);
-
-
- if (!class_exists('Incident'))
- {
- $oRes = new WebServiceResult();
- $oRes->LogError("The class Incident does not exist. Did you install the Incident Management (ITIL) module ?");
- return $oRes->ToSoapStructure();
- }
-
- $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
- $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
- $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
- $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
- $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
-
- $aImpactedCIs = array();
- if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
- foreach($aSOAPImpactedCIs as $oImpactedCIs)
- {
- $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
- }
-
- $oRes = $this->_CreateResponseTicket
- (
- 'Incident',
- $sTitle,
- $sDescription,
- $aCallerDesc,
- $aCustomerDesc,
- $aServiceDesc,
- $aServiceSubcategoryDesc,
- $sProduct,
- $aWorkgroupDesc,
- $aImpactedCIs,
- $sImpact,
- $sUrgency
- );
- return $oRes->ToSoapStructure();
- }
-
- /**
- * Create an ResponseTicket (Incident or UserRequest) from an external system
- * Some CIs might be specified (by their name/IP)
- *
- * @param string sClass The class of the ticket: Incident or UserRequest
- * @param string sTitle
- * @param string sDescription
- * @param array aCallerDesc
- * @param array aCustomerDesc
- * @param array aServiceDesc
- * @param array aServiceSubcategoryDesc
- * @param string sProduct
- * @param array aWorkgroupDesc
- * @param array aImpactedCIs
- * @param string sImpact
- * @param string sUrgency
- *
- * @return WebServiceResult
- */
- protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCallerDesc, $aCustomerDesc, $aServiceDesc, $aServiceSubcategoryDesc, $sProduct, $aWorkgroupDesc, $aImpactedCIs, $sImpact, $sUrgency)
- {
-
- $oRes = new WebServiceResult();
-
- try
- {
- CMDBObject::SetTrackInfo('Administrator');
-
- $oNewTicket = MetaModel::NewObject($sClass);
- $this->MyObjectSetScalar('title', 'title', $sTitle, $oNewTicket, $oRes);
- $this->MyObjectSetScalar('description', 'description', $sDescription, $oNewTicket, $oRes);
-
- $this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
- $this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
-
- $this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
- if (!array_key_exists('service_id', $aServiceSubcategoryDesc))
- {
- $aServiceSubcategoryDesc['service_id'] = $oNewTicket->Get('service_id');
- }
- $this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
- if (MetaModel::IsValidAttCode($sClass, 'product'))
- {
- // 1.x data models
- $this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
- }
-
- if (MetaModel::IsValidAttCode($sClass, 'workgroup_id'))
- {
- // 1.x data models
- $this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
- }
- else if (MetaModel::IsValidAttCode($sClass, 'team_id'))
- {
- // 2.x data models
- $this->MyObjectSetExternalKey('team_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
- }
-
-
- if (MetaModel::IsValidAttCode($sClass, 'ci_list'))
- {
- // 1.x data models
- $aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
- }
- else if (MetaModel::IsValidAttCode($sClass, 'functionalcis_list'))
- {
- // 2.x data models
- $aDevicesNotFound = $this->AddLinkedObjects('functionalcis_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
- }
-
- if (count($aDevicesNotFound) > 0)
- {
- $this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
- }
- else
- {
- $this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
- }
-
- $this->MyObjectSetScalar('impact', 'impact', $sImpact, $oNewTicket, $oRes);
- $this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
-
- $this->MyObjectInsert($oNewTicket, 'created', $oRes);
- }
- catch (CoreException $e)
- {
- $oRes->LogError($e->getMessage());
- }
- catch (Exception $e)
- {
- $oRes->LogError($e->getMessage());
- }
-
- $this->LogUsage(__FUNCTION__, $oRes);
- return $oRes;
- }
-
- /**
- * Given an OQL, returns a set of objects (several objects could be on the same row)
- *
- * @param string sOQL
- */
- public function SearchObjects($sLogin, $sPassword, $sOQL)
- {
- if (!UserRights::CheckCredentials($sLogin, $sPassword))
- {
- $oRes = new WebServiceResultFailedLogin($sLogin);
- $this->LogUsage(__FUNCTION__, $oRes);
-
- return $oRes->ToSoapStructure();
- }
- UserRights::Login($sLogin);
-
- $oRes = $this->_SearchObjects($sOQL);
- return $oRes->ToSoapStructure();
- }
-
- protected function _SearchObjects($sOQL)
- {
- $oRes = new WebServiceResult();
- try
- {
- $oSearch = DBObjectSearch::FromOQL($sOQL);
- $oSet = new DBObjectSet($oSearch);
- $aData = $oSet->ToArrayOfValues();
- foreach($aData as $iRow => $aRow)
- {
- $oRes->AddResultRow("row_$iRow", $aRow);
- }
- }
- catch (CoreException $e)
- {
- $oRes->LogError($e->getMessage());
- }
- catch (Exception $e)
- {
- $oRes->LogError($e->getMessage());
- }
-
- $this->LogUsage(__FUNCTION__, $oRes);
- return $oRes;
- }
+ protected static function GetWSDLFilePath()
+ {
+ return APPROOT.'/webservices/itop.wsdl.tpl';
+ }
+
+ /**
+ * Get the server version (TODO: get it dynamically, where ?)
+ *
+ * @return string WebServiceResult
+ */
+ public static function GetVersion()
+ {
+ if (ITOP_REVISION == 'svn') {
+ $sVersionString = ITOP_VERSION.' [dev]';
+ } else {
+ // This is a build made from SVN, let display the full information
+ $sVersionString = ITOP_VERSION_FULL." ".ITOP_BUILD_DATE;
+ }
+
+ return $sVersionString;
+ }
+
+ public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
+ {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ $oRes = new WebServiceResultFailedLogin($sLogin);
+ $this->LogUsage(__FUNCTION__, $oRes);
+
+ return $oRes->ToSoapStructure();
+ }
+ UserRights::Login($sLogin);
+
+ $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
+ $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
+ $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
+ $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
+ $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
+
+ $aImpactedCIs = [];
+ if (is_null($aSOAPImpactedCIs)) {
+ $aSOAPImpactedCIs = [];
+ }
+ foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
+ $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
+ }
+
+ $oRes = $this->_CreateResponseTicket(
+ 'UserRequest',
+ $sTitle,
+ $sDescription,
+ $aCallerDesc,
+ $aCustomerDesc,
+ $aServiceDesc,
+ $aServiceSubcategoryDesc,
+ $sProduct,
+ $aWorkgroupDesc,
+ $aImpactedCIs,
+ $sImpact,
+ $sUrgency
+ );
+ return $oRes->ToSoapStructure();
+ }
+
+ public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
+ {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ $oRes = new WebServiceResultFailedLogin($sLogin);
+ $this->LogUsage(__FUNCTION__, $oRes);
+
+ return $oRes->ToSoapStructure();
+ }
+ UserRights::Login($sLogin);
+
+ if (!class_exists('Incident')) {
+ $oRes = new WebServiceResult();
+ $oRes->LogError("The class Incident does not exist. Did you install the Incident Management (ITIL) module ?");
+ return $oRes->ToSoapStructure();
+ }
+
+ $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
+ $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
+ $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
+ $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
+ $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
+
+ $aImpactedCIs = [];
+ if (is_null($aSOAPImpactedCIs)) {
+ $aSOAPImpactedCIs = [];
+ }
+ foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
+ $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
+ }
+
+ $oRes = $this->_CreateResponseTicket(
+ 'Incident',
+ $sTitle,
+ $sDescription,
+ $aCallerDesc,
+ $aCustomerDesc,
+ $aServiceDesc,
+ $aServiceSubcategoryDesc,
+ $sProduct,
+ $aWorkgroupDesc,
+ $aImpactedCIs,
+ $sImpact,
+ $sUrgency
+ );
+ return $oRes->ToSoapStructure();
+ }
+
+ /**
+ * Create an ResponseTicket (Incident or UserRequest) from an external system
+ * Some CIs might be specified (by their name/IP)
+ *
+ * @param string sClass The class of the ticket: Incident or UserRequest
+ * @param string sTitle
+ * @param string sDescription
+ * @param array aCallerDesc
+ * @param array aCustomerDesc
+ * @param array aServiceDesc
+ * @param array aServiceSubcategoryDesc
+ * @param string sProduct
+ * @param array aWorkgroupDesc
+ * @param array aImpactedCIs
+ * @param string sImpact
+ * @param string sUrgency
+ *
+ * @return WebServiceResult
+ */
+ protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCallerDesc, $aCustomerDesc, $aServiceDesc, $aServiceSubcategoryDesc, $sProduct, $aWorkgroupDesc, $aImpactedCIs, $sImpact, $sUrgency)
+ {
+
+ $oRes = new WebServiceResult();
+
+ try {
+ CMDBObject::SetTrackInfo('Administrator');
+
+ $oNewTicket = MetaModel::NewObject($sClass);
+ $this->MyObjectSetScalar('title', 'title', $sTitle, $oNewTicket, $oRes);
+ $this->MyObjectSetScalar('description', 'description', $sDescription, $oNewTicket, $oRes);
+
+ $this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
+ $this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
+
+ $this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
+ if (!array_key_exists('service_id', $aServiceSubcategoryDesc)) {
+ $aServiceSubcategoryDesc['service_id'] = $oNewTicket->Get('service_id');
+ }
+ $this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
+ if (MetaModel::IsValidAttCode($sClass, 'product')) {
+ // 1.x data models
+ $this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
+ }
+
+ if (MetaModel::IsValidAttCode($sClass, 'workgroup_id')) {
+ // 1.x data models
+ $this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
+ } elseif (MetaModel::IsValidAttCode($sClass, 'team_id')) {
+ // 2.x data models
+ $this->MyObjectSetExternalKey('team_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
+ }
+
+ if (MetaModel::IsValidAttCode($sClass, 'ci_list')) {
+ // 1.x data models
+ $aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
+ } elseif (MetaModel::IsValidAttCode($sClass, 'functionalcis_list')) {
+ // 2.x data models
+ $aDevicesNotFound = $this->AddLinkedObjects('functionalcis_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
+ }
+
+ if (count($aDevicesNotFound) > 0) {
+ $this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
+ } else {
+ $this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
+ }
+
+ $this->MyObjectSetScalar('impact', 'impact', $sImpact, $oNewTicket, $oRes);
+ $this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
+
+ $this->MyObjectInsert($oNewTicket, 'created', $oRes);
+ } catch (CoreException $e) {
+ $oRes->LogError($e->getMessage());
+ } catch (Exception $e) {
+ $oRes->LogError($e->getMessage());
+ }
+
+ $this->LogUsage(__FUNCTION__, $oRes);
+ return $oRes;
+ }
+
+ /**
+ * Given an OQL, returns a set of objects (several objects could be on the same row)
+ *
+ * @param string sOQL
+ */
+ public function SearchObjects($sLogin, $sPassword, $sOQL)
+ {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ $oRes = new WebServiceResultFailedLogin($sLogin);
+ $this->LogUsage(__FUNCTION__, $oRes);
+
+ return $oRes->ToSoapStructure();
+ }
+ UserRights::Login($sLogin);
+
+ $oRes = $this->_SearchObjects($sOQL);
+ return $oRes->ToSoapStructure();
+ }
+
+ protected function _SearchObjects($sOQL)
+ {
+ $oRes = new WebServiceResult();
+ try {
+ $oSearch = DBObjectSearch::FromOQL($sOQL);
+ $oSet = new DBObjectSet($oSearch);
+ $aData = $oSet->ToArrayOfValues();
+ foreach ($aData as $iRow => $aRow) {
+ $oRes->AddResultRow("row_$iRow", $aRow);
+ }
+ } catch (CoreException $e) {
+ $oRes->LogError($e->getMessage());
+ } catch (Exception $e) {
+ $oRes->LogError($e->getMessage());
+ }
+
+ $this->LogUsage(__FUNCTION__, $oRes);
+ return $oRes;
+ }
}
-?>
diff --git a/webservices/webservices.class.inc.php b/webservices/webservices.class.inc.php
index 5df50d4270..f411b49d8d 100644
--- a/webservices/webservices.class.inc.php
+++ b/webservices/webservices.class.inc.php
@@ -1,9 +1,10 @@
-
/**
* Implementation of iTop SOAP services
*
@@ -24,7 +24,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
-
require_once(APPROOT.'/webservices/itopsoaptypes.class.inc.php');
/**
@@ -34,202 +33,197 @@
*/
class WebServiceResult
{
-
- /**
- * Overall status
- *
- * @var m_bStatus
- */
- public $m_bStatus;
-
- /**
- * Error log
- *
- * @var m_aErrors
- */
- public $m_aErrors;
-
- /**
- * Warning log
- *
- * @var m_aWarnings
- */
- public $m_aWarnings;
-
- /**
- * Information log
- *
- * @var m_aInfos
- */
- public $m_aInfos;
-
- /**
- * Constructor
- *
- * @param status $bStatus
- */
- public function __construct()
- {
- $this->m_bStatus = true;
- $this->m_aResult = array();
- $this->m_aErrors = array();
- $this->m_aWarnings = array();
- $this->m_aInfos = array();
- }
-
- public function ToSoapStructure()
- {
- $aResults = array();
- foreach($this->m_aResult as $sLabel => $aData)
- {
- $aValues = array();
- foreach($aData as $sKey => $value)
- {
- $aValues[] = new SOAPKeyValue($sKey, $value);
- }
- $aResults[] = new SoapResultMessage($sLabel, $aValues);
- }
- $aInfos = array();
- foreach($this->m_aInfos as $sMessage)
- {
- $aInfos[] = new SoapLogMessage($sMessage);
- }
- $aWarnings = array();
- foreach($this->m_aWarnings as $sMessage)
- {
- $aWarnings[] = new SoapLogMessage($sMessage);
- }
- $aErrors = array();
- foreach($this->m_aErrors as $sMessage)
- {
- $aErrors[] = new SoapLogMessage($sMessage);
- }
-
- $oRet = new SOAPResult(
- $this->m_bStatus,
- $aResults,
- new SOAPResultLog($aErrors),
- new SOAPResultLog($aWarnings),
- new SOAPResultLog($aInfos)
- );
-
- return $oRet;
- }
-
- /**
- * Did the current processing encounter a stopper issue ?
- *
- * @return bool
- */
- public function IsOk()
- {
- return $this->m_bStatus;
- }
-
- /**
- * Add result details - object reference
- *
- * @param string sLabel
- * @param object oObject
- */
- public function AddResultObject($sLabel, $oObject)
- {
- $oAppContext = new ApplicationContext();
- $this->m_aResult[$sLabel] = array(
- 'id' => $oObject->GetKey(),
- 'name' => $oObject->GetRawName(),
- 'url' => $oAppContext->MakeObjectUrl(get_class($oObject), $oObject->GetKey(), null, false), // Raw URL without HTML tags
- );
- }
-
- /**
- * Add result details - a table row
- *
- * @param string sLabel
- * @param object oObject
- */
- public function AddResultRow($sLabel, $aRow)
- {
- $this->m_aResult[$sLabel] = $aRow;
- }
-
- /**
- * Log an error
- *
- * @param string sDescription
- */
- public function LogError($sDescription)
- {
- $this->m_aErrors[] = $sDescription;
- // Note: SOAP do transform false into null
- $this->m_bStatus = 0;
- }
-
- /**
- * Log a warning
- *
- * @param string sDescription
- */
- public function LogWarning($sDescription)
- {
- $this->m_aWarnings[] = $sDescription;
- }
-
- /**
- * Log an error or a warning
- *
- * @param string sDescription
- * @param boolean bIsStopper
- */
- public function LogIssue($sDescription, $bIsStopper = true)
- {
- if ($bIsStopper) $this->LogError($sDescription);
- else $this->LogWarning($sDescription);
- }
-
- /**
- * Log operation details
- *
- * @param description $sDescription
- */
- public function LogInfo($sDescription)
- {
- $this->m_aInfos[] = $sDescription;
- }
-
- protected static function LogToText($aLog)
- {
- return implode("\n", $aLog);
- }
-
- public function GetInfoAsText()
- {
- return self::LogToText($this->m_aInfos);
- }
-
- public function GetWarningsAsText()
- {
- return self::LogToText($this->m_aWarnings);
- }
-
- public function GetErrorsAsText()
- {
- return self::LogToText($this->m_aErrors);
- }
-
- public function GetReturnedDataAsText()
- {
- $sRet = '';
- foreach ($this->m_aResult as $sKey => $value)
- {
- $sRet .= "===== $sKey =====\n";
- $sRet .= print_r($value, true);
- }
- return $sRet;
- }
+ /**
+ * Overall status
+ *
+ * @var m_bStatus
+ */
+ public $m_bStatus;
+
+ /**
+ * Error log
+ *
+ * @var m_aErrors
+ */
+ public $m_aErrors;
+
+ /**
+ * Warning log
+ *
+ * @var m_aWarnings
+ */
+ public $m_aWarnings;
+
+ /**
+ * Information log
+ *
+ * @var m_aInfos
+ */
+ public $m_aInfos;
+
+ /**
+ * Constructor
+ *
+ * @param status $bStatus
+ */
+ public function __construct()
+ {
+ $this->m_bStatus = true;
+ $this->m_aResult = [];
+ $this->m_aErrors = [];
+ $this->m_aWarnings = [];
+ $this->m_aInfos = [];
+ }
+
+ public function ToSoapStructure()
+ {
+ $aResults = [];
+ foreach ($this->m_aResult as $sLabel => $aData) {
+ $aValues = [];
+ foreach ($aData as $sKey => $value) {
+ $aValues[] = new SOAPKeyValue($sKey, $value);
+ }
+ $aResults[] = new SoapResultMessage($sLabel, $aValues);
+ }
+ $aInfos = [];
+ foreach ($this->m_aInfos as $sMessage) {
+ $aInfos[] = new SoapLogMessage($sMessage);
+ }
+ $aWarnings = [];
+ foreach ($this->m_aWarnings as $sMessage) {
+ $aWarnings[] = new SoapLogMessage($sMessage);
+ }
+ $aErrors = [];
+ foreach ($this->m_aErrors as $sMessage) {
+ $aErrors[] = new SoapLogMessage($sMessage);
+ }
+
+ $oRet = new SOAPResult(
+ $this->m_bStatus,
+ $aResults,
+ new SOAPResultLog($aErrors),
+ new SOAPResultLog($aWarnings),
+ new SOAPResultLog($aInfos)
+ );
+
+ return $oRet;
+ }
+
+ /**
+ * Did the current processing encounter a stopper issue ?
+ *
+ * @return bool
+ */
+ public function IsOk()
+ {
+ return $this->m_bStatus;
+ }
+
+ /**
+ * Add result details - object reference
+ *
+ * @param string sLabel
+ * @param object oObject
+ */
+ public function AddResultObject($sLabel, $oObject)
+ {
+ $oAppContext = new ApplicationContext();
+ $this->m_aResult[$sLabel] = [
+ 'id' => $oObject->GetKey(),
+ 'name' => $oObject->GetRawName(),
+ 'url' => $oAppContext->MakeObjectUrl(get_class($oObject), $oObject->GetKey(), null, false), // Raw URL without HTML tags
+ ];
+ }
+
+ /**
+ * Add result details - a table row
+ *
+ * @param string sLabel
+ * @param object oObject
+ */
+ public function AddResultRow($sLabel, $aRow)
+ {
+ $this->m_aResult[$sLabel] = $aRow;
+ }
+
+ /**
+ * Log an error
+ *
+ * @param string sDescription
+ */
+ public function LogError($sDescription)
+ {
+ $this->m_aErrors[] = $sDescription;
+ // Note: SOAP do transform false into null
+ $this->m_bStatus = 0;
+ }
+
+ /**
+ * Log a warning
+ *
+ * @param string sDescription
+ */
+ public function LogWarning($sDescription)
+ {
+ $this->m_aWarnings[] = $sDescription;
+ }
+
+ /**
+ * Log an error or a warning
+ *
+ * @param string sDescription
+ * @param boolean bIsStopper
+ */
+ public function LogIssue($sDescription, $bIsStopper = true)
+ {
+ if ($bIsStopper) {
+ $this->LogError($sDescription);
+ } else {
+ $this->LogWarning($sDescription);
+ }
+ }
+
+ /**
+ * Log operation details
+ *
+ * @param description $sDescription
+ */
+ public function LogInfo($sDescription)
+ {
+ $this->m_aInfos[] = $sDescription;
+ }
+
+ protected static function LogToText($aLog)
+ {
+ return implode("\n", $aLog);
+ }
+
+ public function GetInfoAsText()
+ {
+ return self::LogToText($this->m_aInfos);
+ }
+
+ public function GetWarningsAsText()
+ {
+ return self::LogToText($this->m_aWarnings);
+ }
+
+ public function GetErrorsAsText()
+ {
+ return self::LogToText($this->m_aErrors);
+ }
+
+ public function GetReturnedDataAsText()
+ {
+ $sRet = '';
+ foreach ($this->m_aResult as $sKey => $value) {
+ $sRet .= "===== $sKey =====\n";
+ $sRet .= print_r($value, true);
+ }
+ return $sRet;
+ }
}
-
/**
* Generic response of iTop SOAP services - failed login
*
@@ -237,11 +231,11 @@ public function GetReturnedDataAsText()
*/
class WebServiceResultFailedLogin extends WebServiceResult
{
- public function __construct($sLogin)
- {
- parent::__construct();
- $this->LogError("Wrong credentials: '$sLogin'");
- }
+ public function __construct($sLogin)
+ {
+ parent::__construct();
+ $this->LogError("Wrong credentials: '$sLogin'");
+ }
}
/**
@@ -251,355 +245,319 @@ public function __construct($sLogin)
*/
abstract class WebServicesBase
{
- static public function GetWSDLContents($sServiceCategory = '')
- {
- if ($sServiceCategory == '')
- {
- $sServiceCategory = 'BasicServices';
- }
- $sWsdlFilePath = call_user_func(array($sServiceCategory, 'GetWSDLFilePath'));
- return file_get_contents($sWsdlFilePath);
- }
-
- /**
- * Helper to log a service delivery
- *
- * @param string sVerb
- * @param array aArgs
- * @param WebServiceResult oRes
- *
- */
- protected function LogUsage($sVerb, $oRes)
- {
- if (!MetaModel::IsLogEnabledWebService()) return;
-
- $oLog = new EventWebService();
- if ($oRes->IsOk())
- {
- $oLog->Set('message', $sVerb.' was successfully invoked');
- }
- else
- {
- $oLog->Set('message', $sVerb.' returned errors');
- }
- $oLog->Set('userinfo', UserRights::GetUser());
- $oLog->Set('verb', $sVerb);
- $oLog->Set('result', $oRes->IsOk());
- $this->TrimAndSetValue($oLog, 'log_info', (string)$oRes->GetInfoAsText());
- $this->TrimAndSetValue($oLog, 'log_warning', (string)$oRes->GetWarningsAsText());
- $this->TrimAndSetValue($oLog, 'log_error', (string)$oRes->GetErrorsAsText());
- $this->TrimAndSetValue($oLog, 'data', (string)$oRes->GetReturnedDataAsText());
- $oLog->DBInsertNoReload();
- }
-
- protected function TrimAndSetValue($oLog, $sAttCode, $sValue)
- {
- $oAttDef = MetaModel::GetAttributeDef(get_class($oLog), $sAttCode);
- if (is_object($oAttDef))
- {
- $iMaxSize = $oAttDef->GetMaxSize();
- if ($iMaxSize && (mb_strlen($sValue) > $iMaxSize)) {
- $sValue = mb_substr($sValue, 0, $iMaxSize);
- }
- $oLog->Set($sAttCode, $sValue);
- }
- }
-
- /**
- * Helper to set a scalar attribute
- *
- * @param string sAttCode
- * @param scalar value
- * @param DBObject oTargetObj
- * @param WebServiceResult oRes
- *
- */
- protected function MyObjectSetScalar($sAttCode, $sParamName, $value, &$oTargetObj, &$oRes)
- {
- $res = $oTargetObj->CheckValue($sAttCode, $value);
- if ($res === true)
- {
- $oTargetObj->Set($sAttCode, $value);
- }
- else
- {
- // $res contains the error description
- $oRes->LogError("Unexpected value for parameter $sParamName: $res");
- }
- }
-
- /**
- * Helper to set an external key
- *
- * @param string sAttCode
- * @param array aExtKeyDesc
- * @param DBObject oTargetObj
- * @param WebServiceResult oRes
- *
- */
- protected function MyObjectSetExternalKey($sAttCode, $sParamName, $aExtKeyDesc, &$oTargetObj, &$oRes)
- {
- $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
-
- $bIsMandatory = !$oExtKey->IsNullAllowed();
-
- if (is_null($aExtKeyDesc))
- {
- if ($bIsMandatory)
- {
- $oRes->LogError("Parameter $sParamName: found null for a mandatory key");
- }
- else
- {
- // skip silently
- return;
- }
- }
-
- if (count($aExtKeyDesc) == 0)
- {
- $oRes->LogIssue("Parameter $sParamName: no search condition has been specified", $bIsMandatory);
- return;
- }
-
- $sKeyClass = $oExtKey->GetTargetClass();
- $oReconFilter = new DBObjectSearch($sKeyClass);
- foreach ($aExtKeyDesc as $sForeignAttCode => $value)
- {
- if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode))
- {
- $aCodes = MetaModel::GetFiltersList($sKeyClass);
- $sMsg = "Parameter $sParamName: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass', expecting a value in {".implode(', ', $aCodes)."}";
- $oRes->LogIssue($sMsg, $bIsMandatory);
- }
- // The foreign attribute is one of our reconciliation key
- $oReconFilter->AddCondition($sForeignAttCode, $value, '=');
- }
- $oExtObjects = new CMDBObjectSet($oReconFilter);
- switch($oExtObjects->Count())
- {
- case 0:
- $sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
- $oRes->LogIssue($sMsg, $bIsMandatory);
- break;
- case 1:
- // Do change the external key attribute
- $oForeignObj = $oExtObjects->Fetch();
- $oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
-
- // Report it (no need to report if the object already had this value
- if (array_key_exists($sAttCode, $oTargetObj->ListChanges()))
- {
- $oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
- }
- break;
- default:
- $sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
- $oRes->LogIssue($sMsg, $bIsMandatory);
- }
- }
-
- /**
- * Helper to link objects
- *
- * @param string sLinkAttCode
- * @param string sLinkedClass
- * @param array $aLinkList
- * @param DBObject oTargetObj
- * @param WebServiceResult oRes
- *
- * @return array List of objects that could not be found
- */
- protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes)
- {
- $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode);
- $sLinkClass = $oLinkAtt->GetLinkedClass();
- $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
-
- $aItemsFound = array();
- $aItemsNotFound = array();
-
- if (is_null($aLinkList))
- {
- return $aItemsNotFound;
- }
-
- foreach ($aLinkList as $aItemData)
- {
- if (!array_key_exists('class', $aItemData))
- {
- $oRes->LogWarning("Parameter $sParamName: missing 'class' specification");
- continue; // skip
- }
- $sTargetClass = $aItemData['class'];
- if (!MetaModel::IsValidClass($sTargetClass))
- {
- $oRes->LogError("Parameter $sParamName: invalid class '$sTargetClass'");
- continue; // skip
- }
- if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass))
- {
- $oRes->LogError("Parameter $sParamName: '$sTargetClass' is not a child class of '$sLinkedClass'");
- continue; // skip
- }
- $oReconFilter = new DBObjectSearch($sTargetClass);
- $aCIStringDesc = array();
- foreach ($aItemData['search'] as $sAttCode => $value) {
- if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
- $aCodes = MetaModel::GetFiltersList($sTargetClass);
- $oRes->LogError("Parameter $sParamName: '$sAttCode' is not a valid filter code for class '$sTargetClass', expecting a value in {".implode(', ', $aCodes)."}");
- continue 2; // skip the entire item
- }
- $aCIStringDesc[] = "$sAttCode: $value";
-
- // The attribute is one of our reconciliation key
- $oReconFilter->AddCondition($sAttCode, $value, '=');
- }
- if (count($aCIStringDesc) == 1)
- {
- // take the last and unique value to describe the object
- $sItemDesc = $value;
- }
- else
- {
- // describe the object by the given keys
- $sItemDesc = $sTargetClass.'('.implode('/', $aCIStringDesc).')';
- }
-
- $oExtObjects = new CMDBObjectSet($oReconFilter);
- switch($oExtObjects->Count())
- {
- case 0:
- $oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
- $aItemsNotFound[] = $sItemDesc;
- break;
- case 1:
- $aItemsFound[] = array (
- 'object' => $oExtObjects->Fetch(),
- 'link_values' => @$aItemData['link_values'],
- 'desc' => $sItemDesc,
- );
- break;
- default:
- $oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
- $aItemsNotFound[] = $sItemDesc;
- }
- }
-
- if (count($aItemsFound) > 0)
- {
- $aLinks = array();
- foreach($aItemsFound as $aItemData)
- {
- $oLink = MetaModel::NewObject($sLinkClass);
- $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
- foreach($aItemData['link_values'] as $sKey => $value)
- {
- if(!MetaModel::IsValidAttCode($sLinkClass, $sKey))
- {
- $oRes->LogWarning("Parameter $sParamName: Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'");
- }
- else
- {
- $oLink->Set($sKey, $value);
- }
- }
- $aLinks[] = $oLink;
- }
- $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks);
- $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet);
- }
-
- return $aItemsNotFound;
- }
-
- /**
- * @param \CMDBObject $oTargetObj
- * @param string $sResultLabel
- * @param \WebServiceResult $oRes
- *
- * @throws \ArchivedObjectException
- * @throws \CoreCannotSaveObjectException
- * @throws \CoreException
- * @throws \CoreUnexpectedValue
- * @throws \CoreWarning
- * @throws \MySQLException
- * @throws \OQLException
- * @throws \SecurityException
- */
- protected function MyObjectInsert($oTargetObj, $sResultLabel, &$oRes)
- {
- if ($oRes->IsOk())
- {
- list($bRes, $aIssues) = $oTargetObj->CheckToWrite();
- if ($bRes)
- {
- $iId = $oTargetObj->DBInsertNoReload();
- $oRes->LogInfo("Created object ".get_class($oTargetObj)."::$iId");
- $oRes->AddResultObject($sResultLabel, $oTargetObj);
- }
- else
- {
- $oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)");
- foreach($aIssues as $iIssue => $sIssue)
- {
- $oRes->LogError("Issue #$iIssue: $sIssue");
- }
- }
- }
- }
-
-
- static protected function SoapStructToExternalKeySearch($oExternalKeySearch)
- {
- if (is_null($oExternalKeySearch)) return null;
- if ($oExternalKeySearch->IsVoid()) return null;
-
- $aRes = array();
- foreach($oExternalKeySearch->conditions as $oSearchCondition)
- {
- $aRes[$oSearchCondition->attcode] = $oSearchCondition->value;
- }
- return $aRes;
- }
-
- static protected function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
- {
- $aRes = array
- (
- 'class' => $oLinkCreationSpec->class,
- 'search' => array(),
- 'link_values' => array(),
- );
-
- foreach($oLinkCreationSpec->conditions as $oSearchCondition)
- {
- $aRes['search'][$oSearchCondition->attcode] = $oSearchCondition->value;
- }
-
- foreach($oLinkCreationSpec->attributes as $oAttributeValue)
- {
- $aRes['link_values'][$oAttributeValue->attcode] = $oAttributeValue->value;
- }
-
- return $aRes;
- }
-
- static protected function SoapStructToAssociativeArray($aArrayOfAssocArray)
- {
- if (is_null($aArrayOfAssocArray)) return array();
-
- $aRes = array();
- foreach($aArrayOfAssocArray as $aAssocArray)
- {
- $aRow = array();
- foreach ($aAssocArray as $oKeyValuePair)
- {
- $aRow[$oKeyValuePair->key] = $oKeyValuePair->value;
- }
- $aRes[] = $aRow;
- }
- return $aRes;
- }
+ public static function GetWSDLContents($sServiceCategory = '')
+ {
+ if ($sServiceCategory == '') {
+ $sServiceCategory = 'BasicServices';
+ }
+ $sWsdlFilePath = call_user_func([$sServiceCategory, 'GetWSDLFilePath']);
+ return file_get_contents($sWsdlFilePath);
+ }
+
+ /**
+ * Helper to log a service delivery
+ *
+ * @param string sVerb
+ * @param array aArgs
+ * @param WebServiceResult oRes
+ *
+ */
+ protected function LogUsage($sVerb, $oRes)
+ {
+ if (!MetaModel::IsLogEnabledWebService()) {
+ return;
+ }
+
+ $oLog = new EventWebService();
+ if ($oRes->IsOk()) {
+ $oLog->Set('message', $sVerb.' was successfully invoked');
+ } else {
+ $oLog->Set('message', $sVerb.' returned errors');
+ }
+ $oLog->Set('userinfo', UserRights::GetUser());
+ $oLog->Set('verb', $sVerb);
+ $oLog->Set('result', $oRes->IsOk());
+ $this->TrimAndSetValue($oLog, 'log_info', (string)$oRes->GetInfoAsText());
+ $this->TrimAndSetValue($oLog, 'log_warning', (string)$oRes->GetWarningsAsText());
+ $this->TrimAndSetValue($oLog, 'log_error', (string)$oRes->GetErrorsAsText());
+ $this->TrimAndSetValue($oLog, 'data', (string)$oRes->GetReturnedDataAsText());
+ $oLog->DBInsertNoReload();
+ }
+
+ protected function TrimAndSetValue($oLog, $sAttCode, $sValue)
+ {
+ $oAttDef = MetaModel::GetAttributeDef(get_class($oLog), $sAttCode);
+ if (is_object($oAttDef)) {
+ $iMaxSize = $oAttDef->GetMaxSize();
+ if ($iMaxSize && (mb_strlen($sValue) > $iMaxSize)) {
+ $sValue = mb_substr($sValue, 0, $iMaxSize);
+ }
+ $oLog->Set($sAttCode, $sValue);
+ }
+ }
+
+ /**
+ * Helper to set a scalar attribute
+ *
+ * @param string sAttCode
+ * @param scalar value
+ * @param DBObject oTargetObj
+ * @param WebServiceResult oRes
+ *
+ */
+ protected function MyObjectSetScalar($sAttCode, $sParamName, $value, &$oTargetObj, &$oRes)
+ {
+ $res = $oTargetObj->CheckValue($sAttCode, $value);
+ if ($res === true) {
+ $oTargetObj->Set($sAttCode, $value);
+ } else {
+ // $res contains the error description
+ $oRes->LogError("Unexpected value for parameter $sParamName: $res");
+ }
+ }
+
+ /**
+ * Helper to set an external key
+ *
+ * @param string sAttCode
+ * @param array aExtKeyDesc
+ * @param DBObject oTargetObj
+ * @param WebServiceResult oRes
+ *
+ */
+ protected function MyObjectSetExternalKey($sAttCode, $sParamName, $aExtKeyDesc, &$oTargetObj, &$oRes)
+ {
+ $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
+
+ $bIsMandatory = !$oExtKey->IsNullAllowed();
+
+ if (is_null($aExtKeyDesc)) {
+ if ($bIsMandatory) {
+ $oRes->LogError("Parameter $sParamName: found null for a mandatory key");
+ } else {
+ // skip silently
+ return;
+ }
+ }
+
+ if (count($aExtKeyDesc) == 0) {
+ $oRes->LogIssue("Parameter $sParamName: no search condition has been specified", $bIsMandatory);
+ return;
+ }
+
+ $sKeyClass = $oExtKey->GetTargetClass();
+ $oReconFilter = new DBObjectSearch($sKeyClass);
+ foreach ($aExtKeyDesc as $sForeignAttCode => $value) {
+ if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode)) {
+ $aCodes = MetaModel::GetFiltersList($sKeyClass);
+ $sMsg = "Parameter $sParamName: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass', expecting a value in {".implode(', ', $aCodes)."}";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
+ }
+ // The foreign attribute is one of our reconciliation key
+ $oReconFilter->AddCondition($sForeignAttCode, $value, '=');
+ }
+ $oExtObjects = new CMDBObjectSet($oReconFilter);
+ switch ($oExtObjects->Count()) {
+ case 0:
+ $sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
+ break;
+ case 1:
+ // Do change the external key attribute
+ $oForeignObj = $oExtObjects->Fetch();
+ $oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
+
+ // Report it (no need to report if the object already had this value
+ if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) {
+ $oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
+ }
+ break;
+ default:
+ $sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
+ }
+ }
+
+ /**
+ * Helper to link objects
+ *
+ * @param string sLinkAttCode
+ * @param string sLinkedClass
+ * @param array $aLinkList
+ * @param DBObject oTargetObj
+ * @param WebServiceResult oRes
+ *
+ * @return array List of objects that could not be found
+ */
+ protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes)
+ {
+ $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode);
+ $sLinkClass = $oLinkAtt->GetLinkedClass();
+ $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
+
+ $aItemsFound = [];
+ $aItemsNotFound = [];
+
+ if (is_null($aLinkList)) {
+ return $aItemsNotFound;
+ }
+
+ foreach ($aLinkList as $aItemData) {
+ if (!array_key_exists('class', $aItemData)) {
+ $oRes->LogWarning("Parameter $sParamName: missing 'class' specification");
+ continue; // skip
+ }
+ $sTargetClass = $aItemData['class'];
+ if (!MetaModel::IsValidClass($sTargetClass)) {
+ $oRes->LogError("Parameter $sParamName: invalid class '$sTargetClass'");
+ continue; // skip
+ }
+ if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) {
+ $oRes->LogError("Parameter $sParamName: '$sTargetClass' is not a child class of '$sLinkedClass'");
+ continue; // skip
+ }
+ $oReconFilter = new DBObjectSearch($sTargetClass);
+ $aCIStringDesc = [];
+ foreach ($aItemData['search'] as $sAttCode => $value) {
+ if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
+ $aCodes = MetaModel::GetFiltersList($sTargetClass);
+ $oRes->LogError("Parameter $sParamName: '$sAttCode' is not a valid filter code for class '$sTargetClass', expecting a value in {".implode(', ', $aCodes)."}");
+ continue 2; // skip the entire item
+ }
+ $aCIStringDesc[] = "$sAttCode: $value";
+
+ // The attribute is one of our reconciliation key
+ $oReconFilter->AddCondition($sAttCode, $value, '=');
+ }
+ if (count($aCIStringDesc) == 1) {
+ // take the last and unique value to describe the object
+ $sItemDesc = $value;
+ } else {
+ // describe the object by the given keys
+ $sItemDesc = $sTargetClass.'('.implode('/', $aCIStringDesc).')';
+ }
+
+ $oExtObjects = new CMDBObjectSet($oReconFilter);
+ switch ($oExtObjects->Count()) {
+ case 0:
+ $oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
+ $aItemsNotFound[] = $sItemDesc;
+ break;
+ case 1:
+ $aItemsFound[] = [
+ 'object' => $oExtObjects->Fetch(),
+ 'link_values' => @$aItemData['link_values'],
+ 'desc' => $sItemDesc,
+ ];
+ break;
+ default:
+ $oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
+ $aItemsNotFound[] = $sItemDesc;
+ }
+ }
+
+ if (count($aItemsFound) > 0) {
+ $aLinks = [];
+ foreach ($aItemsFound as $aItemData) {
+ $oLink = MetaModel::NewObject($sLinkClass);
+ $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
+ foreach ($aItemData['link_values'] as $sKey => $value) {
+ if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) {
+ $oRes->LogWarning("Parameter $sParamName: Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'");
+ } else {
+ $oLink->Set($sKey, $value);
+ }
+ }
+ $aLinks[] = $oLink;
+ }
+ $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks);
+ $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet);
+ }
+
+ return $aItemsNotFound;
+ }
+
+ /**
+ * @param \CMDBObject $oTargetObj
+ * @param string $sResultLabel
+ * @param \WebServiceResult $oRes
+ *
+ * @throws \ArchivedObjectException
+ * @throws \CoreCannotSaveObjectException
+ * @throws \CoreException
+ * @throws \CoreUnexpectedValue
+ * @throws \CoreWarning
+ * @throws \MySQLException
+ * @throws \OQLException
+ * @throws \SecurityException
+ */
+ protected function MyObjectInsert($oTargetObj, $sResultLabel, &$oRes)
+ {
+ if ($oRes->IsOk()) {
+ list($bRes, $aIssues) = $oTargetObj->CheckToWrite();
+ if ($bRes) {
+ $iId = $oTargetObj->DBInsertNoReload();
+ $oRes->LogInfo("Created object ".get_class($oTargetObj)."::$iId");
+ $oRes->AddResultObject($sResultLabel, $oTargetObj);
+ } else {
+ $oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)");
+ foreach ($aIssues as $iIssue => $sIssue) {
+ $oRes->LogError("Issue #$iIssue: $sIssue");
+ }
+ }
+ }
+ }
+
+ protected static function SoapStructToExternalKeySearch($oExternalKeySearch)
+ {
+ if (is_null($oExternalKeySearch)) {
+ return null;
+ }
+ if ($oExternalKeySearch->IsVoid()) {
+ return null;
+ }
+
+ $aRes = [];
+ foreach ($oExternalKeySearch->conditions as $oSearchCondition) {
+ $aRes[$oSearchCondition->attcode] = $oSearchCondition->value;
+ }
+ return $aRes;
+ }
+
+ protected static function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
+ {
+ $aRes =
+ [
+ 'class' => $oLinkCreationSpec->class,
+ 'search' => [],
+ 'link_values' => [],
+ ];
+
+ foreach ($oLinkCreationSpec->conditions as $oSearchCondition) {
+ $aRes['search'][$oSearchCondition->attcode] = $oSearchCondition->value;
+ }
+
+ foreach ($oLinkCreationSpec->attributes as $oAttributeValue) {
+ $aRes['link_values'][$oAttributeValue->attcode] = $oAttributeValue->value;
+ }
+
+ return $aRes;
+ }
+
+ protected static function SoapStructToAssociativeArray($aArrayOfAssocArray)
+ {
+ if (is_null($aArrayOfAssocArray)) {
+ return [];
+ }
+
+ $aRes = [];
+ foreach ($aArrayOfAssocArray as $aAssocArray) {
+ $aRow = [];
+ foreach ($aAssocArray as $oKeyValuePair) {
+ $aRow[$oKeyValuePair->key] = $oKeyValuePair->value;
+ }
+ $aRes[] = $aRow;
+ }
+ return $aRes;
+ }
}
-?>
From 0ca81145d9c52334687ff7a5c2fd0a19d53832b0 Mon Sep 17 00:00:00 2001
From: odain
Date: Wed, 15 Oct 2025 16:09:14 +0200
Subject: [PATCH 08/15] phpstan: change composer php requirements
---
tests/php-code-style/composer.json | 7 +-
tests/php-code-style/composer.lock | 560 ++++++++++++++++++-----------
2 files changed, 353 insertions(+), 214 deletions(-)
diff --git a/tests/php-code-style/composer.json b/tests/php-code-style/composer.json
index b990d13f5b..1d7e9ec0c7 100644
--- a/tests/php-code-style/composer.json
+++ b/tests/php-code-style/composer.json
@@ -1,6 +1,7 @@
{
- "require": {
- "friendsofphp/php-cs-fixer": "^3.10",
- "phpstan/phpstan": "^2.0"
+ "require-dev": {
+ "php": "^7.4 || ^8.0",
+ "friendsofphp/php-cs-fixer": "^3.88",
+ "phpstan/phpstan": "^2.1"
}
}
diff --git a/tests/php-code-style/composer.lock b/tests/php-code-style/composer.lock
index aeb87a424c..0b38438dd8 100644
--- a/tests/php-code-style/composer.lock
+++ b/tests/php-code-style/composer.lock
@@ -4,8 +4,9 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d76c2ddfcd673d9164eff7c412640c4e",
- "packages": [
+ "content-hash": "afbcf4a8cd7e954326e354a54bd2a1dc",
+ "packages": [],
+ "packages-dev": [
{
"name": "clue/ndjson-react",
"version": "v1.3.0",
@@ -402,16 +403,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
- "version": "v3.87.2",
+ "version": "v3.88.2",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
- "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992"
+ "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
- "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99",
+ "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99",
"shasum": ""
},
"require": {
@@ -438,12 +439,13 @@
"symfony/polyfill-mbstring": "^1.33",
"symfony/polyfill-php80": "^1.33",
"symfony/polyfill-php81": "^1.33",
+ "symfony/polyfill-php84": "^1.33",
"symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.7",
- "infection/infection": "^0.29.14",
+ "infection/infection": "^0.31.0",
"justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12",
@@ -451,7 +453,6 @@
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
- "symfony/polyfill-php84": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
},
@@ -494,7 +495,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
- "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2"
+ "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2"
},
"funding": [
{
@@ -502,20 +503,15 @@
"type": "github"
}
],
- "time": "2025-09-10T09:51:40+00:00"
+ "time": "2025-09-27T00:24:15+00:00"
},
{
"name": "phpstan/phpstan",
- "version": "2.1.27",
- "source": {
- "type": "git",
- "url": "https://github.com/phpstan/phpstan.git",
- "reference": "25da374959afa391992792691093550b3098ef1e"
- },
+ "version": "2.1.31",
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/25da374959afa391992792691093550b3098ef1e",
- "reference": "25da374959afa391992792691093550b3098ef1e",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96",
+ "reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96",
"shasum": ""
},
"require": {
@@ -560,31 +556,26 @@
"type": "github"
}
],
- "time": "2025-09-17T09:55:13+00:00"
+ "time": "2025-10-10T14:14:11+00:00"
},
{
"name": "psr/container",
- "version": "2.0.2",
+ "version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
- "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
- "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
"shasum": ""
},
"require": {
"php": ">=7.4.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
@@ -611,9 +602,9 @@
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
- "source": "https://github.com/php-fig/container/tree/2.0.2"
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
},
- "time": "2021-11-05T16:47:00+00:00"
+ "time": "2021-11-05T16:50:12+00:00"
},
{
"name": "psr/event-dispatcher",
@@ -667,30 +658,30 @@
},
{
"name": "psr/log",
- "version": "3.0.2",
+ "version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
- "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
- "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
"shasum": ""
},
"require": {
- "php": ">=8.0.0"
+ "php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.x-dev"
+ "dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
- "Psr\\Log\\": "src"
+ "Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -711,9 +702,9 @@
"psr-3"
],
"support": {
- "source": "https://github.com/php-fig/log/tree/3.0.2"
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
},
- "time": "2024-09-11T13:17:53+00:00"
+ "time": "2021-05-03T11:20:27+00:00"
},
{
"name": "react/cache",
@@ -1243,29 +1234,29 @@
},
{
"name": "sebastian/diff",
- "version": "6.0.2",
+ "version": "4.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
- "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
"shasum": ""
},
"require": {
- "php": ">=8.2"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^11.0",
+ "phpunit/phpunit": "^9.3",
"symfony/process": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "6.0-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -1297,8 +1288,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
- "security": "https://github.com/sebastianbergmann/diff/security/policy",
- "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
},
"funding": [
{
@@ -1306,51 +1296,56 @@
"type": "github"
}
],
- "time": "2024-07-03T04:53:05+00:00"
+ "time": "2024-03-02T06:30:58+00:00"
},
{
"name": "symfony/console",
- "version": "v7.3.3",
+ "version": "v5.4.47",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
+ "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
- "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
+ "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed",
+ "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed",
"shasum": ""
},
"require": {
- "php": ">=8.2",
- "symfony/deprecation-contracts": "^2.5|^3",
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
"symfony/polyfill-mbstring": "~1.0",
- "symfony/service-contracts": "^2.5|^3",
- "symfony/string": "^7.2"
+ "symfony/polyfill-php73": "^1.9",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.1|^6.0"
},
"conflict": {
- "symfony/dependency-injection": "<6.4",
- "symfony/dotenv": "<6.4",
- "symfony/event-dispatcher": "<6.4",
- "symfony/lock": "<6.4",
- "symfony/process": "<6.4"
+ "psr/log": ">=3",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/dotenv": "<5.1",
+ "symfony/event-dispatcher": "<4.4",
+ "symfony/lock": "<4.4",
+ "symfony/process": "<4.4"
},
"provide": {
- "psr/log-implementation": "1.0|2.0|3.0"
+ "psr/log-implementation": "1.0|2.0"
},
"require-dev": {
- "psr/log": "^1|^2|^3",
- "symfony/config": "^6.4|^7.0",
- "symfony/dependency-injection": "^6.4|^7.0",
- "symfony/event-dispatcher": "^6.4|^7.0",
- "symfony/http-foundation": "^6.4|^7.0",
- "symfony/http-kernel": "^6.4|^7.0",
- "symfony/lock": "^6.4|^7.0",
- "symfony/messenger": "^6.4|^7.0",
- "symfony/process": "^6.4|^7.0",
- "symfony/stopwatch": "^6.4|^7.0",
- "symfony/var-dumper": "^6.4|^7.0"
+ "psr/log": "^1|^2",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/event-dispatcher": "^4.4|^5.0|^6.0",
+ "symfony/lock": "^4.4|^5.0|^6.0",
+ "symfony/process": "^4.4|^5.0|^6.0",
+ "symfony/var-dumper": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "psr/log": "For using the console logger",
+ "symfony/event-dispatcher": "",
+ "symfony/lock": "",
+ "symfony/process": ""
},
"type": "library",
"autoload": {
@@ -1384,7 +1379,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v7.3.3"
+ "source": "https://github.com/symfony/console/tree/v5.4.47"
},
"funding": [
{
@@ -1395,33 +1390,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-08-25T06:35:40+00:00"
+ "time": "2024-11-06T11:30:55+00:00"
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.6.0",
+ "version": "v2.5.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
- "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918",
+ "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
@@ -1430,7 +1421,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.6-dev"
+ "dev-main": "2.5-dev"
}
},
"autoload": {
@@ -1455,7 +1446,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4"
},
"funding": [
{
@@ -1471,43 +1462,48 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:21:43+00:00"
+ "time": "2024-09-25T14:11:13+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v7.3.3",
+ "version": "v5.4.45",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191"
+ "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191",
- "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9",
+ "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9",
"shasum": ""
},
"require": {
- "php": ">=8.2",
- "symfony/event-dispatcher-contracts": "^2.5|^3"
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/event-dispatcher-contracts": "^2|^3",
+ "symfony/polyfill-php80": "^1.16"
},
"conflict": {
- "symfony/dependency-injection": "<6.4",
- "symfony/service-contracts": "<2.5"
+ "symfony/dependency-injection": "<4.4"
},
"provide": {
"psr/event-dispatcher-implementation": "1.0",
- "symfony/event-dispatcher-implementation": "2.0|3.0"
+ "symfony/event-dispatcher-implementation": "2.0"
},
"require-dev": {
"psr/log": "^1|^2|^3",
- "symfony/config": "^6.4|^7.0",
- "symfony/dependency-injection": "^6.4|^7.0",
- "symfony/error-handler": "^6.4|^7.0",
- "symfony/expression-language": "^6.4|^7.0",
- "symfony/http-foundation": "^6.4|^7.0",
- "symfony/service-contracts": "^2.5|^3",
- "symfony/stopwatch": "^6.4|^7.0"
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/error-handler": "^4.4|^5.0|^6.0",
+ "symfony/expression-language": "^4.4|^5.0|^6.0",
+ "symfony/http-foundation": "^4.4|^5.0|^6.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/stopwatch": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "symfony/dependency-injection": "",
+ "symfony/http-kernel": ""
},
"type": "library",
"autoload": {
@@ -1535,7 +1531,7 @@
"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/v7.3.3"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.45"
},
"funding": [
{
@@ -1546,35 +1542,34 @@
"url": "https://github.com/fabpot",
"type": "github"
},
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-08-13T11:49:31+00:00"
+ "time": "2024-09-25T14:11:13+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
- "version": "v3.6.0",
+ "version": "v2.5.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "59eb412e93815df44f05f342958efa9f46b1e586"
+ "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586",
- "reference": "59eb412e93815df44f05f342958efa9f46b1e586",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f",
+ "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f",
"shasum": ""
},
"require": {
- "php": ">=8.1",
+ "php": ">=7.2.5",
"psr/event-dispatcher": "^1"
},
+ "suggest": {
+ "symfony/event-dispatcher-implementation": ""
+ },
"type": "library",
"extra": {
"thanks": {
@@ -1582,7 +1577,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.6-dev"
+ "dev-main": "2.5-dev"
}
},
"autoload": {
@@ -1615,7 +1610,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0"
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.4"
},
"funding": [
{
@@ -1631,29 +1626,30 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:21:43+00:00"
+ "time": "2024-09-25T14:11:13+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v7.3.2",
+ "version": "v5.4.45",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd"
+ "reference": "57c8294ed37d4a055b77057827c67f9558c95c54"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd",
- "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54",
+ "reference": "57c8294ed37d4a055b77057827c67f9558c95c54",
"shasum": ""
},
"require": {
- "php": ">=8.2",
+ "php": ">=7.2.5",
"symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-mbstring": "~1.8"
+ "symfony/polyfill-mbstring": "~1.8",
+ "symfony/polyfill-php80": "^1.16"
},
"require-dev": {
- "symfony/process": "^6.4|^7.0"
+ "symfony/process": "^5.4|^6.4"
},
"type": "library",
"autoload": {
@@ -1681,7 +1677,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v7.3.2"
+ "source": "https://github.com/symfony/filesystem/tree/v5.4.45"
},
"funding": [
{
@@ -1692,36 +1688,31 @@
"url": "https://github.com/fabpot",
"type": "github"
},
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-07-07T08:17:47+00:00"
+ "time": "2024-10-22T13:05:35+00:00"
},
{
"name": "symfony/finder",
- "version": "v7.3.2",
+ "version": "v5.4.45",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe"
+ "reference": "63741784cd7b9967975eec610b256eed3ede022b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe",
- "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b",
+ "reference": "63741784cd7b9967975eec610b256eed3ede022b",
"shasum": ""
},
"require": {
- "php": ">=8.2"
- },
- "require-dev": {
- "symfony/filesystem": "^6.4|^7.0"
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-php80": "^1.16"
},
"type": "library",
"autoload": {
@@ -1749,7 +1740,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v7.3.2"
+ "source": "https://github.com/symfony/finder/tree/v5.4.45"
},
"funding": [
{
@@ -1760,34 +1751,32 @@
"url": "https://github.com/fabpot",
"type": "github"
},
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-07-15T13:41:35+00:00"
+ "time": "2024-09-28T13:32:08+00:00"
},
{
"name": "symfony/options-resolver",
- "version": "v7.3.3",
+ "version": "v5.4.45",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
- "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d"
+ "reference": "74e5b6f0db3e8589e6cfd5efb317a1fc2bb52fb6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
- "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/74e5b6f0db3e8589e6cfd5efb317a1fc2bb52fb6",
+ "reference": "74e5b6f0db3e8589e6cfd5efb317a1fc2bb52fb6",
"shasum": ""
},
"require": {
- "php": ">=8.2",
- "symfony/deprecation-contracts": "^2.5|^3"
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-php73": "~1.0",
+ "symfony/polyfill-php80": "^1.16"
},
"type": "library",
"autoload": {
@@ -1820,7 +1809,7 @@
"options"
],
"support": {
- "source": "https://github.com/symfony/options-resolver/tree/v7.3.3"
+ "source": "https://github.com/symfony/options-resolver/tree/v5.4.45"
},
"funding": [
{
@@ -1831,16 +1820,12 @@
"url": "https://github.com/fabpot",
"type": "github"
},
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-08-05T10:16:07+00:00"
+ "time": "2024-09-25T14:11:13+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -2177,6 +2162,86 @@
],
"time": "2024-12-23T08:48:59+00:00"
},
+ {
+ "name": "symfony/polyfill-php73",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php73.git",
+ "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
+ "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "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.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
{
"name": "symfony/polyfill-php80",
"version": "v1.33.0",
@@ -2341,22 +2406,103 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
+ {
+ "name": "symfony/polyfill-php84",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191",
+ "reference": "d8ced4d875142b6a7426000426b8abc631d6b191",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
+ "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.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-24T13:30:11+00:00"
+ },
{
"name": "symfony/process",
- "version": "v7.3.3",
+ "version": "v5.4.47",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
+ "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
- "reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
+ "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d",
+ "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d",
"shasum": ""
},
"require": {
- "php": ">=8.2"
+ "php": ">=7.2.5",
+ "symfony/polyfill-php80": "^1.16"
},
"type": "library",
"autoload": {
@@ -2384,7 +2530,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v7.3.3"
+ "source": "https://github.com/symfony/process/tree/v5.4.47"
},
"funding": [
{
@@ -2395,39 +2541,38 @@
"url": "https://github.com/fabpot",
"type": "github"
},
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-08-18T09:42:54+00:00"
+ "time": "2024-11-06T11:36:42+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v3.6.0",
+ "version": "v2.5.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
+ "reference": "f37b419f7aea2e9abf10abd261832cace12e3300"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
- "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f37b419f7aea2e9abf10abd261832cace12e3300",
+ "reference": "f37b419f7aea2e9abf10abd261832cace12e3300",
"shasum": ""
},
"require": {
- "php": ">=8.1",
- "psr/container": "^1.1|^2.0",
- "symfony/deprecation-contracts": "^2.5|^3"
+ "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": {
"thanks": {
@@ -2435,16 +2580,13 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.6-dev"
+ "dev-main": "2.5-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Service\\": ""
- },
- "exclude-from-classmap": [
- "/Test/"
- ]
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -2471,7 +2613,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
+ "source": "https://github.com/symfony/service-contracts/tree/v2.5.4"
},
"funding": [
{
@@ -2487,25 +2629,25 @@
"type": "tidelift"
}
],
- "time": "2025-04-25T09:37:31+00:00"
+ "time": "2024-09-25T14:11:13+00:00"
},
{
"name": "symfony/stopwatch",
- "version": "v7.3.0",
+ "version": "v5.4.45",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
- "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd"
+ "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd",
- "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fb2c199cf302eb207f8c23e7ee174c1c31a5c004",
+ "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004",
"shasum": ""
},
"require": {
- "php": ">=8.2",
- "symfony/service-contracts": "^2.5|^3"
+ "php": ">=7.2.5",
+ "symfony/service-contracts": "^1|^2|^3"
},
"type": "library",
"autoload": {
@@ -2533,7 +2675,7 @@
"description": "Provides a way to profile code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/stopwatch/tree/v7.3.0"
+ "source": "https://github.com/symfony/stopwatch/tree/v5.4.45"
},
"funding": [
{
@@ -2549,39 +2691,38 @@
"type": "tidelift"
}
],
- "time": "2025-02-24T10:49:57+00:00"
+ "time": "2024-09-25T14:11:13+00:00"
},
{
"name": "symfony/string",
- "version": "v7.3.3",
+ "version": "v5.4.47",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
+ "reference": "136ca7d72f72b599f2631aca474a4f8e26719799"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
- "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
+ "url": "https://api.github.com/repos/symfony/string/zipball/136ca7d72f72b599f2631aca474a4f8e26719799",
+ "reference": "136ca7d72f72b599f2631aca474a4f8e26719799",
"shasum": ""
},
"require": {
- "php": ">=8.2",
+ "php": ">=7.2.5",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-grapheme": "~1.0",
"symfony/polyfill-intl-normalizer": "~1.0",
- "symfony/polyfill-mbstring": "~1.0"
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php80": "~1.15"
},
"conflict": {
- "symfony/translation-contracts": "<2.5"
+ "symfony/translation-contracts": ">=3.0"
},
"require-dev": {
- "symfony/emoji": "^7.1",
- "symfony/error-handler": "^6.4|^7.0",
- "symfony/http-client": "^6.4|^7.0",
- "symfony/intl": "^6.4|^7.0",
- "symfony/translation-contracts": "^2.5|^3.0",
- "symfony/var-exporter": "^6.4|^7.0"
+ "symfony/error-handler": "^4.4|^5.0|^6.0",
+ "symfony/http-client": "^4.4|^5.0|^6.0",
+ "symfony/translation-contracts": "^1.1|^2",
+ "symfony/var-exporter": "^4.4|^5.0|^6.0"
},
"type": "library",
"autoload": {
@@ -2620,7 +2761,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v7.3.3"
+ "source": "https://github.com/symfony/string/tree/v5.4.47"
},
"funding": [
{
@@ -2631,25 +2772,22 @@
"url": "https://github.com/fabpot",
"type": "github"
},
- {
- "url": "https://github.com/nicolas-grekas",
- "type": "github"
- },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-08-25T06:35:40+00:00"
+ "time": "2024-11-10T20:33:58+00:00"
}
],
- "packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
- "platform-dev": {},
+ "platform-dev": {
+ "php": "^7.4 || ^8.0"
+ },
"plugin-api-version": "2.6.0"
}
From fc6c535dda21c5fc5b0e6aa23d10d37d62751300 Mon Sep 17 00:00:00 2001
From: odain
Date: Fri, 17 Oct 2025 11:30:51 +0200
Subject: [PATCH 09/15] indent with tabs + inception style applied everywhere
even php-cs-fixer itself
---
tests/php-code-style/.php-cs-fixer.dist.php | 25 ++++++++++++---------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/tests/php-code-style/.php-cs-fixer.dist.php b/tests/php-code-style/.php-cs-fixer.dist.php
index 601ff41cdb..7ff4a436e0 100644
--- a/tests/php-code-style/.php-cs-fixer.dist.php
+++ b/tests/php-code-style/.php-cs-fixer.dist.php
@@ -1,10 +1,10 @@
exclude('oql')
+ ->exclude('oql')
->in($APPROOT.'/addons')
->in($APPROOT.'/application')
->in($APPROOT.'/core')
@@ -16,15 +16,18 @@
->in($APPROOT.'/sources')
->in($APPROOT.'/synchro')
->in($APPROOT.'/tests')
- ->in($APPROOT . '/webservices')
- ;
+ ->in($APPROOT . '/webservices')
+;
$config = new PhpCsFixer\Config();
return $config->setRiskyAllowed(true)
- ->setRules([
- '@PSR12' => true,
- 'no_extra_blank_lines' => true,
- 'array_syntax' => ['syntax' => 'short'],
- ])
- ->setFinder($finder)
-;
\ No newline at end of file
+ ->setRules([
+ '@PSR12' => true,
+ 'indentation_type' => true,
+ 'no_extra_blank_lines' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ ])
+ ->setIndent("\t")
+ ->setLineEnding("\n")
+ ->setFinder($finder)
+;
From 34ffb9ea66d12e31baed9ccdd1e936ac9f54bba9 Mon Sep 17 00:00:00 2001
From: odain
Date: Fri, 17 Oct 2025 11:32:04 +0200
Subject: [PATCH 10/15] use tabs for code style indentation
---
webservices/backoffice.dataloader.php | 132 +--
webservices/createfrommail.php | 182 +--
webservices/cron.php | 758 ++++++------
webservices/export-v2.php | 998 ++++++++--------
webservices/export.php | 480 ++++----
webservices/import.php | 1434 +++++++++++------------
webservices/itop.wsdl.php | 32 +-
webservices/itoprest.examples.php | 566 ++++-----
webservices/itopsoap.examples.php | 170 +--
webservices/itopsoaptypes.class.inc.php | 214 ++--
webservices/rest.php | 382 +++---
webservices/soapserver.php | 80 +-
webservices/status.php | 10 +-
webservices/webservices.basic.php | 456 +++----
webservices/webservices.class.inc.php | 1018 ++++++++--------
15 files changed, 3456 insertions(+), 3456 deletions(-)
diff --git a/webservices/backoffice.dataloader.php b/webservices/backoffice.dataloader.php
index effadf4344..b4479974c9 100644
--- a/webservices/backoffice.dataloader.php
+++ b/webservices/backoffice.dataloader.php
@@ -42,24 +42,24 @@
function SetMemoryLimit($oP)
{
- $sMemoryLimit = trim(ini_get('memory_limit'));
- if (empty($sMemoryLimit)) {
- // On some PHP installations, memory_limit does not exist as a PHP setting!
- // (encountered on a 5.2.0 under Windows)
- // In that case, ini_set will not work, let's keep track of this and proceed with the data load
- $oP->p("No memory limit has been defined in this instance of PHP");
- } else {
- // Check that the limit will allow us to load the data
- //
- $iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
- if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY)) {
- if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === false) {
- $oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
- } else {
- $oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
- }
- }
- }
+ $sMemoryLimit = trim(ini_get('memory_limit'));
+ if (empty($sMemoryLimit)) {
+ // On some PHP installations, memory_limit does not exist as a PHP setting!
+ // (encountered on a 5.2.0 under Windows)
+ // In that case, ini_set will not work, let's keep track of this and proceed with the data load
+ $oP->p("No memory limit has been defined in this instance of PHP");
+ } else {
+ // Check that the limit will allow us to load the data
+ //
+ $iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
+ if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY)) {
+ if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === false) {
+ $oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
+ } else {
+ $oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
+ }
+ }
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -80,59 +80,59 @@ function SetMemoryLimit($oP)
$oP = new WebPage("iTop - Backoffice data loader");
try {
- // Note: the data model must be loaded first
- $oDataLoader = new XMLDataLoader();
-
- if (empty($sFileName)) {
- throw(new Exception("Missing argument 'file'"));
- }
- if (!file_exists($sFileName)) {
- throw(new Exception("File $sFileName does not exist"));
- }
-
- SetMemoryLimit($oP);
-
- // The XMLDataLoader constructor has initialized the DB, let's start a transaction
- CMDBSource::Query('START TRANSACTION');
-
- $oP->p("Starting data load.");
- CMDBObject::SetCurrentChangeFromParams('Initialization WS');
- $oDataLoader->StartSession(CMDBObject::GetCurrentChange());
- $oDataLoader->LoadFile($sFileName);
-
- $oP->p("Ending data load session");
- if ($oDataLoader->EndSession(true /* strict */)) {
- $iCountCreated = $oDataLoader->GetCountCreated();
- CMDBSource::Query('COMMIT');
-
- $oP->p("Data successfully written into the DB: $iCountCreated objects created");
- } else {
- CMDBSource::Query('ROLLBACK');
- $oP->p("Some issues have been encountered, changes will not be recorded, please review the source data");
- $aErrors = $oDataLoader->GetErrors();
- if (count($aErrors) > 0) {
- $oP->p('Errors ('.count($aErrors).')');
- foreach ($aErrors as $sMsg) {
- $oP->p(' * '.$sMsg);
- }
- }
- $aWarnings = $oDataLoader->GetWarnings();
- if (count($aWarnings) > 0) {
- $oP->p('Warnings ('.count($aWarnings).')');
- foreach ($aWarnings as $sMsg) {
- $oP->p(' * '.$sMsg);
- }
- }
- }
+ // Note: the data model must be loaded first
+ $oDataLoader = new XMLDataLoader();
+
+ if (empty($sFileName)) {
+ throw(new Exception("Missing argument 'file'"));
+ }
+ if (!file_exists($sFileName)) {
+ throw(new Exception("File $sFileName does not exist"));
+ }
+
+ SetMemoryLimit($oP);
+
+ // The XMLDataLoader constructor has initialized the DB, let's start a transaction
+ CMDBSource::Query('START TRANSACTION');
+
+ $oP->p("Starting data load.");
+ CMDBObject::SetCurrentChangeFromParams('Initialization WS');
+ $oDataLoader->StartSession(CMDBObject::GetCurrentChange());
+ $oDataLoader->LoadFile($sFileName);
+
+ $oP->p("Ending data load session");
+ if ($oDataLoader->EndSession(true /* strict */)) {
+ $iCountCreated = $oDataLoader->GetCountCreated();
+ CMDBSource::Query('COMMIT');
+
+ $oP->p("Data successfully written into the DB: $iCountCreated objects created");
+ } else {
+ CMDBSource::Query('ROLLBACK');
+ $oP->p("Some issues have been encountered, changes will not be recorded, please review the source data");
+ $aErrors = $oDataLoader->GetErrors();
+ if (count($aErrors) > 0) {
+ $oP->p('Errors ('.count($aErrors).')');
+ foreach ($aErrors as $sMsg) {
+ $oP->p(' * '.$sMsg);
+ }
+ }
+ $aWarnings = $oDataLoader->GetWarnings();
+ if (count($aWarnings) > 0) {
+ $oP->p('Warnings ('.count($aWarnings).')');
+ foreach ($aWarnings as $sMsg) {
+ $oP->p(' * '.$sMsg);
+ }
+ }
+ }
} catch (Exception $e) {
- $oP->p("An error happened while loading the data: ".$e->getMessage());
- $oP->p("Aborting (no data written)...");
- CMDBSource::Query('ROLLBACK');
+ $oP->p("An error happened while loading the data: ".$e->getMessage());
+ $oP->p("Aborting (no data written)...");
+ CMDBSource::Query('ROLLBACK');
}
if (function_exists('memory_get_peak_usage')) {
- $oP->p("Information: memory peak usage: ".memory_get_peak_usage());
+ $oP->p("Information: memory peak usage: ".memory_get_peak_usage());
}
$oP->Output();
diff --git a/webservices/createfrommail.php b/webservices/createfrommail.php
index 35edac0f50..4ed93177c0 100644
--- a/webservices/createfrommail.php
+++ b/webservices/createfrommail.php
@@ -41,13 +41,13 @@
function GetSender($aHeaders)
{
- $aResult = ['name' => '', 'email' => ''];
- $aResult['name'] = $aHeaders['From'];
- $aMatches = [];
- if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches)) {
- $aResult['email'] = $aMatches[1].'@'.$aMatches[2];
- }
- return $aResult;
+ $aResult = ['name' => '', 'email' => ''];
+ $aResult['name'] = $aHeaders['From'];
+ $aMatches = [];
+ if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches)) {
+ $aResult['email'] = $aMatches[1].'@'.$aMatches[2];
+ }
+ return $aResult;
}
/**
@@ -59,38 +59,38 @@ function GetSender($aHeaders)
*/
function CreateTicket($sSenderEmail, $sSubject, $sBody)
{
- $oTicket = null;
- try {
- $oContactSearch = new DBObjectSearch('Contact'); // Can be either a Person or a Team, but must be a valid Contact
- $oContactSearch->AddCondition('email', $sSenderEmail, '=');
- $oSet = new DBObjectSet($oContactSearch);
- if ($oSet->Count() == 1) {
- $oContact = $oSet->Fetch();
- $oOrganization = MetaModel::GetObject('Organization', $oContact->Get('org_id'));
- $oTicket = new UserRequest();
- $oTicket->Set('title', $sSubject);
- $oTicket->Set('description', $sBody);
- $oTicket->Set('org_id', $oOrganization->GetKey());
- $oTicket->Set('caller_id', $oContact->GetKey());
- $oTicket->Set('impact', DEFAULT_IMPACT);
- $oTicket->Set('urgency', DEFAULT_URGENCY);
- $oTicket->Set('product', DEFAULT_PRODUCT);
- $oTicket->Set('service_id', DEFAULT_SERVICE_ID); // Can be replaced by a search for a valid service for this 'org_id'
- $oTicket->Set('servicesubcategory_id', DEFAULT_SUBSERVICE_ID); // Same as above...
- $oTicket->Set('workgroup_id', DEFAULT_WORKGROUP_ID); // Same as above...
+ $oTicket = null;
+ try {
+ $oContactSearch = new DBObjectSearch('Contact'); // Can be either a Person or a Team, but must be a valid Contact
+ $oContactSearch->AddCondition('email', $sSenderEmail, '=');
+ $oSet = new DBObjectSet($oContactSearch);
+ if ($oSet->Count() == 1) {
+ $oContact = $oSet->Fetch();
+ $oOrganization = MetaModel::GetObject('Organization', $oContact->Get('org_id'));
+ $oTicket = new UserRequest();
+ $oTicket->Set('title', $sSubject);
+ $oTicket->Set('description', $sBody);
+ $oTicket->Set('org_id', $oOrganization->GetKey());
+ $oTicket->Set('caller_id', $oContact->GetKey());
+ $oTicket->Set('impact', DEFAULT_IMPACT);
+ $oTicket->Set('urgency', DEFAULT_URGENCY);
+ $oTicket->Set('product', DEFAULT_PRODUCT);
+ $oTicket->Set('service_id', DEFAULT_SERVICE_ID); // Can be replaced by a search for a valid service for this 'org_id'
+ $oTicket->Set('servicesubcategory_id', DEFAULT_SUBSERVICE_ID); // Same as above...
+ $oTicket->Set('workgroup_id', DEFAULT_WORKGROUP_ID); // Same as above...
- // Record the change information about the object
- $sUserString = $oContact->GetName().', submitted by email';
- CMDBObject::SetTrackInfo($sUserString);
- $oTicket->DBInsert();
- } else {
- echo "No contact found in iTop having the email: $sSenderEmail, email message ignored.\n";
- }
- } catch (Exception $e) {
- echo "Error: exception ".$e->getMessage();
- $oTicket = null;
- }
- return $oTicket;
+ // Record the change information about the object
+ $sUserString = $oContact->GetName().', submitted by email';
+ CMDBObject::SetTrackInfo($sUserString);
+ $oTicket->DBInsert();
+ } else {
+ echo "No contact found in iTop having the email: $sSenderEmail, email message ignored.\n";
+ }
+ } catch (Exception $e) {
+ echo "Error: exception ".$e->getMessage();
+ $oTicket = null;
+ }
+ return $oTicket;
}
/**
* Main program
@@ -105,61 +105,61 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
// in iTop (identified by her/his email address), otherwise the ticket creation will fail
$iNbMessages = $oPop3->numMsg();
for ($index = 1; $index <= $iNbMessages; $index++) {
- $params['include_bodies'] = true;
- $params['decode_bodies'] = true;
- $params['decode_headers'] = true;
- $params['crlf'] = "\r\n";
- $aHeaders = $oPop3->getParsedHeaders($index);
- $aSender = GetSender($aHeaders);
- $oDecoder = new Mail_mimeDecode($oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index));
- $oStructure = $oDecoder->decode($params);
- $sSubject = $aHeaders['Subject'];
- // Search for the text/plain body part
- $iPartIndex = 0;
- $bFound = false;
- $sTextBody = '';
- //echo "\n";
- //print_r($oStructure);
- //echo " \n";
- if (!isset($oStructure->parts) || count($oStructure->parts) == 0) {
- $sTextBody = $oStructure->body;
- } else {
- // Find the first "part" of the body which is in text/plain
- while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
- //echo "Reading part $iPartIndex
\n";
- if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
- ($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain')) {
- $sTextBody = $oStructure->parts[$iPartIndex]->body;
- $bFound = true;
- //echo "Plain text found ! ($sTextBody)
\n";
- }
- $iPartIndex++;
- }
- // Try again but this time look for an HTML part
- if (!$bFound) {
- while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
- //echo "Reading part $iPartIndex
\n";
- if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
- ($oStructure->parts[$iPartIndex]->ctype_secondary == 'html')) {
- $sTextBody = $oStructure->parts[$iPartIndex]->body;
- $bFound = true;
- //echo "HTML text found ! (".htmlentities($sTextBody, ENT_QUOTES, 'UTF-8').")
\n";
- }
- $iPartIndex++;
- }
- }
- }
+ $params['include_bodies'] = true;
+ $params['decode_bodies'] = true;
+ $params['decode_headers'] = true;
+ $params['crlf'] = "\r\n";
+ $aHeaders = $oPop3->getParsedHeaders($index);
+ $aSender = GetSender($aHeaders);
+ $oDecoder = new Mail_mimeDecode($oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index));
+ $oStructure = $oDecoder->decode($params);
+ $sSubject = $aHeaders['Subject'];
+ // Search for the text/plain body part
+ $iPartIndex = 0;
+ $bFound = false;
+ $sTextBody = '';
+ //echo "\n";
+ //print_r($oStructure);
+ //echo " \n";
+ if (!isset($oStructure->parts) || count($oStructure->parts) == 0) {
+ $sTextBody = $oStructure->body;
+ } else {
+ // Find the first "part" of the body which is in text/plain
+ while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
+ //echo "Reading part $iPartIndex
\n";
+ if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
+ ($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain')) {
+ $sTextBody = $oStructure->parts[$iPartIndex]->body;
+ $bFound = true;
+ //echo "Plain text found ! ($sTextBody)
\n";
+ }
+ $iPartIndex++;
+ }
+ // Try again but this time look for an HTML part
+ if (!$bFound) {
+ while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
+ //echo "Reading part $iPartIndex
\n";
+ if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
+ ($oStructure->parts[$iPartIndex]->ctype_secondary == 'html')) {
+ $sTextBody = $oStructure->parts[$iPartIndex]->body;
+ $bFound = true;
+ //echo "HTML text found ! (".htmlentities($sTextBody, ENT_QUOTES, 'UTF-8').")
\n";
+ }
+ $iPartIndex++;
+ }
+ }
+ }
- // Bug: depending on the email, the email address could be found in :
- // email => 'john.foo@combodo.com'
- // name => 'john foo
+ // Bug: depending on the email, the email address could be found in :
+ // email => 'john.foo@combodo.com'
+ // name => 'john foo
- $oTicket = CreateTicket($aSender['email'], $sSubject, $sTextBody);
- if ($oTicket != null) {
- // Ticket created, delete the email
- $oPop3->deleteMsg($index);
- echo "Ticket: ".$oTicket->GetName()." created.\n";
- }
+ $oTicket = CreateTicket($aSender['email'], $sSubject, $sTextBody);
+ if ($oTicket != null) {
+ // Ticket created, delete the email
+ $oPop3->deleteMsg($index);
+ echo "Ticket: ".$oTicket->GetName()." created.\n";
+ }
}
$oPop3->disconnect();
?>
diff --git a/webservices/cron.php b/webservices/cron.php
index 56cd8d066d..66549e7d15 100644
--- a/webservices/cron.php
+++ b/webservices/cron.php
@@ -29,8 +29,8 @@
const EXIT_CODE_FATAL = -2;
// early exit
if (file_exists(READONLY_MODE_FILE)) {
- echo "iTop is read-only. Exiting...\n";
- exit(EXIT_CODE_ERROR);
+ echo "iTop is read-only. Exiting...\n";
+ exit(EXIT_CODE_ERROR);
}
require_once(APPROOT.'/application/application.inc.php');
@@ -38,8 +38,8 @@
$sConfigFile = APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE;
if (!file_exists($sConfigFile)) {
- echo "iTop is not yet installed. Exiting...\n";
- exit(EXIT_CODE_ERROR);
+ echo "iTop is not yet installed. Exiting...\n";
+ exit(EXIT_CODE_ERROR);
}
require_once(APPROOT.'/application/startup.inc.php');
@@ -48,27 +48,27 @@
function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter')
{
- $sValue = utils::ReadParam($sParam, null, true, $sSanitizationFilter);
- if (is_null($sValue)) {
- $oP->p("ERROR: Missing argument '$sParam'\n");
- UsageAndExit($oP);
- }
+ $sValue = utils::ReadParam($sParam, null, true, $sSanitizationFilter);
+ if (is_null($sValue)) {
+ $oP->p("ERROR: Missing argument '$sParam'\n");
+ UsageAndExit($oP);
+ }
- return trim($sValue);
+ return trim($sValue);
}
function UsageAndExit($oP)
{
- $bModeCLI = ($oP instanceof CLIPage);
-
- if ($bModeCLI) {
- $oP->p("USAGE:\n");
- $oP->p("php cron.php --auth_user= --auth_pwd= [--param_file=] [--verbose=1] [--debug=1] [--status_only=1]\n");
- } else {
- $oP->p("Optional parameters: verbose, param_file, status_only\n");
- }
- $oP->output();
- exit(EXIT_CODE_FATAL);
+ $bModeCLI = ($oP instanceof CLIPage);
+
+ if ($bModeCLI) {
+ $oP->p("USAGE:\n");
+ $oP->p("php cron.php --auth_user= --auth_pwd= [--param_file=] [--verbose=1] [--debug=1] [--status_only=1]\n");
+ } else {
+ $oP->p("Optional parameters: verbose, param_file, status_only\n");
+ }
+ $oP->output();
+ exit(EXIT_CODE_FATAL);
}
/**
@@ -87,84 +87,84 @@ function UsageAndExit($oP)
*/
function RunTask(BackgroundTask $oTask, $iTimeLimit)
{
- $TaskClass = $oTask->Get('class_name');
- $oProcess = new $TaskClass();
- $oRefClass = new ReflectionClass(get_class($oProcess));
- $oDateStarted = new DateTime();
- $oDatePlanned = new DateTime($oTask->Get('next_run_date'));
- $fStart = microtime(true);
- $oCtx = new ContextTag('CRON:Task:'.$TaskClass);
-
- $sMessage = '';
- $oExceptionToThrow = null;
- try {
- // Record (when starting) that this task was started, just in case it crashes during the execution
- $oTask->Set('latest_run_date', $oDateStarted->format('Y-m-d H:i:s'));
- // Record the current user running the cron
- $oTask->Set('system_user', utils::GetCurrentUserName());
- $oTask->Set('running', 1);
- $oTask->DBUpdate();
- // Time in seconds allowed to the task
- $iCurrTimeLimit = $iTimeLimit;
- // Compute allowed time
- if ($oRefClass->implementsInterface('iScheduledProcess') === false) {
- // Periodic task, allow only X times ($iMaxTaskExecutionTime) its periodicity (GetPeriodicity())
- $iMaxTaskExecutionTime = MetaModel::GetConfig()->Get('cron_task_max_execution_time');
- $iTaskLimit = time() + $oProcess->GetPeriodicity() * $iMaxTaskExecutionTime;
- // If our proposed time limit is less than cron limit, and cron_task_max_execution_time is > 0
- if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0) {
- $iCurrTimeLimit = $iTaskLimit;
- }
- }
- $sMessage = $oProcess->Process($iCurrTimeLimit);
- $oTask->Set('running', 0);
- } catch (MySQLHasGoneAwayException $e) {
- throw $e;
- } catch (ProcessFatalException $e) {
- $oExceptionToThrow = $e;
- } catch (Exception $e) { // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
- if ($oTask->IsDebug()) {
- $sMessage = 'Processing failed with message: '. $e->getMessage() . '. ' . $e->getTraceAsString();
- } else {
- $sMessage = 'Processing failed with message: '. $e->getMessage();
- }
- }
- $fDuration = microtime(true) - $fStart;
- if ($oTask->Get('total_exec_count') == 0) {
- // First execution
- $oTask->Set('first_run_date', $oDateStarted->format('Y-m-d H:i:s'));
- }
- $oTask->ComputeDurations($fDuration); // does increment the counter and compute statistics
-
- // Update the timestamp since we want to be able to re-order the tasks based on the time they finished
- $oDateEnded = new DateTime();
- $oTask->Set('latest_run_date', $oDateEnded->format('Y-m-d H:i:s'));
-
- if ($oRefClass->implementsInterface('iScheduledProcess')) {
- // Schedules process do repeat at specific moments
- $oPlannedStart = $oProcess->GetNextOccurrence();
- } else {
- // Background processes do repeat periodically
- $oPlannedStart = clone $oDatePlanned;
- // Let's schedule from the previous planned date of execution to avoid shift
- $oPlannedStart->modify($oProcess->GetPeriodicity().' seconds');
- $oEnd = new DateTime();
- while ($oPlannedStart->format('U') < $oEnd->format('U')) {
- // Next planned start is already in the past, increase it again by a period
- $oPlannedStart = $oPlannedStart->modify('+'.$oProcess->GetPeriodicity().' seconds');
- }
- }
-
- $oTask->Set('next_run_date', $oPlannedStart->format('Y-m-d H:i:s'));
- $oTask->DBUpdate();
-
- if ($oExceptionToThrow) {
- throw $oExceptionToThrow;
- }
-
- unset($oCtx);
-
- return $sMessage;
+ $TaskClass = $oTask->Get('class_name');
+ $oProcess = new $TaskClass();
+ $oRefClass = new ReflectionClass(get_class($oProcess));
+ $oDateStarted = new DateTime();
+ $oDatePlanned = new DateTime($oTask->Get('next_run_date'));
+ $fStart = microtime(true);
+ $oCtx = new ContextTag('CRON:Task:'.$TaskClass);
+
+ $sMessage = '';
+ $oExceptionToThrow = null;
+ try {
+ // Record (when starting) that this task was started, just in case it crashes during the execution
+ $oTask->Set('latest_run_date', $oDateStarted->format('Y-m-d H:i:s'));
+ // Record the current user running the cron
+ $oTask->Set('system_user', utils::GetCurrentUserName());
+ $oTask->Set('running', 1);
+ $oTask->DBUpdate();
+ // Time in seconds allowed to the task
+ $iCurrTimeLimit = $iTimeLimit;
+ // Compute allowed time
+ if ($oRefClass->implementsInterface('iScheduledProcess') === false) {
+ // Periodic task, allow only X times ($iMaxTaskExecutionTime) its periodicity (GetPeriodicity())
+ $iMaxTaskExecutionTime = MetaModel::GetConfig()->Get('cron_task_max_execution_time');
+ $iTaskLimit = time() + $oProcess->GetPeriodicity() * $iMaxTaskExecutionTime;
+ // If our proposed time limit is less than cron limit, and cron_task_max_execution_time is > 0
+ if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0) {
+ $iCurrTimeLimit = $iTaskLimit;
+ }
+ }
+ $sMessage = $oProcess->Process($iCurrTimeLimit);
+ $oTask->Set('running', 0);
+ } catch (MySQLHasGoneAwayException $e) {
+ throw $e;
+ } catch (ProcessFatalException $e) {
+ $oExceptionToThrow = $e;
+ } catch (Exception $e) { // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
+ if ($oTask->IsDebug()) {
+ $sMessage = 'Processing failed with message: '. $e->getMessage() . '. ' . $e->getTraceAsString();
+ } else {
+ $sMessage = 'Processing failed with message: '. $e->getMessage();
+ }
+ }
+ $fDuration = microtime(true) - $fStart;
+ if ($oTask->Get('total_exec_count') == 0) {
+ // First execution
+ $oTask->Set('first_run_date', $oDateStarted->format('Y-m-d H:i:s'));
+ }
+ $oTask->ComputeDurations($fDuration); // does increment the counter and compute statistics
+
+ // Update the timestamp since we want to be able to re-order the tasks based on the time they finished
+ $oDateEnded = new DateTime();
+ $oTask->Set('latest_run_date', $oDateEnded->format('Y-m-d H:i:s'));
+
+ if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ // Schedules process do repeat at specific moments
+ $oPlannedStart = $oProcess->GetNextOccurrence();
+ } else {
+ // Background processes do repeat periodically
+ $oPlannedStart = clone $oDatePlanned;
+ // Let's schedule from the previous planned date of execution to avoid shift
+ $oPlannedStart->modify($oProcess->GetPeriodicity().' seconds');
+ $oEnd = new DateTime();
+ while ($oPlannedStart->format('U') < $oEnd->format('U')) {
+ // Next planned start is already in the past, increase it again by a period
+ $oPlannedStart = $oPlannedStart->modify('+'.$oProcess->GetPeriodicity().' seconds');
+ }
+ }
+
+ $oTask->Set('next_run_date', $oPlannedStart->format('Y-m-d H:i:s'));
+ $oTask->DBUpdate();
+
+ if ($oExceptionToThrow) {
+ throw $oExceptionToThrow;
+ }
+
+ unset($oCtx);
+
+ return $sMessage;
}
/**
@@ -186,114 +186,114 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
*/
function CronExec($oP, $bVerbose, $bDebug = false)
{
- $iStarted = time();
- $iMaxDuration = MetaModel::GetConfig()->Get('cron_max_execution_time');
- $iTimeLimit = $iStarted + $iMaxDuration;
- $iCronSleep = MetaModel::GetConfig()->Get('cron_sleep');
-
- if ($bVerbose) {
- $oP->p("Planned duration = $iMaxDuration seconds");
- $oP->p("Loop pause = $iCronSleep seconds");
- }
-
- ReSyncProcesses($oP, $bVerbose, $bDebug);
-
- while (time() < $iTimeLimit) {
- CheckMaintenanceMode($oP);
-
- $oNow = new DateTime();
- $sNow = $oNow->format('Y-m-d H:i:s');
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oSearch->AddCondition('next_run_date', $sNow, '<=');
- $oSearch->AddCondition('status', 'active');
- $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
- $bWorkDone = false;
-
- if ($oTasks->CountExceeds(0)) {
- $bWorkDone = true;
- $aTasks = [];
- if ($bVerbose) {
- $sCount = $oTasks->Count();
- $oP->p("$sCount Tasks planned to run now ($sNow):");
- $oP->p('+---------------------------+---------+---------------------+---------------------+');
- $oP->p('| Task Class | Status | Last Run | Next Run |');
- $oP->p('+---------------------------+---------+---------------------+---------------------+');
- }
- while ($oTask = $oTasks->Fetch()) {
- $aTasks[$oTask->Get('class_name')] = $oTask;
- if ($bVerbose) {
- $sTaskName = $oTask->Get('class_name');
- $sStatus = $oTask->Get('status');
- $sLastRunDate = $oTask->Get('latest_run_date');
- $sNextRunDate = $oTask->Get('next_run_date');
- $oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s |', $sTaskName, $sStatus, $sLastRunDate, $sNextRunDate));
- }
- }
- if ($bVerbose) {
- $oP->p('+---------------------------+---------+---------------------+---------------------+');
- }
- $aRunTasks = [];
- foreach ($aTasks as $oTask) {
- $sTaskClass = $oTask->Get('class_name');
- $aRunTasks[] = $sTaskClass;
-
- // N°3219 for each process will use a specific CMDBChange object with a specific track info
- // Any BackgroundProcess can overrides this as needed
- CMDBObject::SetCurrentChangeFromParams("Background task ($sTaskClass)");
-
- // Run the task and record its next run time
- if ($bVerbose) {
- $oNow = new DateTime();
- $oP->p(">> === ".$oNow->format('Y-m-d H:i:s').sprintf(" Starting:%-'=49s", ' '.$sTaskClass.' '));
- }
- try {
- $sMessage = RunTask($aTasks[$sTaskClass], $iTimeLimit);
- } catch (MySQLHasGoneAwayException $e) {
- $oP->p("ERROR : 'MySQL has gone away' thrown when processing $sTaskClass (error_code=".$e->getCode().")");
- exit(EXIT_CODE_FATAL);
- } catch (ProcessFatalException $e) {
- $oP->p("ERROR : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().")");
- IssueLog::Error("Cron.php error : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().')');
- }
- if ($bVerbose) {
- if (!empty($sMessage)) {
- $oP->p("$sTaskClass: $sMessage");
- }
- $oEnd = new DateTime();
- $sNextRunDate = $oTask->Get('next_run_date');
- $oP->p("<< === ".$oEnd->format('Y-m-d H:i:s').sprintf(" End of: %-'=42s", ' '.$sTaskClass.' ')." Next: $sNextRunDate");
- }
- if (time() > $iTimeLimit) {
- break 2;
- }
- CheckMaintenanceMode($oP);
- }
-
- // Tasks to run later
- if ($bVerbose) {
- $oP->p('--');
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oSearch->AddCondition('next_run_date', $sNow, '>');
- $oSearch->AddCondition('status', 'active');
- $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
- while ($oTask = $oTasks->Fetch()) {
- if (!in_array($oTask->Get('class_name'), $aRunTasks)) {
- $oP->p(sprintf("-- Skipping task: %-'-40s", $oTask->Get('class_name').' ')." until: ".$oTask->Get('next_run_date'));
- }
- }
- }
- }
-
- if ($bVerbose && $bWorkDone) {
- $oP->p("Sleeping...\n");
- }
- sleep($iCronSleep);
- }
- if ($bVerbose) {
- $oP->p('');
- DisplayStatus($oP, ['next_run_date' => true]);
- $oP->p("Reached normal execution time limit (exceeded by ".(time() - $iTimeLimit)."s)");
- }
+ $iStarted = time();
+ $iMaxDuration = MetaModel::GetConfig()->Get('cron_max_execution_time');
+ $iTimeLimit = $iStarted + $iMaxDuration;
+ $iCronSleep = MetaModel::GetConfig()->Get('cron_sleep');
+
+ if ($bVerbose) {
+ $oP->p("Planned duration = $iMaxDuration seconds");
+ $oP->p("Loop pause = $iCronSleep seconds");
+ }
+
+ ReSyncProcesses($oP, $bVerbose, $bDebug);
+
+ while (time() < $iTimeLimit) {
+ CheckMaintenanceMode($oP);
+
+ $oNow = new DateTime();
+ $sNow = $oNow->format('Y-m-d H:i:s');
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oSearch->AddCondition('next_run_date', $sNow, '<=');
+ $oSearch->AddCondition('status', 'active');
+ $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
+ $bWorkDone = false;
+
+ if ($oTasks->CountExceeds(0)) {
+ $bWorkDone = true;
+ $aTasks = [];
+ if ($bVerbose) {
+ $sCount = $oTasks->Count();
+ $oP->p("$sCount Tasks planned to run now ($sNow):");
+ $oP->p('+---------------------------+---------+---------------------+---------------------+');
+ $oP->p('| Task Class | Status | Last Run | Next Run |');
+ $oP->p('+---------------------------+---------+---------------------+---------------------+');
+ }
+ while ($oTask = $oTasks->Fetch()) {
+ $aTasks[$oTask->Get('class_name')] = $oTask;
+ if ($bVerbose) {
+ $sTaskName = $oTask->Get('class_name');
+ $sStatus = $oTask->Get('status');
+ $sLastRunDate = $oTask->Get('latest_run_date');
+ $sNextRunDate = $oTask->Get('next_run_date');
+ $oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s |', $sTaskName, $sStatus, $sLastRunDate, $sNextRunDate));
+ }
+ }
+ if ($bVerbose) {
+ $oP->p('+---------------------------+---------+---------------------+---------------------+');
+ }
+ $aRunTasks = [];
+ foreach ($aTasks as $oTask) {
+ $sTaskClass = $oTask->Get('class_name');
+ $aRunTasks[] = $sTaskClass;
+
+ // N°3219 for each process will use a specific CMDBChange object with a specific track info
+ // Any BackgroundProcess can overrides this as needed
+ CMDBObject::SetCurrentChangeFromParams("Background task ($sTaskClass)");
+
+ // Run the task and record its next run time
+ if ($bVerbose) {
+ $oNow = new DateTime();
+ $oP->p(">> === ".$oNow->format('Y-m-d H:i:s').sprintf(" Starting:%-'=49s", ' '.$sTaskClass.' '));
+ }
+ try {
+ $sMessage = RunTask($aTasks[$sTaskClass], $iTimeLimit);
+ } catch (MySQLHasGoneAwayException $e) {
+ $oP->p("ERROR : 'MySQL has gone away' thrown when processing $sTaskClass (error_code=".$e->getCode().")");
+ exit(EXIT_CODE_FATAL);
+ } catch (ProcessFatalException $e) {
+ $oP->p("ERROR : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().")");
+ IssueLog::Error("Cron.php error : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().')');
+ }
+ if ($bVerbose) {
+ if (!empty($sMessage)) {
+ $oP->p("$sTaskClass: $sMessage");
+ }
+ $oEnd = new DateTime();
+ $sNextRunDate = $oTask->Get('next_run_date');
+ $oP->p("<< === ".$oEnd->format('Y-m-d H:i:s').sprintf(" End of: %-'=42s", ' '.$sTaskClass.' ')." Next: $sNextRunDate");
+ }
+ if (time() > $iTimeLimit) {
+ break 2;
+ }
+ CheckMaintenanceMode($oP);
+ }
+
+ // Tasks to run later
+ if ($bVerbose) {
+ $oP->p('--');
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oSearch->AddCondition('next_run_date', $sNow, '>');
+ $oSearch->AddCondition('status', 'active');
+ $oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
+ while ($oTask = $oTasks->Fetch()) {
+ if (!in_array($oTask->Get('class_name'), $aRunTasks)) {
+ $oP->p(sprintf("-- Skipping task: %-'-40s", $oTask->Get('class_name').' ')." until: ".$oTask->Get('next_run_date'));
+ }
+ }
+ }
+ }
+
+ if ($bVerbose && $bWorkDone) {
+ $oP->p("Sleeping...\n");
+ }
+ sleep($iCronSleep);
+ }
+ if ($bVerbose) {
+ $oP->p('');
+ DisplayStatus($oP, ['next_run_date' => true]);
+ $oP->p("Reached normal execution time limit (exceeded by ".(time() - $iTimeLimit)."s)");
+ }
}
/**
@@ -301,11 +301,11 @@ function CronExec($oP, $bVerbose, $bDebug = false)
*/
function CheckMaintenanceMode(Page $oP)
{
- // Verify files instead of reloading the full config each time
- if (file_exists(MAINTENANCE_MODE_FILE) || file_exists(READONLY_MODE_FILE)) {
- $oP->p("Maintenance detected, exiting");
- exit(EXIT_CODE_ERROR);
- }
+ // Verify files instead of reloading the full config each time
+ if (file_exists(MAINTENANCE_MODE_FILE) || file_exists(READONLY_MODE_FILE)) {
+ $oP->p("Maintenance detected, exiting");
+ exit(EXIT_CODE_ERROR);
+ }
}
/**
@@ -320,29 +320,29 @@ function CheckMaintenanceMode(Page $oP)
*/
function DisplayStatus($oP, $aTaskOrderBy = [])
{
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oTasks = new DBObjectSet($oSearch, $aTaskOrderBy);
- $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
- $oP->p('| Task Class | Status | Last Run | Next Run | Nb Run | Avg. Dur. |');
- $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
- while ($oTask = $oTasks->Fetch()) {
- $sTaskName = $oTask->Get('class_name');
- $sStatus = $oTask->Get('status');
- $sLastRunDate = $oTask->Get('latest_run_date');
- $sNextRunDate = $oTask->Get('next_run_date');
- $iNbRun = (int)$oTask->Get('total_exec_count');
- $sAverageRunTime = $oTask->Get('average_run_duration');
- $oP->p(sprintf(
- '| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |',
- $sTaskName,
- $sStatus,
- $sLastRunDate,
- $sNextRunDate,
- $iNbRun,
- $sAverageRunTime
- ));
- }
- $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oTasks = new DBObjectSet($oSearch, $aTaskOrderBy);
+ $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
+ $oP->p('| Task Class | Status | Last Run | Next Run | Nb Run | Avg. Dur. |');
+ $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
+ while ($oTask = $oTasks->Fetch()) {
+ $sTaskName = $oTask->Get('class_name');
+ $sStatus = $oTask->Get('status');
+ $sLastRunDate = $oTask->Get('latest_run_date');
+ $sNextRunDate = $oTask->Get('next_run_date');
+ $iNbRun = (int)$oTask->Get('total_exec_count');
+ $sAverageRunTime = $oTask->Get('average_run_duration');
+ $oP->p(sprintf(
+ '| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |',
+ $sTaskName,
+ $sStatus,
+ $sLastRunDate,
+ $sNextRunDate,
+ $iNbRun,
+ $sAverageRunTime
+ ));
+ }
+ $oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
}
/**
@@ -361,83 +361,83 @@ function DisplayStatus($oP, $aTaskOrderBy = [])
*/
function ReSyncProcesses($oP, $bVerbose, $bDebug)
{
- // Enumerate classes implementing BackgroundProcess
- //
- $oSearch = new DBObjectSearch('BackgroundTask');
- $oTasks = new DBObjectSet($oSearch);
- $aTasks = [];
- while ($oTask = $oTasks->Fetch()) {
- $aTasks[$oTask->Get('class_name')] = $oTask;
- }
- $oNow = new DateTime();
-
- $aProcesses = [];
- foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass) {
- $oProcess = new $sTaskClass();
- $aProcesses[$sTaskClass] = $oProcess;
-
- // Create missing entry if needed
- if (!array_key_exists($sTaskClass, $aTasks)) {
- // New entry, let's create a new BackgroundTask record, and plan the first execution
- $oTask = new BackgroundTask();
- $oTask->SetDebug($bDebug);
- $oTask->Set('class_name', $sTaskClass);
- $oTask->Set('total_exec_count', 0);
- $oTask->Set('min_run_duration', 99999.999);
- $oTask->Set('max_run_duration', 0);
- $oTask->Set('average_run_duration', 0);
- $oRefClass = new ReflectionClass($sTaskClass);
- if ($oRefClass->implementsInterface('iScheduledProcess')) {
- $oNextOcc = $oProcess->GetNextOccurrence();
- $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
- } else {
- // Background processes do start asap, i.e. "now"
- $oTask->Set('next_run_date', $oNow->format('Y-m-d H:i:s'));
- }
- if ($bVerbose) {
- $oP->p('Creating record for: '.$sTaskClass);
- $oP->p('First execution planned at: '.$oTask->Get('next_run_date'));
- }
- $oTask->DBInsert();
- } else {
- /** @var \BackgroundTask $oTask */
- $oTask = $aTasks[$sTaskClass];
- if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00') {
- // check for rescheduled tasks
- $oRefClass = new ReflectionClass($sTaskClass);
- if ($oRefClass->implementsInterface('iScheduledProcess')) {
- $oNextOcc = $oProcess->GetNextOccurrence();
- $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
- $oTask->DBUpdate();
- }
- }
- // Reactivate task if necessary
- if ($oTask->Get('status') == 'removed') {
- $oTask->Set('status', 'active');
- $oTask->DBUpdate();
- }
- // task having a real class to execute
- unset($aTasks[$sTaskClass]);
- }
- }
-
- // Remove all the tasks not having a valid class
- foreach ($aTasks as $oTask) {
- $sTaskClass = $oTask->Get('class_name');
- if (!class_exists($sTaskClass)) {
- $oTask->Set('status', 'removed');
- $oTask->DBUpdate();
- }
- }
-
- if ($bVerbose) {
- $aDisplayProcesses = [];
- foreach ($aProcesses as $oExecInstance) {
- $aDisplayProcesses[] = get_class($oExecInstance);
- }
- $sDisplayProcesses = implode(', ', $aDisplayProcesses);
- $oP->p("Background processes: ".$sDisplayProcesses);
- }
+ // Enumerate classes implementing BackgroundProcess
+ //
+ $oSearch = new DBObjectSearch('BackgroundTask');
+ $oTasks = new DBObjectSet($oSearch);
+ $aTasks = [];
+ while ($oTask = $oTasks->Fetch()) {
+ $aTasks[$oTask->Get('class_name')] = $oTask;
+ }
+ $oNow = new DateTime();
+
+ $aProcesses = [];
+ foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass) {
+ $oProcess = new $sTaskClass();
+ $aProcesses[$sTaskClass] = $oProcess;
+
+ // Create missing entry if needed
+ if (!array_key_exists($sTaskClass, $aTasks)) {
+ // New entry, let's create a new BackgroundTask record, and plan the first execution
+ $oTask = new BackgroundTask();
+ $oTask->SetDebug($bDebug);
+ $oTask->Set('class_name', $sTaskClass);
+ $oTask->Set('total_exec_count', 0);
+ $oTask->Set('min_run_duration', 99999.999);
+ $oTask->Set('max_run_duration', 0);
+ $oTask->Set('average_run_duration', 0);
+ $oRefClass = new ReflectionClass($sTaskClass);
+ if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ $oNextOcc = $oProcess->GetNextOccurrence();
+ $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
+ } else {
+ // Background processes do start asap, i.e. "now"
+ $oTask->Set('next_run_date', $oNow->format('Y-m-d H:i:s'));
+ }
+ if ($bVerbose) {
+ $oP->p('Creating record for: '.$sTaskClass);
+ $oP->p('First execution planned at: '.$oTask->Get('next_run_date'));
+ }
+ $oTask->DBInsert();
+ } else {
+ /** @var \BackgroundTask $oTask */
+ $oTask = $aTasks[$sTaskClass];
+ if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00') {
+ // check for rescheduled tasks
+ $oRefClass = new ReflectionClass($sTaskClass);
+ if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ $oNextOcc = $oProcess->GetNextOccurrence();
+ $oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
+ $oTask->DBUpdate();
+ }
+ }
+ // Reactivate task if necessary
+ if ($oTask->Get('status') == 'removed') {
+ $oTask->Set('status', 'active');
+ $oTask->DBUpdate();
+ }
+ // task having a real class to execute
+ unset($aTasks[$sTaskClass]);
+ }
+ }
+
+ // Remove all the tasks not having a valid class
+ foreach ($aTasks as $oTask) {
+ $sTaskClass = $oTask->Get('class_name');
+ if (!class_exists($sTaskClass)) {
+ $oTask->Set('status', 'removed');
+ $oTask->DBUpdate();
+ }
+ }
+
+ if ($bVerbose) {
+ $aDisplayProcesses = [];
+ foreach ($aProcesses as $oExecInstance) {
+ $aDisplayProcesses[] = get_class($oExecInstance);
+ }
+ $sDisplayProcesses = implode(', ', $aDisplayProcesses);
+ $oP->p("Background processes: ".$sDisplayProcesses);
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -449,85 +449,85 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
$bIsModeCLI = utils::IsModeCLI();
if ($bIsModeCLI) {
- $oP = new CLIPage("iTop - cron");
+ $oP = new CLIPage("iTop - cron");
- SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
+ SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
} else {
- $oP = new WebPage("iTop - cron");
+ $oP = new WebPage("iTop - cron");
}
try {
- utils::UseParamFile();
-
- $bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */);
- $bDebug = utils::ReadParam('debug', false, true /* Allow CLI */);
-
- if ($bIsModeCLI) {
- // Next steps:
- // specific arguments: 'csv file'
- //
- $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
- $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
- UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
- $oP->p("Access wrong credentials ('$sAuthUser')");
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
- } else {
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
- LoginWebPage::DoLogin(); // Check user rights and prompt if needed
- }
-
- if (!UserRights::IsAdministrator()) {
- $oP->p("Access restricted to administrators");
- $oP->Output();
- exit(EXIT_CODE_ERROR);
- }
-
- if (utils::ReadParam('status_only', false, true /* Allow CLI */)) {
- // Display status and exit
- DisplayStatus($oP);
- exit(0);
- }
-
- require_once(APPROOT.'core/mutex.class.inc.php');
- $oP->p("Starting: ".time().' ('.date('Y-m-d H:i:s').')');
+ utils::UseParamFile();
+
+ $bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */);
+ $bDebug = utils::ReadParam('debug', false, true /* Allow CLI */);
+
+ if ($bIsModeCLI) {
+ // Next steps:
+ // specific arguments: 'csv file'
+ //
+ $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
+ $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ $oP->p("Access wrong credentials ('$sAuthUser')");
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
+ } else {
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
+ }
+
+ if (!UserRights::IsAdministrator()) {
+ $oP->p("Access restricted to administrators");
+ $oP->Output();
+ exit(EXIT_CODE_ERROR);
+ }
+
+ if (utils::ReadParam('status_only', false, true /* Allow CLI */)) {
+ // Display status and exit
+ DisplayStatus($oP);
+ exit(0);
+ }
+
+ require_once(APPROOT.'core/mutex.class.inc.php');
+ $oP->p("Starting: ".time().' ('.date('Y-m-d H:i:s').')');
} catch (Exception $e) {
- $oP->p("Error: ".$e->GetMessage());
- $oP->output();
- exit(EXIT_CODE_FATAL);
+ $oP->p("Error: ".$e->GetMessage());
+ $oP->output();
+ exit(EXIT_CODE_FATAL);
}
try {
- $oMutex = new iTopMutex('cron');
- if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) {
- $oP->p("A maintenance is ongoing");
- } else {
- if ($oMutex->TryLock()) {
- CronExec($oP, $bVerbose, $bDebug);
- } else {
- // Exit silently
- $oP->p("Already running...");
- }
- }
+ $oMutex = new iTopMutex('cron');
+ if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) {
+ $oP->p("A maintenance is ongoing");
+ } else {
+ if ($oMutex->TryLock()) {
+ CronExec($oP, $bVerbose, $bDebug);
+ } else {
+ // Exit silently
+ $oP->p("Already running...");
+ }
+ }
} catch (Exception $e) {
- $oP->p("ERROR: '".$e->getMessage()."'");
- if ($bDebug) {
- // Might contain verb parameters such a password...
- $oP->p($e->getTraceAsString());
- }
+ $oP->p("ERROR: '".$e->getMessage()."'");
+ if ($bDebug) {
+ // Might contain verb parameters such a password...
+ $oP->p($e->getTraceAsString());
+ }
} finally {
- try {
- $oMutex->Unlock();
- } catch (Exception $e) {
- $oP->p("ERROR: '".$e->getMessage()."'");
- if ($bDebug) {
- // Might contain verb parameters such a password...
- $oP->p($e->getTraceAsString());
- }
- }
+ try {
+ $oMutex->Unlock();
+ } catch (Exception $e) {
+ $oP->p("ERROR: '".$e->getMessage()."'");
+ if ($bDebug) {
+ // Might contain verb parameters such a password...
+ $oP->p($e->getTraceAsString());
+ }
+ }
}
$oP->p("Exiting: ".time().' ('.date('Y-m-d H:i:s').')');
diff --git a/webservices/export-v2.php b/webservices/export-v2.php
index e505ca6c34..ad268232ce 100644
--- a/webservices/export-v2.php
+++ b/webservices/export-v2.php
@@ -36,121 +36,121 @@
function ReportErrorAndExit($sErrorMessage)
{
- if (utils::IsModeCLI()) {
- $oP = new CLIPage("iTop - Export");
- $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
- $oP->output();
- exit(EXIT_CODE_ERROR);
- } else {
- $oP = new WebPage("iTop - Export");
- $oP->add_http_headers();
- $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
+ if (utils::IsModeCLI()) {
+ $oP = new CLIPage("iTop - Export");
+ $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ } else {
+ $oP = new WebPage("iTop - Export");
+ $oP->add_http_headers();
+ $oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
}
function ReportErrorAndUsage($sErrorMessage)
{
- if (utils::IsModeCLI()) {
- $oP = new CLIPage("iTop - Export");
- $oP->p('ERROR: '.$sErrorMessage);
- Usage($oP);
- $oP->output();
- exit(EXIT_CODE_ERROR);
- } else {
- $oP = new WebPage("iTop - Export");
- $oP->add_http_headers();
- $oP->p('ERROR: '.$sErrorMessage);
- Usage($oP);
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
+ if (utils::IsModeCLI()) {
+ $oP = new CLIPage("iTop - Export");
+ $oP->p('ERROR: '.$sErrorMessage);
+ Usage($oP);
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ } else {
+ $oP = new WebPage("iTop - Export");
+ $oP->add_http_headers();
+ $oP->p('ERROR: '.$sErrorMessage);
+ Usage($oP);
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
}
function Usage(Page $oP)
{
- if (Utils::IsModeCLI()) {
- $oP->p('Usage: php '.basename(__FILE__).' --auth_user= --auth_pwd= --expression= --query= [--arg_xxx=] [--no_localize=0|1] [--format=] [--format-options...]');
- $oP->p("Parameters:");
- $oP->p(" * auth_user: the iTop user account for authentication");
- $oP->p(" * auth_pwd: the password of the iTop user account");
- } else {
- $oP->p("Parameters:");
- }
- $oP->p(" * expression: an OQL expression (e.g. SELECT Contact WHERE name LIKE 'm%')");
- $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
- if (Utils::IsModeCLI()) {
- $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
- } else {
- $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
- }
- $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- $oP->p(" * format: (optional, default is html) the desired output format. Can be one of '".implode("', '", array_keys($aSupportedFormats))."'");
- foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
- $oExporter = BulkExport::FindExporter($sFormatCode);
- if ($oExporter !== null) {
- if (!Utils::IsModeCLI()) {
- $oP->add(' ');
- }
- $oExporter->DisplayUsage($oP);
- if (!Utils::IsModeCLI()) {
- $oP->add('');
- }
- }
- }
- //if (!Utils::IsModeCLI())
- //{
- // $oP->add('');
- //}
+ if (Utils::IsModeCLI()) {
+ $oP->p('Usage: php '.basename(__FILE__).' --auth_user= --auth_pwd= --expression= --query= [--arg_xxx=] [--no_localize=0|1] [--format=] [--format-options...]');
+ $oP->p("Parameters:");
+ $oP->p(" * auth_user: the iTop user account for authentication");
+ $oP->p(" * auth_pwd: the password of the iTop user account");
+ } else {
+ $oP->p("Parameters:");
+ }
+ $oP->p(" * expression: an OQL expression (e.g. SELECT Contact WHERE name LIKE 'm%')");
+ $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
+ if (Utils::IsModeCLI()) {
+ $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
+ } else {
+ $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
+ }
+ $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ $oP->p(" * format: (optional, default is html) the desired output format. Can be one of '".implode("', '", array_keys($aSupportedFormats))."'");
+ foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
+ $oExporter = BulkExport::FindExporter($sFormatCode);
+ if ($oExporter !== null) {
+ if (!Utils::IsModeCLI()) {
+ $oP->add(' ');
+ }
+ $oExporter->DisplayUsage($oP);
+ if (!Utils::IsModeCLI()) {
+ $oP->add('');
+ }
+ }
+ }
+ //if (!Utils::IsModeCLI())
+ //{
+ // $oP->add('');
+ //}
}
function DisplayExpressionForm(WebPage $oP, $sAction, $sExpression = '', $sExceptionMessage = '', $oForm = null)
{
- $oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:ScopeDefinition'));
- if ($oForm == null) {
- $oForm = FormUIBlockFactory::MakeStandard('export-form');
- $oForm->SetAction($sAction);
- $oP->AddSubBlock($oForm);
- }
- $oForm->AddSubBlock($oPanel);
-
- $oPanel->AddSubBlock(InputUIBlockFactory::MakeForHidden('interactive', '1'));
-
- $oFieldQuery = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelOQLExpression').' ');
- $oTextArea = new TextArea('expression', utils::EscapeHtml($sExpression), "textarea_oql", 70, 8);
- $oTextArea->SetPlaceholder(Dict::S('Core:BulkExportQueryPlaceholder'));
- $oTextArea->AddCSSClasses(["ibo-input-text", "ibo-query-oql", "ibo-is-code"]);
- $oFieldQuery->AddSubBlock($oTextArea);
- $oPanel->AddSubBlock($oFieldQuery);
- if (!empty($sExceptionMessage)) {
- $oAlert = AlertUIBlockFactory::MakeForFailure($sExceptionMessage);
- $oAlert->SetIsCollapsible(false);
- $oPanel->AddSubBlock($oAlert);
- }
-
- $oFieldPhraseBook = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelPhrasebookEntry').' ');
- $oSelect = SelectUIBlockFactory::MakeForSelect('query', "select_phrasebook");
- $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption("", Dict::S('UI:SelectOne'), false));
-
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL');
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- while ($oQuery = $oQueries->Fetch()) {
- $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($oQuery->GetKey(), $oQuery->Get('name'), false));
- }
- $oFieldPhraseBook->AddSubBlock($oSelect);
- $oPanel->AddSubBlock($oFieldPhraseBook);
-
- $oPanel->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), "", "", true, "next-btn"));
- $oP->p(''.Dict::S('Core:BulkExportCanRunNonInteractive').' ');
- $oP->p(''.Dict::S('Core:BulkExportLegacyExport').' ');
- $sJSEmptyOQL = json_encode(Dict::S('Core:BulkExportMessageEmptyOQL'));
- $sJSEmptyQueryId = json_encode(Dict::S('Core:BulkExportMessageEmptyPhrasebookEntry'));
-
- $oP->add_ready_script(
- <<SetAction($sAction);
+ $oP->AddSubBlock($oForm);
+ }
+ $oForm->AddSubBlock($oPanel);
+
+ $oPanel->AddSubBlock(InputUIBlockFactory::MakeForHidden('interactive', '1'));
+
+ $oFieldQuery = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelOQLExpression').' ');
+ $oTextArea = new TextArea('expression', utils::EscapeHtml($sExpression), "textarea_oql", 70, 8);
+ $oTextArea->SetPlaceholder(Dict::S('Core:BulkExportQueryPlaceholder'));
+ $oTextArea->AddCSSClasses(["ibo-input-text", "ibo-query-oql", "ibo-is-code"]);
+ $oFieldQuery->AddSubBlock($oTextArea);
+ $oPanel->AddSubBlock($oFieldQuery);
+ if (!empty($sExceptionMessage)) {
+ $oAlert = AlertUIBlockFactory::MakeForFailure($sExceptionMessage);
+ $oAlert->SetIsCollapsible(false);
+ $oPanel->AddSubBlock($oAlert);
+ }
+
+ $oFieldPhraseBook = FieldUIBlockFactory::MakeStandard(''.Dict::S('Core:BulkExportLabelPhrasebookEntry').' ');
+ $oSelect = SelectUIBlockFactory::MakeForSelect('query', "select_phrasebook");
+ $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption("", Dict::S('UI:SelectOne'), false));
+
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL');
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ while ($oQuery = $oQueries->Fetch()) {
+ $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($oQuery->GetKey(), $oQuery->Get('name'), false));
+ }
+ $oFieldPhraseBook->AddSubBlock($oSelect);
+ $oPanel->AddSubBlock($oFieldPhraseBook);
+
+ $oPanel->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), "", "", true, "next-btn"));
+ $oP->p(''.Dict::S('Core:BulkExportCanRunNonInteractive').' ');
+ $oP->p(''.Dict::S('Core:BulkExportLegacyExport').' ');
+ $sJSEmptyOQL = json_encode(Dict::S('Core:BulkExportMessageEmptyOQL'));
+ $sJSEmptyQueryId = json_encode(Dict::S('Core:BulkExportMessageEmptyPhrasebookEntry'));
+
+ $oP->add_ready_script(
+ <<add_script(DateTimeFormat::GetJSSQLToCustomFormat());
- $sJSDefaultDateTimeFormat = json_encode((string)AttributeDateTime::GetFormat());
- $oP->add_script(
- <<add_script(DateTimeFormat::GetJSSQLToCustomFormat());
+ $sJSDefaultDateTimeFormat = json_encode((string)AttributeDateTime::GetFormat());
+ $oP->add_script(
+ <<LinkScriptFromAppRoot('js/tabularfieldsselector.js');
- $oP->LinkScriptFromAppRoot('js/jquery.dragtable.js');
- $oP->LinkStylesheetFromAppRoot('css/dragtable.css');
-
- $oForm = FormUIBlockFactory::MakeStandard("export-form");
- $oForm->SetAction($sAction);
- $oForm->AddDataAttribute("state", "not-yet-started");
- $oP->AddSubBlock($oForm);
-
- $bExpressionIsValid = true;
- $sExpressionError = '';
- if (($sExpression === null) && ($sQueryId === null)) {
- $bExpressionIsValid = false;
- } elseif ($sExpression !== '') {
- try {
- $oExportSearch = DBObjectSearch::FromOQL($sExpression);
- $oExportSearch->UpdateContextFromUser();
- } catch (OQLException $e) {
- $bExpressionIsValid = false;
- $sExpressionError = $e->getMessage();
- }
- }
-
- if (!$bExpressionIsValid) {
- DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError, $oForm);
-
- return;
- }
-
- if ($sExpression !== '') {
- $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("expression", $sExpression));
- $oExportSearch = DBObjectSearch::FromOQL($sExpression);
- $oExportSearch->UpdateContextFromUser();
- } else {
- $oQuery = MetaModel::GetObject('QueryOQL', $sQueryId);
- $oExportSearch = DBObjectSearch::FromOQL($oQuery->Get('oql'));
- $oExportSearch->UpdateContextFromUser();
- $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("query", $sQueryId));
- }
- $aFormPartsByFormat = [];
- $aAllFormParts = [];
- if ($sFormat == null) {
- // No specific format chosen
- $sDefaultFormat = utils::ReadParam('format', 'xlsx');
-
- $oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("format", Dict::S('Core:BulkExport:ExportFormatPrompt'), "format_selector");
- $oSelect->SetIsLabelBefore(true);
- $oForm->AddSubBlock($oSelect);
-
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- asort($aSupportedFormats);
- foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
- $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sFormatCode, $sLabel, ($sFormatCode == $sDefaultFormat)));
- $oExporter = BulkExport::FindExporter($sFormatCode);
- $oExporter->SetObjectList($oExportSearch);
- $aParts = $oExporter->EnumFormParts();
- foreach ($aParts as $sPartId => $void) {
- $aAllFormParts[$sPartId] = $oExporter;
- }
- $aFormPartsByFormat[$sFormatCode] = array_keys($aParts);
- }
-
- } else {
- // One specific format was chosen
- $oSelect = InputUIBlockFactory::MakeForHidden("format", utils::EscapeHtml($sFormat));
- $oForm->AddSubBlock($oSelect);
-
- $oExporter = BulkExport::FindExporter($sFormat, $oExportSearch);
- $aParts = $oExporter->EnumFormParts();
- foreach ($aParts as $sPartId => $void) {
- $aAllFormParts[$sPartId] = $oExporter;
- }
- $aFormPartsByFormat[$sFormat] = array_keys($aAllFormParts);
- }
- foreach ($aAllFormParts as $sPartId => $oExport) {
- $UIContentBlock = UIContentBlockUIBlockFactory::MakeStandard('form_part_'.$sPartId)->AddCSSClass('form_part');
- $oForm->AddSubBlock($UIContentBlock);
- $UIContentBlock->AddSubBlock($oExport->GetFormPart($oP, $sPartId));
- }
- //end of form
- $oBlockExport = UIContentBlockUIBlockFactory::MakeStandard("export-feedback")->SetIsHidden(true);
- $oBlockExport->AddSubBlock(new Html(''.Dict::S('ExcelExport:PreparingExport').'
'));
- $oBlockExport->AddSubBlock(new Html(''));
- $oP->AddSubBlock($oBlockExport);
- if ($sFormat == null) {//if it's global export
- $oP->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction('export', Dict::S('UI:Button:Export'), 'export', false, 'export-btn'));
- }
- $oBlockResult = UIContentBlockUIBlockFactory::MakeStandard("export_text_result")->SetIsHidden(true);
- $oBlockResult->AddSubBlock(new Html(Dict::S('Core:BulkExport:ExportResult')));
-
- $oTextArea = new TextArea('export_content', '', 'export_content');
- $oTextArea->AddCSSClass('ibo-input-text--export');
- $oBlockResult->AddSubBlock($oTextArea);
- $oP->AddSubBlock($oBlockResult);
-
- $sJSParts = json_encode($aFormPartsByFormat);
- $oP->add_ready_script(
- <<LinkScriptFromAppRoot('js/tabularfieldsselector.js');
+ $oP->LinkScriptFromAppRoot('js/jquery.dragtable.js');
+ $oP->LinkStylesheetFromAppRoot('css/dragtable.css');
+
+ $oForm = FormUIBlockFactory::MakeStandard("export-form");
+ $oForm->SetAction($sAction);
+ $oForm->AddDataAttribute("state", "not-yet-started");
+ $oP->AddSubBlock($oForm);
+
+ $bExpressionIsValid = true;
+ $sExpressionError = '';
+ if (($sExpression === null) && ($sQueryId === null)) {
+ $bExpressionIsValid = false;
+ } elseif ($sExpression !== '') {
+ try {
+ $oExportSearch = DBObjectSearch::FromOQL($sExpression);
+ $oExportSearch->UpdateContextFromUser();
+ } catch (OQLException $e) {
+ $bExpressionIsValid = false;
+ $sExpressionError = $e->getMessage();
+ }
+ }
+
+ if (!$bExpressionIsValid) {
+ DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError, $oForm);
+
+ return;
+ }
+
+ if ($sExpression !== '') {
+ $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("expression", $sExpression));
+ $oExportSearch = DBObjectSearch::FromOQL($sExpression);
+ $oExportSearch->UpdateContextFromUser();
+ } else {
+ $oQuery = MetaModel::GetObject('QueryOQL', $sQueryId);
+ $oExportSearch = DBObjectSearch::FromOQL($oQuery->Get('oql'));
+ $oExportSearch->UpdateContextFromUser();
+ $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("query", $sQueryId));
+ }
+ $aFormPartsByFormat = [];
+ $aAllFormParts = [];
+ if ($sFormat == null) {
+ // No specific format chosen
+ $sDefaultFormat = utils::ReadParam('format', 'xlsx');
+
+ $oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("format", Dict::S('Core:BulkExport:ExportFormatPrompt'), "format_selector");
+ $oSelect->SetIsLabelBefore(true);
+ $oForm->AddSubBlock($oSelect);
+
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ asort($aSupportedFormats);
+ foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
+ $oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sFormatCode, $sLabel, ($sFormatCode == $sDefaultFormat)));
+ $oExporter = BulkExport::FindExporter($sFormatCode);
+ $oExporter->SetObjectList($oExportSearch);
+ $aParts = $oExporter->EnumFormParts();
+ foreach ($aParts as $sPartId => $void) {
+ $aAllFormParts[$sPartId] = $oExporter;
+ }
+ $aFormPartsByFormat[$sFormatCode] = array_keys($aParts);
+ }
+
+ } else {
+ // One specific format was chosen
+ $oSelect = InputUIBlockFactory::MakeForHidden("format", utils::EscapeHtml($sFormat));
+ $oForm->AddSubBlock($oSelect);
+
+ $oExporter = BulkExport::FindExporter($sFormat, $oExportSearch);
+ $aParts = $oExporter->EnumFormParts();
+ foreach ($aParts as $sPartId => $void) {
+ $aAllFormParts[$sPartId] = $oExporter;
+ }
+ $aFormPartsByFormat[$sFormat] = array_keys($aAllFormParts);
+ }
+ foreach ($aAllFormParts as $sPartId => $oExport) {
+ $UIContentBlock = UIContentBlockUIBlockFactory::MakeStandard('form_part_'.$sPartId)->AddCSSClass('form_part');
+ $oForm->AddSubBlock($UIContentBlock);
+ $UIContentBlock->AddSubBlock($oExport->GetFormPart($oP, $sPartId));
+ }
+ //end of form
+ $oBlockExport = UIContentBlockUIBlockFactory::MakeStandard("export-feedback")->SetIsHidden(true);
+ $oBlockExport->AddSubBlock(new Html(''.Dict::S('ExcelExport:PreparingExport').'
'));
+ $oBlockExport->AddSubBlock(new Html(''));
+ $oP->AddSubBlock($oBlockExport);
+ if ($sFormat == null) {//if it's global export
+ $oP->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction('export', Dict::S('UI:Button:Export'), 'export', false, 'export-btn'));
+ }
+ $oBlockResult = UIContentBlockUIBlockFactory::MakeStandard("export_text_result")->SetIsHidden(true);
+ $oBlockResult->AddSubBlock(new Html(Dict::S('Core:BulkExport:ExportResult')));
+
+ $oTextArea = new TextArea('export_content', '', 'export_content');
+ $oTextArea->AddCSSClass('ibo-input-text--export');
+ $oBlockResult->AddSubBlock($oTextArea);
+ $oP->AddSubBlock($oBlockResult);
+
+ $sJSParts = json_encode($aFormPartsByFormat);
+ $oP->add_ready_script(
+ <<add('');
- $oP->add_ready_script(
- <<
add('');
+ $oP->add_ready_script(
+ <<SetBreadCrumbEntry('ui-tool-export', Dict::S('Menu:ExportMenu'), Dict::S('Menu:ExportMenu+'), '', 'fas fa-file-export', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
- }
-
- if ($sExpression === null) {
- // No expression supplied, let's check if phrasebook entry is given
- if ($sQueryId !== null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- } else {
- ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
- }
- } else {
- if (utils::IsModeCLI()) {
- Usage($oP);
- ReportErrorAndExit("No expression or query phrasebook identifier supplied.");
- } else {
- // form to enter an OQL query or pick a query phrasebook identifier
- DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
- $oP->output();
- exit;
- }
- }
- }
-
- if ($sFormat !== null) {
- $oExporter = BulkExport::FindExporter($sFormat);
- if ($oExporter === null) {
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
- } else {
- DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
- }
- } else {
- DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
- }
- if ($sMode == 'dialog') {
- $oP->add('
');
- }
- $oP->output();
+ );
+ } else {
+ $oP = new iTopWebPage('iTop Export');
+ $oP->SetBreadCrumbEntry('ui-tool-export', Dict::S('Menu:ExportMenu'), Dict::S('Menu:ExportMenu+'), '', 'fas fa-file-export', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
+ }
+
+ if ($sExpression === null) {
+ // No expression supplied, let's check if phrasebook entry is given
+ if ($sQueryId !== null) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ } else {
+ ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
+ }
+ } else {
+ if (utils::IsModeCLI()) {
+ Usage($oP);
+ ReportErrorAndExit("No expression or query phrasebook identifier supplied.");
+ } else {
+ // form to enter an OQL query or pick a query phrasebook identifier
+ DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
+ $oP->output();
+ exit;
+ }
+ }
+ }
+
+ if ($sFormat !== null) {
+ $oExporter = BulkExport::FindExporter($sFormat);
+ if ($oExporter === null) {
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
+ } else {
+ DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
+ }
+ } else {
+ DisplayForm($oP, utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php', $sExpression, $sQueryId, $sFormat);
+ }
+ if ($sMode == 'dialog') {
+ $oP->add(' ');
+ }
+ $oP->output();
}
/**
@@ -422,101 +422,101 @@ function InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode)
*/
function CheckParameters($sExpression, $sQueryId, $sFormat)
{
- $oExporter = null;
- $oQuery = null;
-
- if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
- ReportErrorAndExit("The user account is not authorized to access the archives");
- }
-
- if (($sExpression === null) && ($sQueryId === null)) {
- ReportErrorAndUsage("Missing parameter. The parameter 'expression' or 'query' must be specified.");
- }
-
- // Either $sExpression or $sQueryId must be specified
- if ($sExpression === null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- } else {
- ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
- }
- }
- if ($sFormat === null) {
- ReportErrorAndUsage("Missing parameter 'format'.");
- }
-
- // Check if the supplied query is valid (and all the parameters are supplied
- try {
- $oSearch = DBObjectSearch::FromOQL($sExpression);
- $oSearch->UpdateContextFromUser();
- $aArgs = [];
- foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
- $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
- if (!is_null($value)) {
- $aArgs[$sParam] = $value;
- } else {
- throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
- }
- }
- $oSearch->SetInternalParams($aArgs);
-
- $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
- $oExporter = BulkExport::FindExporter($sFormat, $oSearch);
- if ($oExporter == null) {
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
- }
- } catch (MissingQueryArgument $e) {
- $oSearch = null;
- ReportErrorAndUsage("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (OQLException $e) {
- $oSearch = null;
- ReportErrorAndExit("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (Exception $e) {
- $oSearch = null;
- ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
- }
-
- // update last export information if check parameters ok
- if ($oQuery != null) {
- $oQuery->UpdateLastExportInformation();
- }
-
- $oExporter->SetFormat($sFormat);
- $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
- $oExporter->SetObjectList($oSearch);
- $oExporter->ReadParameters();
-
- return $oExporter;
+ $oExporter = null;
+ $oQuery = null;
+
+ if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
+ ReportErrorAndExit("The user account is not authorized to access the archives");
+ }
+
+ if (($sExpression === null) && ($sQueryId === null)) {
+ ReportErrorAndUsage("Missing parameter. The parameter 'expression' or 'query' must be specified.");
+ }
+
+ // Either $sExpression or $sQueryId must be specified
+ if ($sExpression === null) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ } else {
+ ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
+ }
+ }
+ if ($sFormat === null) {
+ ReportErrorAndUsage("Missing parameter 'format'.");
+ }
+
+ // Check if the supplied query is valid (and all the parameters are supplied
+ try {
+ $oSearch = DBObjectSearch::FromOQL($sExpression);
+ $oSearch->UpdateContextFromUser();
+ $aArgs = [];
+ foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
+ $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
+ if (!is_null($value)) {
+ $aArgs[$sParam] = $value;
+ } else {
+ throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
+ }
+ }
+ $oSearch->SetInternalParams($aArgs);
+
+ $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
+ $oExporter = BulkExport::FindExporter($sFormat, $oSearch);
+ if ($oExporter == null) {
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
+ }
+ } catch (MissingQueryArgument $e) {
+ $oSearch = null;
+ ReportErrorAndUsage("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (OQLException $e) {
+ $oSearch = null;
+ ReportErrorAndExit("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (Exception $e) {
+ $oSearch = null;
+ ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
+ }
+
+ // update last export information if check parameters ok
+ if ($oQuery != null) {
+ $oQuery->UpdateLastExportInformation();
+ }
+
+ $oExporter->SetFormat($sFormat);
+ $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
+ $oExporter->SetObjectList($oSearch);
+ $oExporter->ReadParameters();
+
+ return $oExporter;
}
function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
{
- $oExporter->SetHttpHeaders($oP);
- $exportResult = $oExporter->GetHeader();
- $aStatus = [];
- do {
- $exportResult .= $oExporter->GetNextChunk($aStatus);
- } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
-
- if ($aStatus['code'] == 'error') {
- $oExporter->Cleanup();
- ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
- } else {
- $exportResult .= $oExporter->GetFooter();
- $sMimeType = $oExporter->GetMimeType();
- if (substr($sMimeType, 0, 5) == 'text/') {
- $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet());
- }
- $oP->SetContentType($sMimeType);
- $oP->SetContentDisposition('attachment', $oExporter->GetDownloadFileName());
- $oP->add($exportResult);
- $oExporter->Cleanup();
- }
+ $oExporter->SetHttpHeaders($oP);
+ $exportResult = $oExporter->GetHeader();
+ $aStatus = [];
+ do {
+ $exportResult .= $oExporter->GetNextChunk($aStatus);
+ } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'error') {
+ $oExporter->Cleanup();
+ ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
+ } else {
+ $exportResult .= $oExporter->GetFooter();
+ $sMimeType = $oExporter->GetMimeType();
+ if (substr($sMimeType, 0, 5) == 'text/') {
+ $sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet());
+ }
+ $oP->SetContentType($sMimeType);
+ $oP->SetContentDisposition('attachment', $oExporter->GetDownloadFileName());
+ $oP->add($exportResult);
+ $oExporter->Cleanup();
+ }
}
/////////////////////////////////////////////////////////////////////////////
@@ -531,103 +531,103 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
$oCtx = new ContextTag(ContextTag::TAG_EXPORT);
if (utils::IsModeCLI()) {
- SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('iTop - Export'));
-
- try {
- // Do this before loging, in order to allow setting user credentials from within the file
- utils::UseParamFile();
- } catch (Exception $e) {
- echo "Error: ".utils::HtmlEntities($e->getMessage())." \n";
- exit(EXIT_CODE_FATAL);
- }
-
- $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
- $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
- if ($sAuthUser == null) {
- ReportErrorAndUsage("Missing parameter '--auth_user'");
- }
- if ($sAuthPwd == null) {
- ReportErrorAndUsage("Missing parameter '--auth_pwd'");
- }
-
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
- UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
- ReportErrorAndExit("Access restricted or wrong credentials for user '$sAuthUser'");
- }
-
- $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
- $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
- $bLocalize = (utils::ReadParam('no_localize', 0) != 1);
- if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
- ReportErrorAndExit("The user account is not authorized to access the archives");
- }
-
- if (($sExpression == null) && ($sQueryId == null)) {
- ReportErrorAndUsage("Missing parameter. At least one of '--expression' or '--query' must be specified.");
- }
-
- if ($sExpression === null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
- $oSearch->UpdateContextFromUser();
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- } else {
- ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
- }
- }
- try {
- $oSearch = DBObjectSearch::FromOQL($sExpression);
- $oSearch->UpdateContextFromUser();
- $aArgs = [];
- foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
- $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
- if (!is_null($value)) {
- $aArgs[$sParam] = $value;
- } else {
- throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
- }
- }
- $oSearch->SetInternalParams($aArgs);
-
- $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
- $oExporter = BulkExport::FindExporter($sFormat);
- if ($oExporter == null) {
- $aSupportedFormats = BulkExport::FindSupportedFormats();
- ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
- }
-
- $oExporter->SetFormat($sFormat);
- $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
- $oExporter->SetObjectList($oSearch);
- $oExporter->ReadParameters();
-
- $exportResult = $oExporter->GetHeader();
- $aStatus = [];
-
- do {
- $exportResult .= $oExporter->GetNextChunk($aStatus);
- } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
-
- if ($aStatus['code'] == 'error') {
- ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
- } else {
- $exportResult .= $oExporter->GetFooter();
- echo $exportResult;
- }
- $oExporter->Cleanup();
-
- } catch (MissingQueryArgument $e) {
- ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (OQLException $e) {
- ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (Exception $e) {
- ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
- }
-
- exit;
+ SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('iTop - Export'));
+
+ try {
+ // Do this before loging, in order to allow setting user credentials from within the file
+ utils::UseParamFile();
+ } catch (Exception $e) {
+ echo "Error: ".utils::HtmlEntities($e->getMessage())." \n";
+ exit(EXIT_CODE_FATAL);
+ }
+
+ $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
+ $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
+ if ($sAuthUser == null) {
+ ReportErrorAndUsage("Missing parameter '--auth_user'");
+ }
+ if ($sAuthPwd == null) {
+ ReportErrorAndUsage("Missing parameter '--auth_pwd'");
+ }
+
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ ReportErrorAndExit("Access restricted or wrong credentials for user '$sAuthUser'");
+ }
+
+ $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
+ $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
+ $bLocalize = (utils::ReadParam('no_localize', 0) != 1);
+ if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
+ ReportErrorAndExit("The user account is not authorized to access the archives");
+ }
+
+ if (($sExpression == null) && ($sQueryId == null)) {
+ ReportErrorAndUsage("Missing parameter. At least one of '--expression' or '--query' must be specified.");
+ }
+
+ if ($sExpression === null) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch->UpdateContextFromUser();
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ } else {
+ ReportErrorAndExit("Invalid query phrasebook identifier: '$sQueryId'");
+ }
+ }
+ try {
+ $oSearch = DBObjectSearch::FromOQL($sExpression);
+ $oSearch->UpdateContextFromUser();
+ $aArgs = [];
+ foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
+ $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
+ if (!is_null($value)) {
+ $aArgs[$sParam] = $value;
+ } else {
+ throw new MissingQueryArgument("Missing parameter '--arg_$sParam'");
+ }
+ }
+ $oSearch->SetInternalParams($aArgs);
+
+ $sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
+ $oExporter = BulkExport::FindExporter($sFormat);
+ if ($oExporter == null) {
+ $aSupportedFormats = BulkExport::FindSupportedFormats();
+ ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
+ }
+
+ $oExporter->SetFormat($sFormat);
+ $oExporter->SetChunkSize(EXPORTER_DEFAULT_CHUNK_SIZE);
+ $oExporter->SetObjectList($oSearch);
+ $oExporter->ReadParameters();
+
+ $exportResult = $oExporter->GetHeader();
+ $aStatus = [];
+
+ do {
+ $exportResult .= $oExporter->GetNextChunk($aStatus);
+ } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'error') {
+ ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
+ } else {
+ $exportResult .= $oExporter->GetFooter();
+ echo $exportResult;
+ }
+ $oExporter->Cleanup();
+
+ } catch (MissingQueryArgument $e) {
+ ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (OQLException $e) {
+ ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
+ } catch (Exception $e) {
+ ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
+ }
+
+ exit;
}
/////////////////////////////////////////////////////////////////////////////
@@ -637,55 +637,55 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
/////////////////////////////////////////////////////////////////////////////
try {
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
-
- // Main parameters
- $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
- $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
- $sFormat = utils::ReadParam('format', null, true /* Allow CLI */);
- $sFileName = utils::ReadParam('filename', '', true, 'string');
- $bInteractive = utils::ReadParam('interactive', false);
- $sMode = utils::ReadParam('mode', '');
-
- LoginWebPage::DoLogin(); // Check user rights and prompt if needed
-
- ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
-
- if ($bInteractive) {
- InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode);
- } else {
- $oExporter = CheckParameters($sExpression, $sQueryId, $sFormat);
- $sMimeType = $oExporter->GetMimeType();
- if ($sMimeType == 'text/html') {
- // Note: Using NiceWebPage only for HTML export as it includes JS scripts & files, which makes no sense in other export formats. More over, it breaks Excel spreadsheet import.
- if ($oExporter instanceof HTMLBulkExport) {
- $oP = new NiceWebPage('iTop export');
- $oP->add_http_headers();
- $oP->add_ready_script("$('table.listResults').tablesorter({widgets: ['MyZebra']});");
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
- } else {
- $oP = new WebPage('iTop export');
- $oP->add_http_headers();
- $oP->add_style("table br { mso-data-placement:same-cell; }"); // Trick for Excel: keep line breaks inside the same cell !
- }
- $oP->add_style("body { overflow: auto; }");
- } else {
- $oP = new DownloadPage('iTop export');
- $oP->SetContentType($oExporter->GetMimeType());
- }
- DoExport($oP, $oExporter, false);
- $oP->output();
- }
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+
+ // Main parameters
+ $sExpression = utils::ReadParam('expression', null, true /* Allow CLI */, 'raw_data');
+ $sQueryId = utils::ReadParam('query', null, true /* Allow CLI */, 'raw_data');
+ $sFormat = utils::ReadParam('format', null, true /* Allow CLI */);
+ $sFileName = utils::ReadParam('filename', '', true, 'string');
+ $bInteractive = utils::ReadParam('interactive', false);
+ $sMode = utils::ReadParam('mode', '');
+
+ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
+
+ ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
+
+ if ($bInteractive) {
+ InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode);
+ } else {
+ $oExporter = CheckParameters($sExpression, $sQueryId, $sFormat);
+ $sMimeType = $oExporter->GetMimeType();
+ if ($sMimeType == 'text/html') {
+ // Note: Using NiceWebPage only for HTML export as it includes JS scripts & files, which makes no sense in other export formats. More over, it breaks Excel spreadsheet import.
+ if ($oExporter instanceof HTMLBulkExport) {
+ $oP = new NiceWebPage('iTop export');
+ $oP->add_http_headers();
+ $oP->add_ready_script("$('table.listResults').tablesorter({widgets: ['MyZebra']});");
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
+ } else {
+ $oP = new WebPage('iTop export');
+ $oP->add_http_headers();
+ $oP->add_style("table br { mso-data-placement:same-cell; }"); // Trick for Excel: keep line breaks inside the same cell !
+ }
+ $oP->add_style("body { overflow: auto; }");
+ } else {
+ $oP = new DownloadPage('iTop export');
+ $oP->SetContentType($oExporter->GetMimeType());
+ }
+ DoExport($oP, $oExporter, false);
+ $oP->output();
+ }
} catch (BulkExportMissingParameterException $e) {
- $oP = new AjaxPage('iTop Export');
- $oP->add(utils::HtmlEntities($e->getMessage()));
- Usage($oP);
- $oP->output();
+ $oP = new AjaxPage('iTop Export');
+ $oP->add(utils::HtmlEntities($e->getMessage()));
+ Usage($oP);
+ $oP->output();
} catch (Exception $e) {
- $oP = new WebPage('iTop Export');
- $oP->add_http_headers();
- $oP->add('Error: '.utils::HtmlEntities($e->getMessage()));
- IssueLog::Error(utils::HtmlEntities($e->getMessage())."\n".$e->getTraceAsString());
- $oP->output();
+ $oP = new WebPage('iTop Export');
+ $oP->add_http_headers();
+ $oP->add('Error: '.utils::HtmlEntities($e->getMessage()));
+ IssueLog::Error(utils::HtmlEntities($e->getMessage())."\n".$e->getTraceAsString());
+ $oP->output();
}
diff --git a/webservices/export.php b/webservices/export.php
index c35d4f1f3c..18fa2b441e 100644
--- a/webservices/export.php
+++ b/webservices/export.php
@@ -43,11 +43,11 @@
const EXIT_CODE_FATAL = -2;
try {
- // Do this before loging, in order to allow setting user credentials from within the file
- utils::UseParamFile();
+ // Do this before loging, in order to allow setting user credentials from within the file
+ utils::UseParamFile();
} catch (Exception $e) {
- echo "Error: ".$e->GetMessage()." \n";
- exit(EXIT_CODE_FATAL);
+ echo "Error: ".$e->GetMessage()." \n";
+ exit(EXIT_CODE_FATAL);
}
/**
@@ -55,22 +55,22 @@
*/
$oCtx = new ContextTag(ContextTag::TAG_EXPORT);
if (utils::IsModeCLI()) {
- $oP = new CLIPage("iTop - Export");
- SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
-
- $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
- $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
-
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
- UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
- $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
- $oP->output();
- exit(EXIT_CODE_ERROR);
- }
+ $oP = new CLIPage("iTop - Export");
+ SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
+
+ $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
+ $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
+
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
+ }
} else {
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
- LoginWebPage::DoLogin(); // Check user rights and prompt if needed
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
}
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
@@ -79,10 +79,10 @@
$currentOrganization = utils::ReadParam('org_id', '');
if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
- $oP = new CLIPage("iTop - Export");
- $oP->p("The user account is not authorized to access the archives");
- $oP->output();
- exit(EXIT_CODE_ERROR);
+ $oP = new CLIPage("iTop - Export");
+ $oP->p("The user account is not authorized to access the archives");
+ $oP->output();
+ exit(EXIT_CODE_ERROR);
}
$bLocalize = (utils::ReadParam('no_localize', 0) != 1);
@@ -96,18 +96,18 @@
$oQuery = null;
if (strlen($sExpression) == 0) {
- $sQueryId = trim(utils::ReadParam('query', '', true /* Allow CLI */, 'raw_data'));
- if (strlen($sQueryId) > 0) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
- $oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
- $oQuery = $oQueries->Fetch();
- $sExpression = $oQuery->Get('oql');
- if (strlen($sFields) == 0) {
- $sFields = trim($oQuery->Get('fields'));
- }
- }
- }
+ $sQueryId = trim(utils::ReadParam('query', '', true /* Allow CLI */, 'raw_data'));
+ if (strlen($sQueryId) > 0) {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oQueries = new DBObjectSet($oSearch);
+ if ($oQueries->Count() > 0) {
+ $oQuery = $oQueries->Fetch();
+ $sExpression = $oQuery->Get('oql');
+ if (strlen($sFields) == 0) {
+ $sFields = trim($oQuery->Get('fields'));
+ }
+ }
+ }
}
$sFormat = strtolower(utils::ReadParam('format', 'html', true /* Allow CLI */));
@@ -115,221 +115,221 @@
$aFields = explode(',', $sFields);
// Clean the list of columns (empty it if every string is empty)
foreach ($aFields as $index => $sField) {
- $aFields[$index] = trim($sField);
- if (strlen($aFields[$index]) == 0) {
- unset($aFields[$index]);
- }
+ $aFields[$index] = trim($sField);
+ if (strlen($aFields[$index]) == 0) {
+ unset($aFields[$index]);
+ }
}
$oP = null;
if (!empty($sExpression)) {
- try {
- $oFilter = DBObjectSearch::FromOQL($sExpression);
-
- // Check and adjust column names
- //
- $aAliasToFields = [];
- foreach ($aFields as $index => $sField) {
- if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches)) {
- $sClassAlias = $aMatches[1];
- $sAttCode = $aMatches[2];
- } else {
- $sClassAlias = $oFilter->GetClassAlias();
- $sAttCode = $sField;
- // Disambiguate the class alias
- $aFields[$index] = $sClassAlias.'.'.$sAttCode;
- }
- $aAliasToFields[$sClassAlias][] = $sAttCode;
-
- $sClass = $oFilter->GetClassName($sClassAlias);
- if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
- throw new CoreException("Invalid field specification $sField: $sAttCode is not a valid attribute for $sClass");
- }
- $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
- if ($oAttDef instanceof AttributeSubItem) {
- $aAliasToFields[$sClassAlias][] = $oAttDef->GetParentAttCode();
- } elseif ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) {
- $sKeyAttCode = $oAttDef->GetKeyAttCode();
- $aAliasToFields[$sClassAlias][] = $sKeyAttCode;
- }
- }
-
- // Read query parameters
- //
- $aArgs = [];
- foreach ($oFilter->GetQueryParams() as $sParam => $foo) {
- $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
- if (!is_null($value)) {
- $aArgs[$sParam] = $value;
- }
- }
- $oFilter->SetInternalParams($aArgs);
- foreach ($oFilter->GetSelectedClasses() as $sAlias => $sClass) {
- if ((UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) && UR_ALLOWED_YES) == 0) {
- throw new Exception("The current user does not have permission for exporting data of class $sClass");
- }
- }
-
- // update last export information if check parameters ok
- if ($oQuery != null) {
- $oQuery->UpdateLastExportInformation();
- }
-
- if ($oFilter) {
- $oSet = new CMDBObjectSet($oFilter, [], $aArgs);
- $oSet->OptimizeColumnLoad($aAliasToFields);
- switch ($sFormat) {
- case 'html':
- $oP = new NiceWebPage("iTop - Export");
- $oP->add_style('body { overflow: auto; }'); // Show scroll bars if needed
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
- $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
-
- // Integration within MS-Excel web queries + HTTPS + IIS:
- // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
- // Then the fix is to force the reset of header values Pragma and Cache-control
- header("Cache-control:", true);
- header("Pragma:", true);
-
- // The HTML output is made for pages located in the /pages/ folder
- // since this page is in a different folder, let's adjust the HTML 'base' attribute
- // to make the relative hyperlinks in the page work
- $sUrl = utils::GetAbsoluteUrlAppRoot();
- $oP->set_base($sUrl.'pages/');
-
- if (count($aFields) > 0) {
- $iSearch = array_search('id', $aFields);
- if ($iSearch !== false) {
- $bViewLink = true;
- unset($aFields[$iSearch]);
- } else {
- $bViewLink = false;
- }
- $sFields = implode(',', $aFields);
- $aExtraParams = [
- 'menu' => false,
- 'toolkit_menu' => false,
- 'display_limit' => false,
- 'localize_values' => $bLocalize,
- 'zlist' => false,
- 'extra_fields' => $sFields,
- 'view_link' => $bViewLink,
- ];
- } else {
- $aExtraParams = [
- 'menu' => false,
- 'toolkit_menu' => false,
- 'display_limit' => false,
- 'localize_values' => $bLocalize,
- 'zlist' => 'details',
- ];
- }
-
- $oResultBlock = new DisplayBlock($oFilter, 'list', false, $aExtraParams);
- $oResultBlock->Display($oP, 'expresult');
- break;
-
- case 'csv':
- $oP = new CSVPage("iTop - Export");
- $sFields = implode(',', $aFields);
- $sCharset = utils::ReadParam('charset', MetaModel::GetConfig()->Get('csv_file_default_charset'), true /* Allow CLI */, 'raw_data');
- $sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize], $sCharset);
- if ($sCharset == 'UTF-8') {
- $sOutputData = UTF8_BOM.$sCSVData;
- } else {
- $sOutputData = $sCSVData;
- }
- if ($sFileName == '') {
- // Plain text => Firefox will NOT propose to download the file
- $oP->add_header("Content-type: text/plain; charset=$sCharset");
- } else {
- $oP->add_header("Content-type: text/csv; charset=$sCharset");
- }
- $oP->add($sOutputData);
- break;
-
- case 'spreadsheet':
- $oP = new WebPage("iTop - Export for spreadsheet");
-
- // Integration within MS-Excel web queries + HTTPS + IIS:
- // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
- // Then the fix is to force the reset of header values Pragma and Cache-control
- header("Pragma:", true);
- header("Cache-control:", true);
-
- $sFields = implode(',', $aFields);
- $oP->add_style('table br {mso-data-placement:same-cell;}'); // Trick for Excel: keep line breaks inside the same cell !
- cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize]);
- break;
-
- case 'xml':
- $oP = new XMLPage("iTop - Export", true /* passthrough */);
- cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, ['localize_values' => $bLocalize]);
- break;
-
- case 'xlsx':
- $oP = new AjaxPage('');
- $oExporter = new ExcelExporter();
- $oExporter->SetObjectList($oFilter);
-
- // Run the export by chunk of 1000 objects to limit memory usage
- $oExporter->SetChunkSize(1000);
- do {
- $aStatus = $oExporter->Run(); // process one chunk
- } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
-
- if ($aStatus['code'] == 'done') {
- $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
- $oP->SetContentDisposition('attachment', $oFilter->GetClass().'.xlsx');
- $oP->add(file_get_contents($oExporter->GetExcelFilePath()));
- $oExporter->Cleanup();
- } else {
- $oP->add('Error, xlsx export failed: '.$aStatus['message']);
- }
- break;
-
- default:
- $oP = new WebPage("iTop - Export");
- $oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
- }
- }
- } catch (Exception $e) {
- $oP = new WebPage("iTop - Export");
- $oP->p("Error the query can not be executed.");
- if ($e instanceof CoreException) {
- $oP->p($e->GetHtmlDesc());
- } else {
- $oP->p($e->getMessage());
- }
- }
+ try {
+ $oFilter = DBObjectSearch::FromOQL($sExpression);
+
+ // Check and adjust column names
+ //
+ $aAliasToFields = [];
+ foreach ($aFields as $index => $sField) {
+ if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches)) {
+ $sClassAlias = $aMatches[1];
+ $sAttCode = $aMatches[2];
+ } else {
+ $sClassAlias = $oFilter->GetClassAlias();
+ $sAttCode = $sField;
+ // Disambiguate the class alias
+ $aFields[$index] = $sClassAlias.'.'.$sAttCode;
+ }
+ $aAliasToFields[$sClassAlias][] = $sAttCode;
+
+ $sClass = $oFilter->GetClassName($sClassAlias);
+ if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
+ throw new CoreException("Invalid field specification $sField: $sAttCode is not a valid attribute for $sClass");
+ }
+ $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+ if ($oAttDef instanceof AttributeSubItem) {
+ $aAliasToFields[$sClassAlias][] = $oAttDef->GetParentAttCode();
+ } elseif ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) {
+ $sKeyAttCode = $oAttDef->GetKeyAttCode();
+ $aAliasToFields[$sClassAlias][] = $sKeyAttCode;
+ }
+ }
+
+ // Read query parameters
+ //
+ $aArgs = [];
+ foreach ($oFilter->GetQueryParams() as $sParam => $foo) {
+ $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
+ if (!is_null($value)) {
+ $aArgs[$sParam] = $value;
+ }
+ }
+ $oFilter->SetInternalParams($aArgs);
+ foreach ($oFilter->GetSelectedClasses() as $sAlias => $sClass) {
+ if ((UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) && UR_ALLOWED_YES) == 0) {
+ throw new Exception("The current user does not have permission for exporting data of class $sClass");
+ }
+ }
+
+ // update last export information if check parameters ok
+ if ($oQuery != null) {
+ $oQuery->UpdateLastExportInformation();
+ }
+
+ if ($oFilter) {
+ $oSet = new CMDBObjectSet($oFilter, [], $aArgs);
+ $oSet->OptimizeColumnLoad($aAliasToFields);
+ switch ($sFormat) {
+ case 'html':
+ $oP = new NiceWebPage("iTop - Export");
+ $oP->add_style('body { overflow: auto; }'); // Show scroll bars if needed
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
+ $oP->LinkStylesheetFromAppRoot('css/font-awesome/css/v4-shims.min.css');
+
+ // Integration within MS-Excel web queries + HTTPS + IIS:
+ // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
+ // Then the fix is to force the reset of header values Pragma and Cache-control
+ header("Cache-control:", true);
+ header("Pragma:", true);
+
+ // The HTML output is made for pages located in the /pages/ folder
+ // since this page is in a different folder, let's adjust the HTML 'base' attribute
+ // to make the relative hyperlinks in the page work
+ $sUrl = utils::GetAbsoluteUrlAppRoot();
+ $oP->set_base($sUrl.'pages/');
+
+ if (count($aFields) > 0) {
+ $iSearch = array_search('id', $aFields);
+ if ($iSearch !== false) {
+ $bViewLink = true;
+ unset($aFields[$iSearch]);
+ } else {
+ $bViewLink = false;
+ }
+ $sFields = implode(',', $aFields);
+ $aExtraParams = [
+ 'menu' => false,
+ 'toolkit_menu' => false,
+ 'display_limit' => false,
+ 'localize_values' => $bLocalize,
+ 'zlist' => false,
+ 'extra_fields' => $sFields,
+ 'view_link' => $bViewLink,
+ ];
+ } else {
+ $aExtraParams = [
+ 'menu' => false,
+ 'toolkit_menu' => false,
+ 'display_limit' => false,
+ 'localize_values' => $bLocalize,
+ 'zlist' => 'details',
+ ];
+ }
+
+ $oResultBlock = new DisplayBlock($oFilter, 'list', false, $aExtraParams);
+ $oResultBlock->Display($oP, 'expresult');
+ break;
+
+ case 'csv':
+ $oP = new CSVPage("iTop - Export");
+ $sFields = implode(',', $aFields);
+ $sCharset = utils::ReadParam('charset', MetaModel::GetConfig()->Get('csv_file_default_charset'), true /* Allow CLI */, 'raw_data');
+ $sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize], $sCharset);
+ if ($sCharset == 'UTF-8') {
+ $sOutputData = UTF8_BOM.$sCSVData;
+ } else {
+ $sOutputData = $sCSVData;
+ }
+ if ($sFileName == '') {
+ // Plain text => Firefox will NOT propose to download the file
+ $oP->add_header("Content-type: text/plain; charset=$sCharset");
+ } else {
+ $oP->add_header("Content-type: text/csv; charset=$sCharset");
+ }
+ $oP->add($sOutputData);
+ break;
+
+ case 'spreadsheet':
+ $oP = new WebPage("iTop - Export for spreadsheet");
+
+ // Integration within MS-Excel web queries + HTTPS + IIS:
+ // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
+ // Then the fix is to force the reset of header values Pragma and Cache-control
+ header("Pragma:", true);
+ header("Cache-control:", true);
+
+ $sFields = implode(',', $aFields);
+ $oP->add_style('table br {mso-data-placement:same-cell;}'); // Trick for Excel: keep line breaks inside the same cell !
+ cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize]);
+ break;
+
+ case 'xml':
+ $oP = new XMLPage("iTop - Export", true /* passthrough */);
+ cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, ['localize_values' => $bLocalize]);
+ break;
+
+ case 'xlsx':
+ $oP = new AjaxPage('');
+ $oExporter = new ExcelExporter();
+ $oExporter->SetObjectList($oFilter);
+
+ // Run the export by chunk of 1000 objects to limit memory usage
+ $oExporter->SetChunkSize(1000);
+ do {
+ $aStatus = $oExporter->Run(); // process one chunk
+ } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'done') {
+ $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ $oP->SetContentDisposition('attachment', $oFilter->GetClass().'.xlsx');
+ $oP->add(file_get_contents($oExporter->GetExcelFilePath()));
+ $oExporter->Cleanup();
+ } else {
+ $oP->add('Error, xlsx export failed: '.$aStatus['message']);
+ }
+ break;
+
+ default:
+ $oP = new WebPage("iTop - Export");
+ $oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
+ }
+ }
+ } catch (Exception $e) {
+ $oP = new WebPage("iTop - Export");
+ $oP->p("Error the query can not be executed.");
+ if ($e instanceof CoreException) {
+ $oP->p($e->GetHtmlDesc());
+ } else {
+ $oP->p($e->getMessage());
+ }
+ }
}
if (!$oP) {
- // Display a short message about how to use this page
- $bModeCLI = utils::IsModeCLI();
- if ($bModeCLI) {
- $oP = new CLIPage("iTop - Export");
- } else {
- $oP = new WebPage("iTop - Export");
- }
- $oP->p("General purpose export page.");
- $oP->p("Parameters:");
- $oP->p(" * expression: an OQL expression (URL encoded if needed)");
- $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
- if (Utils::IsModeCLI()) {
- $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
- } else {
- $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
- }
- $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
- $oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv', 'xlsx' or 'xml'");
- $oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes, or alias.attcode) separated by a comma");
- $oP->p(" * fields_advanced: (optional, no effect on XML/HTML formats ; ignored is fields is specified) If set to 1, the default list of fields will include the external keys and their reconciliation keys");
- $oP->p(" * filename: (optional, no effect in CLI mode) if set then the results will be downloaded as a file");
+ // Display a short message about how to use this page
+ $bModeCLI = utils::IsModeCLI();
+ if ($bModeCLI) {
+ $oP = new CLIPage("iTop - Export");
+ } else {
+ $oP = new WebPage("iTop - Export");
+ }
+ $oP->p("General purpose export page.");
+ $oP->p("Parameters:");
+ $oP->p(" * expression: an OQL expression (URL encoded if needed)");
+ $oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
+ if (Utils::IsModeCLI()) {
+ $oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
+ } else {
+ $oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
+ }
+ $oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
+ $oP->p(" * format: (optional, default is html) the desired output format. Can be one of 'html', 'spreadsheet', 'csv', 'xlsx' or 'xml'");
+ $oP->p(" * fields: (optional, no effect on XML format) list of fields (attribute codes, or alias.attcode) separated by a comma");
+ $oP->p(" * fields_advanced: (optional, no effect on XML/HTML formats ; ignored is fields is specified) If set to 1, the default list of fields will include the external keys and their reconciliation keys");
+ $oP->p(" * filename: (optional, no effect in CLI mode) if set then the results will be downloaded as a file");
}
if ($sFileName != '') {
- $oP->add_header('Content-Disposition: attachment; filename="'.$sFileName.'"');
+ $oP->add_header('Content-Disposition: attachment; filename="'.$sFileName.'"');
}
$oP->TrashUnexpectedOutput();
diff --git a/webservices/import.php b/webservices/import.php
index 2c757fc945..807011a6b5 100644
--- a/webservices/import.php
+++ b/webservices/import.php
@@ -42,160 +42,160 @@ class BulkLoadException extends Exception
$aPageParams =
[
- 'auth_user' =>
- [
- 'mandatory' => true,
- 'modes' => 'cli',
- 'default' => null,
- 'description' => 'login (must have enough rights to create objects of the given class)',
- ],
- 'auth_pwd' =>
- [
- 'mandatory' => true,
- 'modes' => 'cli',
- 'default' => null,
- 'description' => 'password',
- ],
- 'class' =>
- [
- 'mandatory' => true,
- 'modes' => 'http,cli',
- 'default' => null,
- 'description' => 'class of loaded objects',
- ],
- 'csvdata' =>
- [
- 'mandatory' => true,
- 'modes' => 'http',
- 'default' => null,
- 'description' => 'data',
- ],
- 'csvfile' =>
- [
- 'mandatory' => true,
- 'modes' => 'cli',
- 'default' => '',
- 'description' => 'local data file, replaces csvdata if specified',
- ],
- 'charset' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'Character set encoding of the CSV data: UTF-8, ISO-8859-1, WINDOWS-1251, WINDOWS-1252, ISO-8859-15, If blank, then the charset is set to config(csv_file_default_charset)',
- ],
- 'date_format' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'Input date format (used both for dates and datetimes) - Examples: Y-m-d H:i:s, d/m/Y H:i:s (Europe) - no transformation is applied if the argument is omitted. (note: old format specification using %Y %m %d is also supported for backward compatibility)',
- ],
- 'separator' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => ',',
- 'description' => 'column separator in CSV data (1 char, or \'tab\')',
- ],
- 'qualifier' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '"',
- 'description' => 'test qualifier in CSV data',
- ],
- 'output' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => 'summary',
- 'description' => '[retcode] to return the count of lines in error, [summary] to return a concise report, [details] to get a detailed report (each line listed)',
- ],
+ 'auth_user' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'cli',
+ 'default' => null,
+ 'description' => 'login (must have enough rights to create objects of the given class)',
+ ],
+ 'auth_pwd' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'cli',
+ 'default' => null,
+ 'description' => 'password',
+ ],
+ 'class' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'http,cli',
+ 'default' => null,
+ 'description' => 'class of loaded objects',
+ ],
+ 'csvdata' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'http',
+ 'default' => null,
+ 'description' => 'data',
+ ],
+ 'csvfile' =>
+ [
+ 'mandatory' => true,
+ 'modes' => 'cli',
+ 'default' => '',
+ 'description' => 'local data file, replaces csvdata if specified',
+ ],
+ 'charset' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'Character set encoding of the CSV data: UTF-8, ISO-8859-1, WINDOWS-1251, WINDOWS-1252, ISO-8859-15, If blank, then the charset is set to config(csv_file_default_charset)',
+ ],
+ 'date_format' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'Input date format (used both for dates and datetimes) - Examples: Y-m-d H:i:s, d/m/Y H:i:s (Europe) - no transformation is applied if the argument is omitted. (note: old format specification using %Y %m %d is also supported for backward compatibility)',
+ ],
+ 'separator' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => ',',
+ 'description' => 'column separator in CSV data (1 char, or \'tab\')',
+ ],
+ 'qualifier' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '"',
+ 'description' => 'test qualifier in CSV data',
+ ],
+ 'output' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => 'summary',
+ 'description' => '[retcode] to return the count of lines in error, [summary] to return a concise report, [details] to get a detailed report (each line listed)',
+ ],
/*
- 'reportlevel' => array
- (
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => 'errors|warnings|created|changed|unchanged',
- 'description' => 'combination of flags to limit the detailed output',
- ),
+ 'reportlevel' => array
+ (
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => 'errors|warnings|created|changed|unchanged',
+ 'description' => 'combination of flags to limit the detailed output',
+ ),
*/
- 'reconciliationkeys' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'name of the columns used to identify existing objects and update them, or create a new one',
- ],
- 'simulate' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '0',
- 'description' => 'If set to 1, then the load will not be executed, but the expected report will be produced',
- ],
- 'comment' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '',
- 'description' => 'Comment to be added into the change log',
- ],
- 'no_localize' =>
- [
- 'mandatory' => false,
- 'modes' => 'http,cli',
- 'default' => '0',
- 'description' => 'If set to 0, then header and values are supposed to be localized in the language of the logged in user. Set to 1 to use internal attribute codes and values (enums)',
- ],
+ 'reconciliationkeys' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'name of the columns used to identify existing objects and update them, or create a new one',
+ ],
+ 'simulate' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '0',
+ 'description' => 'If set to 1, then the load will not be executed, but the expected report will be produced',
+ ],
+ 'comment' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '',
+ 'description' => 'Comment to be added into the change log',
+ ],
+ 'no_localize' =>
+ [
+ 'mandatory' => false,
+ 'modes' => 'http,cli',
+ 'default' => '0',
+ 'description' => 'If set to 0, then header and values are supposed to be localized in the language of the logged in user. Set to 1 to use internal attribute codes and values (enums)',
+ ],
];
function UsageAndExit($oP)
{
- global $aPageParams;
- $bModeCLI = utils::IsModeCLI();
-
- $oP->p("USAGE:\n");
- foreach ($aPageParams as $sParam => $aParamData) {
- $aModes = explode(',', $aParamData['modes']);
- if ($bModeCLI) {
- if (in_array('cli', $aModes)) {
- $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
- $oP->p("$sParam = $sDesc");
- }
- } else {
- if (in_array('http', $aModes)) {
- $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
- $oP->p("$sParam = $sDesc");
- }
- }
- }
- $oP->output();
- exit;
+ global $aPageParams;
+ $bModeCLI = utils::IsModeCLI();
+
+ $oP->p("USAGE:\n");
+ foreach ($aPageParams as $sParam => $aParamData) {
+ $aModes = explode(',', $aParamData['modes']);
+ if ($bModeCLI) {
+ if (in_array('cli', $aModes)) {
+ $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
+ $oP->p("$sParam = $sDesc");
+ }
+ } else {
+ if (in_array('http', $aModes)) {
+ $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
+ $oP->p("$sParam = $sDesc");
+ }
+ }
+ }
+ $oP->output();
+ exit;
}
function ReadParam($oP, $sParam, $sSanitizationFilter = 'parameter')
{
- global $aPageParams;
- assert(isset($aPageParams[$sParam]));
- assert(!$aPageParams[$sParam]['mandatory']);
- $sValue = utils::ReadParam($sParam, $aPageParams[$sParam]['default'], true /* Allow CLI */, $sSanitizationFilter);
- return trim($sValue);
+ global $aPageParams;
+ assert(isset($aPageParams[$sParam]));
+ assert(!$aPageParams[$sParam]['mandatory']);
+ $sValue = utils::ReadParam($sParam, $aPageParams[$sParam]['default'], true /* Allow CLI */, $sSanitizationFilter);
+ return trim($sValue);
}
function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
{
- global $aPageParams;
- assert(isset($aPageParams[$sParam]));
- assert($aPageParams[$sParam]['mandatory']);
-
- $sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter);
- if (is_null($sValue)) {
- $oP->p("ERROR: Missing argument '$sParam'\n");
- UsageAndExit($oP);
- }
- return trim($sValue);
+ global $aPageParams;
+ assert(isset($aPageParams[$sParam]));
+ assert($aPageParams[$sParam]['mandatory']);
+
+ $sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter);
+ if (is_null($sValue)) {
+ $oP->p("ERROR: Missing argument '$sParam'\n");
+ UsageAndExit($oP);
+ }
+ return trim($sValue);
}
/////////////////////////////////
@@ -206,599 +206,599 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
*/
$oCtx = new ContextTag(ContextTag::TAG_IMPORT);
if (utils::IsModeCLI()) {
- $oP = new CLIPage("iTop - Bulk import");
- SetupUtils::CheckPhpAndExtensionsForCli($oP, -2);
+ $oP = new CLIPage("iTop - Bulk import");
+ SetupUtils::CheckPhpAndExtensionsForCli($oP, -2);
} else {
- $oP = new CSVPage("iTop - Bulk import");
+ $oP = new CSVPage("iTop - Bulk import");
}
try {
- utils::UseParamFile();
+ utils::UseParamFile();
} catch (Exception $e) {
- $oP->p("Error: ".$e->GetMessage());
- $oP->output();
- exit(-2);
+ $oP->p("Error: ".$e->GetMessage());
+ $oP->output();
+ exit(-2);
}
if (utils::IsModeCLI()) {
- // Next steps:
- // specific arguments: 'csvfile'
- //
- $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
- $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
- $sCsvFile = ReadMandatoryParam($oP, 'csvfile', 'raw_data');
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
- UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
- $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
- $oP->output();
- exit(-1);
- }
-
- if (!is_readable($sCsvFile)) {
- $oP->p("Input file could not be found or could not be read: '$sCsvFile'");
- $oP->output();
- exit(-1);
- }
- $sCSVData = file_get_contents($sCsvFile);
+ // Next steps:
+ // specific arguments: 'csvfile'
+ //
+ $sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
+ $sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
+ $sCsvFile = ReadMandatoryParam($oP, 'csvfile', 'raw_data');
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ UserRights::Login($sAuthUser); // Login & set the user's language
+ } else {
+ $oP->p("Access restricted or wrong credentials ('$sAuthUser')");
+ $oP->output();
+ exit(-1);
+ }
+
+ if (!is_readable($sCsvFile)) {
+ $oP->p("Input file could not be found or could not be read: '$sCsvFile'");
+ $oP->output();
+ exit(-1);
+ }
+ $sCSVData = file_get_contents($sCsvFile);
} else {
- require_once(APPROOT.'/application/loginwebpage.class.inc.php');
- LoginWebPage::ResetSession(true);
- $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
- if ($iRet !== LoginWebPage::EXIT_CODE_OK) {
- switch ($iRet) {
- case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
- $oP->p("Missing parameter 'auth_user'");
- break;
-
- case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
- $oP->p("Missing parameter 'auth_pwd'");
- break;
-
- case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
- $oP->p('Invalid login');
- break;
-
- case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
- $oP->p('Portal user is not allowed');
- break;
-
- case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
- $oP->p('This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)');
- break;
-
- default:
- $oP->p("Unknown authentication error (retCode=$iRet)");
- }
- $oP->output();
- exit -1;
- }
-
- $sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data');
+ require_once(APPROOT.'/application/loginwebpage.class.inc.php');
+ LoginWebPage::ResetSession(true);
+ $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
+ if ($iRet !== LoginWebPage::EXIT_CODE_OK) {
+ switch ($iRet) {
+ case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
+ $oP->p("Missing parameter 'auth_user'");
+ break;
+
+ case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
+ $oP->p("Missing parameter 'auth_pwd'");
+ break;
+
+ case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
+ $oP->p('Invalid login');
+ break;
+
+ case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
+ $oP->p('Portal user is not allowed');
+ break;
+
+ case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
+ $oP->p('This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)');
+ break;
+
+ default:
+ $oP->p("Unknown authentication error (retCode=$iRet)");
+ }
+ $oP->output();
+ exit -1;
+ }
+
+ $sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data');
}
try {
- $aWarnings = [];
-
- //////////////////////////////////////////////////
- //
- // Read parameters
- //
- $sClass = ReadMandatoryParam($oP, 'class', 'raw_data'); // do not filter as a valid class, we want to produce the report "wrong class" ourselves
- $sSep = ReadParam($oP, 'separator', 'raw_data');
- $sQualifier = ReadParam($oP, 'qualifier', 'raw_data');
- $sCharSet = ReadParam($oP, 'charset', 'raw_data');
- $sDateFormat = ReadParam($oP, 'date_format', 'raw_data');
- if (strpos($sDateFormat, '%') !== false) {
- $sDateFormat = utils::DateTimeFormatToPHP($sDateFormat);
- }
- $sOutput = ReadParam($oP, 'output', 'string');
- $sReconcKeys = ReadParam($oP, 'reconciliationkeys', 'raw_data');
- $sSimulate = ReadParam($oP, 'simulate');
- $sComment = ReadParam($oP, 'comment', 'raw_data');
- $bLocalize = (ReadParam($oP, 'no_localize') != 1);
-
- if (strtolower(trim($sSep)) == 'tab') {
- $sSep = "\t";
- }
-
- //////////////////////////////////////////////////
- //
- // Check parameters format/consistency
- //
- if (strlen($sCSVData) == 0) {
- throw new BulkLoadException("Missing data - at least one line is expected");
- }
-
- if (!MetaModel::IsValidClass($sClass)) {
- throw new BulkLoadException("Unknown class: '$sClass'");
- }
-
- if (strlen($sSep) > 1) {
- throw new BulkLoadException("Separator is limited to one character, found '$sSep'");
- }
-
- if (strlen($sQualifier) > 1) {
- throw new BulkLoadException("Text qualifier is limited to one character, found '$sQualifier'");
- }
-
- if (!in_array($sOutput, ['retcode', 'summary', 'details'])) {
- throw new BulkLoadException("Unknown output format: '$sOutput'");
- }
-
- if (strlen($sDateFormat) == 0) {
- $sDateFormat = null;
- }
-
- if ($sCharSet == '') {
- $sCharSet = MetaModel::GetConfig()->Get('csv_file_default_charset');
- }
-
- if ($sSimulate == '1') {
- $bSimulate = true;
- } else {
- $bSimulate = false;
- }
-
- if (($sOutput == "summary") || ($sOutput == 'details')) {
- $oP->add_comment("Output format: ".$sOutput);
- $oP->add_comment("Class: ".$sClass);
- $oP->add_comment("Separator: ".$sSep);
- $oP->add_comment("Qualifier: ".$sQualifier);
- $oP->add_comment("Charset Encoding:".$sCharSet);
- if (($sDateFormat !== null) && (strlen($sDateFormat) > 0)) {
- $oP->add_comment("Date and time format: '$sDateFormat'");
- $oDateTimeFormat = new DateTimeFormat($sDateFormat);
- $sDateOnlyFormat = $oDateTimeFormat->ToDateFormat();
- $oP->add_comment("Date format: '$sDateOnlyFormat'");
- } else {
- $oP->add_comment("Date format: ");
- }
- $oP->add_comment("Localize: ".($bLocalize ? 'yes' : 'no'));
- $oP->add_comment("Data Size: ".strlen($sCSVData));
- }
- //////////////////////////////////////////////////
- //
- // Security
- //
- if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY)) {
- throw new SecurityException(Dict::Format('UI:Error:BulkModifyNotAllowedOn_Class', $sClass));
- }
-
- //////////////////////////////////////////////////
- //
- // Create an index of the known column names (in lower case)
- // If data is localized, an array of => array of (several leads to ambiguity)
- // Otherwise an array of => array of (1 element by construction)
- //
- // Examples (localized in french):
- // 'lieu' => 'location_id'
- // 'lieu->name' => 'location_id->name'
- //
- // Note: it may happen that an external field has the same label as the external key
- // in that case, we consider that the external key has precedence
- //
- $aKnownColumnNames = ['id' => ['id']];
- foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
- if ($bLocalize) {
- $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCode));
- } else {
- $sColName = strtolower($sAttCode);
- }
- if (!$oAttDef->IsExternalField() || !array_key_exists($sColName, $aKnownColumnNames)) {
- $aKnownColumnNames[$sColName][] = $sAttCode;
- }
- if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
- $sRemoteClass = $oAttDef->GetTargetClass();
- foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
- $sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
- if ($bLocalize) {
- $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCodeEx));
- } else {
- $sColName = strtolower($sAttCodeEx);
- }
- if (!array_key_exists($sColName, $aKnownColumnNames)) {
- $aKnownColumnNames[$sColName][] = $sAttCodeEx;
- }
- }
- }
- }
-
- //print_r($aKnownColumnNames);
- //print_r(array_keys($aKnownColumnNames));
- //exit;
-
- //////////////////////////////////////////////////
- //
- // Parse first line, check attributes, analyse the request
- //
- if ($sCharSet == 'UTF-8') {
- // Remove the BOM if any
- if (substr($sCSVData, 0, 3) == UTF8_BOM) {
- $sCSVData = substr($sCSVData, 3);
- }
- // Clean the input
- // Todo: warn the user if some characters are lost/substituted
- $sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
- } else {
- $sUTF8Data = iconv($sCharSet, 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
- }
- $oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier);
-
- // Limitation: as the attribute list is in the first line, we can not match external key by a third-party attribute
- $aRawFieldList = $oCSVParser->ListFields();
- $iColCount = count($aRawFieldList);
-
- // Translate into internal names
- $aFieldList = [];
- foreach ($aRawFieldList as $iFieldId => $sFieldName) {
- $sFieldName = trim($sFieldName);
- $aMatches = [];
- if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches)) {
- // Ignore any trailing "star" (*) that simply indicates a mandatory field
- $sFieldName = $aMatches[1];
- } elseif (preg_match('/^(.+)\*->(.+)$/', $sFieldName, $aMatches)) {
- // Remove any trailing "star" character before the arrow (->)
- // A star character at the end can be used to indicate a mandatory field
- $sFieldName = $aMatches[1].'->'.$aMatches[2];
- }
- if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames)) {
- $aColumns = $aKnownColumnNames[strtolower($sFieldName)];
- if (count($aColumns) > 1) {
- $aCompetitors = [];
- foreach ($aColumns as $sAttCodeEx) {
- $aCompetitors[] = $sAttCodeEx;
- }
- $aWarnings[] = "Input column '$sFieldName' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
- }
- $aFieldList[$iFieldId] = $aColumns[0];
- } else {
- // Protect against XSS injection
- $sSafeName = str_replace(['"', '<', '>'], '', $sFieldName);
- throw new BulkLoadException("Unknown column: '$sSafeName'. Possible columns: ".implode(', ', array_keys($aKnownColumnNames)));
- }
- }
- // Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
-
- $aAttList = [];
- $aExtKeys = [];
- foreach ($aFieldList as $iFieldId => $sFieldName) {
- $aMatches = [];
- if (preg_match('/^(.+)->(.+)$/', trim($sFieldName), $aMatches)) {
- // The column has been specified as "extkey->attcode"
- //
- $sExtKeyAttCode = $aMatches[1];
- $sRemoteAttCode = $aMatches[2];
- if (!MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode)) {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown attribute '$sExtKeyAttCode' (class: '$sClass')");
- }
- $oAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
- if (!$oAtt->IsExternalKey()) {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Not an external key '$sExtKeyAttCode' (class: '$sClass')");
- }
- $sTargetClass = $oAtt->GetTargetClass();
- if (!MetaModel::IsValidAttCode($sTargetClass, $sRemoteAttCode)) {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown attribute '$sRemoteAttCode' (key: '$sExtKeyAttCode', class: '$sTargetClass')");
- }
- $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
- } elseif ($sFieldName == 'id') {
- $aAttList[$sFieldName] = $iFieldId;
- } else {
- // The column has been specified as "attcode"
- //
- if (!MetaModel::IsValidAttCode($sClass, $sFieldName)) {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown attribute '$sFieldName' (class: '$sClass')");
- }
- $oAtt = MetaModel::GetAttributeDef($sClass, $sFieldName);
- if ($oAtt->IsExternalKey()) {
- $aExtKeys[$sFieldName]['id'] = $iFieldId;
- $aAttList[$sFieldName] = $iFieldId;
- } elseif ($oAtt->IsExternalField()) {
- $sExtKeyAttCode = $oAtt->GetKeyAttCode();
- $sRemoteAttCode = $oAtt->GetExtAttCode();
- $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
- } else {
- $aAttList[$sFieldName] = $iFieldId;
- }
- }
- }
-
- // Make sure there are some reconciliation keys
- //
- if (empty($sReconcKeys)) {
- $aReconcSpec = [];
- // Base reconciliation scheme on the default one
- // The reconciliation attributes not present in the data will be ignored
- foreach (MetaModel::GetReconcKeys($sClass) as $sReconcKeyAttCode) {
- if (in_array($sReconcKeyAttCode, $aFieldList)) {
- if ($bLocalize) {
- $aReconcSpec[] = MetaModel::GetLabel($sClass, $sReconcKeyAttCode);
- } else {
- $aReconcSpec[] = $sReconcKeyAttCode;
- }
- }
- }
- if (count($aReconcSpec) == 0) {
- throw new BulkLoadException("No reconciliation scheme could be defined, please add a column corresponding to one defined reconciliation key (class: '$sClass', reconciliation:".implode(',', MetaModel::GetReconcKeys($sClass)).")");
- }
- $sReconcKeys = implode(',', $aReconcSpec);
- }
-
- // Interpret the list of reconciliation keys
- //
- $aFinalReconcilKeys = [];
- $aReconcilKeysReport = [];
- foreach (explode(',', $sReconcKeys) as $sReconcKey) {
- $sReconcKey = trim($sReconcKey);
- if (empty($sReconcKey)) {
- continue;
- } // skip empty spec
-
- if (array_key_exists(strtolower($sReconcKey), $aKnownColumnNames)) {
- // Translate from a translated name to codes
- $aColumns = $aKnownColumnNames[strtolower($sReconcKey)];
- if (count($aColumns) > 1) {
- $aCompetitors = [];
- foreach ($aColumns as $sAttCodeEx) {
- $aCompetitors[] = $sAttCodeEx;
- }
- $aWarnings[] = "Reconciliation key '$sReconcKey' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
- }
- $sReconcKey = $aColumns[0];
- } else {
- // Protect against XSS injection
- $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
- throw new BulkLoadException("Unknown reconciliation key: '$sSafeName'");
- }
-
- // Check that the reconciliation key is either a given column, or an external key
- if (!in_array($sReconcKey, $aFieldList)) {
- if (!array_key_exists($sReconcKey, $aExtKeys)) {
- // Protect against XSS injection
- $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
- throw new BulkLoadException("Reconciliation key not found in the input columns: '$sSafeName'");
- }
- }
-
- if (preg_match('/^(.+)->(.+)$/', trim($sReconcKey), $aMatches)) {
- // The column has been specified as "extkey->attcode"
- //
- $sExtKeyAttCode = $aMatches[1];
- $sRemoteAttCode = $aMatches[2];
-
- $aFinalReconcilKeys[] = $sExtKeyAttCode;
- $aReconcilKeysReport[$sExtKeyAttCode][] = $sRemoteAttCode;
- } else {
- if (!MetaModel::IsValidAttCode($sClass, $sReconcKey) && $sReconcKey != 'id') {
- // Safety net - should not happen now that column names are checked against known names
- throw new BulkLoadException("Unknown reconciliation attribute '$sReconcKey' (class: '$sClass')");
- }
- if ($sReconcKey == 'id') {
- $aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey] = [];
- } else {
- $oAtt = MetaModel::GetAttributeDef($sClass, $sReconcKey);
- if ($oAtt->IsExternalKey()) {
- $aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey][] = 'id';
- } elseif ($oAtt->IsExternalField()) {
- $sReconcAttCode = $oAtt->GetKeyAttCode();
- $sReconcKeyReport = "$sReconcAttCode ($sReconcKey)";
-
- $aFinalReconcilKeys[] = $sReconcAttCode;
- $aReconcilKeysReport[$sReconcAttCode][] = $sReconcKeyReport;
- } else {
- $aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey] = [];
- }
- }
- }
- }
-
- //////////////////////////////////////////////////
- //
- // Go for parsing and interpretation
- //
-
- $aData = $oCSVParser->ToArray();
- $iLineCount = count($aData);
-
- if (($sOutput == "summary") || ($sOutput == 'details')) {
- $oP->add_comment("Data Lines: ".$iLineCount);
- $oP->add_comment("Simulate: ".($bSimulate ? '1' : '0'));
- $oP->add_comment("Columns: ".implode(', ', $aFieldList));
-
- $aReconciliationReport = [];
- foreach ($aReconcilKeysReport as $sKey => $aKeyDetails) {
- if (count($aKeyDetails) > 0) {
- $aReconciliationReport[] = $sKey.' ('.implode(',', $aKeyDetails).')';
- } else {
- $aReconciliationReport[] = $sKey;
- }
- }
- $oP->add_comment("Reconciliation Keys: ".implode(', ', $aReconciliationReport));
-
- foreach ($aWarnings as $sWarning) {
- $oP->add_comment("Warning: ".$sWarning);
- }
- }
-
- $oBulk = new BulkChange(
- $sClass,
- $aData,
- $aAttList,
- $aExtKeys,
- $aFinalReconcilKeys,
- null, // synchro scope
- null, // on delete
- $sDateFormat,
- $bLocalize
- );
-
- if ($bSimulate) {
- $oMyChange = null;
- } else {
- if (strlen($sComment) > 0) {
- $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV) - '.$sComment;
- } else {
- $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV)';
- }
- CMDBObject::SetCurrentChangeFromParams($sMoreInfo, 'csv-import.php');
- $oMyChange = CMDBObject::GetCurrentChange();
- }
-
- $aRes = $oBulk->Process($oMyChange);
-
- //////////////////////////////////////////////////
- //
- // Compute statistics
- //
- $iCountErrors = 0;
- $iCountWarnings = 0;
- $iCountCreations = 0;
- $iCountUpdates = 0;
- $iCountUnchanged = 0;
- foreach ($aRes as $iRow => $aRowData) {
- $bWritten = false;
-
- $oStatus = $aRowData["__STATUS__"];
- switch (get_class($oStatus)) {
- case 'RowStatus_NoChange':
- $iCountUnchanged++;
- break;
- case 'RowStatus_Modify':
- $iCountUpdates++;
- $bWritten = true;
- break;
- case 'RowStatus_NewObj':
- $iCountCreations++;
- $bWritten = true;
- break;
- case 'RowStatus_Issue':
- $iCountErrors++;
- break;
- }
-
- if ($bWritten) {
- // Something has been done, still there may be some issues to report
- foreach ($aRowData as $key => $value) {
- if (!is_object($value)) {
- continue;
- }
-
- switch (get_class($value)) {
- case 'CellStatus_Void':
- case 'CellStatus_Modify':
- break;
- case 'CellStatus_Issue':
- case 'CellStatus_SearchIssue':
- case 'CellStatus_NullIssue':
- case 'CellStatus_Ambiguous':
- $iCountWarnings++;
- break;
- }
- }
- }
- }
-
- //////////////////////////////////////////////////
- //
- // Summary of settings and results
- //
- if ($sOutput == 'retcode') {
- $oP->add($iCountErrors);
- }
-
- if (($sOutput == "summary") || ($sOutput == 'details')) {
- $oP->add_comment("Change tracking comment: ".$sComment);
- $oP->add_comment("Issues: ".$iCountErrors);
- $oP->add_comment("Warnings: ".$iCountWarnings);
- $oP->add_comment("Created: ".$iCountCreations);
- $oP->add_comment("Updated: ".$iCountUpdates);
- $oP->add_comment("Unchanged: ".$iCountUnchanged);
- }
-
- if ($sOutput == 'details') {
- // Setup result presentation
- //
- $aDisplayConfig = [];
- $aDisplayConfig["__LINE__"] = ["label" => "Line", "description" => ""];
- $aDisplayConfig["__STATUS__"] = ["label" => "Status", "description" => ""];
- $aDisplayConfig["__OBJECT_CLASS__"] = ["label" => "Object Class", "description" => ""];
- $aDisplayConfig["__OBJECT_ID__"] = ["label" => "Object Id", "description" => ""];
- foreach ($aExtKeys as $sExtKeyAttCode => $aRemoteAtt) {
- $sLabel = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode)->GetLabel();
- $aDisplayConfig["$sExtKeyAttCode"] = ["label" => $sExtKeyAttCode, "description" => $sLabel." - ext key"];
- }
- foreach ($aFinalReconcilKeys as $iCol => $sAttCode) {
- // $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
- // $aDisplayConfig["$iCol"] = array("label"=>"$sLabel", "description"=>"");
- }
- foreach ($aAttList as $sAttCode => $iCol) {
- if ($sAttCode == 'id') {
- $sLabel = Dict::S('UI:CSVImport:idField');
-
- $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
- } else {
- $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
- $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
- }
- }
-
- $aResultDisp = []; // to be displayed
- foreach ($aRes as $iRow => $aRowData) {
- $aRowDisp = [];
- $aRowDisp["__LINE__"] = $iRow;
- if (is_object($aRowData["__STATUS__"])) {
- $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription();
- } else {
- $aRowDisp["__STATUS__"] = "*No status available*";
- }
- if (isset($aRowData["finalclass"]) && isset($aRowData["id"])) {
- $aRowDisp["__OBJECT_CLASS__"] = $aRowData["finalclass"];
- $aRowDisp["__OBJECT_ID__"] = $aRowData["id"]->GetCLIValue();
- } else {
- $aRowDisp["__OBJECT_CLASS__"] = "n/a";
- $aRowDisp["__OBJECT_ID__"] = "n/a";
- }
- foreach ($aRowData as $key => $value) {
- $sKey = (string) $key;
-
- if ($sKey == '__STATUS__') {
- continue;
- }
- //__ERRORS__ used by tests only
- if ($sKey == '__ERRORS__') {
- continue;
- }
- if ($sKey == 'finalclass') {
- continue;
- }
- if ($sKey == 'id') {
- continue;
- }
-
- if (is_object($value)) {
- $aRowDisp["$sKey"] = $value->GetCLIValueAndDescription();
- } else {
- $aRowDisp["$sKey"] = $value;
- }
- }
- $aResultDisp[$iRow] = $aRowDisp;
- }
- $oP->table($aDisplayConfig, $aResultDisp);
- }
+ $aWarnings = [];
+
+ //////////////////////////////////////////////////
+ //
+ // Read parameters
+ //
+ $sClass = ReadMandatoryParam($oP, 'class', 'raw_data'); // do not filter as a valid class, we want to produce the report "wrong class" ourselves
+ $sSep = ReadParam($oP, 'separator', 'raw_data');
+ $sQualifier = ReadParam($oP, 'qualifier', 'raw_data');
+ $sCharSet = ReadParam($oP, 'charset', 'raw_data');
+ $sDateFormat = ReadParam($oP, 'date_format', 'raw_data');
+ if (strpos($sDateFormat, '%') !== false) {
+ $sDateFormat = utils::DateTimeFormatToPHP($sDateFormat);
+ }
+ $sOutput = ReadParam($oP, 'output', 'string');
+ $sReconcKeys = ReadParam($oP, 'reconciliationkeys', 'raw_data');
+ $sSimulate = ReadParam($oP, 'simulate');
+ $sComment = ReadParam($oP, 'comment', 'raw_data');
+ $bLocalize = (ReadParam($oP, 'no_localize') != 1);
+
+ if (strtolower(trim($sSep)) == 'tab') {
+ $sSep = "\t";
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // Check parameters format/consistency
+ //
+ if (strlen($sCSVData) == 0) {
+ throw new BulkLoadException("Missing data - at least one line is expected");
+ }
+
+ if (!MetaModel::IsValidClass($sClass)) {
+ throw new BulkLoadException("Unknown class: '$sClass'");
+ }
+
+ if (strlen($sSep) > 1) {
+ throw new BulkLoadException("Separator is limited to one character, found '$sSep'");
+ }
+
+ if (strlen($sQualifier) > 1) {
+ throw new BulkLoadException("Text qualifier is limited to one character, found '$sQualifier'");
+ }
+
+ if (!in_array($sOutput, ['retcode', 'summary', 'details'])) {
+ throw new BulkLoadException("Unknown output format: '$sOutput'");
+ }
+
+ if (strlen($sDateFormat) == 0) {
+ $sDateFormat = null;
+ }
+
+ if ($sCharSet == '') {
+ $sCharSet = MetaModel::GetConfig()->Get('csv_file_default_charset');
+ }
+
+ if ($sSimulate == '1') {
+ $bSimulate = true;
+ } else {
+ $bSimulate = false;
+ }
+
+ if (($sOutput == "summary") || ($sOutput == 'details')) {
+ $oP->add_comment("Output format: ".$sOutput);
+ $oP->add_comment("Class: ".$sClass);
+ $oP->add_comment("Separator: ".$sSep);
+ $oP->add_comment("Qualifier: ".$sQualifier);
+ $oP->add_comment("Charset Encoding:".$sCharSet);
+ if (($sDateFormat !== null) && (strlen($sDateFormat) > 0)) {
+ $oP->add_comment("Date and time format: '$sDateFormat'");
+ $oDateTimeFormat = new DateTimeFormat($sDateFormat);
+ $sDateOnlyFormat = $oDateTimeFormat->ToDateFormat();
+ $oP->add_comment("Date format: '$sDateOnlyFormat'");
+ } else {
+ $oP->add_comment("Date format: ");
+ }
+ $oP->add_comment("Localize: ".($bLocalize ? 'yes' : 'no'));
+ $oP->add_comment("Data Size: ".strlen($sCSVData));
+ }
+ //////////////////////////////////////////////////
+ //
+ // Security
+ //
+ if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY)) {
+ throw new SecurityException(Dict::Format('UI:Error:BulkModifyNotAllowedOn_Class', $sClass));
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // Create an index of the known column names (in lower case)
+ // If data is localized, an array of => array of (several leads to ambiguity)
+ // Otherwise an array of => array of (1 element by construction)
+ //
+ // Examples (localized in french):
+ // 'lieu' => 'location_id'
+ // 'lieu->name' => 'location_id->name'
+ //
+ // Note: it may happen that an external field has the same label as the external key
+ // in that case, we consider that the external key has precedence
+ //
+ $aKnownColumnNames = ['id' => ['id']];
+ foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
+ if ($bLocalize) {
+ $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCode));
+ } else {
+ $sColName = strtolower($sAttCode);
+ }
+ if (!$oAttDef->IsExternalField() || !array_key_exists($sColName, $aKnownColumnNames)) {
+ $aKnownColumnNames[$sColName][] = $sAttCode;
+ }
+ if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
+ $sRemoteClass = $oAttDef->GetTargetClass();
+ foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
+ $sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
+ if ($bLocalize) {
+ $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCodeEx));
+ } else {
+ $sColName = strtolower($sAttCodeEx);
+ }
+ if (!array_key_exists($sColName, $aKnownColumnNames)) {
+ $aKnownColumnNames[$sColName][] = $sAttCodeEx;
+ }
+ }
+ }
+ }
+
+ //print_r($aKnownColumnNames);
+ //print_r(array_keys($aKnownColumnNames));
+ //exit;
+
+ //////////////////////////////////////////////////
+ //
+ // Parse first line, check attributes, analyse the request
+ //
+ if ($sCharSet == 'UTF-8') {
+ // Remove the BOM if any
+ if (substr($sCSVData, 0, 3) == UTF8_BOM) {
+ $sCSVData = substr($sCSVData, 3);
+ }
+ // Clean the input
+ // Todo: warn the user if some characters are lost/substituted
+ $sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
+ } else {
+ $sUTF8Data = iconv($sCharSet, 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
+ }
+ $oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier);
+
+ // Limitation: as the attribute list is in the first line, we can not match external key by a third-party attribute
+ $aRawFieldList = $oCSVParser->ListFields();
+ $iColCount = count($aRawFieldList);
+
+ // Translate into internal names
+ $aFieldList = [];
+ foreach ($aRawFieldList as $iFieldId => $sFieldName) {
+ $sFieldName = trim($sFieldName);
+ $aMatches = [];
+ if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches)) {
+ // Ignore any trailing "star" (*) that simply indicates a mandatory field
+ $sFieldName = $aMatches[1];
+ } elseif (preg_match('/^(.+)\*->(.+)$/', $sFieldName, $aMatches)) {
+ // Remove any trailing "star" character before the arrow (->)
+ // A star character at the end can be used to indicate a mandatory field
+ $sFieldName = $aMatches[1].'->'.$aMatches[2];
+ }
+ if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames)) {
+ $aColumns = $aKnownColumnNames[strtolower($sFieldName)];
+ if (count($aColumns) > 1) {
+ $aCompetitors = [];
+ foreach ($aColumns as $sAttCodeEx) {
+ $aCompetitors[] = $sAttCodeEx;
+ }
+ $aWarnings[] = "Input column '$sFieldName' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
+ }
+ $aFieldList[$iFieldId] = $aColumns[0];
+ } else {
+ // Protect against XSS injection
+ $sSafeName = str_replace(['"', '<', '>'], '', $sFieldName);
+ throw new BulkLoadException("Unknown column: '$sSafeName'. Possible columns: ".implode(', ', array_keys($aKnownColumnNames)));
+ }
+ }
+ // Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
+
+ $aAttList = [];
+ $aExtKeys = [];
+ foreach ($aFieldList as $iFieldId => $sFieldName) {
+ $aMatches = [];
+ if (preg_match('/^(.+)->(.+)$/', trim($sFieldName), $aMatches)) {
+ // The column has been specified as "extkey->attcode"
+ //
+ $sExtKeyAttCode = $aMatches[1];
+ $sRemoteAttCode = $aMatches[2];
+ if (!MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode)) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown attribute '$sExtKeyAttCode' (class: '$sClass')");
+ }
+ $oAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
+ if (!$oAtt->IsExternalKey()) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Not an external key '$sExtKeyAttCode' (class: '$sClass')");
+ }
+ $sTargetClass = $oAtt->GetTargetClass();
+ if (!MetaModel::IsValidAttCode($sTargetClass, $sRemoteAttCode)) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown attribute '$sRemoteAttCode' (key: '$sExtKeyAttCode', class: '$sTargetClass')");
+ }
+ $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
+ } elseif ($sFieldName == 'id') {
+ $aAttList[$sFieldName] = $iFieldId;
+ } else {
+ // The column has been specified as "attcode"
+ //
+ if (!MetaModel::IsValidAttCode($sClass, $sFieldName)) {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown attribute '$sFieldName' (class: '$sClass')");
+ }
+ $oAtt = MetaModel::GetAttributeDef($sClass, $sFieldName);
+ if ($oAtt->IsExternalKey()) {
+ $aExtKeys[$sFieldName]['id'] = $iFieldId;
+ $aAttList[$sFieldName] = $iFieldId;
+ } elseif ($oAtt->IsExternalField()) {
+ $sExtKeyAttCode = $oAtt->GetKeyAttCode();
+ $sRemoteAttCode = $oAtt->GetExtAttCode();
+ $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
+ } else {
+ $aAttList[$sFieldName] = $iFieldId;
+ }
+ }
+ }
+
+ // Make sure there are some reconciliation keys
+ //
+ if (empty($sReconcKeys)) {
+ $aReconcSpec = [];
+ // Base reconciliation scheme on the default one
+ // The reconciliation attributes not present in the data will be ignored
+ foreach (MetaModel::GetReconcKeys($sClass) as $sReconcKeyAttCode) {
+ if (in_array($sReconcKeyAttCode, $aFieldList)) {
+ if ($bLocalize) {
+ $aReconcSpec[] = MetaModel::GetLabel($sClass, $sReconcKeyAttCode);
+ } else {
+ $aReconcSpec[] = $sReconcKeyAttCode;
+ }
+ }
+ }
+ if (count($aReconcSpec) == 0) {
+ throw new BulkLoadException("No reconciliation scheme could be defined, please add a column corresponding to one defined reconciliation key (class: '$sClass', reconciliation:".implode(',', MetaModel::GetReconcKeys($sClass)).")");
+ }
+ $sReconcKeys = implode(',', $aReconcSpec);
+ }
+
+ // Interpret the list of reconciliation keys
+ //
+ $aFinalReconcilKeys = [];
+ $aReconcilKeysReport = [];
+ foreach (explode(',', $sReconcKeys) as $sReconcKey) {
+ $sReconcKey = trim($sReconcKey);
+ if (empty($sReconcKey)) {
+ continue;
+ } // skip empty spec
+
+ if (array_key_exists(strtolower($sReconcKey), $aKnownColumnNames)) {
+ // Translate from a translated name to codes
+ $aColumns = $aKnownColumnNames[strtolower($sReconcKey)];
+ if (count($aColumns) > 1) {
+ $aCompetitors = [];
+ foreach ($aColumns as $sAttCodeEx) {
+ $aCompetitors[] = $sAttCodeEx;
+ }
+ $aWarnings[] = "Reconciliation key '$sReconcKey' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
+ }
+ $sReconcKey = $aColumns[0];
+ } else {
+ // Protect against XSS injection
+ $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
+ throw new BulkLoadException("Unknown reconciliation key: '$sSafeName'");
+ }
+
+ // Check that the reconciliation key is either a given column, or an external key
+ if (!in_array($sReconcKey, $aFieldList)) {
+ if (!array_key_exists($sReconcKey, $aExtKeys)) {
+ // Protect against XSS injection
+ $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
+ throw new BulkLoadException("Reconciliation key not found in the input columns: '$sSafeName'");
+ }
+ }
+
+ if (preg_match('/^(.+)->(.+)$/', trim($sReconcKey), $aMatches)) {
+ // The column has been specified as "extkey->attcode"
+ //
+ $sExtKeyAttCode = $aMatches[1];
+ $sRemoteAttCode = $aMatches[2];
+
+ $aFinalReconcilKeys[] = $sExtKeyAttCode;
+ $aReconcilKeysReport[$sExtKeyAttCode][] = $sRemoteAttCode;
+ } else {
+ if (!MetaModel::IsValidAttCode($sClass, $sReconcKey) && $sReconcKey != 'id') {
+ // Safety net - should not happen now that column names are checked against known names
+ throw new BulkLoadException("Unknown reconciliation attribute '$sReconcKey' (class: '$sClass')");
+ }
+ if ($sReconcKey == 'id') {
+ $aFinalReconcilKeys[] = $sReconcKey;
+ $aReconcilKeysReport[$sReconcKey] = [];
+ } else {
+ $oAtt = MetaModel::GetAttributeDef($sClass, $sReconcKey);
+ if ($oAtt->IsExternalKey()) {
+ $aFinalReconcilKeys[] = $sReconcKey;
+ $aReconcilKeysReport[$sReconcKey][] = 'id';
+ } elseif ($oAtt->IsExternalField()) {
+ $sReconcAttCode = $oAtt->GetKeyAttCode();
+ $sReconcKeyReport = "$sReconcAttCode ($sReconcKey)";
+
+ $aFinalReconcilKeys[] = $sReconcAttCode;
+ $aReconcilKeysReport[$sReconcAttCode][] = $sReconcKeyReport;
+ } else {
+ $aFinalReconcilKeys[] = $sReconcKey;
+ $aReconcilKeysReport[$sReconcKey] = [];
+ }
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // Go for parsing and interpretation
+ //
+
+ $aData = $oCSVParser->ToArray();
+ $iLineCount = count($aData);
+
+ if (($sOutput == "summary") || ($sOutput == 'details')) {
+ $oP->add_comment("Data Lines: ".$iLineCount);
+ $oP->add_comment("Simulate: ".($bSimulate ? '1' : '0'));
+ $oP->add_comment("Columns: ".implode(', ', $aFieldList));
+
+ $aReconciliationReport = [];
+ foreach ($aReconcilKeysReport as $sKey => $aKeyDetails) {
+ if (count($aKeyDetails) > 0) {
+ $aReconciliationReport[] = $sKey.' ('.implode(',', $aKeyDetails).')';
+ } else {
+ $aReconciliationReport[] = $sKey;
+ }
+ }
+ $oP->add_comment("Reconciliation Keys: ".implode(', ', $aReconciliationReport));
+
+ foreach ($aWarnings as $sWarning) {
+ $oP->add_comment("Warning: ".$sWarning);
+ }
+ }
+
+ $oBulk = new BulkChange(
+ $sClass,
+ $aData,
+ $aAttList,
+ $aExtKeys,
+ $aFinalReconcilKeys,
+ null, // synchro scope
+ null, // on delete
+ $sDateFormat,
+ $bLocalize
+ );
+
+ if ($bSimulate) {
+ $oMyChange = null;
+ } else {
+ if (strlen($sComment) > 0) {
+ $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV) - '.$sComment;
+ } else {
+ $sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV)';
+ }
+ CMDBObject::SetCurrentChangeFromParams($sMoreInfo, 'csv-import.php');
+ $oMyChange = CMDBObject::GetCurrentChange();
+ }
+
+ $aRes = $oBulk->Process($oMyChange);
+
+ //////////////////////////////////////////////////
+ //
+ // Compute statistics
+ //
+ $iCountErrors = 0;
+ $iCountWarnings = 0;
+ $iCountCreations = 0;
+ $iCountUpdates = 0;
+ $iCountUnchanged = 0;
+ foreach ($aRes as $iRow => $aRowData) {
+ $bWritten = false;
+
+ $oStatus = $aRowData["__STATUS__"];
+ switch (get_class($oStatus)) {
+ case 'RowStatus_NoChange':
+ $iCountUnchanged++;
+ break;
+ case 'RowStatus_Modify':
+ $iCountUpdates++;
+ $bWritten = true;
+ break;
+ case 'RowStatus_NewObj':
+ $iCountCreations++;
+ $bWritten = true;
+ break;
+ case 'RowStatus_Issue':
+ $iCountErrors++;
+ break;
+ }
+
+ if ($bWritten) {
+ // Something has been done, still there may be some issues to report
+ foreach ($aRowData as $key => $value) {
+ if (!is_object($value)) {
+ continue;
+ }
+
+ switch (get_class($value)) {
+ case 'CellStatus_Void':
+ case 'CellStatus_Modify':
+ break;
+ case 'CellStatus_Issue':
+ case 'CellStatus_SearchIssue':
+ case 'CellStatus_NullIssue':
+ case 'CellStatus_Ambiguous':
+ $iCountWarnings++;
+ break;
+ }
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ //
+ // Summary of settings and results
+ //
+ if ($sOutput == 'retcode') {
+ $oP->add($iCountErrors);
+ }
+
+ if (($sOutput == "summary") || ($sOutput == 'details')) {
+ $oP->add_comment("Change tracking comment: ".$sComment);
+ $oP->add_comment("Issues: ".$iCountErrors);
+ $oP->add_comment("Warnings: ".$iCountWarnings);
+ $oP->add_comment("Created: ".$iCountCreations);
+ $oP->add_comment("Updated: ".$iCountUpdates);
+ $oP->add_comment("Unchanged: ".$iCountUnchanged);
+ }
+
+ if ($sOutput == 'details') {
+ // Setup result presentation
+ //
+ $aDisplayConfig = [];
+ $aDisplayConfig["__LINE__"] = ["label" => "Line", "description" => ""];
+ $aDisplayConfig["__STATUS__"] = ["label" => "Status", "description" => ""];
+ $aDisplayConfig["__OBJECT_CLASS__"] = ["label" => "Object Class", "description" => ""];
+ $aDisplayConfig["__OBJECT_ID__"] = ["label" => "Object Id", "description" => ""];
+ foreach ($aExtKeys as $sExtKeyAttCode => $aRemoteAtt) {
+ $sLabel = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode)->GetLabel();
+ $aDisplayConfig["$sExtKeyAttCode"] = ["label" => $sExtKeyAttCode, "description" => $sLabel." - ext key"];
+ }
+ foreach ($aFinalReconcilKeys as $iCol => $sAttCode) {
+ // $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
+ // $aDisplayConfig["$iCol"] = array("label"=>"$sLabel", "description"=>"");
+ }
+ foreach ($aAttList as $sAttCode => $iCol) {
+ if ($sAttCode == 'id') {
+ $sLabel = Dict::S('UI:CSVImport:idField');
+
+ $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
+ } else {
+ $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
+ $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
+ }
+ }
+
+ $aResultDisp = []; // to be displayed
+ foreach ($aRes as $iRow => $aRowData) {
+ $aRowDisp = [];
+ $aRowDisp["__LINE__"] = $iRow;
+ if (is_object($aRowData["__STATUS__"])) {
+ $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription();
+ } else {
+ $aRowDisp["__STATUS__"] = "*No status available*";
+ }
+ if (isset($aRowData["finalclass"]) && isset($aRowData["id"])) {
+ $aRowDisp["__OBJECT_CLASS__"] = $aRowData["finalclass"];
+ $aRowDisp["__OBJECT_ID__"] = $aRowData["id"]->GetCLIValue();
+ } else {
+ $aRowDisp["__OBJECT_CLASS__"] = "n/a";
+ $aRowDisp["__OBJECT_ID__"] = "n/a";
+ }
+ foreach ($aRowData as $key => $value) {
+ $sKey = (string) $key;
+
+ if ($sKey == '__STATUS__') {
+ continue;
+ }
+ //__ERRORS__ used by tests only
+ if ($sKey == '__ERRORS__') {
+ continue;
+ }
+ if ($sKey == 'finalclass') {
+ continue;
+ }
+ if ($sKey == 'id') {
+ continue;
+ }
+
+ if (is_object($value)) {
+ $aRowDisp["$sKey"] = $value->GetCLIValueAndDescription();
+ } else {
+ $aRowDisp["$sKey"] = $value;
+ }
+ }
+ $aResultDisp[$iRow] = $aRowDisp;
+ }
+ $oP->table($aDisplayConfig, $aResultDisp);
+ }
} catch (BulkLoadException $e) {
- $oP->add_comment($e->getMessage());
+ $oP->add_comment($e->getMessage());
} catch (SecurityException $e) {
- $oP->add_comment($e->getMessage());
+ $oP->add_comment($e->getMessage());
} catch (Exception $e) {
- $oP->add_comment((string)$e);
+ $oP->add_comment((string)$e);
}
$oP->output();
diff --git a/webservices/itop.wsdl.php b/webservices/itop.wsdl.php
index d818859889..03aaed90d7 100644
--- a/webservices/itop.wsdl.php
+++ b/webservices/itop.wsdl.php
@@ -19,17 +19,17 @@
*/
if (isset($_REQUEST['debug'])) {
- if ($_REQUEST['debug'] == 'text') {
- header('Content-Type: text/plain; charset=UTF-8');
- } else {
- header('Content-Type: application/xml; charset=UTF-8');
- }
+ if ($_REQUEST['debug'] == 'text') {
+ header('Content-Type: text/plain; charset=UTF-8');
+ } else {
+ header('Content-Type: application/xml; charset=UTF-8');
+ }
} else {
- // This is to make sure that the client will accept it....
- //
- header('Content-Type: application/xml; charset=UTF-8');
- ////header('Content-Disposition: attachment; filename="itop.wsdl"');
- header('Content-Disposition: online; filename="itop.wsdl"');
+ // This is to make sure that the client will accept it....
+ //
+ header('Content-Type: application/xml; charset=UTF-8');
+ ////header('Content-Disposition: attachment; filename="itop.wsdl"');
+ header('Content-Disposition: online; filename="itop.wsdl"');
}
require_once('../approot.inc.php');
@@ -45,20 +45,20 @@
$sServiceCategory = utils::ReadParam('service_category');
if (!empty($sServiceCategory)) {
- $sRawFile = WebServicesBase::GetWSDLContents($sServiceCategory);
+ $sRawFile = WebServicesBase::GetWSDLContents($sServiceCategory);
} else {
- $sRawFile = WebServicesBase::GetWSDLContents();
+ $sRawFile = WebServicesBase::GetWSDLContents();
}
$sServerURI = utils::GetAbsoluteUrlAppRoot().'webservices/soapserver.php';
if (!empty($sServiceCategory)) {
- $sServerURI .= "?service_category=".$sServiceCategory;
+ $sServerURI .= "?service_category=".$sServiceCategory;
}
$sFinalFile = str_replace(
- '___SOAP_SERVER_URI___',
- $sServerURI,
- $sRawFile
+ '___SOAP_SERVER_URI___',
+ $sServerURI,
+ $sRawFile
);
echo $sFinalFile;
diff --git a/webservices/itoprest.examples.php b/webservices/itoprest.examples.php
index 58d02c63d1..4d07389c19 100644
--- a/webservices/itoprest.examples.php
+++ b/webservices/itoprest.examples.php
@@ -38,99 +38,99 @@
*/
function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = [])
{
- // $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
+ // $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
- if (function_exists('curl_init')) {
- // If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
- // For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
- // by setting the SSLVERSION to 3 as done below.
- $aHTTPHeaders = [];
- if ($sOptionnalHeaders !== null) {
- $aHeaders = explode("\n", $sOptionnalHeaders);
- // N°3267 - Webservices: Fix optional headers not being taken into account
- // See https://www.php.net/curl_setopt CURLOPT_HTTPHEADER
- $aHTTPHeaders = [];
- foreach ($aHeaders as $sHeaderString) {
- $aHTTPHeaders[] = trim($sHeaderString);
- }
- }
- // Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
- $aOptions = [
- CURLOPT_RETURNTRANSFER => true, // return the content of the request
- CURLOPT_HEADER => false, // don't return the headers in the output
- CURLOPT_FOLLOWLOCATION => true, // follow redirects
- CURLOPT_ENCODING => "", // handle all encodings
- CURLOPT_USERAGENT => "spider", // who am i
- CURLOPT_AUTOREFERER => true, // set referer on redirect
- CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
- CURLOPT_TIMEOUT => 120, // timeout on response
- CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
- CURLOPT_SSL_VERIFYHOST => 0, // Disabled SSL Cert checks
- CURLOPT_SSL_VERIFYPEER => 0, // Disabled SSL Cert checks
- // SSLV3 (CURL_SSLVERSION_SSLv3 = 3) is now considered as obsolete/dangerous: http://disablessl3.com/#why
- // but it used to be a MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
- // CURLOPT_SSLVERSION => 3,
- CURLOPT_POST => count($aData),
- CURLOPT_POSTFIELDS => http_build_query($aData),
- CURLOPT_HTTPHEADER => $aHTTPHeaders,
- ];
- $aAllOptions = $aCurlOptions + $aOptions;
- $ch = curl_init($sUrl);
- curl_setopt_array($ch, $aAllOptions);
- $response = curl_exec($ch);
- $iErr = curl_errno($ch);
- $sErrMsg = curl_error($ch);
- if ($iErr !== 0) {
- throw new Exception("Problem opening URL: $sUrl, $sErrMsg");
- }
- if (is_array($aResponseHeaders)) {
- $aHeaders = curl_getinfo($ch);
- foreach ($aHeaders as $sCode => $sValue) {
- $sName = str_replace(' ', '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
- $aResponseHeaders[$sName] = $sValue;
- }
- }
- curl_close($ch);
- } else {
- // cURL is not available let's try with streams and fopen...
+ if (function_exists('curl_init')) {
+ // If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
+ // For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
+ // by setting the SSLVERSION to 3 as done below.
+ $aHTTPHeaders = [];
+ if ($sOptionnalHeaders !== null) {
+ $aHeaders = explode("\n", $sOptionnalHeaders);
+ // N°3267 - Webservices: Fix optional headers not being taken into account
+ // See https://www.php.net/curl_setopt CURLOPT_HTTPHEADER
+ $aHTTPHeaders = [];
+ foreach ($aHeaders as $sHeaderString) {
+ $aHTTPHeaders[] = trim($sHeaderString);
+ }
+ }
+ // Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
+ $aOptions = [
+ CURLOPT_RETURNTRANSFER => true, // return the content of the request
+ CURLOPT_HEADER => false, // don't return the headers in the output
+ CURLOPT_FOLLOWLOCATION => true, // follow redirects
+ CURLOPT_ENCODING => "", // handle all encodings
+ CURLOPT_USERAGENT => "spider", // who am i
+ CURLOPT_AUTOREFERER => true, // set referer on redirect
+ CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
+ CURLOPT_TIMEOUT => 120, // timeout on response
+ CURLOPT_MAXREDIRS => 10, // stop after 10 redirects
+ CURLOPT_SSL_VERIFYHOST => 0, // Disabled SSL Cert checks
+ CURLOPT_SSL_VERIFYPEER => 0, // Disabled SSL Cert checks
+ // SSLV3 (CURL_SSLVERSION_SSLv3 = 3) is now considered as obsolete/dangerous: http://disablessl3.com/#why
+ // but it used to be a MUST to prevent a strange SSL error: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
+ // CURLOPT_SSLVERSION => 3,
+ CURLOPT_POST => count($aData),
+ CURLOPT_POSTFIELDS => http_build_query($aData),
+ CURLOPT_HTTPHEADER => $aHTTPHeaders,
+ ];
+ $aAllOptions = $aCurlOptions + $aOptions;
+ $ch = curl_init($sUrl);
+ curl_setopt_array($ch, $aAllOptions);
+ $response = curl_exec($ch);
+ $iErr = curl_errno($ch);
+ $sErrMsg = curl_error($ch);
+ if ($iErr !== 0) {
+ throw new Exception("Problem opening URL: $sUrl, $sErrMsg");
+ }
+ if (is_array($aResponseHeaders)) {
+ $aHeaders = curl_getinfo($ch);
+ foreach ($aHeaders as $sCode => $sValue) {
+ $sName = str_replace(' ', '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
+ $aResponseHeaders[$sName] = $sValue;
+ }
+ }
+ curl_close($ch);
+ } else {
+ // cURL is not available let's try with streams and fopen...
- $sData = http_build_query($aData);
- $aParams = ['http' => [
- 'method' => 'POST',
- 'content' => $sData,
- 'header' => "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
- ]];
- if ($sOptionnalHeaders !== null) {
- $aParams['http']['header'] .= $sOptionnalHeaders;
- }
- $ctx = stream_context_create($aParams);
+ $sData = http_build_query($aData);
+ $aParams = ['http' => [
+ 'method' => 'POST',
+ 'content' => $sData,
+ 'header' => "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
+ ]];
+ if ($sOptionnalHeaders !== null) {
+ $aParams['http']['header'] .= $sOptionnalHeaders;
+ }
+ $ctx = stream_context_create($aParams);
- $fp = @fopen($sUrl, 'rb', false, $ctx);
- if (!$fp) {
- global $php_errormsg;
- if (isset($php_errormsg)) {
- throw new Exception("Wrong URL: $sUrl, $php_errormsg");
- } elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl')) {
- throw new Exception("Cannot connect to $sUrl: missing module 'openssl'");
- } else {
- throw new Exception("Wrong URL: $sUrl");
- }
- }
- $response = @stream_get_contents($fp);
- if ($response === false) {
- throw new Exception("Problem reading data from $sUrl, $php_errormsg");
- }
- if (is_array($aResponseHeaders)) {
- $aMeta = stream_get_meta_data($fp);
- $aHeaders = $aMeta['wrapper_data'];
- foreach ($aHeaders as $sHeaderString) {
- if (preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches)) {
- $aResponseHeaders[$aMatches[1]] = trim($aMatches[2]);
- }
- }
- }
- }
- return $response;
+ $fp = @fopen($sUrl, 'rb', false, $ctx);
+ if (!$fp) {
+ global $php_errormsg;
+ if (isset($php_errormsg)) {
+ throw new Exception("Wrong URL: $sUrl, $php_errormsg");
+ } elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl')) {
+ throw new Exception("Cannot connect to $sUrl: missing module 'openssl'");
+ } else {
+ throw new Exception("Wrong URL: $sUrl");
+ }
+ }
+ $response = @stream_get_contents($fp);
+ if ($response === false) {
+ throw new Exception("Problem reading data from $sUrl, $php_errormsg");
+ }
+ if (is_array($aResponseHeaders)) {
+ $aMeta = stream_get_meta_data($fp);
+ $aHeaders = $aMeta['wrapper_data'];
+ foreach ($aHeaders as $sHeaderString) {
+ if (preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches)) {
+ $aResponseHeaders[$aMatches[1]] = trim($aMatches[2]);
+ }
+ }
+ }
+ }
+ return $response;
}
////////////////////////////////////////////////////////////////////////////////
@@ -143,195 +143,195 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
//
$aOperations = [
- [
- 'operation' => 'list_operations', // operation code
- ],
- [
- 'operation' => 'core/create', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => [
- 'org_id' => "SELECT Organization WHERE name = 'Demo'",
- 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
- 'title' => 'issue blah',
- 'description' => 'something happened'
- ],
- ],
- [
- 'operation' => 'core/update', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest WHERE id=1',
- 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => [
- 'title' => 'Issue #'.rand(0, 100),
- 'contacts_list' => [
- [
- 'role' => 'fireman #'.rand(0, 100),
- 'contact_id' => ['finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'],
- ],
- ],
- ],
- ],
- // Rewrite the full CaseLog on an existing UserRequest with id=1, setting date and user (optional)
- [
- 'operation' => 'core/update',
- 'comment' => 'Synchronization from Client A', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest WHERE id=1',
- 'output_fields' => 'id, friendlyname, title',
- 'fields' => [
- 'public_log' => [
- 'items' => [
- 0 => [
- 'date' => '2001-02-01 23:59:59', //Allow to set the date of a true event, an alarm for eg.
- 'user_login' => 'Alarm monitoring', //Free text
- 'user_id' => 0, //0 is required for the user_login to be taken into account
- 'message' => 'This is 1st entry as an HTML formatted text',
- ],
- 1 => [
- 'date' => '2001-02-02 00:00:00', //If ommitted set automatically.
- 'user_login' => 'Alarm monitoring', //user=id=0 is missing so will be ignored
- 'message' => 'Second entry in text format:
+ [
+ 'operation' => 'list_operations', // operation code
+ ],
+ [
+ 'operation' => 'core/create', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'org_id' => "SELECT Organization WHERE name = 'Demo'",
+ 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
+ 'title' => 'issue blah',
+ 'description' => 'something happened'
+ ],
+ ],
+ [
+ 'operation' => 'core/update', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest WHERE id=1',
+ 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'title' => 'Issue #'.rand(0, 100),
+ 'contacts_list' => [
+ [
+ 'role' => 'fireman #'.rand(0, 100),
+ 'contact_id' => ['finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'],
+ ],
+ ],
+ ],
+ ],
+ // Rewrite the full CaseLog on an existing UserRequest with id=1, setting date and user (optional)
+ [
+ 'operation' => 'core/update',
+ 'comment' => 'Synchronization from Client A', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest WHERE id=1',
+ 'output_fields' => 'id, friendlyname, title',
+ 'fields' => [
+ 'public_log' => [
+ 'items' => [
+ 0 => [
+ 'date' => '2001-02-01 23:59:59', //Allow to set the date of a true event, an alarm for eg.
+ 'user_login' => 'Alarm monitoring', //Free text
+ 'user_id' => 0, //0 is required for the user_login to be taken into account
+ 'message' => 'This is 1st entry as an HTML formatted text',
+ ],
+ 1 => [
+ 'date' => '2001-02-02 00:00:00', //If ommitted set automatically.
+ 'user_login' => 'Alarm monitoring', //user=id=0 is missing so will be ignored
+ 'message' => 'Second entry in text format:
with new line, but format not specified, so treated as HTML!, user_id=0 missing, so user_login ignored',
- ],
- ],
- ],
- ],
- ],
- // Add a Text entry in the HTML CaseLog of the UserRequest with id=1, setting date and user (optional)
- [
- 'operation' => 'core/update', // operation code
- 'comment' => 'Synchronization from Alarm Monitoring', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 1, // object id or OQL
- 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
- // Example of adding an entry into a CaseLog on an existing UserRequest
- 'fields' => [
- 'public_log' => [
- 'add_item' => [
- 'user_login' => 'New Entry', //Free text
- 'user_id' => 0, //0 is required for the user_login to be taken into account
- 'format' => 'text', //If ommitted, source is expected to be HTML
- 'message' => 'This text is not HTML formatted with 3 lines:
+ ],
+ ],
+ ],
+ ],
+ ],
+ // Add a Text entry in the HTML CaseLog of the UserRequest with id=1, setting date and user (optional)
+ [
+ 'operation' => 'core/update', // operation code
+ 'comment' => 'Synchronization from Alarm Monitoring', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 1, // object id or OQL
+ 'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
+ // Example of adding an entry into a CaseLog on an existing UserRequest
+ 'fields' => [
+ 'public_log' => [
+ 'add_item' => [
+ 'user_login' => 'New Entry', //Free text
+ 'user_id' => 0, //0 is required for the user_login to be taken into account
+ 'format' => 'text', //If ommitted, source is expected to be HTML
+ 'message' => 'This text is not HTML formatted with 3 lines:
new line
3rd and last line',
- ],
- ],
- ],
- ],
- [
- 'operation' => 'core/get', // operation code
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest',
- 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
- ],
- [
- 'operation' => 'core/delete', // operation code
- 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 'SELECT UserRequest WHERE org_id = 2',
- 'simulate' => true,
- ],
- [
- 'operation' => 'core/apply_stimulus', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'key' => 1,
- 'stimulus' => 'ev_assign',
- // Values to set
- 'fields' => [
- 'team_id' => 15, // Helpdesk
- 'agent_id' => 9 // Jules Verne
- ],
- 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
- ],
- [
- 'operation' => 'core/get_related', // operation code
- 'class' => 'Server',
- 'key' => 'SELECT Server',
- 'relation' => 'impacts', // relation code
- 'depth' => 4, // max recursion depth
- ],
+ ],
+ ],
+ ],
+ ],
+ [
+ 'operation' => 'core/get', // operation code
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest',
+ 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
+ ],
+ [
+ 'operation' => 'core/delete', // operation code
+ 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 'SELECT UserRequest WHERE org_id = 2',
+ 'simulate' => true,
+ ],
+ [
+ 'operation' => 'core/apply_stimulus', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'key' => 1,
+ 'stimulus' => 'ev_assign',
+ // Values to set
+ 'fields' => [
+ 'team_id' => 15, // Helpdesk
+ 'agent_id' => 9 // Jules Verne
+ ],
+ 'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
+ ],
+ [
+ 'operation' => 'core/get_related', // operation code
+ 'class' => 'Server',
+ 'key' => 'SELECT Server',
+ 'relation' => 'impacts', // relation code
+ 'depth' => 4, // max recursion depth
+ ],
];
$aOperations = [
- [
- 'operation' => 'core/create', // operation code
- 'comment' => 'Automatic creation of attachment blah blah...', // comment recorded in the change tracking log
- 'class' => 'Attachment',
- 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => [
- 'item_class' => 'UserRequest',
- 'item_id' => 1,
- 'item_org_id' => 3,
- 'contents' => [
- 'data' => 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVChTfZHRDYMwDESzQ2fqhHx3C3ao+MkW/WlnaFxfzk7sEnE6JHJ+NgaKZN2zLHVN2ssfkae0Da7FQ5PRk/ve4Hcx19Ie6CEGuh/6vMgNhwanHVUNbt73lUDbYJ+6pg8b3+m2RehsVPdMXyvQY+OVkB+Rrv64lUjb3nq+aCA6v4leRqtfaIgimr53atBy9PlfUhoh3fFCNDmErv9FWR6ylBL5AREbmHBnFj5lAAAAAElFTkSuQmCC',
- 'filename' => 'myself.png',
- 'mimetype' => 'image/png'
- ],
- ],
- ],
- [
- 'operation' => 'core/get', // operation code
- 'class' => 'Attachment',
- 'key' => 'SELECT Attachment',
- 'output_fields' => '*',
- ]
+ [
+ 'operation' => 'core/create', // operation code
+ 'comment' => 'Automatic creation of attachment blah blah...', // comment recorded in the change tracking log
+ 'class' => 'Attachment',
+ 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'item_class' => 'UserRequest',
+ 'item_id' => 1,
+ 'item_org_id' => 3,
+ 'contents' => [
+ 'data' => 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVChTfZHRDYMwDESzQ2fqhHx3C3ao+MkW/WlnaFxfzk7sEnE6JHJ+NgaKZN2zLHVN2ssfkae0Da7FQ5PRk/ve4Hcx19Ie6CEGuh/6vMgNhwanHVUNbt73lUDbYJ+6pg8b3+m2RehsVPdMXyvQY+OVkB+Rrv64lUjb3nq+aCA6v4leRqtfaIgimr53atBy9PlfUhoh3fFCNDmErv9FWR6ylBL5AREbmHBnFj5lAAAAAElFTkSuQmCC',
+ 'filename' => 'myself.png',
+ 'mimetype' => 'image/png'
+ ],
+ ],
+ ],
+ [
+ 'operation' => 'core/get', // operation code
+ 'class' => 'Attachment',
+ 'key' => 'SELECT Attachment',
+ 'output_fields' => '*',
+ ]
];
$aOperations = [
- [
- 'operation' => 'core/update', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'Server',
- 'key' => 'SELECT Server WHERE name="Server1"',
- 'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => [
- 'description' => 'Issue #'.time(),
- ],
- ],
+ [
+ 'operation' => 'core/update', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'Server',
+ 'key' => 'SELECT Server WHERE name="Server1"',
+ 'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'description' => 'Issue #'.time(),
+ ],
+ ],
];
$aOperations = [
- [
- 'operation' => 'core/create', // operation code
- 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
- 'class' => 'UserRequest',
- 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
- // Values for the object to create
- 'fields' => [
- 'org_id' => "SELECT Organization WHERE name = 'Demo'",
- 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
- 'title' => 'issue blah',
- 'description' => 'something happened'
- ],
- ],
+ [
+ 'operation' => 'core/create', // operation code
+ 'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
+ 'class' => 'UserRequest',
+ 'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
+ // Values for the object to create
+ 'fields' => [
+ 'org_id' => "SELECT Organization WHERE name = 'Demo'",
+ 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
+ 'title' => 'issue blah',
+ 'description' => 'something happened'
+ ],
+ ],
];
$aXXXOperations = [
- [
- 'operation' => 'core/check_credentials', // operation code
- 'user' => 'admin',
- 'password' => 'admin',
- ],
+ [
+ 'operation' => 'core/check_credentials', // operation code
+ 'user' => 'admin',
+ 'password' => 'admin',
+ ],
];
$aDeleteOperations = [
- [
- 'operation' => 'core/delete', // operation code
- 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
- 'class' => 'Server',
- 'key' => 'SELECT Server',
- 'simulate' => false,
- ],
+ [
+ 'operation' => 'core/delete', // operation code
+ 'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
+ 'class' => 'Server',
+ 'key' => 'SELECT Server',
+ 'simulate' => false,
+ ],
];
if (false) {
- echo "Please edit the sample script and configure the server URL";
- exit;
+ echo "Please edit the sample script and configure the server URL";
+ exit;
} else {
- $sUrl = "https://localhost/itop/webservices/rest.php?version=1.3";
+ $sUrl = "https://localhost/itop/webservices/rest.php?version=1.3";
}
$aData = [];
@@ -339,26 +339,26 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
$aData['auth_pwd'] = 'rest';
foreach ($aOperations as $iOp => $aOperation) {
- echo "======================================\n";
- echo "Operation #$iOp: ".$aOperation['operation']."\n";
- $aData['json_data'] = json_encode($aOperation);
+ echo "======================================\n";
+ echo "Operation #$iOp: ".$aOperation['operation']."\n";
+ $aData['json_data'] = json_encode($aOperation);
- echo "--------------------------------------\n";
- echo "Input:\n";
- print_r($aOperation);
- $aResults = null;
- try {
- $response = DoPostRequest($sUrl, $aData);
- $aResults = json_decode($response);
- } catch (Exception $e) {
- $response = $e->getMessage();
- }
- if ($aResults) {
- echo "--------------------------------------\n";
- echo "Reply:\n";
- print_r($aResults);
- } else {
- echo "ERROR rest.php replied:\n";
- echo $response;
- }
+ echo "--------------------------------------\n";
+ echo "Input:\n";
+ print_r($aOperation);
+ $aResults = null;
+ try {
+ $response = DoPostRequest($sUrl, $aData);
+ $aResults = json_decode($response);
+ } catch (Exception $e) {
+ $response = $e->getMessage();
+ }
+ if ($aResults) {
+ echo "--------------------------------------\n";
+ echo "Reply:\n";
+ print_r($aResults);
+ } else {
+ echo "ERROR rest.php replied:\n";
+ echo $response;
+ }
}
diff --git a/webservices/itopsoap.examples.php b/webservices/itopsoap.examples.php
index b869dde628..6427a02843 100644
--- a/webservices/itopsoap.examples.php
+++ b/webservices/itopsoap.examples.php
@@ -33,100 +33,100 @@
ini_set("soap.wsdl_cache_enabled", "0");
$oSoapClient = new SoapClient(
- $sWsdlUri,
- [
- 'trace' => 1,
- 'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php
- ]
+ $sWsdlUri,
+ [
+ 'trace' => 1,
+ 'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php
+ ]
);
try {
- // The most simple service, returning a string
- //
- $sServerVersion = $oSoapClient->GetVersion();
- echo "GetVersion() returned $sServerVersion
";
+ // The most simple service, returning a string
+ //
+ $sServerVersion = $oSoapClient->GetVersion();
+ echo "GetVersion() returned $sServerVersion
";
- // More complex ones, returning a SOAPResult structure
- // (run the page to know more about the returned data)
- //
- $oRes = $oSoapClient->CreateIncidentTicket(
- 'admin', /* login */
- 'admin', /* password */
- 'Email server down', /* title */
- 'HW found shutdown', /* description */
- null, /* caller */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Demo')]), /* customer */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW Management')]), /* service */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Troubleshooting')]), /* service subcategory */
- '', /* product */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW support')]), /* workgroup */
- [
- new SOAPLinkCreationSpec(
- 'Device',
- [new SOAPSearchCondition('name', 'switch01')],
- []
- ),
- new SOAPLinkCreationSpec(
- 'Server',
- [new SOAPSearchCondition('name', 'dbserver1.demo.com')],
- []
- ),
- ], /* impacted cis */
- '1', /* impact */
- '1' /* urgency */
- );
+ // More complex ones, returning a SOAPResult structure
+ // (run the page to know more about the returned data)
+ //
+ $oRes = $oSoapClient->CreateIncidentTicket(
+ 'admin', /* login */
+ 'admin', /* password */
+ 'Email server down', /* title */
+ 'HW found shutdown', /* description */
+ null, /* caller */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Demo')]), /* customer */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW Management')]), /* service */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Troubleshooting')]), /* service subcategory */
+ '', /* product */
+ new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW support')]), /* workgroup */
+ [
+ new SOAPLinkCreationSpec(
+ 'Device',
+ [new SOAPSearchCondition('name', 'switch01')],
+ []
+ ),
+ new SOAPLinkCreationSpec(
+ 'Server',
+ [new SOAPSearchCondition('name', 'dbserver1.demo.com')],
+ []
+ ),
+ ], /* impacted cis */
+ '1', /* impact */
+ '1' /* urgency */
+ );
- echo "CreateIncidentTicket() returned:\n";
- echo "
\n";
- print_r($oRes);
- echo " \n";
- echo "\n";
+ echo "CreateIncidentTicket() returned:\n";
+ echo "
\n";
+ print_r($oRes);
+ echo " \n";
+ echo "\n";
- $oRes = $oSoapClient->SearchObjects(
- 'admin', /* login */
- 'admin', /* password */
- 'SELECT URP_Profiles' /* oql */
- );
+ $oRes = $oSoapClient->SearchObjects(
+ 'admin', /* login */
+ 'admin', /* password */
+ 'SELECT URP_Profiles' /* oql */
+ );
- echo "SearchObjects() returned:\n";
- if ($oRes->status) {
- $aResults = $oRes->result;
+ echo "
SearchObjects() returned:\n";
+ if ($oRes->status) {
+ $aResults = $oRes->result;
- echo "
\n";
+ echo "\n";
- // Header made after the first line
- echo "\n";
- foreach ($aResults[0]->values as $aKeyValuePair) {
- echo " ".$aKeyValuePair->key." \n";
- }
- echo " \n";
+ // Header made after the first line
+ echo "\n";
+ foreach ($aResults[0]->values as $aKeyValuePair) {
+ echo " ".$aKeyValuePair->key." \n";
+ }
+ echo " \n";
- foreach ($aResults as $iRow => $aData) {
- echo "\n";
- foreach ($aData->values as $aKeyValuePair) {
- echo " ".$aKeyValuePair->value." \n";
- }
- echo " \n";
- }
- echo "
\n";
- } else {
- $aErrors = [];
- foreach ($oRes->errors->messages as $oMessage) {
- $aErrors[] = $oMessage->text;
- }
- $sErrorMsg = implode(', ', $aErrors);
- echo "SearchObjects() failed with message: $sErrorMsg
\n";
- //echo "\n";
- //print_r($oRes);
- //echo " \n";
- }
- echo "\n";
+ foreach ($aResults as $iRow => $aData) {
+ echo "\n";
+ foreach ($aData->values as $aKeyValuePair) {
+ echo " ".$aKeyValuePair->value." \n";
+ }
+ echo " \n";
+ }
+ echo "
\n";
+ } else {
+ $aErrors = [];
+ foreach ($oRes->errors->messages as $oMessage) {
+ $aErrors[] = $oMessage->text;
+ }
+ $sErrorMsg = implode(', ', $aErrors);
+ echo "SearchObjects() failed with message: $sErrorMsg
\n";
+ //echo "\n";
+ //print_r($oRes);
+ //echo " \n";
+ }
+ echo "\n";
} catch (SoapFault $e) {
- echo "SoapFault Exception: {$e->getMessage()} \n";
- echo "Request \n";
- echo "\n";
- echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
- echo " ";
- echo "Response ";
- echo $oSoapClient->__getLastResponse()."\n";
+ echo "SoapFault Exception: {$e->getMessage()} \n";
+ echo "Request \n";
+ echo "\n";
+ echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
+ echo " ";
+ echo "Response ";
+ echo $oSoapClient->__getLastResponse()."\n";
}
diff --git a/webservices/itopsoaptypes.class.inc.php b/webservices/itopsoaptypes.class.inc.php
index ce1ca86a59..679dc49961 100644
--- a/webservices/itopsoaptypes.class.inc.php
+++ b/webservices/itopsoaptypes.class.inc.php
@@ -29,153 +29,153 @@
class SOAPSearchCondition
{
- public $attcode; // string
- public $value; // mixed
-
- public function __construct($sAttCode, $value)
- {
- $this->attcode = $sAttCode;
- $this->value = $value;
- }
+ public $attcode; // string
+ public $value; // mixed
+
+ public function __construct($sAttCode, $value)
+ {
+ $this->attcode = $sAttCode;
+ $this->value = $value;
+ }
}
class SOAPExternalKeySearch
{
- public $conditions; // array of SOAPSearchCondition
-
- public function __construct($aConditions = null)
- {
- $this->conditions = $aConditions;
- }
-
- public function IsVoid()
- {
- if (is_null($this->conditions)) {
- return true;
- }
- if (count($this->conditions) == 0) {
- return true;
- }
- }
+ public $conditions; // array of SOAPSearchCondition
+
+ public function __construct($aConditions = null)
+ {
+ $this->conditions = $aConditions;
+ }
+
+ public function IsVoid()
+ {
+ if (is_null($this->conditions)) {
+ return true;
+ }
+ if (count($this->conditions) == 0) {
+ return true;
+ }
+ }
}
class SOAPAttributeValue
{
- public $attcode; // string
- public $value; // mixed
-
- public function __construct($sAttCode, $value)
- {
- $this->attcode = $sAttCode;
- $this->value = $value;
- }
+ public $attcode; // string
+ public $value; // mixed
+
+ public function __construct($sAttCode, $value)
+ {
+ $this->attcode = $sAttCode;
+ $this->value = $value;
+ }
}
class SOAPLinkCreationSpec
{
- public $class;
- public $conditions; // array of SOAPSearchCondition
- public $attributes; // array of SOAPAttributeValue
-
- public function __construct($sClass, $aConditions, $aAttributes)
- {
- $this->class = $sClass;
- $this->conditions = $aConditions;
- $this->attributes = $aAttributes;
- }
+ public $class;
+ public $conditions; // array of SOAPSearchCondition
+ public $attributes; // array of SOAPAttributeValue
+
+ public function __construct($sClass, $aConditions, $aAttributes)
+ {
+ $this->class = $sClass;
+ $this->conditions = $aConditions;
+ $this->attributes = $aAttributes;
+ }
}
class SOAPLogMessage
{
- public $text; // string
+ public $text; // string
- public function __construct($sText)
- {
- $this->text = $sText;
- }
+ public function __construct($sText)
+ {
+ $this->text = $sText;
+ }
}
class SOAPResultLog
{
- public $messages; // array of SOAPLogMessage
+ public $messages; // array of SOAPLogMessage
- public function __construct($aMessages)
- {
- $this->messages = $aMessages;
- }
+ public function __construct($aMessages)
+ {
+ $this->messages = $aMessages;
+ }
}
class SOAPKeyValue
{
- public $key; // string
- public $value; // string
-
- public function __construct($sKey, $sValue)
- {
- $this->key = $sKey;
- $this->value = $sValue;
- }
+ public $key; // string
+ public $value; // string
+
+ public function __construct($sKey, $sValue)
+ {
+ $this->key = $sKey;
+ $this->value = $sValue;
+ }
}
class SOAPResultMessage
{
- public $label; // string
- public $values; // array of SOAPKeyValue
-
- public function __construct($sLabel, $aValues)
- {
- $this->label = $sLabel;
- $this->values = $aValues;
- }
+ public $label; // string
+ public $values; // array of SOAPKeyValue
+
+ public function __construct($sLabel, $aValues)
+ {
+ $this->label = $sLabel;
+ $this->values = $aValues;
+ }
}
class SOAPResult
{
- public $status; // boolean
- public $result; // array of SOAPResultMessage
- public $errors; // array of SOAPResultLog
- public $warnings; // array of SOAPResultLog
- public $infos; // array of SOAPResultLog
-
- public function __construct($bStatus, $aResult, $aErrors, $aWarnings, $aInfos)
- {
- $this->status = $bStatus;
- $this->result = $aResult;
- $this->errors = $aErrors;
- $this->warnings = $aWarnings;
- $this->infos = $aInfos;
- }
+ public $status; // boolean
+ public $result; // array of SOAPResultMessage
+ public $errors; // array of SOAPResultLog
+ public $warnings; // array of SOAPResultLog
+ public $infos; // array of SOAPResultLog
+
+ public function __construct($bStatus, $aResult, $aErrors, $aWarnings, $aInfos)
+ {
+ $this->status = $bStatus;
+ $this->result = $aResult;
+ $this->errors = $aErrors;
+ $this->warnings = $aWarnings;
+ $this->infos = $aInfos;
+ }
}
class SOAPSimpleResult
{
- public $status; // boolean
- public $message; // string
-
- public function __construct($bStatus, $sMessage)
- {
- $this->status = $bStatus;
- $this->message = $sMessage;
- }
+ public $status; // boolean
+ public $message; // string
+
+ public function __construct($bStatus, $sMessage)
+ {
+ $this->status = $bStatus;
+ $this->message = $sMessage;
+ }
}
class SOAPMapping
{
- public static function GetMapping()
- {
- $aSOAPMapping = [
- 'SearchCondition' => 'SOAPSearchCondition',
- 'ExternalKeySearch' => 'SOAPExternalKeySearch',
- 'AttributeValue' => 'SOAPAttributeValue',
- 'LinkCreationSpec' => 'SOAPLinkCreationSpec',
- 'KeyValue' => 'SOAPKeyValue',
- 'LogMessage' => 'SOAPLogMessage',
- 'ResultLog' => 'SOAPResultLog',
- 'ResultData' => 'SOAPKeyValue',
- 'ResultMessage' => 'SOAPResultMessage',
- 'Result' => 'SOAPResult',
- 'SimpleResult' => 'SOAPSimpleResult',
- ];
- return $aSOAPMapping;
- }
+ public static function GetMapping()
+ {
+ $aSOAPMapping = [
+ 'SearchCondition' => 'SOAPSearchCondition',
+ 'ExternalKeySearch' => 'SOAPExternalKeySearch',
+ 'AttributeValue' => 'SOAPAttributeValue',
+ 'LinkCreationSpec' => 'SOAPLinkCreationSpec',
+ 'KeyValue' => 'SOAPKeyValue',
+ 'LogMessage' => 'SOAPLogMessage',
+ 'ResultLog' => 'SOAPResultLog',
+ 'ResultData' => 'SOAPKeyValue',
+ 'ResultMessage' => 'SOAPResultMessage',
+ 'Result' => 'SOAPResult',
+ 'SimpleResult' => 'SOAPSimpleResult',
+ ];
+ return $aSOAPMapping;
+ }
}
diff --git a/webservices/rest.php b/webservices/rest.php
index c16b8df703..878a3e9faf 100644
--- a/webservices/rest.php
+++ b/webservices/rest.php
@@ -33,34 +33,34 @@
*/
class RestResultListOperations extends RestResult
{
- public $version;
- public $operations;
-
- public function AddOperation($sVerb, $sDescription, $sServiceProviderClass)
- {
- $this->operations[] = [
- 'verb' => $sVerb,
- 'description' => $sDescription,
- 'extension' => $sServiceProviderClass,
- ];
- }
+ public $version;
+ public $operations;
+
+ public function AddOperation($sVerb, $sDescription, $sServiceProviderClass)
+ {
+ $this->operations[] = [
+ 'verb' => $sVerb,
+ 'description' => $sDescription,
+ 'extension' => $sServiceProviderClass,
+ ];
+ }
}
if (!function_exists('json_last_error_msg')) {
- function json_last_error_msg()
- {
- static $ERRORS = [
- JSON_ERROR_NONE => 'No error',
- JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
- JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
- JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
- JSON_ERROR_SYNTAX => 'Syntax error',
- JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
- ];
-
- $error = json_last_error();
- return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
- }
+ function json_last_error_msg()
+ {
+ static $ERRORS = [
+ JSON_ERROR_NONE => 'No error',
+ JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
+ JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
+ JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
+ JSON_ERROR_SYNTAX => 'Syntax error',
+ JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
+ ];
+
+ $error = json_last_error();
+ return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -76,150 +76,150 @@ function json_last_error_msg()
$sJsonString = utils::ReadParam('json_data', null, false, 'raw_data');
if (empty($sJsonString)) {
- //N °3455: read json_data parameter via a file passed by http protocol
- if (isset($_FILES['json_data']['tmp_name'])) {
- $sTmpFilePath = $_FILES['json_data']['tmp_name'];
- if (is_file($sTmpFilePath)) {
- $sValue = file_get_contents($sTmpFilePath);
- unlink($sTmpFilePath);
- if (! empty($sValue)) {
- $sJsonString = utils::Sanitize($sValue, null, 'raw_data');
- }
- }
- }
+ //N °3455: read json_data parameter via a file passed by http protocol
+ if (isset($_FILES['json_data']['tmp_name'])) {
+ $sTmpFilePath = $_FILES['json_data']['tmp_name'];
+ if (is_file($sTmpFilePath)) {
+ $sValue = file_get_contents($sTmpFilePath);
+ unlink($sTmpFilePath);
+ if (! empty($sValue)) {
+ $sJsonString = utils::Sanitize($sValue, null, 'raw_data');
+ }
+ }
+ }
}
$sProvider = '';
$oKPI = new ExecutionKPI();
try {
- utils::UseParamFile();
-
- $oKPI->ComputeAndReport('Data model loaded');
-
- // N°6358 - force credentials for REST calls
- LoginWebPage::ResetSession(true);
- $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
- $oKPI->ComputeAndReport('User login');
-
- if ($iRet == LoginWebPage::EXIT_CODE_OK) {
- // Extra validation of the profile
- if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User')) {
- // Web services access is limited to the users with the profile REST Web Services
- $iRet = LoginWebPage::EXIT_CODE_NOTAUTHORIZED;
- }
- }
- if ($iRet != LoginWebPage::EXIT_CODE_OK) {
- switch ($iRet) {
- case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
- throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
- break;
-
- case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
- throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
- break;
-
- case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
- throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
- break;
-
- case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
- throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
- break;
-
- case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
- throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
- break;
-
- default:
- throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
- }
- }
-
- if ($sVersion == null) {
- throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
- }
-
- if ($sJsonString == null) {
- throw new Exception("Missing parameter 'json_data'", RestResult::MISSING_JSON);
- }
-
- if (is_string($sJsonString)) {
- $aJsonData = @json_decode($sJsonString);
- } elseif (is_array($sJsonString)) {
- $aJsonData = (object) $sJsonString;
- $sJsonString = json_encode($aJsonData);
- } else {
- $aJsonData = null;
- }
-
- if ($aJsonData == null) {
- throw new Exception('Parameter json_data is not a valid JSON structure', RestResult::INVALID_JSON);
- }
-
- $oKPI->ComputeAndReport('Parameters validated');
-
- /** @var iRestServiceProvider[] $aProviders */
- $oKPI = new ExecutionKPI();
- $aProviders = [];
- foreach (get_declared_classes() as $sPHPClass) {
- $oRefClass = new ReflectionClass($sPHPClass);
- if ($oRefClass->implementsInterface('iRestServiceProvider')) {
- $aProviders[] = new $sPHPClass();
- }
- }
-
- $aOpToRestService = []; // verb => $oRestServiceProvider
- /** @var iRestServiceProvider $oRestSP */
- foreach ($aProviders as $oRestSP) {
- $aOperations = $oRestSP->ListOperations($sVersion);
- foreach ($aOperations as $aOpData) {
- $aOpToRestService[$aOpData['verb']] =
- [
- 'service_provider' => $oRestSP,
- 'description' => $aOpData['description'],
- ];
- }
- }
- $oKPI->ComputeAndReport('iRestServiceProvider loaded with operations');
-
- if (count($aOpToRestService) == 0) {
- throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
- }
-
- $sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
- if ($sOperation == 'list_operations') {
- $oResult = new RestResultListOperations();
- $oResult->message = "Operations: ".count($aOpToRestService);
- $oResult->version = $sVersion;
- foreach ($aOpToRestService as $sVerb => $aOpData) {
- $oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
- }
- } else {
- if (!array_key_exists($sOperation, $aOpToRestService)) {
- throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
- }
- /** @var iRestServiceProvider $oRS */
- $oRS = $aOpToRestService[$sOperation]['service_provider'];
- $sProvider = get_class($oRS);
-
- if ($oRS instanceof iRestInputSanitizer) {
- $sSanitizedJsonInput = $oRS->SanitizeJsonInput($sJsonString);
- }
-
- CMDBObject::SetTrackOrigin('webservice-rest');
- $oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
- }
- $oKPI->ComputeAndReport('Operation finished');
+ utils::UseParamFile();
+
+ $oKPI->ComputeAndReport('Data model loaded');
+
+ // N°6358 - force credentials for REST calls
+ LoginWebPage::ResetSession(true);
+ $iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
+ $oKPI->ComputeAndReport('User login');
+
+ if ($iRet == LoginWebPage::EXIT_CODE_OK) {
+ // Extra validation of the profile
+ if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User')) {
+ // Web services access is limited to the users with the profile REST Web Services
+ $iRet = LoginWebPage::EXIT_CODE_NOTAUTHORIZED;
+ }
+ }
+ if ($iRet != LoginWebPage::EXIT_CODE_OK) {
+ switch ($iRet) {
+ case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
+ throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
+ break;
+
+ case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
+ throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
+ break;
+
+ case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
+ throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
+ break;
+
+ case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
+ throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
+ break;
+
+ case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
+ throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
+ break;
+
+ default:
+ throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
+ }
+ }
+
+ if ($sVersion == null) {
+ throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
+ }
+
+ if ($sJsonString == null) {
+ throw new Exception("Missing parameter 'json_data'", RestResult::MISSING_JSON);
+ }
+
+ if (is_string($sJsonString)) {
+ $aJsonData = @json_decode($sJsonString);
+ } elseif (is_array($sJsonString)) {
+ $aJsonData = (object) $sJsonString;
+ $sJsonString = json_encode($aJsonData);
+ } else {
+ $aJsonData = null;
+ }
+
+ if ($aJsonData == null) {
+ throw new Exception('Parameter json_data is not a valid JSON structure', RestResult::INVALID_JSON);
+ }
+
+ $oKPI->ComputeAndReport('Parameters validated');
+
+ /** @var iRestServiceProvider[] $aProviders */
+ $oKPI = new ExecutionKPI();
+ $aProviders = [];
+ foreach (get_declared_classes() as $sPHPClass) {
+ $oRefClass = new ReflectionClass($sPHPClass);
+ if ($oRefClass->implementsInterface('iRestServiceProvider')) {
+ $aProviders[] = new $sPHPClass();
+ }
+ }
+
+ $aOpToRestService = []; // verb => $oRestServiceProvider
+ /** @var iRestServiceProvider $oRestSP */
+ foreach ($aProviders as $oRestSP) {
+ $aOperations = $oRestSP->ListOperations($sVersion);
+ foreach ($aOperations as $aOpData) {
+ $aOpToRestService[$aOpData['verb']] =
+ [
+ 'service_provider' => $oRestSP,
+ 'description' => $aOpData['description'],
+ ];
+ }
+ }
+ $oKPI->ComputeAndReport('iRestServiceProvider loaded with operations');
+
+ if (count($aOpToRestService) == 0) {
+ throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
+ }
+
+ $sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
+ if ($sOperation == 'list_operations') {
+ $oResult = new RestResultListOperations();
+ $oResult->message = "Operations: ".count($aOpToRestService);
+ $oResult->version = $sVersion;
+ foreach ($aOpToRestService as $sVerb => $aOpData) {
+ $oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
+ }
+ } else {
+ if (!array_key_exists($sOperation, $aOpToRestService)) {
+ throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
+ }
+ /** @var iRestServiceProvider $oRS */
+ $oRS = $aOpToRestService[$sOperation]['service_provider'];
+ $sProvider = get_class($oRS);
+
+ if ($oRS instanceof iRestInputSanitizer) {
+ $sSanitizedJsonInput = $oRS->SanitizeJsonInput($sJsonString);
+ }
+
+ CMDBObject::SetTrackOrigin('webservice-rest');
+ $oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
+ }
+ $oKPI->ComputeAndReport('Operation finished');
} catch (Exception $e) {
- $oResult = new RestResult();
- if ($e->GetCode() == 0) {
- $oResult->code = RestResult::INTERNAL_ERROR;
- } else {
- $oResult->code = $e->GetCode();
- }
- $oResult->message = "Error: ".$e->GetMessage();
- $oKPI->ComputeAndReport('Exception catched');
+ $oResult = new RestResult();
+ if ($e->GetCode() == 0) {
+ $oResult->code = RestResult::INTERNAL_ERROR;
+ } else {
+ $oResult->code = $e->GetCode();
+ }
+ $oResult->message = "Error: ".$e->GetMessage();
+ $oKPI->ComputeAndReport('Exception catched');
}
// Output the results
@@ -227,17 +227,17 @@ function json_last_error_msg()
$sResponse = json_encode($oResult);
if ($sResponse === false) {
- $oJsonIssue = new RestResult();
- $oJsonIssue->code = RestResult::INTERNAL_ERROR;
- $oJsonIssue->message = 'json encoding failed with message: '.json_last_error_msg().'. Full response structure for debugging purposes (print_r+bin2hex): '.bin2hex(print_r($oResult, true));
- $sResponse = json_encode($oJsonIssue);
+ $oJsonIssue = new RestResult();
+ $oJsonIssue->code = RestResult::INTERNAL_ERROR;
+ $oJsonIssue->message = 'json encoding failed with message: '.json_last_error_msg().'. Full response structure for debugging purposes (print_r+bin2hex): '.bin2hex(print_r($oResult, true));
+ $sResponse = json_encode($oJsonIssue);
}
$sCallback = utils::ReadParam('callback', null);
if ($sCallback == null) {
- $oP = new JsonPage();
+ $oP = new JsonPage();
} else {
- $oP = new JsonPPage($sCallback);
+ $oP = new JsonPPage($sCallback);
}
$oP->add_header('Access-Control-Allow-Origin: *');
$oP->SetData(json_decode($sResponse, true));
@@ -248,28 +248,28 @@ function json_last_error_msg()
// Log usage
//
if (MetaModel::GetConfig()->Get('log_rest_service')) {
- $oLog = new EventRestService();
- $oLog->SetTrim('userinfo', UserRights::GetUser());
- $oLog->Set('version', $sVersion);
- $oLog->Set('operation', $sOperation);
- $oLog->SetTrim('json_input', $sSanitizedJsonInput ?? $sJsonString);
-
- $oLog->Set('provider', $sProvider);
- $sMessage = $oResult->message;
- if (empty($oResult->message)) {
- $sMessage = 'Ok';
- }
- $oLog->SetTrim('message', $sMessage);
- $oLog->Set('code', $oResult->code);
- $oResult->SanitizeContent();
- $iUnescapeSlashAndUnicode = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
- $sJsonOuputWithPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode | JSON_PRETTY_PRINT);
- $sJsonOutputWithoutPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode);
- !StringFitsInLogField($sJsonOuputWithPrettyPrinting) ?
- $oLog->SetTrim('json_output', $sJsonOutputWithoutPrettyPrinting) : // too long, we don't make it pretty
- $oLog->SetTrim('json_output', $sJsonOuputWithPrettyPrinting);
-
- $oLog->DBInsertNoReload();
+ $oLog = new EventRestService();
+ $oLog->SetTrim('userinfo', UserRights::GetUser());
+ $oLog->Set('version', $sVersion);
+ $oLog->Set('operation', $sOperation);
+ $oLog->SetTrim('json_input', $sSanitizedJsonInput ?? $sJsonString);
+
+ $oLog->Set('provider', $sProvider);
+ $sMessage = $oResult->message;
+ if (empty($oResult->message)) {
+ $sMessage = 'Ok';
+ }
+ $oLog->SetTrim('message', $sMessage);
+ $oLog->Set('code', $oResult->code);
+ $oResult->SanitizeContent();
+ $iUnescapeSlashAndUnicode = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
+ $sJsonOuputWithPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode | JSON_PRETTY_PRINT);
+ $sJsonOutputWithoutPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode);
+ !StringFitsInLogField($sJsonOuputWithPrettyPrinting) ?
+ $oLog->SetTrim('json_output', $sJsonOutputWithoutPrettyPrinting) : // too long, we don't make it pretty
+ $oLog->SetTrim('json_output', $sJsonOuputWithPrettyPrinting);
+
+ $oLog->DBInsertNoReload();
}
/**
@@ -277,5 +277,5 @@ function json_last_error_msg()
*/
function StringFitsInLogField(string $sLog): bool
{
- return mb_strlen($sLog) <= 16383; // hardcoded value, see N°8260
+ return mb_strlen($sLog) <= 16383; // hardcoded value, see N°8260
}
diff --git a/webservices/soapserver.php b/webservices/soapserver.php
index e0b01ac4ab..3c45c11653 100644
--- a/webservices/soapserver.php
+++ b/webservices/soapserver.php
@@ -29,60 +29,60 @@
$sWsdlUri = utils::GetAbsoluteUrlAppRoot().'webservices/itop.wsdl.php';
$sServiceCategory = utils::ReadParam('service_category');
if (!empty($sServiceCategory)) {
- $sWsdlUri .= "?service_category=".$sServiceCategory;
+ $sWsdlUri .= "?service_category=".$sServiceCategory;
}
ini_set("soap.wsdl_cache_enabled", "0");
$aSOAPMapping = SOAPMapping::GetMapping();
$oSoapServer = new SoapServer(
- $sWsdlUri,
- [
- 'classmap' => $aSOAPMapping
- ]
+ $sWsdlUri,
+ [
+ 'classmap' => $aSOAPMapping
+ ]
);
// $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION);
if (!empty($sServiceCategory)) {
- $sServiceClass = $sServiceCategory;
- if (!class_exists($sServiceClass)) {
- // not a valid class name (not a PHP class at all)
- throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not a PHP class");
- } elseif (!is_subclass_of($sServiceClass, 'WebServicesBase')) {
- // not a valid class name (not deriving from WebServicesBase)
- throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not derived from WebServicesBase");
- } else {
- $oSoapServer->setClass($sServiceClass, null);
- }
+ $sServiceClass = $sServiceCategory;
+ if (!class_exists($sServiceClass)) {
+ // not a valid class name (not a PHP class at all)
+ throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not a PHP class");
+ } elseif (!is_subclass_of($sServiceClass, 'WebServicesBase')) {
+ // not a valid class name (not deriving from WebServicesBase)
+ throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not derived from WebServicesBase");
+ } else {
+ $oSoapServer->setClass($sServiceClass, null);
+ }
} else {
- $oSoapServer->setClass('BasicServices', null);
+ $oSoapServer->setClass('BasicServices', null);
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
- CMDBObject::SetTrackOrigin('webservice-soap');
- $oSoapServer->handle();
+ CMDBObject::SetTrackOrigin('webservice-soap');
+ $oSoapServer->handle();
} else {
- echo "This SOAP server can handle the following functions: ";
- $aFunctions = $oSoapServer->getFunctions();
- echo "\n";
- foreach ($aFunctions as $sFunc) {
- if ($sFunc == 'GetWSDLContents') {
- continue;
- }
+ echo "This SOAP server can handle the following functions: ";
+ $aFunctions = $oSoapServer->getFunctions();
+ echo "\n";
+ foreach ($aFunctions as $sFunc) {
+ if ($sFunc == 'GetWSDLContents') {
+ continue;
+ }
- echo "$sFunc \n";
- }
- echo " \n";
- echo "Here the WSDL file
";
+ echo "
$sFunc \n";
+ }
+ echo " \n";
+ echo "Here the WSDL file
";
- echo "You may also want to try the following service categories: ";
- echo "
\n";
- foreach (get_declared_classes() as $sPHPClass) {
- if (is_subclass_of($sPHPClass, 'WebServicesBase')) {
- $sServiceCategory = $sPHPClass;
- $sSoapServerUri = utils::GetAbsoluteUrlAppRoot().'webservices/soapserver.php';
- $sSoapServerUri .= "?service_category=$sServiceCategory";
- echo "$sServiceCategory \n";
- }
- }
- echo " \n";
+ echo "You may also want to try the following service categories: ";
+ echo "\n";
+ foreach (get_declared_classes() as $sPHPClass) {
+ if (is_subclass_of($sPHPClass, 'WebServicesBase')) {
+ $sServiceCategory = $sPHPClass;
+ $sSoapServerUri = utils::GetAbsoluteUrlAppRoot().'webservices/soapserver.php';
+ $sSoapServerUri .= "?service_category=$sServiceCategory";
+ echo "$sServiceCategory \n";
+ }
+ }
+ echo " \n";
}
diff --git a/webservices/status.php b/webservices/status.php
index 55f297519d..793ebca8e5 100644
--- a/webservices/status.php
+++ b/webservices/status.php
@@ -7,12 +7,12 @@
//Do check Status
try {
- new Status();
- $aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
+ new Status();
+ $aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
} catch (Exception $e) {
- $iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
- $aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
- http_response_code(500);
+ $iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
+ $aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
+ http_response_code(500);
}
//Set headers, based on webservices/rest.php
diff --git a/webservices/webservices.basic.php b/webservices/webservices.basic.php
index 336ace0713..e322aee18a 100644
--- a/webservices/webservices.basic.php
+++ b/webservices/webservices.basic.php
@@ -28,232 +28,232 @@
class BasicServices extends WebServicesBase
{
- protected static function GetWSDLFilePath()
- {
- return APPROOT.'/webservices/itop.wsdl.tpl';
- }
-
- /**
- * Get the server version (TODO: get it dynamically, where ?)
- *
- * @return string WebServiceResult
- */
- public static function GetVersion()
- {
- if (ITOP_REVISION == 'svn') {
- $sVersionString = ITOP_VERSION.' [dev]';
- } else {
- // This is a build made from SVN, let display the full information
- $sVersionString = ITOP_VERSION_FULL." ".ITOP_BUILD_DATE;
- }
-
- return $sVersionString;
- }
-
- public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
- {
- if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
- $oRes = new WebServiceResultFailedLogin($sLogin);
- $this->LogUsage(__FUNCTION__, $oRes);
-
- return $oRes->ToSoapStructure();
- }
- UserRights::Login($sLogin);
-
- $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
- $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
- $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
- $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
- $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
-
- $aImpactedCIs = [];
- if (is_null($aSOAPImpactedCIs)) {
- $aSOAPImpactedCIs = [];
- }
- foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
- $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
- }
-
- $oRes = $this->_CreateResponseTicket(
- 'UserRequest',
- $sTitle,
- $sDescription,
- $aCallerDesc,
- $aCustomerDesc,
- $aServiceDesc,
- $aServiceSubcategoryDesc,
- $sProduct,
- $aWorkgroupDesc,
- $aImpactedCIs,
- $sImpact,
- $sUrgency
- );
- return $oRes->ToSoapStructure();
- }
-
- public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
- {
- if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
- $oRes = new WebServiceResultFailedLogin($sLogin);
- $this->LogUsage(__FUNCTION__, $oRes);
-
- return $oRes->ToSoapStructure();
- }
- UserRights::Login($sLogin);
-
- if (!class_exists('Incident')) {
- $oRes = new WebServiceResult();
- $oRes->LogError("The class Incident does not exist. Did you install the Incident Management (ITIL) module ?");
- return $oRes->ToSoapStructure();
- }
-
- $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
- $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
- $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
- $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
- $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
-
- $aImpactedCIs = [];
- if (is_null($aSOAPImpactedCIs)) {
- $aSOAPImpactedCIs = [];
- }
- foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
- $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
- }
-
- $oRes = $this->_CreateResponseTicket(
- 'Incident',
- $sTitle,
- $sDescription,
- $aCallerDesc,
- $aCustomerDesc,
- $aServiceDesc,
- $aServiceSubcategoryDesc,
- $sProduct,
- $aWorkgroupDesc,
- $aImpactedCIs,
- $sImpact,
- $sUrgency
- );
- return $oRes->ToSoapStructure();
- }
-
- /**
- * Create an ResponseTicket (Incident or UserRequest) from an external system
- * Some CIs might be specified (by their name/IP)
- *
- * @param string sClass The class of the ticket: Incident or UserRequest
- * @param string sTitle
- * @param string sDescription
- * @param array aCallerDesc
- * @param array aCustomerDesc
- * @param array aServiceDesc
- * @param array aServiceSubcategoryDesc
- * @param string sProduct
- * @param array aWorkgroupDesc
- * @param array aImpactedCIs
- * @param string sImpact
- * @param string sUrgency
- *
- * @return WebServiceResult
- */
- protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCallerDesc, $aCustomerDesc, $aServiceDesc, $aServiceSubcategoryDesc, $sProduct, $aWorkgroupDesc, $aImpactedCIs, $sImpact, $sUrgency)
- {
-
- $oRes = new WebServiceResult();
-
- try {
- CMDBObject::SetTrackInfo('Administrator');
-
- $oNewTicket = MetaModel::NewObject($sClass);
- $this->MyObjectSetScalar('title', 'title', $sTitle, $oNewTicket, $oRes);
- $this->MyObjectSetScalar('description', 'description', $sDescription, $oNewTicket, $oRes);
-
- $this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
- $this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
-
- $this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
- if (!array_key_exists('service_id', $aServiceSubcategoryDesc)) {
- $aServiceSubcategoryDesc['service_id'] = $oNewTicket->Get('service_id');
- }
- $this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
- if (MetaModel::IsValidAttCode($sClass, 'product')) {
- // 1.x data models
- $this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
- }
-
- if (MetaModel::IsValidAttCode($sClass, 'workgroup_id')) {
- // 1.x data models
- $this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
- } elseif (MetaModel::IsValidAttCode($sClass, 'team_id')) {
- // 2.x data models
- $this->MyObjectSetExternalKey('team_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
- }
-
- if (MetaModel::IsValidAttCode($sClass, 'ci_list')) {
- // 1.x data models
- $aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
- } elseif (MetaModel::IsValidAttCode($sClass, 'functionalcis_list')) {
- // 2.x data models
- $aDevicesNotFound = $this->AddLinkedObjects('functionalcis_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
- }
-
- if (count($aDevicesNotFound) > 0) {
- $this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
- } else {
- $this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
- }
-
- $this->MyObjectSetScalar('impact', 'impact', $sImpact, $oNewTicket, $oRes);
- $this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
-
- $this->MyObjectInsert($oNewTicket, 'created', $oRes);
- } catch (CoreException $e) {
- $oRes->LogError($e->getMessage());
- } catch (Exception $e) {
- $oRes->LogError($e->getMessage());
- }
-
- $this->LogUsage(__FUNCTION__, $oRes);
- return $oRes;
- }
-
- /**
- * Given an OQL, returns a set of objects (several objects could be on the same row)
- *
- * @param string sOQL
- */
- public function SearchObjects($sLogin, $sPassword, $sOQL)
- {
- if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
- $oRes = new WebServiceResultFailedLogin($sLogin);
- $this->LogUsage(__FUNCTION__, $oRes);
-
- return $oRes->ToSoapStructure();
- }
- UserRights::Login($sLogin);
-
- $oRes = $this->_SearchObjects($sOQL);
- return $oRes->ToSoapStructure();
- }
-
- protected function _SearchObjects($sOQL)
- {
- $oRes = new WebServiceResult();
- try {
- $oSearch = DBObjectSearch::FromOQL($sOQL);
- $oSet = new DBObjectSet($oSearch);
- $aData = $oSet->ToArrayOfValues();
- foreach ($aData as $iRow => $aRow) {
- $oRes->AddResultRow("row_$iRow", $aRow);
- }
- } catch (CoreException $e) {
- $oRes->LogError($e->getMessage());
- } catch (Exception $e) {
- $oRes->LogError($e->getMessage());
- }
-
- $this->LogUsage(__FUNCTION__, $oRes);
- return $oRes;
- }
+ protected static function GetWSDLFilePath()
+ {
+ return APPROOT.'/webservices/itop.wsdl.tpl';
+ }
+
+ /**
+ * Get the server version (TODO: get it dynamically, where ?)
+ *
+ * @return string WebServiceResult
+ */
+ public static function GetVersion()
+ {
+ if (ITOP_REVISION == 'svn') {
+ $sVersionString = ITOP_VERSION.' [dev]';
+ } else {
+ // This is a build made from SVN, let display the full information
+ $sVersionString = ITOP_VERSION_FULL." ".ITOP_BUILD_DATE;
+ }
+
+ return $sVersionString;
+ }
+
+ public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
+ {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ $oRes = new WebServiceResultFailedLogin($sLogin);
+ $this->LogUsage(__FUNCTION__, $oRes);
+
+ return $oRes->ToSoapStructure();
+ }
+ UserRights::Login($sLogin);
+
+ $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
+ $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
+ $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
+ $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
+ $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
+
+ $aImpactedCIs = [];
+ if (is_null($aSOAPImpactedCIs)) {
+ $aSOAPImpactedCIs = [];
+ }
+ foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
+ $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
+ }
+
+ $oRes = $this->_CreateResponseTicket(
+ 'UserRequest',
+ $sTitle,
+ $sDescription,
+ $aCallerDesc,
+ $aCustomerDesc,
+ $aServiceDesc,
+ $aServiceSubcategoryDesc,
+ $sProduct,
+ $aWorkgroupDesc,
+ $aImpactedCIs,
+ $sImpact,
+ $sUrgency
+ );
+ return $oRes->ToSoapStructure();
+ }
+
+ public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
+ {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ $oRes = new WebServiceResultFailedLogin($sLogin);
+ $this->LogUsage(__FUNCTION__, $oRes);
+
+ return $oRes->ToSoapStructure();
+ }
+ UserRights::Login($sLogin);
+
+ if (!class_exists('Incident')) {
+ $oRes = new WebServiceResult();
+ $oRes->LogError("The class Incident does not exist. Did you install the Incident Management (ITIL) module ?");
+ return $oRes->ToSoapStructure();
+ }
+
+ $aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
+ $aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
+ $aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
+ $aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
+ $aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
+
+ $aImpactedCIs = [];
+ if (is_null($aSOAPImpactedCIs)) {
+ $aSOAPImpactedCIs = [];
+ }
+ foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
+ $aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
+ }
+
+ $oRes = $this->_CreateResponseTicket(
+ 'Incident',
+ $sTitle,
+ $sDescription,
+ $aCallerDesc,
+ $aCustomerDesc,
+ $aServiceDesc,
+ $aServiceSubcategoryDesc,
+ $sProduct,
+ $aWorkgroupDesc,
+ $aImpactedCIs,
+ $sImpact,
+ $sUrgency
+ );
+ return $oRes->ToSoapStructure();
+ }
+
+ /**
+ * Create an ResponseTicket (Incident or UserRequest) from an external system
+ * Some CIs might be specified (by their name/IP)
+ *
+ * @param string sClass The class of the ticket: Incident or UserRequest
+ * @param string sTitle
+ * @param string sDescription
+ * @param array aCallerDesc
+ * @param array aCustomerDesc
+ * @param array aServiceDesc
+ * @param array aServiceSubcategoryDesc
+ * @param string sProduct
+ * @param array aWorkgroupDesc
+ * @param array aImpactedCIs
+ * @param string sImpact
+ * @param string sUrgency
+ *
+ * @return WebServiceResult
+ */
+ protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCallerDesc, $aCustomerDesc, $aServiceDesc, $aServiceSubcategoryDesc, $sProduct, $aWorkgroupDesc, $aImpactedCIs, $sImpact, $sUrgency)
+ {
+
+ $oRes = new WebServiceResult();
+
+ try {
+ CMDBObject::SetTrackInfo('Administrator');
+
+ $oNewTicket = MetaModel::NewObject($sClass);
+ $this->MyObjectSetScalar('title', 'title', $sTitle, $oNewTicket, $oRes);
+ $this->MyObjectSetScalar('description', 'description', $sDescription, $oNewTicket, $oRes);
+
+ $this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
+ $this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
+
+ $this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
+ if (!array_key_exists('service_id', $aServiceSubcategoryDesc)) {
+ $aServiceSubcategoryDesc['service_id'] = $oNewTicket->Get('service_id');
+ }
+ $this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
+ if (MetaModel::IsValidAttCode($sClass, 'product')) {
+ // 1.x data models
+ $this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
+ }
+
+ if (MetaModel::IsValidAttCode($sClass, 'workgroup_id')) {
+ // 1.x data models
+ $this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
+ } elseif (MetaModel::IsValidAttCode($sClass, 'team_id')) {
+ // 2.x data models
+ $this->MyObjectSetExternalKey('team_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
+ }
+
+ if (MetaModel::IsValidAttCode($sClass, 'ci_list')) {
+ // 1.x data models
+ $aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
+ } elseif (MetaModel::IsValidAttCode($sClass, 'functionalcis_list')) {
+ // 2.x data models
+ $aDevicesNotFound = $this->AddLinkedObjects('functionalcis_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
+ }
+
+ if (count($aDevicesNotFound) > 0) {
+ $this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
+ } else {
+ $this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
+ }
+
+ $this->MyObjectSetScalar('impact', 'impact', $sImpact, $oNewTicket, $oRes);
+ $this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
+
+ $this->MyObjectInsert($oNewTicket, 'created', $oRes);
+ } catch (CoreException $e) {
+ $oRes->LogError($e->getMessage());
+ } catch (Exception $e) {
+ $oRes->LogError($e->getMessage());
+ }
+
+ $this->LogUsage(__FUNCTION__, $oRes);
+ return $oRes;
+ }
+
+ /**
+ * Given an OQL, returns a set of objects (several objects could be on the same row)
+ *
+ * @param string sOQL
+ */
+ public function SearchObjects($sLogin, $sPassword, $sOQL)
+ {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ $oRes = new WebServiceResultFailedLogin($sLogin);
+ $this->LogUsage(__FUNCTION__, $oRes);
+
+ return $oRes->ToSoapStructure();
+ }
+ UserRights::Login($sLogin);
+
+ $oRes = $this->_SearchObjects($sOQL);
+ return $oRes->ToSoapStructure();
+ }
+
+ protected function _SearchObjects($sOQL)
+ {
+ $oRes = new WebServiceResult();
+ try {
+ $oSearch = DBObjectSearch::FromOQL($sOQL);
+ $oSet = new DBObjectSet($oSearch);
+ $aData = $oSet->ToArrayOfValues();
+ foreach ($aData as $iRow => $aRow) {
+ $oRes->AddResultRow("row_$iRow", $aRow);
+ }
+ } catch (CoreException $e) {
+ $oRes->LogError($e->getMessage());
+ } catch (Exception $e) {
+ $oRes->LogError($e->getMessage());
+ }
+
+ $this->LogUsage(__FUNCTION__, $oRes);
+ return $oRes;
+ }
}
diff --git a/webservices/webservices.class.inc.php b/webservices/webservices.class.inc.php
index f411b49d8d..a65f56ab19 100644
--- a/webservices/webservices.class.inc.php
+++ b/webservices/webservices.class.inc.php
@@ -33,195 +33,195 @@
*/
class WebServiceResult
{
- /**
- * Overall status
- *
- * @var m_bStatus
- */
- public $m_bStatus;
-
- /**
- * Error log
- *
- * @var m_aErrors
- */
- public $m_aErrors;
-
- /**
- * Warning log
- *
- * @var m_aWarnings
- */
- public $m_aWarnings;
-
- /**
- * Information log
- *
- * @var m_aInfos
- */
- public $m_aInfos;
-
- /**
- * Constructor
- *
- * @param status $bStatus
- */
- public function __construct()
- {
- $this->m_bStatus = true;
- $this->m_aResult = [];
- $this->m_aErrors = [];
- $this->m_aWarnings = [];
- $this->m_aInfos = [];
- }
-
- public function ToSoapStructure()
- {
- $aResults = [];
- foreach ($this->m_aResult as $sLabel => $aData) {
- $aValues = [];
- foreach ($aData as $sKey => $value) {
- $aValues[] = new SOAPKeyValue($sKey, $value);
- }
- $aResults[] = new SoapResultMessage($sLabel, $aValues);
- }
- $aInfos = [];
- foreach ($this->m_aInfos as $sMessage) {
- $aInfos[] = new SoapLogMessage($sMessage);
- }
- $aWarnings = [];
- foreach ($this->m_aWarnings as $sMessage) {
- $aWarnings[] = new SoapLogMessage($sMessage);
- }
- $aErrors = [];
- foreach ($this->m_aErrors as $sMessage) {
- $aErrors[] = new SoapLogMessage($sMessage);
- }
-
- $oRet = new SOAPResult(
- $this->m_bStatus,
- $aResults,
- new SOAPResultLog($aErrors),
- new SOAPResultLog($aWarnings),
- new SOAPResultLog($aInfos)
- );
-
- return $oRet;
- }
-
- /**
- * Did the current processing encounter a stopper issue ?
- *
- * @return bool
- */
- public function IsOk()
- {
- return $this->m_bStatus;
- }
-
- /**
- * Add result details - object reference
- *
- * @param string sLabel
- * @param object oObject
- */
- public function AddResultObject($sLabel, $oObject)
- {
- $oAppContext = new ApplicationContext();
- $this->m_aResult[$sLabel] = [
- 'id' => $oObject->GetKey(),
- 'name' => $oObject->GetRawName(),
- 'url' => $oAppContext->MakeObjectUrl(get_class($oObject), $oObject->GetKey(), null, false), // Raw URL without HTML tags
- ];
- }
-
- /**
- * Add result details - a table row
- *
- * @param string sLabel
- * @param object oObject
- */
- public function AddResultRow($sLabel, $aRow)
- {
- $this->m_aResult[$sLabel] = $aRow;
- }
-
- /**
- * Log an error
- *
- * @param string sDescription
- */
- public function LogError($sDescription)
- {
- $this->m_aErrors[] = $sDescription;
- // Note: SOAP do transform false into null
- $this->m_bStatus = 0;
- }
-
- /**
- * Log a warning
- *
- * @param string sDescription
- */
- public function LogWarning($sDescription)
- {
- $this->m_aWarnings[] = $sDescription;
- }
-
- /**
- * Log an error or a warning
- *
- * @param string sDescription
- * @param boolean bIsStopper
- */
- public function LogIssue($sDescription, $bIsStopper = true)
- {
- if ($bIsStopper) {
- $this->LogError($sDescription);
- } else {
- $this->LogWarning($sDescription);
- }
- }
-
- /**
- * Log operation details
- *
- * @param description $sDescription
- */
- public function LogInfo($sDescription)
- {
- $this->m_aInfos[] = $sDescription;
- }
-
- protected static function LogToText($aLog)
- {
- return implode("\n", $aLog);
- }
-
- public function GetInfoAsText()
- {
- return self::LogToText($this->m_aInfos);
- }
-
- public function GetWarningsAsText()
- {
- return self::LogToText($this->m_aWarnings);
- }
-
- public function GetErrorsAsText()
- {
- return self::LogToText($this->m_aErrors);
- }
-
- public function GetReturnedDataAsText()
- {
- $sRet = '';
- foreach ($this->m_aResult as $sKey => $value) {
- $sRet .= "===== $sKey =====\n";
- $sRet .= print_r($value, true);
- }
- return $sRet;
- }
+ /**
+ * Overall status
+ *
+ * @var m_bStatus
+ */
+ public $m_bStatus;
+
+ /**
+ * Error log
+ *
+ * @var m_aErrors
+ */
+ public $m_aErrors;
+
+ /**
+ * Warning log
+ *
+ * @var m_aWarnings
+ */
+ public $m_aWarnings;
+
+ /**
+ * Information log
+ *
+ * @var m_aInfos
+ */
+ public $m_aInfos;
+
+ /**
+ * Constructor
+ *
+ * @param status $bStatus
+ */
+ public function __construct()
+ {
+ $this->m_bStatus = true;
+ $this->m_aResult = [];
+ $this->m_aErrors = [];
+ $this->m_aWarnings = [];
+ $this->m_aInfos = [];
+ }
+
+ public function ToSoapStructure()
+ {
+ $aResults = [];
+ foreach ($this->m_aResult as $sLabel => $aData) {
+ $aValues = [];
+ foreach ($aData as $sKey => $value) {
+ $aValues[] = new SOAPKeyValue($sKey, $value);
+ }
+ $aResults[] = new SoapResultMessage($sLabel, $aValues);
+ }
+ $aInfos = [];
+ foreach ($this->m_aInfos as $sMessage) {
+ $aInfos[] = new SoapLogMessage($sMessage);
+ }
+ $aWarnings = [];
+ foreach ($this->m_aWarnings as $sMessage) {
+ $aWarnings[] = new SoapLogMessage($sMessage);
+ }
+ $aErrors = [];
+ foreach ($this->m_aErrors as $sMessage) {
+ $aErrors[] = new SoapLogMessage($sMessage);
+ }
+
+ $oRet = new SOAPResult(
+ $this->m_bStatus,
+ $aResults,
+ new SOAPResultLog($aErrors),
+ new SOAPResultLog($aWarnings),
+ new SOAPResultLog($aInfos)
+ );
+
+ return $oRet;
+ }
+
+ /**
+ * Did the current processing encounter a stopper issue ?
+ *
+ * @return bool
+ */
+ public function IsOk()
+ {
+ return $this->m_bStatus;
+ }
+
+ /**
+ * Add result details - object reference
+ *
+ * @param string sLabel
+ * @param object oObject
+ */
+ public function AddResultObject($sLabel, $oObject)
+ {
+ $oAppContext = new ApplicationContext();
+ $this->m_aResult[$sLabel] = [
+ 'id' => $oObject->GetKey(),
+ 'name' => $oObject->GetRawName(),
+ 'url' => $oAppContext->MakeObjectUrl(get_class($oObject), $oObject->GetKey(), null, false), // Raw URL without HTML tags
+ ];
+ }
+
+ /**
+ * Add result details - a table row
+ *
+ * @param string sLabel
+ * @param object oObject
+ */
+ public function AddResultRow($sLabel, $aRow)
+ {
+ $this->m_aResult[$sLabel] = $aRow;
+ }
+
+ /**
+ * Log an error
+ *
+ * @param string sDescription
+ */
+ public function LogError($sDescription)
+ {
+ $this->m_aErrors[] = $sDescription;
+ // Note: SOAP do transform false into null
+ $this->m_bStatus = 0;
+ }
+
+ /**
+ * Log a warning
+ *
+ * @param string sDescription
+ */
+ public function LogWarning($sDescription)
+ {
+ $this->m_aWarnings[] = $sDescription;
+ }
+
+ /**
+ * Log an error or a warning
+ *
+ * @param string sDescription
+ * @param boolean bIsStopper
+ */
+ public function LogIssue($sDescription, $bIsStopper = true)
+ {
+ if ($bIsStopper) {
+ $this->LogError($sDescription);
+ } else {
+ $this->LogWarning($sDescription);
+ }
+ }
+
+ /**
+ * Log operation details
+ *
+ * @param description $sDescription
+ */
+ public function LogInfo($sDescription)
+ {
+ $this->m_aInfos[] = $sDescription;
+ }
+
+ protected static function LogToText($aLog)
+ {
+ return implode("\n", $aLog);
+ }
+
+ public function GetInfoAsText()
+ {
+ return self::LogToText($this->m_aInfos);
+ }
+
+ public function GetWarningsAsText()
+ {
+ return self::LogToText($this->m_aWarnings);
+ }
+
+ public function GetErrorsAsText()
+ {
+ return self::LogToText($this->m_aErrors);
+ }
+
+ public function GetReturnedDataAsText()
+ {
+ $sRet = '';
+ foreach ($this->m_aResult as $sKey => $value) {
+ $sRet .= "===== $sKey =====\n";
+ $sRet .= print_r($value, true);
+ }
+ return $sRet;
+ }
}
/**
@@ -231,11 +231,11 @@ public function GetReturnedDataAsText()
*/
class WebServiceResultFailedLogin extends WebServiceResult
{
- public function __construct($sLogin)
- {
- parent::__construct();
- $this->LogError("Wrong credentials: '$sLogin'");
- }
+ public function __construct($sLogin)
+ {
+ parent::__construct();
+ $this->LogError("Wrong credentials: '$sLogin'");
+ }
}
/**
@@ -245,319 +245,319 @@ public function __construct($sLogin)
*/
abstract class WebServicesBase
{
- public static function GetWSDLContents($sServiceCategory = '')
- {
- if ($sServiceCategory == '') {
- $sServiceCategory = 'BasicServices';
- }
- $sWsdlFilePath = call_user_func([$sServiceCategory, 'GetWSDLFilePath']);
- return file_get_contents($sWsdlFilePath);
- }
-
- /**
- * Helper to log a service delivery
- *
- * @param string sVerb
- * @param array aArgs
- * @param WebServiceResult oRes
- *
- */
- protected function LogUsage($sVerb, $oRes)
- {
- if (!MetaModel::IsLogEnabledWebService()) {
- return;
- }
-
- $oLog = new EventWebService();
- if ($oRes->IsOk()) {
- $oLog->Set('message', $sVerb.' was successfully invoked');
- } else {
- $oLog->Set('message', $sVerb.' returned errors');
- }
- $oLog->Set('userinfo', UserRights::GetUser());
- $oLog->Set('verb', $sVerb);
- $oLog->Set('result', $oRes->IsOk());
- $this->TrimAndSetValue($oLog, 'log_info', (string)$oRes->GetInfoAsText());
- $this->TrimAndSetValue($oLog, 'log_warning', (string)$oRes->GetWarningsAsText());
- $this->TrimAndSetValue($oLog, 'log_error', (string)$oRes->GetErrorsAsText());
- $this->TrimAndSetValue($oLog, 'data', (string)$oRes->GetReturnedDataAsText());
- $oLog->DBInsertNoReload();
- }
-
- protected function TrimAndSetValue($oLog, $sAttCode, $sValue)
- {
- $oAttDef = MetaModel::GetAttributeDef(get_class($oLog), $sAttCode);
- if (is_object($oAttDef)) {
- $iMaxSize = $oAttDef->GetMaxSize();
- if ($iMaxSize && (mb_strlen($sValue) > $iMaxSize)) {
- $sValue = mb_substr($sValue, 0, $iMaxSize);
- }
- $oLog->Set($sAttCode, $sValue);
- }
- }
-
- /**
- * Helper to set a scalar attribute
- *
- * @param string sAttCode
- * @param scalar value
- * @param DBObject oTargetObj
- * @param WebServiceResult oRes
- *
- */
- protected function MyObjectSetScalar($sAttCode, $sParamName, $value, &$oTargetObj, &$oRes)
- {
- $res = $oTargetObj->CheckValue($sAttCode, $value);
- if ($res === true) {
- $oTargetObj->Set($sAttCode, $value);
- } else {
- // $res contains the error description
- $oRes->LogError("Unexpected value for parameter $sParamName: $res");
- }
- }
-
- /**
- * Helper to set an external key
- *
- * @param string sAttCode
- * @param array aExtKeyDesc
- * @param DBObject oTargetObj
- * @param WebServiceResult oRes
- *
- */
- protected function MyObjectSetExternalKey($sAttCode, $sParamName, $aExtKeyDesc, &$oTargetObj, &$oRes)
- {
- $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
-
- $bIsMandatory = !$oExtKey->IsNullAllowed();
-
- if (is_null($aExtKeyDesc)) {
- if ($bIsMandatory) {
- $oRes->LogError("Parameter $sParamName: found null for a mandatory key");
- } else {
- // skip silently
- return;
- }
- }
-
- if (count($aExtKeyDesc) == 0) {
- $oRes->LogIssue("Parameter $sParamName: no search condition has been specified", $bIsMandatory);
- return;
- }
-
- $sKeyClass = $oExtKey->GetTargetClass();
- $oReconFilter = new DBObjectSearch($sKeyClass);
- foreach ($aExtKeyDesc as $sForeignAttCode => $value) {
- if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode)) {
- $aCodes = MetaModel::GetFiltersList($sKeyClass);
- $sMsg = "Parameter $sParamName: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass', expecting a value in {".implode(', ', $aCodes)."}";
- $oRes->LogIssue($sMsg, $bIsMandatory);
- }
- // The foreign attribute is one of our reconciliation key
- $oReconFilter->AddCondition($sForeignAttCode, $value, '=');
- }
- $oExtObjects = new CMDBObjectSet($oReconFilter);
- switch ($oExtObjects->Count()) {
- case 0:
- $sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
- $oRes->LogIssue($sMsg, $bIsMandatory);
- break;
- case 1:
- // Do change the external key attribute
- $oForeignObj = $oExtObjects->Fetch();
- $oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
-
- // Report it (no need to report if the object already had this value
- if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) {
- $oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
- }
- break;
- default:
- $sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
- $oRes->LogIssue($sMsg, $bIsMandatory);
- }
- }
-
- /**
- * Helper to link objects
- *
- * @param string sLinkAttCode
- * @param string sLinkedClass
- * @param array $aLinkList
- * @param DBObject oTargetObj
- * @param WebServiceResult oRes
- *
- * @return array List of objects that could not be found
- */
- protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes)
- {
- $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode);
- $sLinkClass = $oLinkAtt->GetLinkedClass();
- $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
-
- $aItemsFound = [];
- $aItemsNotFound = [];
-
- if (is_null($aLinkList)) {
- return $aItemsNotFound;
- }
-
- foreach ($aLinkList as $aItemData) {
- if (!array_key_exists('class', $aItemData)) {
- $oRes->LogWarning("Parameter $sParamName: missing 'class' specification");
- continue; // skip
- }
- $sTargetClass = $aItemData['class'];
- if (!MetaModel::IsValidClass($sTargetClass)) {
- $oRes->LogError("Parameter $sParamName: invalid class '$sTargetClass'");
- continue; // skip
- }
- if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) {
- $oRes->LogError("Parameter $sParamName: '$sTargetClass' is not a child class of '$sLinkedClass'");
- continue; // skip
- }
- $oReconFilter = new DBObjectSearch($sTargetClass);
- $aCIStringDesc = [];
- foreach ($aItemData['search'] as $sAttCode => $value) {
- if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
- $aCodes = MetaModel::GetFiltersList($sTargetClass);
- $oRes->LogError("Parameter $sParamName: '$sAttCode' is not a valid filter code for class '$sTargetClass', expecting a value in {".implode(', ', $aCodes)."}");
- continue 2; // skip the entire item
- }
- $aCIStringDesc[] = "$sAttCode: $value";
-
- // The attribute is one of our reconciliation key
- $oReconFilter->AddCondition($sAttCode, $value, '=');
- }
- if (count($aCIStringDesc) == 1) {
- // take the last and unique value to describe the object
- $sItemDesc = $value;
- } else {
- // describe the object by the given keys
- $sItemDesc = $sTargetClass.'('.implode('/', $aCIStringDesc).')';
- }
-
- $oExtObjects = new CMDBObjectSet($oReconFilter);
- switch ($oExtObjects->Count()) {
- case 0:
- $oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
- $aItemsNotFound[] = $sItemDesc;
- break;
- case 1:
- $aItemsFound[] = [
- 'object' => $oExtObjects->Fetch(),
- 'link_values' => @$aItemData['link_values'],
- 'desc' => $sItemDesc,
- ];
- break;
- default:
- $oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
- $aItemsNotFound[] = $sItemDesc;
- }
- }
-
- if (count($aItemsFound) > 0) {
- $aLinks = [];
- foreach ($aItemsFound as $aItemData) {
- $oLink = MetaModel::NewObject($sLinkClass);
- $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
- foreach ($aItemData['link_values'] as $sKey => $value) {
- if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) {
- $oRes->LogWarning("Parameter $sParamName: Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'");
- } else {
- $oLink->Set($sKey, $value);
- }
- }
- $aLinks[] = $oLink;
- }
- $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks);
- $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet);
- }
-
- return $aItemsNotFound;
- }
-
- /**
- * @param \CMDBObject $oTargetObj
- * @param string $sResultLabel
- * @param \WebServiceResult $oRes
- *
- * @throws \ArchivedObjectException
- * @throws \CoreCannotSaveObjectException
- * @throws \CoreException
- * @throws \CoreUnexpectedValue
- * @throws \CoreWarning
- * @throws \MySQLException
- * @throws \OQLException
- * @throws \SecurityException
- */
- protected function MyObjectInsert($oTargetObj, $sResultLabel, &$oRes)
- {
- if ($oRes->IsOk()) {
- list($bRes, $aIssues) = $oTargetObj->CheckToWrite();
- if ($bRes) {
- $iId = $oTargetObj->DBInsertNoReload();
- $oRes->LogInfo("Created object ".get_class($oTargetObj)."::$iId");
- $oRes->AddResultObject($sResultLabel, $oTargetObj);
- } else {
- $oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)");
- foreach ($aIssues as $iIssue => $sIssue) {
- $oRes->LogError("Issue #$iIssue: $sIssue");
- }
- }
- }
- }
-
- protected static function SoapStructToExternalKeySearch($oExternalKeySearch)
- {
- if (is_null($oExternalKeySearch)) {
- return null;
- }
- if ($oExternalKeySearch->IsVoid()) {
- return null;
- }
-
- $aRes = [];
- foreach ($oExternalKeySearch->conditions as $oSearchCondition) {
- $aRes[$oSearchCondition->attcode] = $oSearchCondition->value;
- }
- return $aRes;
- }
-
- protected static function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
- {
- $aRes =
- [
- 'class' => $oLinkCreationSpec->class,
- 'search' => [],
- 'link_values' => [],
- ];
-
- foreach ($oLinkCreationSpec->conditions as $oSearchCondition) {
- $aRes['search'][$oSearchCondition->attcode] = $oSearchCondition->value;
- }
-
- foreach ($oLinkCreationSpec->attributes as $oAttributeValue) {
- $aRes['link_values'][$oAttributeValue->attcode] = $oAttributeValue->value;
- }
-
- return $aRes;
- }
-
- protected static function SoapStructToAssociativeArray($aArrayOfAssocArray)
- {
- if (is_null($aArrayOfAssocArray)) {
- return [];
- }
-
- $aRes = [];
- foreach ($aArrayOfAssocArray as $aAssocArray) {
- $aRow = [];
- foreach ($aAssocArray as $oKeyValuePair) {
- $aRow[$oKeyValuePair->key] = $oKeyValuePair->value;
- }
- $aRes[] = $aRow;
- }
- return $aRes;
- }
+ public static function GetWSDLContents($sServiceCategory = '')
+ {
+ if ($sServiceCategory == '') {
+ $sServiceCategory = 'BasicServices';
+ }
+ $sWsdlFilePath = call_user_func([$sServiceCategory, 'GetWSDLFilePath']);
+ return file_get_contents($sWsdlFilePath);
+ }
+
+ /**
+ * Helper to log a service delivery
+ *
+ * @param string sVerb
+ * @param array aArgs
+ * @param WebServiceResult oRes
+ *
+ */
+ protected function LogUsage($sVerb, $oRes)
+ {
+ if (!MetaModel::IsLogEnabledWebService()) {
+ return;
+ }
+
+ $oLog = new EventWebService();
+ if ($oRes->IsOk()) {
+ $oLog->Set('message', $sVerb.' was successfully invoked');
+ } else {
+ $oLog->Set('message', $sVerb.' returned errors');
+ }
+ $oLog->Set('userinfo', UserRights::GetUser());
+ $oLog->Set('verb', $sVerb);
+ $oLog->Set('result', $oRes->IsOk());
+ $this->TrimAndSetValue($oLog, 'log_info', (string)$oRes->GetInfoAsText());
+ $this->TrimAndSetValue($oLog, 'log_warning', (string)$oRes->GetWarningsAsText());
+ $this->TrimAndSetValue($oLog, 'log_error', (string)$oRes->GetErrorsAsText());
+ $this->TrimAndSetValue($oLog, 'data', (string)$oRes->GetReturnedDataAsText());
+ $oLog->DBInsertNoReload();
+ }
+
+ protected function TrimAndSetValue($oLog, $sAttCode, $sValue)
+ {
+ $oAttDef = MetaModel::GetAttributeDef(get_class($oLog), $sAttCode);
+ if (is_object($oAttDef)) {
+ $iMaxSize = $oAttDef->GetMaxSize();
+ if ($iMaxSize && (mb_strlen($sValue) > $iMaxSize)) {
+ $sValue = mb_substr($sValue, 0, $iMaxSize);
+ }
+ $oLog->Set($sAttCode, $sValue);
+ }
+ }
+
+ /**
+ * Helper to set a scalar attribute
+ *
+ * @param string sAttCode
+ * @param scalar value
+ * @param DBObject oTargetObj
+ * @param WebServiceResult oRes
+ *
+ */
+ protected function MyObjectSetScalar($sAttCode, $sParamName, $value, &$oTargetObj, &$oRes)
+ {
+ $res = $oTargetObj->CheckValue($sAttCode, $value);
+ if ($res === true) {
+ $oTargetObj->Set($sAttCode, $value);
+ } else {
+ // $res contains the error description
+ $oRes->LogError("Unexpected value for parameter $sParamName: $res");
+ }
+ }
+
+ /**
+ * Helper to set an external key
+ *
+ * @param string sAttCode
+ * @param array aExtKeyDesc
+ * @param DBObject oTargetObj
+ * @param WebServiceResult oRes
+ *
+ */
+ protected function MyObjectSetExternalKey($sAttCode, $sParamName, $aExtKeyDesc, &$oTargetObj, &$oRes)
+ {
+ $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
+
+ $bIsMandatory = !$oExtKey->IsNullAllowed();
+
+ if (is_null($aExtKeyDesc)) {
+ if ($bIsMandatory) {
+ $oRes->LogError("Parameter $sParamName: found null for a mandatory key");
+ } else {
+ // skip silently
+ return;
+ }
+ }
+
+ if (count($aExtKeyDesc) == 0) {
+ $oRes->LogIssue("Parameter $sParamName: no search condition has been specified", $bIsMandatory);
+ return;
+ }
+
+ $sKeyClass = $oExtKey->GetTargetClass();
+ $oReconFilter = new DBObjectSearch($sKeyClass);
+ foreach ($aExtKeyDesc as $sForeignAttCode => $value) {
+ if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode)) {
+ $aCodes = MetaModel::GetFiltersList($sKeyClass);
+ $sMsg = "Parameter $sParamName: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass', expecting a value in {".implode(', ', $aCodes)."}";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
+ }
+ // The foreign attribute is one of our reconciliation key
+ $oReconFilter->AddCondition($sForeignAttCode, $value, '=');
+ }
+ $oExtObjects = new CMDBObjectSet($oReconFilter);
+ switch ($oExtObjects->Count()) {
+ case 0:
+ $sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
+ break;
+ case 1:
+ // Do change the external key attribute
+ $oForeignObj = $oExtObjects->Fetch();
+ $oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
+
+ // Report it (no need to report if the object already had this value
+ if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) {
+ $oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
+ }
+ break;
+ default:
+ $sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
+ }
+ }
+
+ /**
+ * Helper to link objects
+ *
+ * @param string sLinkAttCode
+ * @param string sLinkedClass
+ * @param array $aLinkList
+ * @param DBObject oTargetObj
+ * @param WebServiceResult oRes
+ *
+ * @return array List of objects that could not be found
+ */
+ protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes)
+ {
+ $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode);
+ $sLinkClass = $oLinkAtt->GetLinkedClass();
+ $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
+
+ $aItemsFound = [];
+ $aItemsNotFound = [];
+
+ if (is_null($aLinkList)) {
+ return $aItemsNotFound;
+ }
+
+ foreach ($aLinkList as $aItemData) {
+ if (!array_key_exists('class', $aItemData)) {
+ $oRes->LogWarning("Parameter $sParamName: missing 'class' specification");
+ continue; // skip
+ }
+ $sTargetClass = $aItemData['class'];
+ if (!MetaModel::IsValidClass($sTargetClass)) {
+ $oRes->LogError("Parameter $sParamName: invalid class '$sTargetClass'");
+ continue; // skip
+ }
+ if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) {
+ $oRes->LogError("Parameter $sParamName: '$sTargetClass' is not a child class of '$sLinkedClass'");
+ continue; // skip
+ }
+ $oReconFilter = new DBObjectSearch($sTargetClass);
+ $aCIStringDesc = [];
+ foreach ($aItemData['search'] as $sAttCode => $value) {
+ if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
+ $aCodes = MetaModel::GetFiltersList($sTargetClass);
+ $oRes->LogError("Parameter $sParamName: '$sAttCode' is not a valid filter code for class '$sTargetClass', expecting a value in {".implode(', ', $aCodes)."}");
+ continue 2; // skip the entire item
+ }
+ $aCIStringDesc[] = "$sAttCode: $value";
+
+ // The attribute is one of our reconciliation key
+ $oReconFilter->AddCondition($sAttCode, $value, '=');
+ }
+ if (count($aCIStringDesc) == 1) {
+ // take the last and unique value to describe the object
+ $sItemDesc = $value;
+ } else {
+ // describe the object by the given keys
+ $sItemDesc = $sTargetClass.'('.implode('/', $aCIStringDesc).')';
+ }
+
+ $oExtObjects = new CMDBObjectSet($oReconFilter);
+ switch ($oExtObjects->Count()) {
+ case 0:
+ $oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
+ $aItemsNotFound[] = $sItemDesc;
+ break;
+ case 1:
+ $aItemsFound[] = [
+ 'object' => $oExtObjects->Fetch(),
+ 'link_values' => @$aItemData['link_values'],
+ 'desc' => $sItemDesc,
+ ];
+ break;
+ default:
+ $oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
+ $aItemsNotFound[] = $sItemDesc;
+ }
+ }
+
+ if (count($aItemsFound) > 0) {
+ $aLinks = [];
+ foreach ($aItemsFound as $aItemData) {
+ $oLink = MetaModel::NewObject($sLinkClass);
+ $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
+ foreach ($aItemData['link_values'] as $sKey => $value) {
+ if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) {
+ $oRes->LogWarning("Parameter $sParamName: Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'");
+ } else {
+ $oLink->Set($sKey, $value);
+ }
+ }
+ $aLinks[] = $oLink;
+ }
+ $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks);
+ $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet);
+ }
+
+ return $aItemsNotFound;
+ }
+
+ /**
+ * @param \CMDBObject $oTargetObj
+ * @param string $sResultLabel
+ * @param \WebServiceResult $oRes
+ *
+ * @throws \ArchivedObjectException
+ * @throws \CoreCannotSaveObjectException
+ * @throws \CoreException
+ * @throws \CoreUnexpectedValue
+ * @throws \CoreWarning
+ * @throws \MySQLException
+ * @throws \OQLException
+ * @throws \SecurityException
+ */
+ protected function MyObjectInsert($oTargetObj, $sResultLabel, &$oRes)
+ {
+ if ($oRes->IsOk()) {
+ list($bRes, $aIssues) = $oTargetObj->CheckToWrite();
+ if ($bRes) {
+ $iId = $oTargetObj->DBInsertNoReload();
+ $oRes->LogInfo("Created object ".get_class($oTargetObj)."::$iId");
+ $oRes->AddResultObject($sResultLabel, $oTargetObj);
+ } else {
+ $oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)");
+ foreach ($aIssues as $iIssue => $sIssue) {
+ $oRes->LogError("Issue #$iIssue: $sIssue");
+ }
+ }
+ }
+ }
+
+ protected static function SoapStructToExternalKeySearch($oExternalKeySearch)
+ {
+ if (is_null($oExternalKeySearch)) {
+ return null;
+ }
+ if ($oExternalKeySearch->IsVoid()) {
+ return null;
+ }
+
+ $aRes = [];
+ foreach ($oExternalKeySearch->conditions as $oSearchCondition) {
+ $aRes[$oSearchCondition->attcode] = $oSearchCondition->value;
+ }
+ return $aRes;
+ }
+
+ protected static function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
+ {
+ $aRes =
+ [
+ 'class' => $oLinkCreationSpec->class,
+ 'search' => [],
+ 'link_values' => [],
+ ];
+
+ foreach ($oLinkCreationSpec->conditions as $oSearchCondition) {
+ $aRes['search'][$oSearchCondition->attcode] = $oSearchCondition->value;
+ }
+
+ foreach ($oLinkCreationSpec->attributes as $oAttributeValue) {
+ $aRes['link_values'][$oAttributeValue->attcode] = $oAttributeValue->value;
+ }
+
+ return $aRes;
+ }
+
+ protected static function SoapStructToAssociativeArray($aArrayOfAssocArray)
+ {
+ if (is_null($aArrayOfAssocArray)) {
+ return [];
+ }
+
+ $aRes = [];
+ foreach ($aArrayOfAssocArray as $aAssocArray) {
+ $aRow = [];
+ foreach ($aAssocArray as $oKeyValuePair) {
+ $aRow[$oKeyValuePair->key] = $oKeyValuePair->value;
+ }
+ $aRes[] = $aRow;
+ }
+ return $aRes;
+ }
}
From 2d5acc4c698c50e9e642b49f73254cb8f05d5af6 Mon Sep 17 00:00:00 2001
From: odain
Date: Fri, 17 Oct 2025 12:08:40 +0200
Subject: [PATCH 11/15] format concat with spaces
---
tests/php-code-style/.php-cs-fixer.dist.php | 23 +++++++++++----------
webservices/cron.php | 4 ++--
webservices/status.php | 2 +-
3 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/tests/php-code-style/.php-cs-fixer.dist.php b/tests/php-code-style/.php-cs-fixer.dist.php
index 7ff4a436e0..6ee2e9dd9f 100644
--- a/tests/php-code-style/.php-cs-fixer.dist.php
+++ b/tests/php-code-style/.php-cs-fixer.dist.php
@@ -5,17 +5,17 @@
echo $APPROOT;
$finder = PhpCsFixer\Finder::create()
->exclude('oql')
- ->in($APPROOT.'/addons')
- ->in($APPROOT.'/application')
- ->in($APPROOT.'/core')
- ->in($APPROOT.'/datamodels')
- ->in($APPROOT.'/dictionaries')
- ->in($APPROOT.'/pages')
- ->in($APPROOT.'/portal')
- ->in($APPROOT.'/setup')
- ->in($APPROOT.'/sources')
- ->in($APPROOT.'/synchro')
- ->in($APPROOT.'/tests')
+ ->in($APPROOT . '/addons')
+ ->in($APPROOT . '/application')
+ ->in($APPROOT . '/core')
+ ->in($APPROOT . '/datamodels')
+ ->in($APPROOT . '/dictionaries')
+ ->in($APPROOT . '/pages')
+ ->in($APPROOT . '/portal')
+ ->in($APPROOT . '/setup')
+ ->in($APPROOT . '/sources')
+ ->in($APPROOT . '/synchro')
+ ->in($APPROOT . '/tests')
->in($APPROOT . '/webservices')
;
@@ -26,6 +26,7 @@
'indentation_type' => true,
'no_extra_blank_lines' => true,
'array_syntax' => ['syntax' => 'short'],
+ 'concat_space' => true,
])
->setIndent("\t")
->setLineEnding("\n")
diff --git a/webservices/cron.php b/webservices/cron.php
index 66549e7d15..1d788cca64 100644
--- a/webservices/cron.php
+++ b/webservices/cron.php
@@ -124,9 +124,9 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
$oExceptionToThrow = $e;
} catch (Exception $e) { // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
if ($oTask->IsDebug()) {
- $sMessage = 'Processing failed with message: '. $e->getMessage() . '. ' . $e->getTraceAsString();
+ $sMessage = 'Processing failed with message: '.$e->getMessage().'. '.$e->getTraceAsString();
} else {
- $sMessage = 'Processing failed with message: '. $e->getMessage();
+ $sMessage = 'Processing failed with message: '.$e->getMessage();
}
}
$fDuration = microtime(true) - $fStart;
diff --git a/webservices/status.php b/webservices/status.php
index 793ebca8e5..7ef91b2c0a 100644
--- a/webservices/status.php
+++ b/webservices/status.php
@@ -17,7 +17,7 @@
//Set headers, based on webservices/rest.php
$sContentType = 'application/json';
-header('Content-type: ' . $sContentType);
+header('Content-type: '.$sContentType);
header('Access-Control-Allow-Origin: *');
//Output result
From 013ab40599c6662a48882acaf15426edd19cea8e Mon Sep 17 00:00:00 2001
From: odain
Date: Fri, 17 Oct 2025 16:03:27 +0200
Subject: [PATCH 12/15] finalize included/excluded folders to formatting tool
---
tests/php-code-style/.php-cs-fixer.dist.php | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/tests/php-code-style/.php-cs-fixer.dist.php b/tests/php-code-style/.php-cs-fixer.dist.php
index 6ee2e9dd9f..6b23a172b2 100644
--- a/tests/php-code-style/.php-cs-fixer.dist.php
+++ b/tests/php-code-style/.php-cs-fixer.dist.php
@@ -4,19 +4,9 @@
echo $APPROOT;
$finder = PhpCsFixer\Finder::create()
- ->exclude('oql')
- ->in($APPROOT . '/addons')
- ->in($APPROOT . '/application')
- ->in($APPROOT . '/core')
- ->in($APPROOT . '/datamodels')
- ->in($APPROOT . '/dictionaries')
- ->in($APPROOT . '/pages')
- ->in($APPROOT . '/portal')
- ->in($APPROOT . '/setup')
- ->in($APPROOT . '/sources')
- ->in($APPROOT . '/synchro')
- ->in($APPROOT . '/tests')
- ->in($APPROOT . '/webservices')
+ ->in($APPROOT)
+ ->exclude(['oql', 'data', 'extensions'])
+ ->notPath(['/env-*/', '/cache-*/', 'lib', 'vendor', 'node_modules'])
;
$config = new PhpCsFixer\Config();
@@ -31,4 +21,4 @@
->setIndent("\t")
->setLineEnding("\n")
->setFinder($finder)
-;
+;
\ No newline at end of file
From 44d0910992f891b656d95820be0c7863d2827289 Mon Sep 17 00:00:00 2001
From: odain
Date: Fri, 17 Oct 2025 16:22:58 +0200
Subject: [PATCH 13/15] add trailing_comma_in_multiline option
---
tests/php-code-style/.php-cs-fixer.dist.php | 1 +
webservices/itoprest.examples.php | 10 +++++-----
webservices/soapserver.php | 2 +-
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/tests/php-code-style/.php-cs-fixer.dist.php b/tests/php-code-style/.php-cs-fixer.dist.php
index 6b23a172b2..1478f735cd 100644
--- a/tests/php-code-style/.php-cs-fixer.dist.php
+++ b/tests/php-code-style/.php-cs-fixer.dist.php
@@ -17,6 +17,7 @@
'no_extra_blank_lines' => true,
'array_syntax' => ['syntax' => 'short'],
'concat_space' => true,
+ 'trailing_comma_in_multiline' => true,
])
->setIndent("\t")
->setLineEnding("\n")
diff --git a/webservices/itoprest.examples.php b/webservices/itoprest.examples.php
index 4d07389c19..6408a1ff8f 100644
--- a/webservices/itoprest.examples.php
+++ b/webservices/itoprest.examples.php
@@ -156,7 +156,7 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
'org_id' => "SELECT Organization WHERE name = 'Demo'",
'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
'title' => 'issue blah',
- 'description' => 'something happened'
+ 'description' => 'something happened',
],
],
[
@@ -245,7 +245,7 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
// Values to set
'fields' => [
'team_id' => 15, // Helpdesk
- 'agent_id' => 9 // Jules Verne
+ 'agent_id' => 9, // Jules Verne
],
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
],
@@ -271,7 +271,7 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
'contents' => [
'data' => 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVChTfZHRDYMwDESzQ2fqhHx3C3ao+MkW/WlnaFxfzk7sEnE6JHJ+NgaKZN2zLHVN2ssfkae0Da7FQ5PRk/ve4Hcx19Ie6CEGuh/6vMgNhwanHVUNbt73lUDbYJ+6pg8b3+m2RehsVPdMXyvQY+OVkB+Rrv64lUjb3nq+aCA6v4leRqtfaIgimr53atBy9PlfUhoh3fFCNDmErv9FWR6ylBL5AREbmHBnFj5lAAAAAElFTkSuQmCC',
'filename' => 'myself.png',
- 'mimetype' => 'image/png'
+ 'mimetype' => 'image/png',
],
],
],
@@ -280,7 +280,7 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
'class' => 'Attachment',
'key' => 'SELECT Attachment',
'output_fields' => '*',
- ]
+ ],
];
$aOperations = [
[
@@ -306,7 +306,7 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
'org_id' => "SELECT Organization WHERE name = 'Demo'",
'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
'title' => 'issue blah',
- 'description' => 'something happened'
+ 'description' => 'something happened',
],
],
];
diff --git a/webservices/soapserver.php b/webservices/soapserver.php
index 3c45c11653..055ceb0ebe 100644
--- a/webservices/soapserver.php
+++ b/webservices/soapserver.php
@@ -38,7 +38,7 @@
$oSoapServer = new SoapServer(
$sWsdlUri,
[
- 'classmap' => $aSOAPMapping
+ 'classmap' => $aSOAPMapping,
]
);
// $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION);
From b72fdb4c81ff207fb21da6fd0d2c412f186a6d59 Mon Sep 17 00:00:00 2001
From: odain
Date: Mon, 20 Oct 2025 12:01:52 +0200
Subject: [PATCH 14/15] adapt composer.json requirements
---
tests/php-code-style/composer.json | 4 ++--
tests/php-code-style/composer.lock | 17 ++++++++---------
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/tests/php-code-style/composer.json b/tests/php-code-style/composer.json
index 1d7e9ec0c7..b5c397bde1 100644
--- a/tests/php-code-style/composer.json
+++ b/tests/php-code-style/composer.json
@@ -1,7 +1,7 @@
{
"require-dev": {
- "php": "^7.4 || ^8.0",
- "friendsofphp/php-cs-fixer": "^3.88",
+ "php": "^7.0 || ^8.0",
+ "friendsofphp/php-cs-fixer": "^3.89",
"phpstan/phpstan": "^2.1"
}
}
diff --git a/tests/php-code-style/composer.lock b/tests/php-code-style/composer.lock
index 0b38438dd8..0584bac3a1 100644
--- a/tests/php-code-style/composer.lock
+++ b/tests/php-code-style/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": "afbcf4a8cd7e954326e354a54bd2a1dc",
+ "content-hash": "316ab7e70be8ef55aca6979d178f56d5",
"packages": [],
"packages-dev": [
{
@@ -403,16 +403,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
- "version": "v3.88.2",
+ "version": "v3.89.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
- "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99"
+ "reference": "4dd6768cb7558440d27d18f54909eee417317ce9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99",
- "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4dd6768cb7558440d27d18f54909eee417317ce9",
+ "reference": "4dd6768cb7558440d27d18f54909eee417317ce9",
"shasum": ""
},
"require": {
@@ -427,7 +427,6 @@
"php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6",
"react/event-loop": "^1.5",
- "react/promise": "^3.3",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
@@ -495,7 +494,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
- "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2"
+ "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.89.0"
},
"funding": [
{
@@ -503,7 +502,7 @@
"type": "github"
}
],
- "time": "2025-09-27T00:24:15+00:00"
+ "time": "2025-10-18T19:30:16+00:00"
},
{
"name": "phpstan/phpstan",
@@ -2787,7 +2786,7 @@
"prefer-lowest": false,
"platform": {},
"platform-dev": {
- "php": "^7.4 || ^8.0"
+ "php": "^7.0 || ^8.0"
},
"plugin-api-version": "2.6.0"
}
From 5af9123ef93228212f6ce13075be7df2a05213fb Mon Sep 17 00:00:00 2001
From: odain
Date: Fri, 7 Nov 2025 15:10:19 +0100
Subject: [PATCH 15/15] Revert changes on webservices folder
---
webservices/backoffice.dataloader.php | 50 +-
webservices/createfrommail.php | 61 ++-
webservices/cron.php | 275 ++++++----
webservices/export-v2.php | 141 ++++--
webservices/export.php | 248 +++++----
webservices/import.php | 648 ++++++++++++++----------
webservices/itop.wsdl.php | 11 +-
webservices/itoprest.examples.php | 273 +++++-----
webservices/itopsoap.examples.php | 77 +--
webservices/itopsoaptypes.class.inc.php | 29 +-
webservices/rest.php | 192 ++++---
webservices/soapserver.php | 55 +-
webservices/status.php | 19 +-
webservices/webservices.basic.php | 121 +++--
webservices/webservices.class.inc.php | 288 ++++++-----
15 files changed, 1511 insertions(+), 977 deletions(-)
diff --git a/webservices/backoffice.dataloader.php b/webservices/backoffice.dataloader.php
index b4479974c9..c73ca047f0 100644
--- a/webservices/backoffice.dataloader.php
+++ b/webservices/backoffice.dataloader.php
@@ -1,5 +1,4 @@
p("No memory limit has been defined in this instance of PHP");
- } else {
+ $oP->p("No memory limit has been defined in this instance of PHP");
+ }
+ else
+ {
// Check that the limit will allow us to load the data
//
$iMemoryLimit = utils::ConvertToBytes($sMemoryLimit);
- if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY)) {
- if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === false) {
- $oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
- } else {
- $oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
+ if (!utils::IsMemoryLimitOk($iMemoryLimit, SAFE_MINIMUM_MEMORY))
+ {
+ if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE)
+ {
+ $oP->p("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself.");
+ }
+ else
+ {
+ $oP->p("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY.".");
}
}
}
}
+
////////////////////////////////////////////////////////////////////////////////
//
// Main
@@ -79,7 +87,9 @@ function SetMemoryLimit($oP)
$oP = new WebPage("iTop - Backoffice data loader");
-try {
+
+try
+{
// Note: the data model must be loaded first
$oDataLoader = new XMLDataLoader();
@@ -92,7 +102,8 @@ function SetMemoryLimit($oP)
SetMemoryLimit($oP);
- // The XMLDataLoader constructor has initialized the DB, let's start a transaction
+
+ // The XMLDataLoader constructor has initialized the DB, let's start a transaction
CMDBSource::Query('START TRANSACTION');
$oP->p("Starting data load.");
@@ -117,21 +128,26 @@ function SetMemoryLimit($oP)
}
}
$aWarnings = $oDataLoader->GetWarnings();
- if (count($aWarnings) > 0) {
+ if (count($aWarnings) > 0)
+ {
$oP->p('Warnings ('.count($aWarnings).')');
- foreach ($aWarnings as $sMsg) {
+ foreach ($aWarnings as $sMsg)
+ {
$oP->p(' * '.$sMsg);
}
}
}
-} catch (Exception $e) {
- $oP->p("An error happened while loading the data: ".$e->getMessage());
+}
+catch(Exception $e)
+{
+ $oP->p("An error happened while loading the data: ".$e->getMessage());
$oP->p("Aborting (no data written)...");
CMDBSource::Query('ROLLBACK');
}
-if (function_exists('memory_get_peak_usage')) {
+if (function_exists('memory_get_peak_usage'))
+{
$oP->p("Information: memory peak usage: ".memory_get_peak_usage());
}
diff --git a/webservices/createfrommail.php b/webservices/createfrommail.php
index 4ed93177c0..a83488754a 100644
--- a/webservices/createfrommail.php
+++ b/webservices/createfrommail.php
@@ -41,11 +41,12 @@
function GetSender($aHeaders)
{
- $aResult = ['name' => '', 'email' => ''];
+ $aResult = array('name' => '', 'email' => '');
$aResult['name'] = $aHeaders['From'];
- $aMatches = [];
- if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches)) {
- $aResult['email'] = $aMatches[1].'@'.$aMatches[2];
+ $aMatches = array();
+ if (preg_match('/\(([0-9a-zA-Z\._]+)@(.+)@(.+)\)/U', array_pop($aHeaders['Received']), $aMatches))
+ {
+ $aResult['email'] = $aMatches[1].'@'.$aMatches[2];
}
return $aResult;
}
@@ -60,14 +61,16 @@ function GetSender($aHeaders)
function CreateTicket($sSenderEmail, $sSubject, $sBody)
{
$oTicket = null;
- try {
+ try
+ {
$oContactSearch = new DBObjectSearch('Contact'); // Can be either a Person or a Team, but must be a valid Contact
$oContactSearch->AddCondition('email', $sSenderEmail, '=');
$oSet = new DBObjectSet($oContactSearch);
- if ($oSet->Count() == 1) {
+ if ($oSet->Count() == 1)
+ {
$oContact = $oSet->Fetch();
$oOrganization = MetaModel::GetObject('Organization', $oContact->Get('org_id'));
- $oTicket = new UserRequest();
+ $oTicket = new UserRequest;
$oTicket->Set('title', $sSubject);
$oTicket->Set('description', $sBody);
$oTicket->Set('org_id', $oOrganization->GetKey());
@@ -83,10 +86,14 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
$sUserString = $oContact->GetName().', submitted by email';
CMDBObject::SetTrackInfo($sUserString);
$oTicket->DBInsert();
- } else {
+ }
+ else
+ {
echo "No contact found in iTop having the email: $sSenderEmail, email message ignored.\n";
}
- } catch (Exception $e) {
+ }
+ catch(Exception $e)
+ {
echo "Error: exception ".$e->getMessage();
$oTicket = null;
}
@@ -104,14 +111,15 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
// Note: it is expected that the sender of the email exists a valid contact as a 'Contact'
// in iTop (identified by her/his email address), otherwise the ticket creation will fail
$iNbMessages = $oPop3->numMsg();
-for ($index = 1; $index <= $iNbMessages; $index++) {
+for($index = 1; $index <= $iNbMessages; $index++)
+{
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$params['crlf'] = "\r\n";
$aHeaders = $oPop3->getParsedHeaders($index);
$aSender = GetSender($aHeaders);
- $oDecoder = new Mail_mimeDecode($oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index));
+ $oDecoder = new Mail_mimeDecode( $oPop3->getRawHeaders($index).$params['crlf'].$oPop3->getBody($index) );
$oStructure = $oDecoder->decode($params);
$sSubject = $aHeaders['Subject'];
// Search for the text/plain body part
@@ -121,14 +129,19 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
//echo "\n";
//print_r($oStructure);
//echo " \n";
- if (!isset($oStructure->parts) || count($oStructure->parts) == 0) {
+ if (!isset($oStructure->parts) || count($oStructure->parts) == 0)
+ {
$sTextBody = $oStructure->body;
- } else {
+ }
+ else
+ {
// Find the first "part" of the body which is in text/plain
- while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
+ while( ($iPartIndex < count($oStructure->parts)) && (!$bFound) )
+ {
//echo "Reading part $iPartIndex
\n";
- if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
- ($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain')) {
+ if ( ($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
+ ($oStructure->parts[$iPartIndex]->ctype_secondary == 'plain') )
+ {
$sTextBody = $oStructure->parts[$iPartIndex]->body;
$bFound = true;
//echo "Plain text found ! ($sTextBody)
\n";
@@ -136,11 +149,14 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
$iPartIndex++;
}
// Try again but this time look for an HTML part
- if (!$bFound) {
- while (($iPartIndex < count($oStructure->parts)) && (!$bFound)) {
+ if (!$bFound)
+ {
+ while( ($iPartIndex < count($oStructure->parts)) && (!$bFound) )
+ {
//echo "Reading part $iPartIndex
\n";
- if (($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
- ($oStructure->parts[$iPartIndex]->ctype_secondary == 'html')) {
+ if ( ($oStructure->parts[$iPartIndex]->ctype_primary == 'text') &&
+ ($oStructure->parts[$iPartIndex]->ctype_secondary == 'html') )
+ {
$sTextBody = $oStructure->parts[$iPartIndex]->body;
$bFound = true;
//echo "HTML text found ! (".htmlentities($sTextBody, ENT_QUOTES, 'UTF-8').")
\n";
@@ -155,10 +171,11 @@ function CreateTicket($sSenderEmail, $sSubject, $sBody)
// name => 'john foo
$oTicket = CreateTicket($aSender['email'], $sSubject, $sTextBody);
- if ($oTicket != null) {
+ if ($oTicket != null)
+ {
// Ticket created, delete the email
$oPop3->deleteMsg($index);
- echo "Ticket: ".$oTicket->GetName()." created.\n";
+ echo "Ticket: ".$oTicket->GetName()." created.\n";
}
}
$oPop3->disconnect();
diff --git a/webservices/cron.php b/webservices/cron.php
index 1d788cca64..f51a46f64c 100644
--- a/webservices/cron.php
+++ b/webservices/cron.php
@@ -1,5 +1,4 @@
p("ERROR: Missing argument '$sParam'\n");
UsageAndExit($oP);
}
@@ -61,10 +63,13 @@ function UsageAndExit($oP)
{
$bModeCLI = ($oP instanceof CLIPage);
- if ($bModeCLI) {
+ if ($bModeCLI)
+ {
$oP->p("USAGE:\n");
$oP->p("php cron.php --auth_user= --auth_pwd= [--param_file=] [--verbose=1] [--debug=1] [--status_only=1]\n");
- } else {
+ }
+ else
+ {
$oP->p("Optional parameters: verbose, param_file, status_only\n");
}
$oP->output();
@@ -88,7 +93,7 @@ function UsageAndExit($oP)
function RunTask(BackgroundTask $oTask, $iTimeLimit)
{
$TaskClass = $oTask->Get('class_name');
- $oProcess = new $TaskClass();
+ $oProcess = new $TaskClass;
$oRefClass = new ReflectionClass(get_class($oProcess));
$oDateStarted = new DateTime();
$oDatePlanned = new DateTime($oTask->Get('next_run_date'));
@@ -97,7 +102,8 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
$sMessage = '';
$oExceptionToThrow = null;
- try {
+ try
+ {
// Record (when starting) that this task was started, just in case it crashes during the execution
$oTask->Set('latest_run_date', $oDateStarted->format('Y-m-d H:i:s'));
// Record the current user running the cron
@@ -107,30 +113,42 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
// Time in seconds allowed to the task
$iCurrTimeLimit = $iTimeLimit;
// Compute allowed time
- if ($oRefClass->implementsInterface('iScheduledProcess') === false) {
+ if ($oRefClass->implementsInterface('iScheduledProcess') === false)
+ {
// Periodic task, allow only X times ($iMaxTaskExecutionTime) its periodicity (GetPeriodicity())
$iMaxTaskExecutionTime = MetaModel::GetConfig()->Get('cron_task_max_execution_time');
$iTaskLimit = time() + $oProcess->GetPeriodicity() * $iMaxTaskExecutionTime;
// If our proposed time limit is less than cron limit, and cron_task_max_execution_time is > 0
- if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0) {
+ if ($iTaskLimit < $iTimeLimit && $iMaxTaskExecutionTime > 0)
+ {
$iCurrTimeLimit = $iTaskLimit;
}
}
$sMessage = $oProcess->Process($iCurrTimeLimit);
$oTask->Set('running', 0);
- } catch (MySQLHasGoneAwayException $e) {
+ }
+ catch (MySQLHasGoneAwayException $e)
+ {
throw $e;
- } catch (ProcessFatalException $e) {
+ }
+ catch (ProcessFatalException $e)
+ {
$oExceptionToThrow = $e;
- } catch (Exception $e) { // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
- if ($oTask->IsDebug()) {
- $sMessage = 'Processing failed with message: '.$e->getMessage().'. '.$e->getTraceAsString();
- } else {
- $sMessage = 'Processing failed with message: '.$e->getMessage();
+ }
+ catch (Exception $e) // we shouldn't get so much exceptions... but we need to handle legacy code, and cron.php has to keep running
+ {
+ if ($oTask->IsDebug())
+ {
+ $sMessage = 'Processing failed with message: '. $e->getMessage() . '. ' . $e->getTraceAsString();
+ }
+ else
+ {
+ $sMessage = 'Processing failed with message: '. $e->getMessage();
}
}
$fDuration = microtime(true) - $fStart;
- if ($oTask->Get('total_exec_count') == 0) {
+ if ($oTask->Get('total_exec_count') == 0)
+ {
// First execution
$oTask->Set('first_run_date', $oDateStarted->format('Y-m-d H:i:s'));
}
@@ -140,16 +158,20 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
$oDateEnded = new DateTime();
$oTask->Set('latest_run_date', $oDateEnded->format('Y-m-d H:i:s'));
- if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ if ($oRefClass->implementsInterface('iScheduledProcess'))
+ {
// Schedules process do repeat at specific moments
$oPlannedStart = $oProcess->GetNextOccurrence();
- } else {
+ }
+ else
+ {
// Background processes do repeat periodically
$oPlannedStart = clone $oDatePlanned;
// Let's schedule from the previous planned date of execution to avoid shift
$oPlannedStart->modify($oProcess->GetPeriodicity().' seconds');
$oEnd = new DateTime();
- while ($oPlannedStart->format('U') < $oEnd->format('U')) {
+ while ($oPlannedStart->format('U') < $oEnd->format('U'))
+ {
// Next planned start is already in the past, increase it again by a period
$oPlannedStart = $oPlannedStart->modify('+'.$oProcess->GetPeriodicity().' seconds');
}
@@ -158,7 +180,8 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
$oTask->Set('next_run_date', $oPlannedStart->format('Y-m-d H:i:s'));
$oTask->DBUpdate();
- if ($oExceptionToThrow) {
+ if ($oExceptionToThrow)
+ {
throw $oExceptionToThrow;
}
@@ -184,21 +207,23 @@ function RunTask(BackgroundTask $oTask, $iTimeLimit)
* @throws \OQLException
* @throws \ReflectionException
*/
-function CronExec($oP, $bVerbose, $bDebug = false)
+function CronExec($oP, $bVerbose, $bDebug=false)
{
$iStarted = time();
$iMaxDuration = MetaModel::GetConfig()->Get('cron_max_execution_time');
$iTimeLimit = $iStarted + $iMaxDuration;
$iCronSleep = MetaModel::GetConfig()->Get('cron_sleep');
- if ($bVerbose) {
+ if ($bVerbose)
+ {
$oP->p("Planned duration = $iMaxDuration seconds");
$oP->p("Loop pause = $iCronSleep seconds");
}
ReSyncProcesses($oP, $bVerbose, $bDebug);
- while (time() < $iTimeLimit) {
+ while (time() < $iTimeLimit)
+ {
CheckMaintenanceMode($oP);
$oNow = new DateTime();
@@ -209,19 +234,23 @@ function CronExec($oP, $bVerbose, $bDebug = false)
$oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
$bWorkDone = false;
- if ($oTasks->CountExceeds(0)) {
+ if ($oTasks->CountExceeds(0))
+ {
$bWorkDone = true;
- $aTasks = [];
- if ($bVerbose) {
+ $aTasks = array();
+ if ($bVerbose)
+ {
$sCount = $oTasks->Count();
$oP->p("$sCount Tasks planned to run now ($sNow):");
$oP->p('+---------------------------+---------+---------------------+---------------------+');
$oP->p('| Task Class | Status | Last Run | Next Run |');
$oP->p('+---------------------------+---------+---------------------+---------------------+');
}
- while ($oTask = $oTasks->Fetch()) {
+ while ($oTask = $oTasks->Fetch())
+ {
$aTasks[$oTask->Get('class_name')] = $oTask;
- if ($bVerbose) {
+ if ($bVerbose)
+ {
$sTaskName = $oTask->Get('class_name');
$sStatus = $oTask->Get('status');
$sLastRunDate = $oTask->Get('latest_run_date');
@@ -229,11 +258,13 @@ function CronExec($oP, $bVerbose, $bDebug = false)
$oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s |', $sTaskName, $sStatus, $sLastRunDate, $sNextRunDate));
}
}
- if ($bVerbose) {
+ if ($bVerbose)
+ {
$oP->p('+---------------------------+---------+---------------------+---------------------+');
}
$aRunTasks = [];
- foreach ($aTasks as $oTask) {
+ foreach ($aTasks as $oTask)
+ {
$sTaskClass = $oTask->Get('class_name');
$aRunTasks[] = $sTaskClass;
@@ -242,54 +273,66 @@ function CronExec($oP, $bVerbose, $bDebug = false)
CMDBObject::SetCurrentChangeFromParams("Background task ($sTaskClass)");
// Run the task and record its next run time
- if ($bVerbose) {
+ if ($bVerbose)
+ {
$oNow = new DateTime();
$oP->p(">> === ".$oNow->format('Y-m-d H:i:s').sprintf(" Starting:%-'=49s", ' '.$sTaskClass.' '));
}
- try {
+ try
+ {
$sMessage = RunTask($aTasks[$sTaskClass], $iTimeLimit);
- } catch (MySQLHasGoneAwayException $e) {
+ } catch (MySQLHasGoneAwayException $e)
+ {
$oP->p("ERROR : 'MySQL has gone away' thrown when processing $sTaskClass (error_code=".$e->getCode().")");
exit(EXIT_CODE_FATAL);
- } catch (ProcessFatalException $e) {
+ } catch (ProcessFatalException $e)
+ {
$oP->p("ERROR : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().")");
IssueLog::Error("Cron.php error : an exception was thrown when processing '$sTaskClass' (".$e->getInfoLog().')');
}
- if ($bVerbose) {
- if (!empty($sMessage)) {
+ if ($bVerbose)
+ {
+ if (!empty($sMessage))
+ {
$oP->p("$sTaskClass: $sMessage");
}
$oEnd = new DateTime();
$sNextRunDate = $oTask->Get('next_run_date');
$oP->p("<< === ".$oEnd->format('Y-m-d H:i:s').sprintf(" End of: %-'=42s", ' '.$sTaskClass.' ')." Next: $sNextRunDate");
}
- if (time() > $iTimeLimit) {
+ if (time() > $iTimeLimit)
+ {
break 2;
}
CheckMaintenanceMode($oP);
}
// Tasks to run later
- if ($bVerbose) {
+ if ($bVerbose)
+ {
$oP->p('--');
$oSearch = new DBObjectSearch('BackgroundTask');
$oSearch->AddCondition('next_run_date', $sNow, '>');
$oSearch->AddCondition('status', 'active');
$oTasks = new DBObjectSet($oSearch, ['next_run_date' => true]);
- while ($oTask = $oTasks->Fetch()) {
- if (!in_array($oTask->Get('class_name'), $aRunTasks)) {
+ while ($oTask = $oTasks->Fetch())
+ {
+ if (!in_array($oTask->Get('class_name'), $aRunTasks))
+ {
$oP->p(sprintf("-- Skipping task: %-'-40s", $oTask->Get('class_name').' ')." until: ".$oTask->Get('next_run_date'));
}
}
}
}
- if ($bVerbose && $bWorkDone) {
+ if ($bVerbose && $bWorkDone)
+ {
$oP->p("Sleeping...\n");
}
sleep($iCronSleep);
}
- if ($bVerbose) {
+ if ($bVerbose)
+ {
$oP->p('');
DisplayStatus($oP, ['next_run_date' => true]);
$oP->p("Reached normal execution time limit (exceeded by ".(time() - $iTimeLimit)."s)");
@@ -299,9 +342,8 @@ function CronExec($oP, $bVerbose, $bDebug = false)
/**
* @param WebPage $oP
*/
-function CheckMaintenanceMode(Page $oP)
-{
- // Verify files instead of reloading the full config each time
+function CheckMaintenanceMode(Page $oP) {
+// Verify files instead of reloading the full config each time
if (file_exists(MAINTENANCE_MODE_FILE) || file_exists(READONLY_MODE_FILE)) {
$oP->p("Maintenance detected, exiting");
exit(EXIT_CODE_ERROR);
@@ -325,22 +367,16 @@ function DisplayStatus($oP, $aTaskOrderBy = [])
$oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
$oP->p('| Task Class | Status | Last Run | Next Run | Nb Run | Avg. Dur. |');
$oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
- while ($oTask = $oTasks->Fetch()) {
+ while ($oTask = $oTasks->Fetch())
+ {
$sTaskName = $oTask->Get('class_name');
$sStatus = $oTask->Get('status');
$sLastRunDate = $oTask->Get('latest_run_date');
$sNextRunDate = $oTask->Get('next_run_date');
$iNbRun = (int)$oTask->Get('total_exec_count');
$sAverageRunTime = $oTask->Get('average_run_duration');
- $oP->p(sprintf(
- '| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |',
- $sTaskName,
- $sStatus,
- $sLastRunDate,
- $sNextRunDate,
- $iNbRun,
- $sAverageRunTime
- ));
+ $oP->p(sprintf('| %1$-25.25s | %2$-7s | %3$-19s | %4$-19s | %5$6d | %6$7s s |', $sTaskName, $sStatus,
+ $sLastRunDate, $sNextRunDate, $iNbRun, $sAverageRunTime));
}
$oP->p('+---------------------------+---------+---------------------+---------------------+--------+-----------+');
}
@@ -365,19 +401,22 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
//
$oSearch = new DBObjectSearch('BackgroundTask');
$oTasks = new DBObjectSet($oSearch);
- $aTasks = [];
- while ($oTask = $oTasks->Fetch()) {
+ $aTasks = array();
+ while ($oTask = $oTasks->Fetch())
+ {
$aTasks[$oTask->Get('class_name')] = $oTask;
}
$oNow = new DateTime();
- $aProcesses = [];
- foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass) {
- $oProcess = new $sTaskClass();
+ $aProcesses = array();
+ foreach (InterfaceDiscovery::GetInstance()->FindItopClasses(iProcess::class) as $sTaskClass)
+ {
+ $oProcess = new $sTaskClass;
$aProcesses[$sTaskClass] = $oProcess;
// Create missing entry if needed
- if (!array_key_exists($sTaskClass, $aTasks)) {
+ if (!array_key_exists($sTaskClass, $aTasks))
+ {
// New entry, let's create a new BackgroundTask record, and plan the first execution
$oTask = new BackgroundTask();
$oTask->SetDebug($bDebug);
@@ -387,32 +426,41 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
$oTask->Set('max_run_duration', 0);
$oTask->Set('average_run_duration', 0);
$oRefClass = new ReflectionClass($sTaskClass);
- if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ if ($oRefClass->implementsInterface('iScheduledProcess'))
+ {
$oNextOcc = $oProcess->GetNextOccurrence();
$oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
- } else {
+ }
+ else
+ {
// Background processes do start asap, i.e. "now"
$oTask->Set('next_run_date', $oNow->format('Y-m-d H:i:s'));
}
- if ($bVerbose) {
+ if ($bVerbose)
+ {
$oP->p('Creating record for: '.$sTaskClass);
$oP->p('First execution planned at: '.$oTask->Get('next_run_date'));
}
$oTask->DBInsert();
- } else {
+ }
+ else
+ {
/** @var \BackgroundTask $oTask */
$oTask = $aTasks[$sTaskClass];
- if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00') {
+ if ($oTask->Get('next_run_date') == '3000-01-01 00:00:00')
+ {
// check for rescheduled tasks
$oRefClass = new ReflectionClass($sTaskClass);
- if ($oRefClass->implementsInterface('iScheduledProcess')) {
+ if ($oRefClass->implementsInterface('iScheduledProcess'))
+ {
$oNextOcc = $oProcess->GetNextOccurrence();
$oTask->Set('next_run_date', $oNextOcc->format('Y-m-d H:i:s'));
$oTask->DBUpdate();
}
}
// Reactivate task if necessary
- if ($oTask->Get('status') == 'removed') {
+ if ($oTask->Get('status') == 'removed')
+ {
$oTask->Set('status', 'active');
$oTask->DBUpdate();
}
@@ -422,17 +470,21 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
}
// Remove all the tasks not having a valid class
- foreach ($aTasks as $oTask) {
+ foreach ($aTasks as $oTask)
+ {
$sTaskClass = $oTask->Get('class_name');
- if (!class_exists($sTaskClass)) {
+ if (!class_exists($sTaskClass))
+ {
$oTask->Set('status', 'removed');
$oTask->DBUpdate();
}
}
- if ($bVerbose) {
- $aDisplayProcesses = [];
- foreach ($aProcesses as $oExecInstance) {
+ if ($bVerbose)
+ {
+ $aDisplayProcesses = array();
+ foreach ($aProcesses as $oExecInstance)
+ {
$aDisplayProcesses[] = get_class($oExecInstance);
}
$sDisplayProcesses = implode(', ', $aDisplayProcesses);
@@ -448,45 +500,58 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
set_time_limit(0); // Some background actions may really take long to finish (like backup)
$bIsModeCLI = utils::IsModeCLI();
-if ($bIsModeCLI) {
+if ($bIsModeCLI)
+{
$oP = new CLIPage("iTop - cron");
SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
-} else {
+}
+else
+{
$oP = new WebPage("iTop - cron");
}
-try {
+try
+{
utils::UseParamFile();
$bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */);
$bDebug = utils::ReadParam('debug', false, true /* Allow CLI */);
- if ($bIsModeCLI) {
+ if ($bIsModeCLI)
+ {
// Next steps:
// specific arguments: 'csv file'
//
$sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
+ {
UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
+ }
+ else
+ {
$oP->p("Access wrong credentials ('$sAuthUser')");
$oP->output();
exit(EXIT_CODE_ERROR);
}
- } else {
+ }
+ else
+ {
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
}
- if (!UserRights::IsAdministrator()) {
+ if (!UserRights::IsAdministrator())
+ {
$oP->p("Access restricted to administrators");
$oP->Output();
exit(EXIT_CODE_ERROR);
}
- if (utils::ReadParam('status_only', false, true /* Allow CLI */)) {
+
+ if (utils::ReadParam('status_only', false, true /* Allow CLI */))
+ {
// Display status and exit
DisplayStatus($oP);
exit(0);
@@ -494,36 +559,54 @@ function ReSyncProcesses($oP, $bVerbose, $bDebug)
require_once(APPROOT.'core/mutex.class.inc.php');
$oP->p("Starting: ".time().' ('.date('Y-m-d H:i:s').')');
-} catch (Exception $e) {
+}
+catch (Exception $e)
+{
$oP->p("Error: ".$e->GetMessage());
$oP->output();
exit(EXIT_CODE_FATAL);
}
-try {
+try
+{
$oMutex = new iTopMutex('cron');
- if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) {
+ if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
+ {
$oP->p("A maintenance is ongoing");
- } else {
- if ($oMutex->TryLock()) {
+ }
+ else
+ {
+ if ($oMutex->TryLock())
+ {
CronExec($oP, $bVerbose, $bDebug);
- } else {
+ }
+ else
+ {
// Exit silently
$oP->p("Already running...");
}
}
-} catch (Exception $e) {
+}
+catch (Exception $e)
+{
$oP->p("ERROR: '".$e->getMessage()."'");
- if ($bDebug) {
+ if ($bDebug)
+ {
// Might contain verb parameters such a password...
$oP->p($e->getTraceAsString());
}
-} finally {
- try {
+}
+finally
+{
+ try
+ {
$oMutex->Unlock();
- } catch (Exception $e) {
+ }
+ catch (Exception $e)
+ {
$oP->p("ERROR: '".$e->getMessage()."'");
- if ($bDebug) {
+ if ($bDebug)
+ {
// Might contain verb parameters such a password...
$oP->p($e->getTraceAsString());
}
diff --git a/webservices/export-v2.php b/webservices/export-v2.php
index ad268232ce..a902bf0442 100644
--- a/webservices/export-v2.php
+++ b/webservices/export-v2.php
@@ -1,5 +1,4 @@
p('ERROR: '.utils::HtmlEntities($sErrorMessage));
$oP->output();
exit(EXIT_CODE_ERROR);
- } else {
+ }
+ else
+ {
$oP = new WebPage("iTop - Export");
$oP->add_http_headers();
$oP->p('ERROR: '.utils::HtmlEntities($sErrorMessage));
@@ -52,13 +54,15 @@ function ReportErrorAndExit($sErrorMessage)
function ReportErrorAndUsage($sErrorMessage)
{
- if (utils::IsModeCLI()) {
+ if (utils::IsModeCLI())
+ {
$oP = new CLIPage("iTop - Export");
$oP->p('ERROR: '.$sErrorMessage);
Usage($oP);
$oP->output();
exit(EXIT_CODE_ERROR);
- } else {
+ }
+ else {
$oP = new WebPage("iTop - Export");
$oP->add_http_headers();
$oP->p('ERROR: '.$sErrorMessage);
@@ -70,32 +74,42 @@ function ReportErrorAndUsage($sErrorMessage)
function Usage(Page $oP)
{
- if (Utils::IsModeCLI()) {
+ if (Utils::IsModeCLI())
+ {
$oP->p('Usage: php '.basename(__FILE__).' --auth_user= --auth_pwd= --expression= --query= [--arg_xxx=] [--no_localize=0|1] [--format=] [--format-options...]');
$oP->p("Parameters:");
$oP->p(" * auth_user: the iTop user account for authentication");
$oP->p(" * auth_pwd: the password of the iTop user account");
- } else {
+ }
+ else
+ {
$oP->p("Parameters:");
}
$oP->p(" * expression: an OQL expression (e.g. SELECT Contact WHERE name LIKE 'm%')");
$oP->p(" * query: (alternative to 'expression') the id of an entry from the query phrasebook");
- if (Utils::IsModeCLI()) {
+ if (Utils::IsModeCLI())
+ {
$oP->p(" * with_archive: (optional, defaults to 0) if set to 1 then the result set will include archived objects");
- } else {
+ }
+ else
+ {
$oP->p(" * with_archive: (optional, defaults to the current mode) if set to 1 then the result set will include archived objects");
}
$oP->p(" * arg_xxx: (needed if the query has parameters) the value of the parameter 'xxx'");
$aSupportedFormats = BulkExport::FindSupportedFormats();
$oP->p(" * format: (optional, default is html) the desired output format. Can be one of '".implode("', '", array_keys($aSupportedFormats))."'");
- foreach ($aSupportedFormats as $sFormatCode => $sLabel) {
+ foreach($aSupportedFormats as $sFormatCode => $sLabel)
+ {
$oExporter = BulkExport::FindExporter($sFormatCode);
- if ($oExporter !== null) {
- if (!Utils::IsModeCLI()) {
+ if ($oExporter !== null)
+ {
+ if (!Utils::IsModeCLI())
+ {
$oP->add(' ');
}
$oExporter->DisplayUsage($oP);
- if (!Utils::IsModeCLI()) {
+ if (!Utils::IsModeCLI())
+ {
$oP->add('');
}
}
@@ -234,18 +248,19 @@ function FormatDatesInPreview(sRadioSelector, sPreviewSelector)
$sExpressionError = '';
if (($sExpression === null) && ($sQueryId === null)) {
$bExpressionIsValid = false;
- } elseif ($sExpression !== '') {
+ } else if ($sExpression !== '') {
try {
$oExportSearch = DBObjectSearch::FromOQL($sExpression);
$oExportSearch->UpdateContextFromUser();
- } catch (OQLException $e) {
+ }
+ catch (OQLException $e) {
$bExpressionIsValid = false;
$sExpressionError = $e->getMessage();
}
}
if (!$bExpressionIsValid) {
- DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError, $oForm);
+ DisplayExpressionForm($oP, $sAction, $sExpression, $sExpressionError,$oForm);
return;
}
@@ -260,12 +275,13 @@ function FormatDatesInPreview(sRadioSelector, sPreviewSelector)
$oExportSearch->UpdateContextFromUser();
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("query", $sQueryId));
}
- $aFormPartsByFormat = [];
- $aAllFormParts = [];
+ $aFormPartsByFormat = array();
+ $aAllFormParts = array();
if ($sFormat == null) {
// No specific format chosen
$sDefaultFormat = utils::ReadParam('format', 'xlsx');
+
$oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("format", Dict::S('Core:BulkExport:ExportFormatPrompt'), "format_selector");
$oSelect->SetIsLabelBefore(true);
$oForm->AddSubBlock($oSelect);
@@ -374,7 +390,7 @@ function InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode)
if ($sExpression === null) {
// No expression supplied, let's check if phrasebook entry is given
if ($sQueryId !== null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
$oSearch->UpdateContextFromUser();
$oQueries = new DBObjectSet($oSearch);
if ($oQueries->Count() > 0) {
@@ -396,6 +412,7 @@ function InteractiveShell($sExpression, $sQueryId, $sFormat, $sFileName, $sMode)
}
}
+
if ($sFormat !== null) {
$oExporter = BulkExport::FindExporter($sFormat);
if ($oExporter === null) {
@@ -435,7 +452,7 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
// Either $sExpression or $sQueryId must be specified
if ($sExpression === null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
$oSearch->UpdateContextFromUser();
$oQueries = new DBObjectSet($oSearch);
if ($oQueries->Count() > 0) {
@@ -453,7 +470,7 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
try {
$oSearch = DBObjectSearch::FromOQL($sExpression);
$oSearch->UpdateContextFromUser();
- $aArgs = [];
+ $aArgs = array();
foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
$value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
if (!is_null($value)) {
@@ -466,23 +483,30 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
$sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
$oExporter = BulkExport::FindExporter($sFormat, $oSearch);
- if ($oExporter == null) {
+ if ($oExporter == null)
+ {
$aSupportedFormats = BulkExport::FindSupportedFormats();
ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
}
- } catch (MissingQueryArgument $e) {
+ }
+ catch(MissingQueryArgument $e)
+ {
$oSearch = null;
ReportErrorAndUsage("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (OQLException $e) {
+ }
+ catch(OQLException $e)
+ {
$oSearch = null;
ReportErrorAndExit("Invalid OQL query: '".utils::HtmlEntities($sExpression)."'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (Exception $e) {
+ }
+ catch(Exception $e)
+ {
$oSearch = null;
ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
}
// update last export information if check parameters ok
- if ($oQuery != null) {
+ if($oQuery != null){
$oQuery->UpdateLastExportInformation();
}
@@ -498,18 +522,24 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
{
$oExporter->SetHttpHeaders($oP);
$exportResult = $oExporter->GetHeader();
- $aStatus = [];
- do {
+ $aStatus = array();
+ do
+ {
$exportResult .= $oExporter->GetNextChunk($aStatus);
- } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+ }
+ while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
- if ($aStatus['code'] == 'error') {
+ if ($aStatus['code'] == 'error')
+ {
$oExporter->Cleanup();
ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
- } else {
+ }
+ else
+ {
$exportResult .= $oExporter->GetFooter();
$sMimeType = $oExporter->GetMimeType();
- if (substr($sMimeType, 0, 5) == 'text/') {
+ if (substr($sMimeType, 0, 5) == 'text/')
+ {
$sMimeType .= ';charset='.strtolower($oExporter->GetCharacterSet());
}
$oP->SetContentType($sMimeType);
@@ -519,6 +549,7 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
}
}
+
/////////////////////////////////////////////////////////////////////////////
//
// Command Line mode
@@ -536,7 +567,8 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
try {
// Do this before loging, in order to allow setting user credentials from within the file
utils::UseParamFile();
- } catch (Exception $e) {
+ }
+ catch (Exception $e) {
echo "Error: ".utils::HtmlEntities($e->getMessage())." \n";
exit(EXIT_CODE_FATAL);
}
@@ -568,7 +600,7 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
}
if ($sExpression === null) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
$oSearch->UpdateContextFromUser();
$oQueries = new DBObjectSet($oSearch);
if ($oQueries->Count() > 0) {
@@ -581,7 +613,7 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
try {
$oSearch = DBObjectSearch::FromOQL($sExpression);
$oSearch->UpdateContextFromUser();
- $aArgs = [];
+ $aArgs = array();
foreach ($oSearch->GetQueryParams() as $sParam => $foo) {
$value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
if (!is_null($value)) {
@@ -594,7 +626,8 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
$sFormat = utils::ReadParam('format', 'html', true /* Allow CLI */, 'raw_data');
$oExporter = BulkExport::FindExporter($sFormat);
- if ($oExporter == null) {
+ if ($oExporter == null)
+ {
$aSupportedFormats = BulkExport::FindSupportedFormats();
ReportErrorAndExit("Invalid output format: '$sFormat'. The supported formats are: ".implode(', ', array_keys($aSupportedFormats)));
}
@@ -605,25 +638,36 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
$oExporter->ReadParameters();
$exportResult = $oExporter->GetHeader();
- $aStatus = [];
+ $aStatus = array();
- do {
+ do
+ {
$exportResult .= $oExporter->GetNextChunk($aStatus);
- } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+ }
+ while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
- if ($aStatus['code'] == 'error') {
+ if ($aStatus['code'] == 'error')
+ {
ReportErrorAndExit("Export failed: '{$aStatus['message']}'");
- } else {
+ }
+ else
+ {
$exportResult .= $oExporter->GetFooter();
echo $exportResult;
}
$oExporter->Cleanup();
- } catch (MissingQueryArgument $e) {
+ }
+ catch(MissingQueryArgument $e)
+ {
ReportErrorAndUsage("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (OQLException $e) {
+ }
+ catch(OQLException $e)
+ {
ReportErrorAndExit("Invalid OQL query: '$sExpression'.\n".utils::HtmlEntities($e->getMessage()));
- } catch (Exception $e) {
+ }
+ catch(Exception $e)
+ {
ReportErrorAndExit(utils::HtmlEntities($e->getMessage()));
}
@@ -636,7 +680,8 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
//
/////////////////////////////////////////////////////////////////////////////
-try {
+try
+{
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
// Main parameters
@@ -677,12 +722,14 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false)
DoExport($oP, $oExporter, false);
$oP->output();
}
-} catch (BulkExportMissingParameterException $e) {
+}
+catch (BulkExportMissingParameterException $e) {
$oP = new AjaxPage('iTop Export');
$oP->add(utils::HtmlEntities($e->getMessage()));
Usage($oP);
$oP->output();
-} catch (Exception $e) {
+}
+catch (Exception $e) {
$oP = new WebPage('iTop Export');
$oP->add_http_headers();
$oP->add('Error: '.utils::HtmlEntities($e->getMessage()));
diff --git a/webservices/export.php b/webservices/export.php
index 18fa2b441e..3a43ce4089 100644
--- a/webservices/export.php
+++ b/webservices/export.php
@@ -1,5 +1,4 @@
GetMessage()." \n";
exit(EXIT_CODE_FATAL);
}
@@ -54,31 +59,39 @@
* @since 3.1.0 N°6047
*/
$oCtx = new ContextTag(ContextTag::TAG_EXPORT);
-if (utils::IsModeCLI()) {
+if (utils::IsModeCLI())
+{
$oP = new CLIPage("iTop - Export");
SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL);
$sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data');
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
+ {
UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
+ }
+ else
+ {
$oP->p("Access restricted or wrong credentials ('$sAuthUser')");
$oP->output();
exit(EXIT_CODE_ERROR);
}
-} else {
+}
+else
+{
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
}
ApplicationContext::SetUrlMakerClass('iTopStandardURLMaker');
+
$oAppContext = new ApplicationContext();
$iActiveNodeId = utils::ReadParam('menu', -1);
$currentOrganization = utils::ReadParam('org_id', '');
-if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) {
+if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive())
+{
$oP = new CLIPage("iTop - Export");
$oP->p("The user account is not authorized to access the archives");
$oP->output();
@@ -95,15 +108,19 @@
$oQuery = null;
-if (strlen($sExpression) == 0) {
+if (strlen($sExpression) == 0)
+{
$sQueryId = trim(utils::ReadParam('query', '', true /* Allow CLI */, 'raw_data'));
- if (strlen($sQueryId) > 0) {
- $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', ['query_id' => $sQueryId]);
+ if (strlen($sQueryId) > 0)
+ {
+ $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId));
$oQueries = new DBObjectSet($oSearch);
- if ($oQueries->Count() > 0) {
+ if ($oQueries->Count() > 0)
+ {
$oQuery = $oQueries->Fetch();
$sExpression = $oQuery->Get('oql');
- if (strlen($sFields) == 0) {
+ if (strlen($sFields) == 0)
+ {
$sFields = trim($oQuery->Get('fields'));
}
}
@@ -112,29 +129,38 @@
$sFormat = strtolower(utils::ReadParam('format', 'html', true /* Allow CLI */));
+
$aFields = explode(',', $sFields);
// Clean the list of columns (empty it if every string is empty)
-foreach ($aFields as $index => $sField) {
+foreach($aFields as $index => $sField)
+{
$aFields[$index] = trim($sField);
- if (strlen($aFields[$index]) == 0) {
+ if(strlen($aFields[$index]) == 0)
+ {
unset($aFields[$index]);
}
}
$oP = null;
-if (!empty($sExpression)) {
- try {
+if (!empty($sExpression))
+{
+ try
+ {
$oFilter = DBObjectSearch::FromOQL($sExpression);
// Check and adjust column names
//
- $aAliasToFields = [];
- foreach ($aFields as $index => $sField) {
- if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches)) {
+ $aAliasToFields = array();
+ foreach($aFields as $index => $sField)
+ {
+ if (preg_match('/^(.*)\.(.*)$/', $sField, $aMatches))
+ {
$sClassAlias = $aMatches[1];
$sAttCode = $aMatches[2];
- } else {
+ }
+ else
+ {
$sClassAlias = $oFilter->GetClassAlias();
$sAttCode = $sField;
// Disambiguate the class alias
@@ -143,13 +169,17 @@
$aAliasToFields[$sClassAlias][] = $sAttCode;
$sClass = $oFilter->GetClassName($sClassAlias);
- if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
+ if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
+ {
throw new CoreException("Invalid field specification $sField: $sAttCode is not a valid attribute for $sClass");
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
- if ($oAttDef instanceof AttributeSubItem) {
+ if ($oAttDef instanceof AttributeSubItem)
+ {
$aAliasToFields[$sClassAlias][] = $oAttDef->GetParentAttCode();
- } elseif ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) {
+ }
+ else if($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName())
+ {
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$aAliasToFields[$sClassAlias][] = $sKeyAttCode;
}
@@ -157,29 +187,35 @@
// Read query parameters
//
- $aArgs = [];
- foreach ($oFilter->GetQueryParams() as $sParam => $foo) {
+ $aArgs = array();
+ foreach($oFilter->GetQueryParams() as $sParam => $foo)
+ {
$value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
- if (!is_null($value)) {
+ if (!is_null($value))
+ {
$aArgs[$sParam] = $value;
}
}
$oFilter->SetInternalParams($aArgs);
- foreach ($oFilter->GetSelectedClasses() as $sAlias => $sClass) {
- if ((UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) && UR_ALLOWED_YES) == 0) {
+ foreach ($oFilter->GetSelectedClasses() as $sAlias => $sClass)
+ {
+ if ((UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_READ) && UR_ALLOWED_YES) == 0)
+ {
throw new Exception("The current user does not have permission for exporting data of class $sClass");
}
}
// update last export information if check parameters ok
- if ($oQuery != null) {
+ if($oQuery != null){
$oQuery->UpdateLastExportInformation();
}
- if ($oFilter) {
- $oSet = new CMDBObjectSet($oFilter, [], $aArgs);
+ if ($oFilter)
+ {
+ $oSet = new CMDBObjectSet($oFilter, array(), $aArgs);
$oSet->OptimizeColumnLoad($aAliasToFields);
- switch ($sFormat) {
+ switch($sFormat)
+ {
case 'html':
$oP = new NiceWebPage("iTop - Export");
$oP->add_style('body { overflow: auto; }'); // Show scroll bars if needed
@@ -207,7 +243,7 @@
$bViewLink = false;
}
$sFields = implode(',', $aFields);
- $aExtraParams = [
+ $aExtraParams = array(
'menu' => false,
'toolkit_menu' => false,
'display_limit' => false,
@@ -215,15 +251,15 @@
'zlist' => false,
'extra_fields' => $sFields,
'view_link' => $bViewLink,
- ];
+ );
} else {
- $aExtraParams = [
+ $aExtraParams = array(
'menu' => false,
'toolkit_menu' => false,
'display_limit' => false,
'localize_values' => $bLocalize,
'zlist' => 'details',
- ];
+ );
}
$oResultBlock = new DisplayBlock($oFilter, 'list', false, $aExtraParams);
@@ -231,85 +267,105 @@
break;
case 'csv':
- $oP = new CSVPage("iTop - Export");
- $sFields = implode(',', $aFields);
- $sCharset = utils::ReadParam('charset', MetaModel::GetConfig()->Get('csv_file_default_charset'), true /* Allow CLI */, 'raw_data');
- $sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize], $sCharset);
- if ($sCharset == 'UTF-8') {
- $sOutputData = UTF8_BOM.$sCSVData;
- } else {
- $sOutputData = $sCSVData;
- }
- if ($sFileName == '') {
- // Plain text => Firefox will NOT propose to download the file
- $oP->add_header("Content-type: text/plain; charset=$sCharset");
- } else {
- $oP->add_header("Content-type: text/csv; charset=$sCharset");
- }
- $oP->add($sOutputData);
- break;
+ $oP = new CSVPage("iTop - Export");
+ $sFields = implode(',', $aFields);
+ $sCharset = utils::ReadParam('charset', MetaModel::GetConfig()->Get('csv_file_default_charset'), true /* Allow CLI */, 'raw_data');
+ $sCSVData = cmdbAbstractObject::GetSetAsCSV($oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize), $sCharset);
+ if ($sCharset == 'UTF-8')
+ {
+ $sOutputData = UTF8_BOM.$sCSVData;
+ }
+ else
+ {
+ $sOutputData = $sCSVData;
+ }
+ if ($sFileName == '')
+ {
+ // Plain text => Firefox will NOT propose to download the file
+ $oP->add_header("Content-type: text/plain; charset=$sCharset");
+ }
+ else
+ {
+ $oP->add_header("Content-type: text/csv; charset=$sCharset");
+ }
+ $oP->add($sOutputData);
+ break;
case 'spreadsheet':
- $oP = new WebPage("iTop - Export for spreadsheet");
+ $oP = new WebPage("iTop - Export for spreadsheet");
- // Integration within MS-Excel web queries + HTTPS + IIS:
- // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
- // Then the fix is to force the reset of header values Pragma and Cache-control
- header("Pragma:", true);
- header("Cache-control:", true);
+ // Integration within MS-Excel web queries + HTTPS + IIS:
+ // MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
+ // Then the fix is to force the reset of header values Pragma and Cache-control
+ header("Pragma:", true);
+ header("Cache-control:", true);
- $sFields = implode(',', $aFields);
- $oP->add_style('table br {mso-data-placement:same-cell;}'); // Trick for Excel: keep line breaks inside the same cell !
- cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, ['fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize]);
- break;
+ $sFields = implode(',', $aFields);
+ $oP->add_style('table br {mso-data-placement:same-cell;}'); // Trick for Excel: keep line breaks inside the same cell !
+ cmdbAbstractObject::DisplaySetAsHTMLSpreadsheet($oP, $oSet, array('fields' => $sFields, 'fields_advanced' => $bFieldsAdvanced, 'localize_values' => $bLocalize));
+ break;
case 'xml':
- $oP = new XMLPage("iTop - Export", true /* passthrough */);
- cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, ['localize_values' => $bLocalize]);
- break;
+ $oP = new XMLPage("iTop - Export", true /* passthrough */);
+ cmdbAbstractObject::DisplaySetAsXML($oP, $oSet, array('localize_values' => $bLocalize));
+ break;
case 'xlsx':
- $oP = new AjaxPage('');
- $oExporter = new ExcelExporter();
- $oExporter->SetObjectList($oFilter);
-
- // Run the export by chunk of 1000 objects to limit memory usage
- $oExporter->SetChunkSize(1000);
- do {
- $aStatus = $oExporter->Run(); // process one chunk
- } while (($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
-
- if ($aStatus['code'] == 'done') {
- $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
- $oP->SetContentDisposition('attachment', $oFilter->GetClass().'.xlsx');
- $oP->add(file_get_contents($oExporter->GetExcelFilePath()));
- $oExporter->Cleanup();
- } else {
- $oP->add('Error, xlsx export failed: '.$aStatus['message']);
- }
- break;
+ $oP = new AjaxPage('');
+ $oExporter = new ExcelExporter();
+ $oExporter->SetObjectList($oFilter);
+
+ // Run the export by chunk of 1000 objects to limit memory usage
+ $oExporter->SetChunkSize(1000);
+ do
+ {
+ $aStatus = $oExporter->Run(); // process one chunk
+ }
+ while( ($aStatus['code'] != 'done') && ($aStatus['code'] != 'error'));
+
+ if ($aStatus['code'] == 'done')
+ {
+ $oP->SetContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ $oP->SetContentDisposition('attachment', $oFilter->GetClass().'.xlsx');
+ $oP->add(file_get_contents($oExporter->GetExcelFilePath()));
+ $oExporter->Cleanup();
+ }
+ else
+ {
+ $oP->add('Error, xlsx export failed: '.$aStatus['message']);
+ }
+ break;
default:
- $oP = new WebPage("iTop - Export");
- $oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
+ $oP = new WebPage("iTop - Export");
+ $oP->add("Unsupported format '$sFormat'. Possible values are: html, csv, spreadsheet or xml.");
}
}
- } catch (Exception $e) {
+ }
+ catch(Exception $e)
+ {
$oP = new WebPage("iTop - Export");
$oP->p("Error the query can not be executed.");
- if ($e instanceof CoreException) {
+ if ($e instanceof CoreException)
+ {
$oP->p($e->GetHtmlDesc());
- } else {
+ }
+ else
+ {
$oP->p($e->getMessage());
}
}
}
-if (!$oP) {
+if (!$oP)
+{
// Display a short message about how to use this page
$bModeCLI = utils::IsModeCLI();
- if ($bModeCLI) {
+ if ($bModeCLI)
+ {
$oP = new CLIPage("iTop - Export");
- } else {
+ }
+ else
+ {
$oP = new WebPage("iTop - Export");
}
$oP->p("General purpose export page.");
@@ -328,9 +384,11 @@
$oP->p(" * filename: (optional, no effect in CLI mode) if set then the results will be downloaded as a file");
}
-if ($sFileName != '') {
+if ($sFileName != '')
+{
$oP->add_header('Content-Disposition: attachment; filename="'.$sFileName.'"');
}
$oP->TrashUnexpectedOutput();
$oP->output();
+?>
diff --git a/webservices/import.php b/webservices/import.php
index 807011a6b5..dc009b8409 100644
--- a/webservices/import.php
+++ b/webservices/import.php
@@ -1,5 +1,4 @@
- [
+$aPageParams = array
+(
+ 'auth_user' => array
+ (
'mandatory' => true,
'modes' => 'cli',
'default' => null,
'description' => 'login (must have enough rights to create objects of the given class)',
- ],
- 'auth_pwd' =>
- [
+ ),
+ 'auth_pwd' => array
+ (
'mandatory' => true,
'modes' => 'cli',
'default' => null,
'description' => 'password',
- ],
- 'class' =>
- [
+ ),
+ 'class' => array
+ (
'mandatory' => true,
'modes' => 'http,cli',
'default' => null,
'description' => 'class of loaded objects',
- ],
- 'csvdata' =>
- [
+ ),
+ 'csvdata' => array
+ (
'mandatory' => true,
'modes' => 'http',
'default' => null,
'description' => 'data',
- ],
- 'csvfile' =>
- [
+ ),
+ 'csvfile' => array
+ (
'mandatory' => true,
'modes' => 'cli',
'default' => '',
'description' => 'local data file, replaces csvdata if specified',
- ],
- 'charset' =>
- [
+ ),
+ 'charset' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => '',
'description' => 'Character set encoding of the CSV data: UTF-8, ISO-8859-1, WINDOWS-1251, WINDOWS-1252, ISO-8859-15, If blank, then the charset is set to config(csv_file_default_charset)',
- ],
- 'date_format' =>
- [
+ ),
+ 'date_format' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => '',
'description' => 'Input date format (used both for dates and datetimes) - Examples: Y-m-d H:i:s, d/m/Y H:i:s (Europe) - no transformation is applied if the argument is omitted. (note: old format specification using %Y %m %d is also supported for backward compatibility)',
- ],
- 'separator' =>
- [
+ ),
+ 'separator' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => ',',
'description' => 'column separator in CSV data (1 char, or \'tab\')',
- ],
- 'qualifier' =>
- [
+ ),
+ 'qualifier' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => '"',
'description' => 'test qualifier in CSV data',
- ],
- 'output' =>
- [
+ ),
+ 'output' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => 'summary',
'description' => '[retcode] to return the count of lines in error, [summary] to return a concise report, [details] to get a detailed report (each line listed)',
- ],
+ ),
/*
'reportlevel' => array
(
@@ -121,35 +121,35 @@ class BulkLoadException extends Exception
'description' => 'combination of flags to limit the detailed output',
),
*/
- 'reconciliationkeys' =>
- [
+ 'reconciliationkeys' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => '',
'description' => 'name of the columns used to identify existing objects and update them, or create a new one',
- ],
- 'simulate' =>
- [
+ ),
+ 'simulate' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => '0',
'description' => 'If set to 1, then the load will not be executed, but the expected report will be produced',
- ],
- 'comment' =>
- [
+ ),
+ 'comment' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => '',
'description' => 'Comment to be added into the change log',
- ],
- 'no_localize' =>
- [
+ ),
+ 'no_localize' => array
+ (
'mandatory' => false,
'modes' => 'http,cli',
'default' => '0',
'description' => 'If set to 0, then header and values are supposed to be localized in the language of the logged in user. Set to 1 to use internal attribute codes and values (enums)',
- ],
-];
+ ),
+);
function UsageAndExit($oP)
{
@@ -157,15 +157,21 @@ function UsageAndExit($oP)
$bModeCLI = utils::IsModeCLI();
$oP->p("USAGE:\n");
- foreach ($aPageParams as $sParam => $aParamData) {
+ foreach($aPageParams as $sParam => $aParamData)
+ {
$aModes = explode(',', $aParamData['modes']);
- if ($bModeCLI) {
- if (in_array('cli', $aModes)) {
+ if ($bModeCLI)
+ {
+ if (in_array('cli', $aModes))
+ {
$sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
$oP->p("$sParam = $sDesc");
}
- } else {
- if (in_array('http', $aModes)) {
+ }
+ else
+ {
+ if (in_array('http', $aModes))
+ {
$sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']');
$oP->p("$sParam = $sDesc");
}
@@ -175,6 +181,7 @@ function UsageAndExit($oP)
exit;
}
+
function ReadParam($oP, $sParam, $sSanitizationFilter = 'parameter')
{
global $aPageParams;
@@ -191,7 +198,8 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
assert($aPageParams[$sParam]['mandatory']);
$sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter);
- if (is_null($sValue)) {
+ if (is_null($sValue))
+ {
$oP->p("ERROR: Missing argument '$sParam'\n");
UsageAndExit($oP);
}
@@ -205,81 +213,96 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
* @since 3.1.0 N°6047
*/
$oCtx = new ContextTag(ContextTag::TAG_IMPORT);
-if (utils::IsModeCLI()) {
+if (utils::IsModeCLI())
+{
$oP = new CLIPage("iTop - Bulk import");
SetupUtils::CheckPhpAndExtensionsForCli($oP, -2);
-} else {
+}
+else
+{
$oP = new CSVPage("iTop - Bulk import");
}
-try {
+try
+{
utils::UseParamFile();
-} catch (Exception $e) {
+}
+catch(Exception $e)
+{
$oP->p("Error: ".$e->GetMessage());
$oP->output();
exit(-2);
}
-if (utils::IsModeCLI()) {
+if (utils::IsModeCLI())
+{
// Next steps:
// specific arguments: 'csvfile'
//
$sAuthUser = ReadMandatoryParam($oP, 'auth_user', 'raw_data');
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd', 'raw_data');
$sCsvFile = ReadMandatoryParam($oP, 'csvfile', 'raw_data');
- if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
+ if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
+ {
UserRights::Login($sAuthUser); // Login & set the user's language
- } else {
+ }
+ else
+ {
$oP->p("Access restricted or wrong credentials ('$sAuthUser')");
$oP->output();
exit(-1);
}
- if (!is_readable($sCsvFile)) {
+ if (!is_readable($sCsvFile))
+ {
$oP->p("Input file could not be found or could not be read: '$sCsvFile'");
$oP->output();
exit(-1);
}
$sCSVData = file_get_contents($sCsvFile);
-} else {
+}
+else
+{
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
- LoginWebPage::ResetSession(true);
+ LoginWebPage::ResetSession(true);
$iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
- if ($iRet !== LoginWebPage::EXIT_CODE_OK) {
- switch ($iRet) {
- case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
- $oP->p("Missing parameter 'auth_user'");
- break;
-
- case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
- $oP->p("Missing parameter 'auth_pwd'");
- break;
-
- case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
- $oP->p('Invalid login');
- break;
-
- case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
- $oP->p('Portal user is not allowed');
- break;
-
- case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
- $oP->p('This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)');
- break;
-
- default:
- $oP->p("Unknown authentication error (retCode=$iRet)");
- }
- $oP->output();
- exit -1;
- }
+ if ($iRet !== LoginWebPage::EXIT_CODE_OK) {
+ switch ($iRet) {
+ case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
+ $oP->p("Missing parameter 'auth_user'");
+ break;
+
+ case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
+ $oP->p("Missing parameter 'auth_pwd'");
+ break;
+
+ case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
+ $oP->p('Invalid login');
+ break;
+
+ case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
+ $oP->p('Portal user is not allowed');
+ break;
+
+ case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
+ $oP->p('This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)');
+ break;
+
+ default:
+ $oP->p("Unknown authentication error (retCode=$iRet)");
+ }
+ $oP->output();
+ exit -1;
+ }
$sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data');
}
-try {
- $aWarnings = [];
+
+try
+{
+ $aWarnings = array();
//////////////////////////////////////////////////
//
@@ -290,7 +313,8 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$sQualifier = ReadParam($oP, 'qualifier', 'raw_data');
$sCharSet = ReadParam($oP, 'charset', 'raw_data');
$sDateFormat = ReadParam($oP, 'date_format', 'raw_data');
- if (strpos($sDateFormat, '%') !== false) {
+ if (strpos($sDateFormat, '%') !== false)
+ {
$sDateFormat = utils::DateTimeFormatToPHP($sDateFormat);
}
$sOutput = ReadParam($oP, 'output', 'string');
@@ -299,7 +323,8 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$sComment = ReadParam($oP, 'comment', 'raw_data');
$bLocalize = (ReadParam($oP, 'no_localize') != 1);
- if (strtolower(trim($sSep)) == 'tab') {
+ if (strtolower(trim($sSep)) == 'tab')
+ {
$sSep = "\t";
}
@@ -307,62 +332,77 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
//
// Check parameters format/consistency
//
- if (strlen($sCSVData) == 0) {
+ if (strlen($sCSVData) == 0)
+ {
throw new BulkLoadException("Missing data - at least one line is expected");
}
- if (!MetaModel::IsValidClass($sClass)) {
+ if (!MetaModel::IsValidClass($sClass))
+ {
throw new BulkLoadException("Unknown class: '$sClass'");
}
- if (strlen($sSep) > 1) {
+ if (strlen($sSep) > 1)
+ {
throw new BulkLoadException("Separator is limited to one character, found '$sSep'");
}
- if (strlen($sQualifier) > 1) {
+ if (strlen($sQualifier) > 1)
+ {
throw new BulkLoadException("Text qualifier is limited to one character, found '$sQualifier'");
}
- if (!in_array($sOutput, ['retcode', 'summary', 'details'])) {
+ if (!in_array($sOutput, array('retcode', 'summary', 'details')))
+ {
throw new BulkLoadException("Unknown output format: '$sOutput'");
}
- if (strlen($sDateFormat) == 0) {
+ if (strlen($sDateFormat) == 0)
+ {
$sDateFormat = null;
}
- if ($sCharSet == '') {
+ if ($sCharSet == '')
+ {
$sCharSet = MetaModel::GetConfig()->Get('csv_file_default_charset');
}
- if ($sSimulate == '1') {
+ if ($sSimulate == '1')
+ {
$bSimulate = true;
- } else {
+ }
+ else
+ {
$bSimulate = false;
}
- if (($sOutput == "summary") || ($sOutput == 'details')) {
+ if (($sOutput == "summary") || ($sOutput == 'details'))
+ {
$oP->add_comment("Output format: ".$sOutput);
$oP->add_comment("Class: ".$sClass);
$oP->add_comment("Separator: ".$sSep);
$oP->add_comment("Qualifier: ".$sQualifier);
$oP->add_comment("Charset Encoding:".$sCharSet);
- if (($sDateFormat !== null) && (strlen($sDateFormat) > 0)) {
+ if (($sDateFormat !== null) && (strlen($sDateFormat) > 0))
+ {
$oP->add_comment("Date and time format: '$sDateFormat'");
$oDateTimeFormat = new DateTimeFormat($sDateFormat);
$sDateOnlyFormat = $oDateTimeFormat->ToDateFormat();
$oP->add_comment("Date format: '$sDateOnlyFormat'");
- } else {
+ }
+ else
+ {
$oP->add_comment("Date format: ");
}
- $oP->add_comment("Localize: ".($bLocalize ? 'yes' : 'no'));
+ $oP->add_comment("Localize: ".($bLocalize?'yes':'no'));
$oP->add_comment("Data Size: ".strlen($sCSVData));
}
//////////////////////////////////////////////////
//
// Security
//
- if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY)) {
+ if (!UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY))
+ {
throw new SecurityException(Dict::Format('UI:Error:BulkModifyNotAllowedOn_Class', $sClass));
}
@@ -379,31 +419,42 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
// Note: it may happen that an external field has the same label as the external key
// in that case, we consider that the external key has precedence
//
- $aKnownColumnNames = ['id' => ['id']];
- foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
- if ($bLocalize) {
- $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCode));
- } else {
- $sColName = strtolower($sAttCode);
+ $aKnownColumnNames = ['id'=>['id']];
+ foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
+ {
+ if ($bLocalize)
+ {
+ $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCode));
+ }
+ else
+ {
+ $sColName = strtolower($sAttCode);
+ }
+ if (!$oAttDef->IsExternalField() || !array_key_exists($sColName, $aKnownColumnNames))
+ {
+ $aKnownColumnNames[$sColName][] = $sAttCode;
}
- if (!$oAttDef->IsExternalField() || !array_key_exists($sColName, $aKnownColumnNames)) {
- $aKnownColumnNames[$sColName][] = $sAttCode;
- }
- if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
- $sRemoteClass = $oAttDef->GetTargetClass();
- foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
- $sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
- if ($bLocalize) {
- $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCodeEx));
- } else {
- $sColName = strtolower($sAttCodeEx);
- }
- if (!array_key_exists($sColName, $aKnownColumnNames)) {
- $aKnownColumnNames[$sColName][] = $sAttCodeEx;
- }
- }
+ if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
+ {
+ $sRemoteClass = $oAttDef->GetTargetClass();
+ foreach(MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef)
+ {
+ $sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
+ if ($bLocalize)
+ {
+ $sColName = strtolower(MetaModel::GetLabel($sClass, $sAttCodeEx));
+ }
+ else
+ {
+ $sColName = strtolower($sAttCodeEx);
+ }
+ if (!array_key_exists($sColName, $aKnownColumnNames))
+ {
+ $aKnownColumnNames[$sColName][] = $sAttCodeEx;
+ }
+ }
}
- }
+ }
//print_r($aKnownColumnNames);
//print_r(array_keys($aKnownColumnNames));
@@ -413,15 +464,19 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
//
// Parse first line, check attributes, analyse the request
//
- if ($sCharSet == 'UTF-8') {
+ if ($sCharSet == 'UTF-8')
+ {
// Remove the BOM if any
- if (substr($sCSVData, 0, 3) == UTF8_BOM) {
+ if (substr($sCSVData, 0, 3) == UTF8_BOM)
+ {
$sCSVData = substr($sCSVData, 3);
}
// Clean the input
// Todo: warn the user if some characters are lost/substituted
$sUTF8Data = iconv('UTF-8', 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
- } else {
+ }
+ else
+ {
$sUTF8Data = iconv($sCharSet, 'UTF-8//IGNORE//TRANSLIT', $sCSVData);
}
$oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier);
@@ -432,77 +487,101 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
// Translate into internal names
$aFieldList = [];
- foreach ($aRawFieldList as $iFieldId => $sFieldName) {
+ foreach($aRawFieldList as $iFieldId => $sFieldName)
+ {
$sFieldName = trim($sFieldName);
- $aMatches = [];
- if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches)) {
+ $aMatches = array();
+ if (preg_match('/^(.+)\*$/', $sFieldName, $aMatches))
+ {
// Ignore any trailing "star" (*) that simply indicates a mandatory field
$sFieldName = $aMatches[1];
- } elseif (preg_match('/^(.+)\*->(.+)$/', $sFieldName, $aMatches)) {
+ }
+ else if (preg_match('/^(.+)\*->(.+)$/', $sFieldName, $aMatches))
+ {
// Remove any trailing "star" character before the arrow (->)
// A star character at the end can be used to indicate a mandatory field
$sFieldName = $aMatches[1].'->'.$aMatches[2];
}
- if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames)) {
+ if (array_key_exists(strtolower($sFieldName), $aKnownColumnNames))
+ {
$aColumns = $aKnownColumnNames[strtolower($sFieldName)];
- if (count($aColumns) > 1) {
- $aCompetitors = [];
- foreach ($aColumns as $sAttCodeEx) {
+ if (count($aColumns) > 1)
+ {
+ $aCompetitors = array();
+ foreach ($aColumns as $sAttCodeEx)
+ {
$aCompetitors[] = $sAttCodeEx;
}
- $aWarnings[] = "Input column '$sFieldName' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
+ $aWarnings[] = "Input column '$sFieldName' is ambiguous. Could be related to ".implode (' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
}
$aFieldList[$iFieldId] = $aColumns[0];
- } else {
+ }
+ else
+ {
// Protect against XSS injection
- $sSafeName = str_replace(['"', '<', '>'], '', $sFieldName);
+ $sSafeName = str_replace(array('"', '<', '>'), '', $sFieldName);
throw new BulkLoadException("Unknown column: '$sSafeName'. Possible columns: ".implode(', ', array_keys($aKnownColumnNames)));
}
}
// Note: at this stage the list of fields is supposed to be made of attcodes (and the symbol '->')
- $aAttList = [];
- $aExtKeys = [];
- foreach ($aFieldList as $iFieldId => $sFieldName) {
- $aMatches = [];
- if (preg_match('/^(.+)->(.+)$/', trim($sFieldName), $aMatches)) {
+ $aAttList = array();
+ $aExtKeys = array();
+ foreach($aFieldList as $iFieldId => $sFieldName)
+ {
+ $aMatches = array();
+ if (preg_match('/^(.+)->(.+)$/', trim($sFieldName), $aMatches))
+ {
// The column has been specified as "extkey->attcode"
//
$sExtKeyAttCode = $aMatches[1];
$sRemoteAttCode = $aMatches[2];
- if (!MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode)) {
+ if (!MetaModel::IsValidAttCode($sClass, $sExtKeyAttCode))
+ {
// Safety net - should not happen now that column names are checked against known names
throw new BulkLoadException("Unknown attribute '$sExtKeyAttCode' (class: '$sClass')");
}
$oAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode);
- if (!$oAtt->IsExternalKey()) {
+ if (!$oAtt->IsExternalKey())
+ {
// Safety net - should not happen now that column names are checked against known names
throw new BulkLoadException("Not an external key '$sExtKeyAttCode' (class: '$sClass')");
}
$sTargetClass = $oAtt->GetTargetClass();
- if (!MetaModel::IsValidAttCode($sTargetClass, $sRemoteAttCode)) {
+ if (!MetaModel::IsValidAttCode($sTargetClass, $sRemoteAttCode))
+ {
// Safety net - should not happen now that column names are checked against known names
throw new BulkLoadException("Unknown attribute '$sRemoteAttCode' (key: '$sExtKeyAttCode', class: '$sTargetClass')");
}
$aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
- } elseif ($sFieldName == 'id') {
+ }
+ elseif ($sFieldName == 'id')
+ {
$aAttList[$sFieldName] = $iFieldId;
- } else {
+ }
+ else
+ {
// The column has been specified as "attcode"
//
- if (!MetaModel::IsValidAttCode($sClass, $sFieldName)) {
+ if (!MetaModel::IsValidAttCode($sClass, $sFieldName))
+ {
// Safety net - should not happen now that column names are checked against known names
throw new BulkLoadException("Unknown attribute '$sFieldName' (class: '$sClass')");
}
$oAtt = MetaModel::GetAttributeDef($sClass, $sFieldName);
- if ($oAtt->IsExternalKey()) {
+ if ($oAtt->IsExternalKey())
+ {
$aExtKeys[$sFieldName]['id'] = $iFieldId;
$aAttList[$sFieldName] = $iFieldId;
- } elseif ($oAtt->IsExternalField()) {
+ }
+ elseif ($oAtt->IsExternalField())
+ {
$sExtKeyAttCode = $oAtt->GetKeyAttCode();
$sRemoteAttCode = $oAtt->GetExtAttCode();
$aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId;
- } else {
+ }
+ else
+ {
$aAttList[$sFieldName] = $iFieldId;
}
}
@@ -510,20 +589,27 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
// Make sure there are some reconciliation keys
//
- if (empty($sReconcKeys)) {
- $aReconcSpec = [];
+ if (empty($sReconcKeys))
+ {
+ $aReconcSpec = array();
// Base reconciliation scheme on the default one
// The reconciliation attributes not present in the data will be ignored
- foreach (MetaModel::GetReconcKeys($sClass) as $sReconcKeyAttCode) {
- if (in_array($sReconcKeyAttCode, $aFieldList)) {
- if ($bLocalize) {
+ foreach(MetaModel::GetReconcKeys($sClass) as $sReconcKeyAttCode)
+ {
+ if (in_array($sReconcKeyAttCode, $aFieldList))
+ {
+ if ($bLocalize)
+ {
$aReconcSpec[] = MetaModel::GetLabel($sClass, $sReconcKeyAttCode);
- } else {
+ }
+ else
+ {
$aReconcSpec[] = $sReconcKeyAttCode;
}
}
}
- if (count($aReconcSpec) == 0) {
+ if (count($aReconcSpec) == 0)
+ {
throw new BulkLoadException("No reconciliation scheme could be defined, please add a column corresponding to one defined reconciliation key (class: '$sClass', reconciliation:".implode(',', MetaModel::GetReconcKeys($sClass)).")");
}
$sReconcKeys = implode(',', $aReconcSpec);
@@ -531,41 +617,48 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
// Interpret the list of reconciliation keys
//
- $aFinalReconcilKeys = [];
- $aReconcilKeysReport = [];
- foreach (explode(',', $sReconcKeys) as $sReconcKey) {
+ $aFinalReconcilKeys = array();
+ $aReconcilKeysReport = array();
+ foreach (explode(',', $sReconcKeys) as $sReconcKey)
+ {
$sReconcKey = trim($sReconcKey);
- if (empty($sReconcKey)) {
- continue;
- } // skip empty spec
+ if (empty($sReconcKey)) continue; // skip empty spec
- if (array_key_exists(strtolower($sReconcKey), $aKnownColumnNames)) {
+ if (array_key_exists(strtolower($sReconcKey), $aKnownColumnNames))
+ {
// Translate from a translated name to codes
$aColumns = $aKnownColumnNames[strtolower($sReconcKey)];
- if (count($aColumns) > 1) {
- $aCompetitors = [];
- foreach ($aColumns as $sAttCodeEx) {
+ if (count($aColumns) > 1)
+ {
+ $aCompetitors = array();
+ foreach ($aColumns as $sAttCodeEx)
+ {
$aCompetitors[] = $sAttCodeEx;
}
- $aWarnings[] = "Reconciliation key '$sReconcKey' is ambiguous. Could be related to ".implode(' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
+ $aWarnings[] = "Reconciliation key '$sReconcKey' is ambiguous. Could be related to ".implode (' or ', $aCompetitors).". The first one will be used: ".$aColumns[0];
}
$sReconcKey = $aColumns[0];
- } else {
+ }
+ else
+ {
// Protect against XSS injection
- $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
+ $sSafeName = str_replace(array('"', '<', '>'), '', $sReconcKey);
throw new BulkLoadException("Unknown reconciliation key: '$sSafeName'");
}
// Check that the reconciliation key is either a given column, or an external key
- if (!in_array($sReconcKey, $aFieldList)) {
- if (!array_key_exists($sReconcKey, $aExtKeys)) {
+ if (!in_array($sReconcKey, $aFieldList))
+ {
+ if (!array_key_exists($sReconcKey, $aExtKeys))
+ {
// Protect against XSS injection
- $sSafeName = str_replace(['"', '<', '>'], '', $sReconcKey);
+ $sSafeName = str_replace(array('"', '<', '>'), '', $sReconcKey);
throw new BulkLoadException("Reconciliation key not found in the input columns: '$sSafeName'");
}
}
- if (preg_match('/^(.+)->(.+)$/', trim($sReconcKey), $aMatches)) {
+ if (preg_match('/^(.+)->(.+)$/', trim($sReconcKey), $aMatches))
+ {
// The column has been specified as "extkey->attcode"
//
$sExtKeyAttCode = $aMatches[1];
@@ -573,14 +666,17 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$aFinalReconcilKeys[] = $sExtKeyAttCode;
$aReconcilKeysReport[$sExtKeyAttCode][] = $sRemoteAttCode;
- } else {
- if (!MetaModel::IsValidAttCode($sClass, $sReconcKey) && $sReconcKey != 'id') {
+ }
+ else
+ {
+ if (!MetaModel::IsValidAttCode($sClass, $sReconcKey) && $sReconcKey != 'id')
+ {
// Safety net - should not happen now that column names are checked against known names
throw new BulkLoadException("Unknown reconciliation attribute '$sReconcKey' (class: '$sClass')");
}
if ($sReconcKey == 'id') {
$aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey] = [];
+ $aReconcilKeysReport[$sReconcKey] = array();
} else {
$oAtt = MetaModel::GetAttributeDef($sClass, $sReconcKey);
if ($oAtt->IsExternalKey()) {
@@ -594,7 +690,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$aReconcilKeysReport[$sReconcAttCode][] = $sReconcKeyReport;
} else {
$aFinalReconcilKeys[] = $sReconcKey;
- $aReconcilKeysReport[$sReconcKey] = [];
+ $aReconcilKeysReport[$sReconcKey] = array();
}
}
}
@@ -608,22 +704,28 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$aData = $oCSVParser->ToArray();
$iLineCount = count($aData);
- if (($sOutput == "summary") || ($sOutput == 'details')) {
+ if (($sOutput == "summary") || ($sOutput == 'details'))
+ {
$oP->add_comment("Data Lines: ".$iLineCount);
$oP->add_comment("Simulate: ".($bSimulate ? '1' : '0'));
$oP->add_comment("Columns: ".implode(', ', $aFieldList));
- $aReconciliationReport = [];
- foreach ($aReconcilKeysReport as $sKey => $aKeyDetails) {
- if (count($aKeyDetails) > 0) {
+ $aReconciliationReport = array();
+ foreach($aReconcilKeysReport as $sKey => $aKeyDetails)
+ {
+ if (count($aKeyDetails) > 0)
+ {
$aReconciliationReport[] = $sKey.' ('.implode(',', $aKeyDetails).')';
- } else {
+ }
+ else
+ {
$aReconciliationReport[] = $sKey;
}
}
$oP->add_comment("Reconciliation Keys: ".implode(', ', $aReconciliationReport));
- foreach ($aWarnings as $sWarning) {
+ foreach ($aWarnings as $sWarning)
+ {
$oP->add_comment("Warning: ".$sWarning);
}
}
@@ -640,9 +742,12 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$bLocalize
);
- if ($bSimulate) {
+ if ($bSimulate)
+ {
$oMyChange = null;
- } else {
+ }
+ else
+ {
if (strlen($sComment) > 0) {
$sMoreInfo = CMDBChange::GetCurrentUserName().', Web Service (CSV) - '.$sComment;
} else {
@@ -663,35 +768,38 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$iCountCreations = 0;
$iCountUpdates = 0;
$iCountUnchanged = 0;
- foreach ($aRes as $iRow => $aRowData) {
+ foreach($aRes as $iRow => $aRowData)
+ {
$bWritten = false;
$oStatus = $aRowData["__STATUS__"];
- switch (get_class($oStatus)) {
- case 'RowStatus_NoChange':
- $iCountUnchanged++;
- break;
- case 'RowStatus_Modify':
- $iCountUpdates++;
- $bWritten = true;
- break;
- case 'RowStatus_NewObj':
- $iCountCreations++;
- $bWritten = true;
- break;
- case 'RowStatus_Issue':
- $iCountErrors++;
- break;
+ switch(get_class($oStatus))
+ {
+ case 'RowStatus_NoChange':
+ $iCountUnchanged++;
+ break;
+ case 'RowStatus_Modify':
+ $iCountUpdates++;
+ $bWritten = true;
+ break;
+ case 'RowStatus_NewObj':
+ $iCountCreations++;
+ $bWritten = true;
+ break;
+ case 'RowStatus_Issue':
+ $iCountErrors++;
+ break;
}
- if ($bWritten) {
+ if ($bWritten)
+ {
// Something has been done, still there may be some issues to report
- foreach ($aRowData as $key => $value) {
- if (!is_object($value)) {
- continue;
- }
+ foreach($aRowData as $key => $value)
+ {
+ if (!is_object($value)) continue;
- switch (get_class($value)) {
+ switch (get_class($value))
+ {
case 'CellStatus_Void':
case 'CellStatus_Modify':
break;
@@ -710,11 +818,13 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
//
// Summary of settings and results
//
- if ($sOutput == 'retcode') {
+ if ($sOutput == 'retcode')
+ {
$oP->add($iCountErrors);
}
- if (($sOutput == "summary") || ($sOutput == 'details')) {
+ if (($sOutput == "summary") || ($sOutput == 'details'))
+ {
$oP->add_comment("Change tracking comment: ".$sComment);
$oP->add_comment("Issues: ".$iCountErrors);
$oP->add_comment("Warnings: ".$iCountWarnings);
@@ -723,69 +833,80 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
$oP->add_comment("Unchanged: ".$iCountUnchanged);
}
- if ($sOutput == 'details') {
+
+ if ($sOutput == 'details')
+ {
// Setup result presentation
//
- $aDisplayConfig = [];
- $aDisplayConfig["__LINE__"] = ["label" => "Line", "description" => ""];
- $aDisplayConfig["__STATUS__"] = ["label" => "Status", "description" => ""];
- $aDisplayConfig["__OBJECT_CLASS__"] = ["label" => "Object Class", "description" => ""];
- $aDisplayConfig["__OBJECT_ID__"] = ["label" => "Object Id", "description" => ""];
- foreach ($aExtKeys as $sExtKeyAttCode => $aRemoteAtt) {
+ $aDisplayConfig = array();
+ $aDisplayConfig["__LINE__"] = array("label"=>"Line", "description"=>"");
+ $aDisplayConfig["__STATUS__"] = array("label"=>"Status", "description"=>"");
+ $aDisplayConfig["__OBJECT_CLASS__"] = array("label"=>"Object Class", "description"=>"");
+ $aDisplayConfig["__OBJECT_ID__"] = array("label"=>"Object Id", "description"=>"");
+ foreach($aExtKeys as $sExtKeyAttCode => $aRemoteAtt)
+ {
$sLabel = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode)->GetLabel();
- $aDisplayConfig["$sExtKeyAttCode"] = ["label" => $sExtKeyAttCode, "description" => $sLabel." - ext key"];
+ $aDisplayConfig["$sExtKeyAttCode"] = array("label"=>$sExtKeyAttCode, "description"=>$sLabel." - ext key");
}
- foreach ($aFinalReconcilKeys as $iCol => $sAttCode) {
- // $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
- // $aDisplayConfig["$iCol"] = array("label"=>"$sLabel", "description"=>"");
+ foreach($aFinalReconcilKeys as $iCol => $sAttCode)
+ {
+ // $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
+ // $aDisplayConfig["$iCol"] = array("label"=>"$sLabel", "description"=>"");
}
- foreach ($aAttList as $sAttCode => $iCol) {
- if ($sAttCode == 'id') {
+ foreach ($aAttList as $sAttCode => $iCol)
+ {
+ if ($sAttCode == 'id')
+ {
$sLabel = Dict::S('UI:CSVImport:idField');
- $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
- } else {
+ $aDisplayConfig["$iCol"] = array("label"=>$sAttCode, "description"=>$sLabel);
+ }
+ else
+ {
$sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel();
- $aDisplayConfig["$iCol"] = ["label" => $sAttCode, "description" => $sLabel];
+ $aDisplayConfig["$iCol"] = array("label"=>$sAttCode, "description"=>$sLabel);
}
}
- $aResultDisp = []; // to be displayed
- foreach ($aRes as $iRow => $aRowData) {
- $aRowDisp = [];
+ $aResultDisp = array(); // to be displayed
+ foreach($aRes as $iRow => $aRowData)
+ {
+ $aRowDisp = array();
$aRowDisp["__LINE__"] = $iRow;
- if (is_object($aRowData["__STATUS__"])) {
+ if (is_object($aRowData["__STATUS__"]))
+ {
$aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription();
- } else {
+ }
+ else
+ {
$aRowDisp["__STATUS__"] = "*No status available*";
}
- if (isset($aRowData["finalclass"]) && isset($aRowData["id"])) {
+ if (isset($aRowData["finalclass"]) && isset($aRowData["id"]))
+ {
$aRowDisp["__OBJECT_CLASS__"] = $aRowData["finalclass"];
$aRowDisp["__OBJECT_ID__"] = $aRowData["id"]->GetCLIValue();
- } else {
+ }
+ else
+ {
$aRowDisp["__OBJECT_CLASS__"] = "n/a";
$aRowDisp["__OBJECT_ID__"] = "n/a";
}
- foreach ($aRowData as $key => $value) {
+ foreach($aRowData as $key => $value)
+ {
$sKey = (string) $key;
- if ($sKey == '__STATUS__') {
- continue;
- }
+ if ($sKey == '__STATUS__') continue;
//__ERRORS__ used by tests only
- if ($sKey == '__ERRORS__') {
- continue;
- }
- if ($sKey == 'finalclass') {
- continue;
- }
- if ($sKey == 'id') {
- continue;
- }
+ if ($sKey == '__ERRORS__') continue;
+ if ($sKey == 'finalclass') continue;
+ if ($sKey == 'id') continue;
- if (is_object($value)) {
+ if (is_object($value))
+ {
$aRowDisp["$sKey"] = $value->GetCLIValueAndDescription();
- } else {
+ }
+ else
+ {
$aRowDisp["$sKey"] = $value;
}
}
@@ -793,12 +914,19 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter)
}
$oP->table($aDisplayConfig, $aResultDisp);
}
-} catch (BulkLoadException $e) {
+}
+catch(BulkLoadException $e)
+{
$oP->add_comment($e->getMessage());
-} catch (SecurityException $e) {
+}
+catch(SecurityException $e)
+{
$oP->add_comment($e->getMessage());
-} catch (Exception $e) {
+}
+catch(Exception $e)
+{
$oP->add_comment((string)$e);
}
$oP->output();
+?>
diff --git a/webservices/itop.wsdl.php b/webservices/itop.wsdl.php
index 03aaed90d7..5b9b37b1f9 100644
--- a/webservices/itop.wsdl.php
+++ b/webservices/itop.wsdl.php
@@ -1,5 +1,4 @@
diff --git a/webservices/itoprest.examples.php b/webservices/itoprest.examples.php
index 6408a1ff8f..e14988c0c3 100644
--- a/webservices/itoprest.examples.php
+++ b/webservices/itoprest.examples.php
@@ -1,10 +1,9 @@
+
/**
- * Shows a usage of the SOAP queries
+ * Shows a usage of the SOAP queries
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -36,26 +36,29 @@
* @return bool|false|string
* @throws \Exception
*/
-function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = [])
+function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
{
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
- if (function_exists('curl_init')) {
+ if (function_exists('curl_init'))
+ {
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
// For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
// by setting the SSLVERSION to 3 as done below.
- $aHTTPHeaders = [];
- if ($sOptionnalHeaders !== null) {
+ $aHTTPHeaders = array();
+ if ($sOptionnalHeaders !== null)
+ {
$aHeaders = explode("\n", $sOptionnalHeaders);
// N°3267 - Webservices: Fix optional headers not being taken into account
// See https://www.php.net/curl_setopt CURLOPT_HTTPHEADER
- $aHTTPHeaders = [];
- foreach ($aHeaders as $sHeaderString) {
+ $aHTTPHeaders = array();
+ foreach($aHeaders as $sHeaderString)
+ {
$aHTTPHeaders[] = trim($sHeaderString);
}
}
// Default options, can be overloaded/extended with the 4th parameter of this method, see above $aCurlOptions
- $aOptions = [
+ $aOptions = array(
CURLOPT_RETURNTRANSFER => true, // return the content of the request
CURLOPT_HEADER => false, // don't return the headers in the output
CURLOPT_FOLLOWLOCATION => true, // follow redirects
@@ -73,58 +76,74 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
CURLOPT_POST => count($aData),
CURLOPT_POSTFIELDS => http_build_query($aData),
CURLOPT_HTTPHEADER => $aHTTPHeaders,
- ];
+ );
$aAllOptions = $aCurlOptions + $aOptions;
$ch = curl_init($sUrl);
curl_setopt_array($ch, $aAllOptions);
$response = curl_exec($ch);
$iErr = curl_errno($ch);
- $sErrMsg = curl_error($ch);
- if ($iErr !== 0) {
+ $sErrMsg = curl_error( $ch );
+ if ($iErr !== 0)
+ {
throw new Exception("Problem opening URL: $sUrl, $sErrMsg");
}
- if (is_array($aResponseHeaders)) {
+ if (is_array($aResponseHeaders))
+ {
$aHeaders = curl_getinfo($ch);
- foreach ($aHeaders as $sCode => $sValue) {
- $sName = str_replace(' ', '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
+ foreach($aHeaders as $sCode => $sValue)
+ {
+ $sName = str_replace(' ' , '-', ucwords(str_replace('_', ' ', $sCode))); // Transform "content_type" into "Content-Type"
$aResponseHeaders[$sName] = $sValue;
}
}
- curl_close($ch);
- } else {
+ curl_close( $ch );
+ }
+ else
+ {
// cURL is not available let's try with streams and fopen...
$sData = http_build_query($aData);
- $aParams = ['http' => [
+ $aParams = array('http' => array(
'method' => 'POST',
'content' => $sData,
- 'header' => "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
- ]];
- if ($sOptionnalHeaders !== null) {
+ 'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
+ ));
+ if ($sOptionnalHeaders !== null)
+ {
$aParams['http']['header'] .= $sOptionnalHeaders;
}
$ctx = stream_context_create($aParams);
$fp = @fopen($sUrl, 'rb', false, $ctx);
- if (!$fp) {
+ if (!$fp)
+ {
global $php_errormsg;
- if (isset($php_errormsg)) {
+ if (isset($php_errormsg))
+ {
throw new Exception("Wrong URL: $sUrl, $php_errormsg");
- } elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl')) {
+ }
+ elseif ((strtolower(substr($sUrl, 0, 5)) == 'https') && !extension_loaded('openssl'))
+ {
throw new Exception("Cannot connect to $sUrl: missing module 'openssl'");
- } else {
+ }
+ else
+ {
throw new Exception("Wrong URL: $sUrl");
}
}
$response = @stream_get_contents($fp);
- if ($response === false) {
+ if ($response === false)
+ {
throw new Exception("Problem reading data from $sUrl, $php_errormsg");
}
- if (is_array($aResponseHeaders)) {
+ if (is_array($aResponseHeaders))
+ {
$aMeta = stream_get_meta_data($fp);
$aHeaders = $aMeta['wrapper_data'];
- foreach ($aHeaders as $sHeaderString) {
- if (preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches)) {
+ foreach($aHeaders as $sHeaderString)
+ {
+ if(preg_match('/^([^:]+): (.+)$/', $sHeaderString, $aMatches))
+ {
$aResponseHeaders[$aMatches[1]] = trim($aMatches[2]);
}
}
@@ -142,203 +161,208 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
// Define the operations to perform (one operation per call the rest service)
//
-$aOperations = [
- [
+$aOperations = array(
+ array(
'operation' => 'list_operations', // operation code
- ],
- [
+ ),
+ array(
'operation' => 'core/create', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
- 'fields' => [
+ 'fields' => array(
'org_id' => "SELECT Organization WHERE name = 'Demo'",
- 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
+ 'caller_id' => array('name' => 'monet', 'first_name' => 'claude'),
'title' => 'issue blah',
- 'description' => 'something happened',
- ],
- ],
- [
+ 'description' => 'something happened'
+ ),
+ ),
+ array(
'operation' => 'core/update', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE id=1',
'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
- 'fields' => [
+ 'fields' => array(
'title' => 'Issue #'.rand(0, 100),
- 'contacts_list' => [
- [
+ 'contacts_list' => array(
+ array(
'role' => 'fireman #'.rand(0, 100),
- 'contact_id' => ['finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'],
- ],
- ],
- ],
- ],
+ 'contact_id' => array('finalclass' => 'Person', 'name' => 'monet', 'first_name' => 'claude'),
+ ),
+ ),
+ ),
+ ),
// Rewrite the full CaseLog on an existing UserRequest with id=1, setting date and user (optional)
- [
+ array(
'operation' => 'core/update',
'comment' => 'Synchronization from Client A', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE id=1',
'output_fields' => 'id, friendlyname, title',
- 'fields' => [
- 'public_log' => [
- 'items' => [
- 0 => [
+ 'fields' => array(
+ 'public_log' => array(
+ 'items' => array(
+ 0 => array(
'date' => '2001-02-01 23:59:59', //Allow to set the date of a true event, an alarm for eg.
'user_login' => 'Alarm monitoring', //Free text
'user_id' => 0, //0 is required for the user_login to be taken into account
'message' => 'This is 1st entry as an HTML formatted text',
- ],
- 1 => [
+ ),
+ 1 => array(
'date' => '2001-02-02 00:00:00', //If ommitted set automatically.
'user_login' => 'Alarm monitoring', //user=id=0 is missing so will be ignored
'message' => 'Second entry in text format:
with new line, but format not specified, so treated as HTML!, user_id=0 missing, so user_login ignored',
- ],
- ],
- ],
- ],
- ],
+ ),
+ ),
+ ),
+ ),
+ ),
// Add a Text entry in the HTML CaseLog of the UserRequest with id=1, setting date and user (optional)
- [
+ array(
'operation' => 'core/update', // operation code
'comment' => 'Synchronization from Alarm Monitoring', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 1, // object id or OQL
'output_fields' => 'id, friendlyname, title', // list of fields to show in the results (* or a,b,c)
// Example of adding an entry into a CaseLog on an existing UserRequest
- 'fields' => [
- 'public_log' => [
- 'add_item' => [
+ 'fields' => array(
+ 'public_log' => array(
+ 'add_item' => array(
'user_login' => 'New Entry', //Free text
'user_id' => 0, //0 is required for the user_login to be taken into account
'format' => 'text', //If ommitted, source is expected to be HTML
'message' => 'This text is not HTML formatted with 3 lines:
new line
3rd and last line',
- ],
- ],
- ],
- ],
- [
+ ),
+ ),
+ ),
+ ),
+ array(
'operation' => 'core/get', // operation code
'class' => 'UserRequest',
'key' => 'SELECT UserRequest',
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
- ],
- [
+ ),
+ array(
'operation' => 'core/delete', // operation code
'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE org_id = 2',
'simulate' => true,
- ],
- [
+ ),
+ array(
'operation' => 'core/apply_stimulus', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 1,
'stimulus' => 'ev_assign',
// Values to set
- 'fields' => [
+ 'fields' => array(
'team_id' => 15, // Helpdesk
- 'agent_id' => 9, // Jules Verne
- ],
+ 'agent_id' => 9 // Jules Verne
+ ),
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
- ],
- [
+ ),
+ array(
'operation' => 'core/get_related', // operation code
'class' => 'Server',
'key' => 'SELECT Server',
'relation' => 'impacts', // relation code
'depth' => 4, // max recursion depth
- ],
-];
-$aOperations = [
- [
+ ),
+);
+$aOperations = array(
+ array(
'operation' => 'core/create', // operation code
'comment' => 'Automatic creation of attachment blah blah...', // comment recorded in the change tracking log
'class' => 'Attachment',
'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
- 'fields' => [
+ 'fields' => array(
'item_class' => 'UserRequest',
'item_id' => 1,
'item_org_id' => 3,
- 'contents' => [
+ 'contents' => array(
'data' => 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACmSURBVChTfZHRDYMwDESzQ2fqhHx3C3ao+MkW/WlnaFxfzk7sEnE6JHJ+NgaKZN2zLHVN2ssfkae0Da7FQ5PRk/ve4Hcx19Ie6CEGuh/6vMgNhwanHVUNbt73lUDbYJ+6pg8b3+m2RehsVPdMXyvQY+OVkB+Rrv64lUjb3nq+aCA6v4leRqtfaIgimr53atBy9PlfUhoh3fFCNDmErv9FWR6ylBL5AREbmHBnFj5lAAAAAElFTkSuQmCC',
'filename' => 'myself.png',
- 'mimetype' => 'image/png',
- ],
- ],
- ],
- [
+ 'mimetype' => 'image/png'
+ ),
+ ),
+ ),
+ array(
'operation' => 'core/get', // operation code
'class' => 'Attachment',
'key' => 'SELECT Attachment',
'output_fields' => '*',
- ],
-];
-$aOperations = [
- [
+ )
+);
+$aOperations = array(
+ array(
'operation' => 'core/update', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'Server',
'key' => 'SELECT Server WHERE name="Server1"',
'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
- 'fields' => [
+ 'fields' => array(
'description' => 'Issue #'.time(),
- ],
- ],
-];
-$aOperations = [
- [
+ ),
+ ),
+);
+$aOperations = array(
+ array(
'operation' => 'core/create', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'output_fields' => 'id, friendlyname', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
- 'fields' => [
+ 'fields' => array(
'org_id' => "SELECT Organization WHERE name = 'Demo'",
- 'caller_id' => ['name' => 'monet', 'first_name' => 'claude'],
+ 'caller_id' => array('name' => 'monet', 'first_name' => 'claude'),
'title' => 'issue blah',
- 'description' => 'something happened',
- ],
- ],
-];
-$aXXXOperations = [
- [
+ 'description' => 'something happened'
+ ),
+ ),
+);
+$aXXXOperations = array(
+ array(
'operation' => 'core/check_credentials', // operation code
'user' => 'admin',
'password' => 'admin',
- ],
-];
-$aDeleteOperations = [
- [
+ ),
+);
+$aDeleteOperations = array(
+ array(
'operation' => 'core/delete', // operation code
'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
'class' => 'Server',
'key' => 'SELECT Server',
'simulate' => false,
- ],
-];
+ ),
+);
-if (false) {
+if (false)
+{
echo "Please edit the sample script and configure the server URL";
exit;
-} else {
+}
+else
+{
$sUrl = "https://localhost/itop/webservices/rest.php?version=1.3";
}
-$aData = [];
+$aData = array();
$aData['auth_user'] = 'rest';
$aData['auth_pwd'] = 'rest';
-foreach ($aOperations as $iOp => $aOperation) {
+
+foreach ($aOperations as $iOp => $aOperation)
+{
echo "======================================\n";
echo "Operation #$iOp: ".$aOperation['operation']."\n";
$aData['json_data'] = json_encode($aOperation);
@@ -347,18 +371,25 @@ function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHead
echo "Input:\n";
print_r($aOperation);
$aResults = null;
- try {
+ try
+ {
$response = DoPostRequest($sUrl, $aData);
$aResults = json_decode($response);
- } catch (Exception $e) {
+ }
+ catch (Exception $e)
+ {
$response = $e->getMessage();
}
- if ($aResults) {
+ if ($aResults)
+ {
echo "--------------------------------------\n";
echo "Reply:\n";
print_r($aResults);
- } else {
+ }
+ else
+ {
echo "ERROR rest.php replied:\n";
echo $response;
}
}
+
diff --git a/webservices/itopsoap.examples.php b/webservices/itopsoap.examples.php
index 6427a02843..5ed1bdc24c 100644
--- a/webservices/itopsoap.examples.php
+++ b/webservices/itopsoap.examples.php
@@ -1,10 +1,9 @@
+
/**
- * Shows a usage of the SOAP queries
+ * Shows a usage of the SOAP queries
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -31,16 +31,17 @@
$aSOAPMapping = SOAPMapping::GetMapping();
-ini_set("soap.wsdl_cache_enabled", "0");
+ini_set("soap.wsdl_cache_enabled","0");
$oSoapClient = new SoapClient(
$sWsdlUri,
- [
+ array(
'trace' => 1,
'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php
- ]
+ )
);
-try {
+try
+{
// The most simple service, returning a string
//
$sServerVersion = $oSoapClient->GetVersion();
@@ -49,29 +50,30 @@
// More complex ones, returning a SOAPResult structure
// (run the page to know more about the returned data)
//
- $oRes = $oSoapClient->CreateIncidentTicket(
+ $oRes = $oSoapClient->CreateIncidentTicket
+ (
'admin', /* login */
'admin', /* password */
'Email server down', /* title */
'HW found shutdown', /* description */
null, /* caller */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Demo')]), /* customer */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW Management')]), /* service */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'Troubleshooting')]), /* service subcategory */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* customer */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'NW Management'))), /* service */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Troubleshooting'))), /* service subcategory */
'', /* product */
- new SOAPExternalKeySearch([new SOAPSearchCondition('name', 'NW support')]), /* workgroup */
- [
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'NW support'))), /* workgroup */
+ array(
new SOAPLinkCreationSpec(
'Device',
- [new SOAPSearchCondition('name', 'switch01')],
- []
+ array(new SOAPSearchCondition('name', 'switch01')),
+ array()
),
new SOAPLinkCreationSpec(
'Server',
- [new SOAPSearchCondition('name', 'dbserver1.demo.com')],
- []
+ array(new SOAPSearchCondition('name', 'dbserver1.demo.com')),
+ array()
),
- ], /* impacted cis */
+ ), /* impacted cis */
'1', /* impact */
'1' /* urgency */
);
@@ -82,36 +84,44 @@
echo "\n";
echo "\n";
- $oRes = $oSoapClient->SearchObjects(
+ $oRes = $oSoapClient->SearchObjects
+ (
'admin', /* login */
'admin', /* password */
'SELECT URP_Profiles' /* oql */
);
echo "SearchObjects() returned:\n";
- if ($oRes->status) {
+ if ($oRes->status)
+ {
$aResults = $oRes->result;
echo "
\n";
// Header made after the first line
echo "\n";
- foreach ($aResults[0]->values as $aKeyValuePair) {
+ foreach ($aResults[0]->values as $aKeyValuePair)
+ {
echo " ".$aKeyValuePair->key." \n";
}
echo " \n";
- foreach ($aResults as $iRow => $aData) {
+ foreach ($aResults as $iRow => $aData)
+ {
echo "\n";
- foreach ($aData->values as $aKeyValuePair) {
+ foreach ($aData->values as $aKeyValuePair)
+ {
echo " ".$aKeyValuePair->value." \n";
}
echo " \n";
}
echo "
\n";
- } else {
- $aErrors = [];
- foreach ($oRes->errors->messages as $oMessage) {
+ }
+ else
+ {
+ $aErrors = array();
+ foreach ($oRes->errors->messages as $oMessage)
+ {
$aErrors[] = $oMessage->text;
}
$sErrorMsg = implode(', ', $aErrors);
@@ -121,12 +131,15 @@
//echo "\n";
}
echo "\n";
-} catch (SoapFault $e) {
- echo "SoapFault Exception: {$e->getMessage()} \n";
- echo "Request \n";
- echo "\n";
- echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
- echo " ";
+}
+catch(SoapFault $e)
+{
+ echo "SoapFault Exception: {$e->getMessage()} \n";
+ echo "Request \n";
+ echo "\n";
+ echo htmlspecialchars($oSoapClient->__getLastRequest())."\n";
+ echo " ";
echo "Response ";
echo $oSoapClient->__getLastResponse()."\n";
}
+?>
diff --git a/webservices/itopsoaptypes.class.inc.php b/webservices/itopsoaptypes.class.inc.php
index 679dc49961..6145d85fae 100644
--- a/webservices/itopsoaptypes.class.inc.php
+++ b/webservices/itopsoaptypes.class.inc.php
@@ -1,10 +1,9 @@
+
/**
* Declarations required for the WSDL
*
@@ -24,6 +24,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
+
// Note: the attributes must have the same names (case sensitive) as in the WSDL specification
//
@@ -39,6 +40,7 @@ public function __construct($sAttCode, $value)
}
}
+
class SOAPExternalKeySearch
{
public $conditions; // array of SOAPSearchCondition
@@ -50,15 +52,12 @@ public function __construct($aConditions = null)
public function IsVoid()
{
- if (is_null($this->conditions)) {
- return true;
- }
- if (count($this->conditions) == 0) {
- return true;
- }
+ if (is_null($this->conditions)) return true;
+ if (count($this->conditions) == 0) return true;
}
}
+
class SOAPAttributeValue
{
public $attcode; // string
@@ -71,6 +70,7 @@ public function __construct($sAttCode, $value)
}
}
+
class SOAPLinkCreationSpec
{
public $class;
@@ -85,6 +85,7 @@ public function __construct($sClass, $aConditions, $aAttributes)
}
}
+
class SOAPLogMessage
{
public $text; // string
@@ -95,6 +96,7 @@ public function __construct($sText)
}
}
+
class SOAPResultLog
{
public $messages; // array of SOAPLogMessage
@@ -105,6 +107,7 @@ public function __construct($aMessages)
}
}
+
class SOAPKeyValue
{
public $key; // string
@@ -129,6 +132,7 @@ public function __construct($sLabel, $aValues)
}
}
+
class SOAPResult
{
public $status; // boolean
@@ -159,11 +163,12 @@ public function __construct($bStatus, $sMessage)
}
}
+
class SOAPMapping
{
- public static function GetMapping()
+ static function GetMapping()
{
- $aSOAPMapping = [
+ $aSOAPMapping = array(
'SearchCondition' => 'SOAPSearchCondition',
'ExternalKeySearch' => 'SOAPExternalKeySearch',
'AttributeValue' => 'SOAPAttributeValue',
@@ -175,7 +180,9 @@ public static function GetMapping()
'ResultMessage' => 'SOAPResultMessage',
'Result' => 'SOAPResult',
'SimpleResult' => 'SOAPSimpleResult',
- ];
+ );
return $aSOAPMapping;
}
}
+
+?>
diff --git a/webservices/rest.php b/webservices/rest.php
index 878a3e9faf..70f08cb0e2 100644
--- a/webservices/rest.php
+++ b/webservices/rest.php
@@ -1,5 +1,4 @@
operations[] = [
+ $this->operations[] = array(
'verb' => $sVerb,
'description' => $sDescription,
'extension' => $sServiceProviderClass,
- ];
+ );
}
}
if (!function_exists('json_last_error_msg')) {
- function json_last_error_msg()
- {
- static $ERRORS = [
+ function json_last_error_msg() {
+ static $ERRORS = array(
JSON_ERROR_NONE => 'No error',
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
JSON_ERROR_SYNTAX => 'Syntax error',
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
- ];
+ );
$error = json_last_error();
return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
@@ -75,14 +74,15 @@ function json_last_error_msg()
//read json_data parameter via as a string (standard behaviour)
$sJsonString = utils::ReadParam('json_data', null, false, 'raw_data');
-if (empty($sJsonString)) {
+if (empty($sJsonString)){
//N °3455: read json_data parameter via a file passed by http protocol
- if (isset($_FILES['json_data']['tmp_name'])) {
+ if(isset($_FILES['json_data']['tmp_name']))
+ {
$sTmpFilePath = $_FILES['json_data']['tmp_name'];
- if (is_file($sTmpFilePath)) {
+ if (is_file($sTmpFilePath)){
$sValue = file_get_contents($sTmpFilePath);
unlink($sTmpFilePath);
- if (! empty($sValue)) {
+ if (! empty($sValue)){
$sJsonString = utils::Sanitize($sValue, null, 'raw_data');
}
}
@@ -92,111 +92,136 @@ function json_last_error_msg()
$sProvider = '';
$oKPI = new ExecutionKPI();
-try {
+try
+{
utils::UseParamFile();
-
+
$oKPI->ComputeAndReport('Data model loaded');
- // N°6358 - force credentials for REST calls
- LoginWebPage::ResetSession(true);
+ // N°6358 - force credentials for REST calls
+ LoginWebPage::ResetSession(true);
$iRet = LoginWebPage::DoLogin(false, false, LoginWebPage::EXIT_RETURN);
- $oKPI->ComputeAndReport('User login');
+ $oKPI->ComputeAndReport('User login');
- if ($iRet == LoginWebPage::EXIT_CODE_OK) {
+ if ($iRet == LoginWebPage::EXIT_CODE_OK)
+ {
// Extra validation of the profile
- if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User')) {
+ if ((MetaModel::GetConfig()->Get('secure_rest_services') == true) && !UserRights::HasProfile('REST Services User'))
+ {
// Web services access is limited to the users with the profile REST Web Services
$iRet = LoginWebPage::EXIT_CODE_NOTAUTHORIZED;
}
}
- if ($iRet != LoginWebPage::EXIT_CODE_OK) {
- switch ($iRet) {
+ if ($iRet != LoginWebPage::EXIT_CODE_OK)
+ {
+ switch($iRet)
+ {
case LoginWebPage::EXIT_CODE_MISSINGLOGIN:
- throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
- break;
-
+ throw new Exception("Missing parameter 'auth_user'", RestResult::MISSING_AUTH_USER);
+ break;
+
case LoginWebPage::EXIT_CODE_MISSINGPASSWORD:
- throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
- break;
-
+ throw new Exception("Missing parameter 'auth_pwd'", RestResult::MISSING_AUTH_PWD);
+ break;
+
case LoginWebPage::EXIT_CODE_WRONGCREDENTIALS:
- throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
- break;
-
+ throw new Exception("Invalid login", RestResult::UNAUTHORIZED);
+ break;
+
case LoginWebPage::EXIT_CODE_PORTALUSERNOTAUTHORIZED:
- throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
- break;
-
+ throw new Exception("Portal user is not allowed", RestResult::UNAUTHORIZED);
+ break;
+
case LoginWebPage::EXIT_CODE_NOTAUTHORIZED:
- throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
- break;
-
+ throw new Exception("This user is not authorized to use the web services. (The profile REST Services User is required to access the REST web services)", RestResult::UNAUTHORIZED);
+ break;
+
default:
- throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
+ throw new Exception("Unknown authentication error (retCode=$iRet)", RestResult::UNAUTHORIZED);
}
}
- if ($sVersion == null) {
+ if ($sVersion == null)
+ {
throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION);
}
- if ($sJsonString == null) {
+ if ($sJsonString == null)
+ {
throw new Exception("Missing parameter 'json_data'", RestResult::MISSING_JSON);
}
- if (is_string($sJsonString)) {
- $aJsonData = @json_decode($sJsonString);
- } elseif (is_array($sJsonString)) {
- $aJsonData = (object) $sJsonString;
- $sJsonString = json_encode($aJsonData);
- } else {
- $aJsonData = null;
- }
-
- if ($aJsonData == null) {
- throw new Exception('Parameter json_data is not a valid JSON structure', RestResult::INVALID_JSON);
- }
+ if (is_string($sJsonString))
+ {
+ $aJsonData = @json_decode($sJsonString);
+ }
+ elseif(is_array($sJsonString))
+ {
+ $aJsonData = (object) $sJsonString;
+ $sJsonString = json_encode($aJsonData);
+ }
+ else
+ {
+ $aJsonData = null;
+ }
+
+ if ($aJsonData == null)
+ {
+ throw new Exception('Parameter json_data is not a valid JSON structure', RestResult::INVALID_JSON);
+ }
$oKPI->ComputeAndReport('Parameters validated');
+
/** @var iRestServiceProvider[] $aProviders */
$oKPI = new ExecutionKPI();
- $aProviders = [];
- foreach (get_declared_classes() as $sPHPClass) {
+ $aProviders = array();
+ foreach(get_declared_classes() as $sPHPClass)
+ {
$oRefClass = new ReflectionClass($sPHPClass);
- if ($oRefClass->implementsInterface('iRestServiceProvider')) {
- $aProviders[] = new $sPHPClass();
+ if ($oRefClass->implementsInterface('iRestServiceProvider'))
+ {
+ $aProviders[] = new $sPHPClass;
}
}
- $aOpToRestService = []; // verb => $oRestServiceProvider
+ $aOpToRestService = array(); // verb => $oRestServiceProvider
/** @var iRestServiceProvider $oRestSP */
- foreach ($aProviders as $oRestSP) {
+ foreach ($aProviders as $oRestSP)
+ {
$aOperations = $oRestSP->ListOperations($sVersion);
- foreach ($aOperations as $aOpData) {
- $aOpToRestService[$aOpData['verb']] =
- [
+ foreach ($aOperations as $aOpData)
+ {
+ $aOpToRestService[$aOpData['verb']] = array
+ (
'service_provider' => $oRestSP,
'description' => $aOpData['description'],
- ];
+ );
}
}
$oKPI->ComputeAndReport('iRestServiceProvider loaded with operations');
- if (count($aOpToRestService) == 0) {
+ if (count($aOpToRestService) == 0)
+ {
throw new Exception("There is no service available for version '$sVersion'", RestResult::UNSUPPORTED_VERSION);
}
+
$sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation');
- if ($sOperation == 'list_operations') {
+ if ($sOperation == 'list_operations')
+ {
$oResult = new RestResultListOperations();
$oResult->message = "Operations: ".count($aOpToRestService);
$oResult->version = $sVersion;
- foreach ($aOpToRestService as $sVerb => $aOpData) {
+ foreach ($aOpToRestService as $sVerb => $aOpData)
+ {
$oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider']));
}
- } else {
- if (!array_key_exists($sOperation, $aOpToRestService)) {
+ }
+ else
+ {
+ if (!array_key_exists($sOperation, $aOpToRestService))
+ {
throw new Exception("Unknown verb '$sOperation' in version '$sVersion'", RestResult::UNKNOWN_OPERATION);
}
/** @var iRestServiceProvider $oRS */
@@ -211,11 +236,16 @@ function json_last_error_msg()
$oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData);
}
$oKPI->ComputeAndReport('Operation finished');
-} catch (Exception $e) {
+}
+catch(Exception $e)
+{
$oResult = new RestResult();
- if ($e->GetCode() == 0) {
+ if ($e->GetCode() == 0)
+ {
$oResult->code = RestResult::INTERNAL_ERROR;
- } else {
+ }
+ else
+ {
$oResult->code = $e->GetCode();
}
$oResult->message = "Error: ".$e->GetMessage();
@@ -226,17 +256,23 @@ function json_last_error_msg()
//
$sResponse = json_encode($oResult);
-if ($sResponse === false) {
+
+if ($sResponse === false)
+{
$oJsonIssue = new RestResult();
$oJsonIssue->code = RestResult::INTERNAL_ERROR;
$oJsonIssue->message = 'json encoding failed with message: '.json_last_error_msg().'. Full response structure for debugging purposes (print_r+bin2hex): '.bin2hex(print_r($oResult, true));
$sResponse = json_encode($oJsonIssue);
}
+
$sCallback = utils::ReadParam('callback', null);
-if ($sCallback == null) {
+if ($sCallback == null)
+{
$oP = new JsonPage();
-} else {
+}
+else
+{
$oP = new JsonPPage($sCallback);
}
$oP->add_header('Access-Control-Allow-Origin: *');
@@ -247,16 +283,18 @@ function json_last_error_msg()
// Log usage
//
-if (MetaModel::GetConfig()->Get('log_rest_service')) {
+if (MetaModel::GetConfig()->Get('log_rest_service'))
+{
$oLog = new EventRestService();
$oLog->SetTrim('userinfo', UserRights::GetUser());
$oLog->Set('version', $sVersion);
$oLog->Set('operation', $sOperation);
- $oLog->SetTrim('json_input', $sSanitizedJsonInput ?? $sJsonString);
+ $oLog->SetTrim('json_input', $sSanitizedJsonInput ?? $sJsonString);
$oLog->Set('provider', $sProvider);
$sMessage = $oResult->message;
- if (empty($oResult->message)) {
+ if (empty($oResult->message))
+ {
$sMessage = 'Ok';
}
$oLog->SetTrim('message', $sMessage);
@@ -265,7 +303,7 @@ function json_last_error_msg()
$iUnescapeSlashAndUnicode = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
$sJsonOuputWithPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode | JSON_PRETTY_PRINT);
$sJsonOutputWithoutPrettyPrinting = json_encode($oResult, $iUnescapeSlashAndUnicode);
- !StringFitsInLogField($sJsonOuputWithPrettyPrinting) ?
+ !StringFitsInLogField( $sJsonOuputWithPrettyPrinting) ?
$oLog->SetTrim('json_output', $sJsonOutputWithoutPrettyPrinting) : // too long, we don't make it pretty
$oLog->SetTrim('json_output', $sJsonOuputWithPrettyPrinting);
@@ -278,4 +316,4 @@ function json_last_error_msg()
function StringFitsInLogField(string $sLog): bool
{
return mb_strlen($sLog) <= 16383; // hardcoded value, see N°8260
-}
+}
\ No newline at end of file
diff --git a/webservices/soapserver.php b/webservices/soapserver.php
index 055ceb0ebe..2d9b49ec46 100644
--- a/webservices/soapserver.php
+++ b/webservices/soapserver.php
@@ -1,5 +1,4 @@
$aSOAPMapping,
- ]
+ array(
+ 'classmap' => $aSOAPMapping
+ )
);
// $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION);
-if (!empty($sServiceCategory)) {
+if (!empty($sServiceCategory))
+{
$sServiceClass = $sServiceCategory;
- if (!class_exists($sServiceClass)) {
+ if (!class_exists($sServiceClass))
+ {
// not a valid class name (not a PHP class at all)
throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not a PHP class");
- } elseif (!is_subclass_of($sServiceClass, 'WebServicesBase')) {
+ }
+ elseif (!is_subclass_of($sServiceClass, 'WebServicesBase'))
+ {
// not a valid class name (not deriving from WebServicesBase)
throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not derived from WebServicesBase");
- } else {
+ }
+ else
+ {
$oSoapServer->setClass($sServiceClass, null);
}
-} else {
+}
+else
+{
$oSoapServer->setClass('BasicServices', null);
}
-if ($_SERVER["REQUEST_METHOD"] == "POST") {
+if ($_SERVER["REQUEST_METHOD"] == "POST")
+{
CMDBObject::SetTrackOrigin('webservice-soap');
$oSoapServer->handle();
-} else {
+}
+else
+{
echo "This SOAP server can handle the following functions: ";
$aFunctions = $oSoapServer->getFunctions();
echo "\n";
- foreach ($aFunctions as $sFunc) {
- if ($sFunc == 'GetWSDLContents') {
- continue;
- }
+ foreach($aFunctions as $sFunc)
+ {
+ if ($sFunc == 'GetWSDLContents') continue;
echo "$sFunc \n";
}
@@ -76,8 +88,10 @@
echo "You may also want to try the following service categories: ";
echo "\n";
- foreach (get_declared_classes() as $sPHPClass) {
- if (is_subclass_of($sPHPClass, 'WebServicesBase')) {
+ foreach(get_declared_classes() as $sPHPClass)
+ {
+ if (is_subclass_of($sPHPClass, 'WebServicesBase'))
+ {
$sServiceCategory = $sPHPClass;
$sSoapServerUri = utils::GetAbsoluteUrlAppRoot().'webservices/soapserver.php';
$sSoapServerUri .= "?service_category=$sServiceCategory";
@@ -86,3 +100,4 @@
}
echo " \n";
}
+?>
diff --git a/webservices/status.php b/webservices/status.php
index 7ef91b2c0a..636ab4ec64 100644
--- a/webservices/status.php
+++ b/webservices/status.php
@@ -6,18 +6,21 @@
use Combodo\iTop\Application\Status\Status;
//Do check Status
-try {
- new Status();
- $aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
-} catch (Exception $e) {
- $iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
- $aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
- http_response_code(500);
+try
+{
+ new Status();
+ $aResult = ['status' => STATUS_RUNNING, 'code' => RestResult::OK, 'message' => ''];
+}
+catch (Exception $e)
+{
+ $iCode = (defined('\RestResult::INTERNAL_ERROR')) ? RestResult::INTERNAL_ERROR : 100;
+ $aResult = ['status' => STATUS_ERROR, 'code' => $iCode, 'message' => $e->getMessage()];
+ http_response_code(500);
}
//Set headers, based on webservices/rest.php
$sContentType = 'application/json';
-header('Content-type: '.$sContentType);
+header('Content-type: ' . $sContentType);
header('Access-Control-Allow-Origin: *');
//Output result
diff --git a/webservices/webservices.basic.php b/webservices/webservices.basic.php
index e322aee18a..bf8c364474 100644
--- a/webservices/webservices.basic.php
+++ b/webservices/webservices.basic.php
@@ -1,10 +1,9 @@
+
/**
* Implementation of iTop SOAP services
*
@@ -26,9 +26,10 @@
require_once(APPROOT.'/webservices/webservices.class.inc.php');
+
class BasicServices extends WebServicesBase
{
- protected static function GetWSDLFilePath()
+ static protected function GetWSDLFilePath()
{
return APPROOT.'/webservices/itop.wsdl.tpl';
}
@@ -38,11 +39,14 @@ protected static function GetWSDLFilePath()
*
* @return string WebServiceResult
*/
- public static function GetVersion()
+ static public function GetVersion()
{
- if (ITOP_REVISION == 'svn') {
+ if (ITOP_REVISION == 'svn')
+ {
$sVersionString = ITOP_VERSION.' [dev]';
- } else {
+ }
+ else
+ {
// This is a build made from SVN, let display the full information
$sVersionString = ITOP_VERSION_FULL." ".ITOP_BUILD_DATE;
}
@@ -52,7 +56,8 @@ public static function GetVersion()
public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
{
- if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword))
+ {
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
@@ -66,15 +71,15 @@ public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription,
$aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
$aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
- $aImpactedCIs = [];
- if (is_null($aSOAPImpactedCIs)) {
- $aSOAPImpactedCIs = [];
- }
- foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
+ $aImpactedCIs = array();
+ if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
+ foreach($aSOAPImpactedCIs as $oImpactedCIs)
+ {
$aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
}
- $oRes = $this->_CreateResponseTicket(
+ $oRes = $this->_CreateResponseTicket
+ (
'UserRequest',
$sTitle,
$sDescription,
@@ -93,7 +98,8 @@ public function CreateRequestTicket($sLogin, $sPassword, $sTitle, $sDescription,
public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
{
- if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword))
+ {
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
@@ -101,27 +107,29 @@ public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription
}
UserRights::Login($sLogin);
- if (!class_exists('Incident')) {
+
+ if (!class_exists('Incident'))
+ {
$oRes = new WebServiceResult();
$oRes->LogError("The class Incident does not exist. Did you install the Incident Management (ITIL) module ?");
return $oRes->ToSoapStructure();
}
-
+
$aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
$aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
$aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
$aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
$aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
- $aImpactedCIs = [];
- if (is_null($aSOAPImpactedCIs)) {
- $aSOAPImpactedCIs = [];
- }
- foreach ($aSOAPImpactedCIs as $oImpactedCIs) {
+ $aImpactedCIs = array();
+ if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
+ foreach($aSOAPImpactedCIs as $oImpactedCIs)
+ {
$aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
}
- $oRes = $this->_CreateResponseTicket(
+ $oRes = $this->_CreateResponseTicket
+ (
'Incident',
$sTitle,
$sDescription,
@@ -137,11 +145,11 @@ public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription
);
return $oRes->ToSoapStructure();
}
-
+
/**
* Create an ResponseTicket (Incident or UserRequest) from an external system
* Some CIs might be specified (by their name/IP)
- *
+ *
* @param string sClass The class of the ticket: Incident or UserRequest
* @param string sTitle
* @param string sDescription
@@ -162,7 +170,8 @@ protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCall
$oRes = new WebServiceResult();
- try {
+ try
+ {
CMDBObject::SetTrackInfo('Administrator');
$oNewTicket = MetaModel::NewObject($sClass);
@@ -171,36 +180,48 @@ protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCall
$this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
-
+
$this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
- if (!array_key_exists('service_id', $aServiceSubcategoryDesc)) {
+ if (!array_key_exists('service_id', $aServiceSubcategoryDesc))
+ {
$aServiceSubcategoryDesc['service_id'] = $oNewTicket->Get('service_id');
}
$this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
- if (MetaModel::IsValidAttCode($sClass, 'product')) {
+ if (MetaModel::IsValidAttCode($sClass, 'product'))
+ {
// 1.x data models
$this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
}
- if (MetaModel::IsValidAttCode($sClass, 'workgroup_id')) {
+ if (MetaModel::IsValidAttCode($sClass, 'workgroup_id'))
+ {
// 1.x data models
$this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
- } elseif (MetaModel::IsValidAttCode($sClass, 'team_id')) {
+ }
+ else if (MetaModel::IsValidAttCode($sClass, 'team_id'))
+ {
// 2.x data models
$this->MyObjectSetExternalKey('team_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
}
- if (MetaModel::IsValidAttCode($sClass, 'ci_list')) {
+
+ if (MetaModel::IsValidAttCode($sClass, 'ci_list'))
+ {
// 1.x data models
$aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
- } elseif (MetaModel::IsValidAttCode($sClass, 'functionalcis_list')) {
+ }
+ else if (MetaModel::IsValidAttCode($sClass, 'functionalcis_list'))
+ {
// 2.x data models
$aDevicesNotFound = $this->AddLinkedObjects('functionalcis_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
}
-
- if (count($aDevicesNotFound) > 0) {
+
+ if (count($aDevicesNotFound) > 0)
+ {
$this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
- } else {
+ }
+ else
+ {
$this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
}
@@ -208,9 +229,13 @@ protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCall
$this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
$this->MyObjectInsert($oNewTicket, 'created', $oRes);
- } catch (CoreException $e) {
+ }
+ catch (CoreException $e)
+ {
$oRes->LogError($e->getMessage());
- } catch (Exception $e) {
+ }
+ catch (Exception $e)
+ {
$oRes->LogError($e->getMessage());
}
@@ -220,12 +245,13 @@ protected function _CreateResponseTicket($sClass, $sTitle, $sDescription, $aCall
/**
* Given an OQL, returns a set of objects (several objects could be on the same row)
- *
+ *
* @param string sOQL
- */
+ */
public function SearchObjects($sLogin, $sPassword, $sOQL)
{
- if (!UserRights::CheckCredentials($sLogin, $sPassword)) {
+ if (!UserRights::CheckCredentials($sLogin, $sPassword))
+ {
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
@@ -240,16 +266,22 @@ public function SearchObjects($sLogin, $sPassword, $sOQL)
protected function _SearchObjects($sOQL)
{
$oRes = new WebServiceResult();
- try {
+ try
+ {
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSet = new DBObjectSet($oSearch);
$aData = $oSet->ToArrayOfValues();
- foreach ($aData as $iRow => $aRow) {
+ foreach($aData as $iRow => $aRow)
+ {
$oRes->AddResultRow("row_$iRow", $aRow);
}
- } catch (CoreException $e) {
+ }
+ catch (CoreException $e)
+ {
$oRes->LogError($e->getMessage());
- } catch (Exception $e) {
+ }
+ catch (Exception $e)
+ {
$oRes->LogError($e->getMessage());
}
@@ -257,3 +289,4 @@ protected function _SearchObjects($sOQL)
return $oRes;
}
}
+?>
diff --git a/webservices/webservices.class.inc.php b/webservices/webservices.class.inc.php
index a65f56ab19..5df50d4270 100644
--- a/webservices/webservices.class.inc.php
+++ b/webservices/webservices.class.inc.php
@@ -1,10 +1,9 @@
+
/**
* Implementation of iTop SOAP services
*
@@ -24,6 +24,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
+
require_once(APPROOT.'/webservices/itopsoaptypes.class.inc.php');
/**
@@ -33,6 +34,7 @@
*/
class WebServiceResult
{
+
/**
* Overall status
*
@@ -69,32 +71,37 @@ class WebServiceResult
public function __construct()
{
$this->m_bStatus = true;
- $this->m_aResult = [];
- $this->m_aErrors = [];
- $this->m_aWarnings = [];
- $this->m_aInfos = [];
+ $this->m_aResult = array();
+ $this->m_aErrors = array();
+ $this->m_aWarnings = array();
+ $this->m_aInfos = array();
}
public function ToSoapStructure()
{
- $aResults = [];
- foreach ($this->m_aResult as $sLabel => $aData) {
- $aValues = [];
- foreach ($aData as $sKey => $value) {
+ $aResults = array();
+ foreach($this->m_aResult as $sLabel => $aData)
+ {
+ $aValues = array();
+ foreach($aData as $sKey => $value)
+ {
$aValues[] = new SOAPKeyValue($sKey, $value);
}
$aResults[] = new SoapResultMessage($sLabel, $aValues);
}
- $aInfos = [];
- foreach ($this->m_aInfos as $sMessage) {
+ $aInfos = array();
+ foreach($this->m_aInfos as $sMessage)
+ {
$aInfos[] = new SoapLogMessage($sMessage);
}
- $aWarnings = [];
- foreach ($this->m_aWarnings as $sMessage) {
+ $aWarnings = array();
+ foreach($this->m_aWarnings as $sMessage)
+ {
$aWarnings[] = new SoapLogMessage($sMessage);
}
- $aErrors = [];
- foreach ($this->m_aErrors as $sMessage) {
+ $aErrors = array();
+ foreach($this->m_aErrors as $sMessage)
+ {
$aErrors[] = new SoapLogMessage($sMessage);
}
@@ -128,11 +135,11 @@ public function IsOk()
public function AddResultObject($sLabel, $oObject)
{
$oAppContext = new ApplicationContext();
- $this->m_aResult[$sLabel] = [
+ $this->m_aResult[$sLabel] = array(
'id' => $oObject->GetKey(),
'name' => $oObject->GetRawName(),
'url' => $oAppContext->MakeObjectUrl(get_class($oObject), $oObject->GetKey(), null, false), // Raw URL without HTML tags
- ];
+ );
}
/**
@@ -176,11 +183,8 @@ public function LogWarning($sDescription)
*/
public function LogIssue($sDescription, $bIsStopper = true)
{
- if ($bIsStopper) {
- $this->LogError($sDescription);
- } else {
- $this->LogWarning($sDescription);
- }
+ if ($bIsStopper) $this->LogError($sDescription);
+ else $this->LogWarning($sDescription);
}
/**
@@ -216,7 +220,8 @@ public function GetErrorsAsText()
public function GetReturnedDataAsText()
{
$sRet = '';
- foreach ($this->m_aResult as $sKey => $value) {
+ foreach ($this->m_aResult as $sKey => $value)
+ {
$sRet .= "===== $sKey =====\n";
$sRet .= print_r($value, true);
}
@@ -224,6 +229,7 @@ public function GetReturnedDataAsText()
}
}
+
/**
* Generic response of iTop SOAP services - failed login
*
@@ -245,12 +251,13 @@ public function __construct($sLogin)
*/
abstract class WebServicesBase
{
- public static function GetWSDLContents($sServiceCategory = '')
+ static public function GetWSDLContents($sServiceCategory = '')
{
- if ($sServiceCategory == '') {
+ if ($sServiceCategory == '')
+ {
$sServiceCategory = 'BasicServices';
}
- $sWsdlFilePath = call_user_func([$sServiceCategory, 'GetWSDLFilePath']);
+ $sWsdlFilePath = call_user_func(array($sServiceCategory, 'GetWSDLFilePath'));
return file_get_contents($sWsdlFilePath);
}
@@ -264,14 +271,15 @@ public static function GetWSDLContents($sServiceCategory = '')
*/
protected function LogUsage($sVerb, $oRes)
{
- if (!MetaModel::IsLogEnabledWebService()) {
- return;
- }
+ if (!MetaModel::IsLogEnabledWebService()) return;
$oLog = new EventWebService();
- if ($oRes->IsOk()) {
+ if ($oRes->IsOk())
+ {
$oLog->Set('message', $sVerb.' was successfully invoked');
- } else {
+ }
+ else
+ {
$oLog->Set('message', $sVerb.' returned errors');
}
$oLog->Set('userinfo', UserRights::GetUser());
@@ -283,11 +291,12 @@ protected function LogUsage($sVerb, $oRes)
$this->TrimAndSetValue($oLog, 'data', (string)$oRes->GetReturnedDataAsText());
$oLog->DBInsertNoReload();
}
-
+
protected function TrimAndSetValue($oLog, $sAttCode, $sValue)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oLog), $sAttCode);
- if (is_object($oAttDef)) {
+ if (is_object($oAttDef))
+ {
$iMaxSize = $oAttDef->GetMaxSize();
if ($iMaxSize && (mb_strlen($sValue) > $iMaxSize)) {
$sValue = mb_substr($sValue, 0, $iMaxSize);
@@ -308,9 +317,12 @@ protected function TrimAndSetValue($oLog, $sAttCode, $sValue)
protected function MyObjectSetScalar($sAttCode, $sParamName, $value, &$oTargetObj, &$oRes)
{
$res = $oTargetObj->CheckValue($sAttCode, $value);
- if ($res === true) {
+ if ($res === true)
+ {
$oTargetObj->Set($sAttCode, $value);
- } else {
+ }
+ else
+ {
// $res contains the error description
$oRes->LogError("Unexpected value for parameter $sParamName: $res");
}
@@ -331,24 +343,31 @@ protected function MyObjectSetExternalKey($sAttCode, $sParamName, $aExtKeyDesc,
$bIsMandatory = !$oExtKey->IsNullAllowed();
- if (is_null($aExtKeyDesc)) {
- if ($bIsMandatory) {
+ if (is_null($aExtKeyDesc))
+ {
+ if ($bIsMandatory)
+ {
$oRes->LogError("Parameter $sParamName: found null for a mandatory key");
- } else {
+ }
+ else
+ {
// skip silently
return;
}
}
- if (count($aExtKeyDesc) == 0) {
+ if (count($aExtKeyDesc) == 0)
+ {
$oRes->LogIssue("Parameter $sParamName: no search condition has been specified", $bIsMandatory);
return;
}
$sKeyClass = $oExtKey->GetTargetClass();
$oReconFilter = new DBObjectSearch($sKeyClass);
- foreach ($aExtKeyDesc as $sForeignAttCode => $value) {
- if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode)) {
+ foreach ($aExtKeyDesc as $sForeignAttCode => $value)
+ {
+ if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode))
+ {
$aCodes = MetaModel::GetFiltersList($sKeyClass);
$sMsg = "Parameter $sParamName: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass', expecting a value in {".implode(', ', $aCodes)."}";
$oRes->LogIssue($sMsg, $bIsMandatory);
@@ -357,24 +376,26 @@ protected function MyObjectSetExternalKey($sAttCode, $sParamName, $aExtKeyDesc,
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
- switch ($oExtObjects->Count()) {
- case 0:
- $sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
- $oRes->LogIssue($sMsg, $bIsMandatory);
- break;
- case 1:
- // Do change the external key attribute
- $oForeignObj = $oExtObjects->Fetch();
- $oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
-
- // Report it (no need to report if the object already had this value
- if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) {
- $oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
- }
- break;
- default:
- $sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
- $oRes->LogIssue($sMsg, $bIsMandatory);
+ switch($oExtObjects->Count())
+ {
+ case 0:
+ $sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL(true)."')";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
+ break;
+ case 1:
+ // Do change the external key attribute
+ $oForeignObj = $oExtObjects->Fetch();
+ $oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
+
+ // Report it (no need to report if the object already had this value
+ if (array_key_exists($sAttCode, $oTargetObj->ListChanges()))
+ {
+ $oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'");
+ }
+ break;
+ default:
+ $sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL(true)."')";
+ $oRes->LogIssue($sMsg, $bIsMandatory);
}
}
@@ -395,29 +416,34 @@ protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $
$sLinkClass = $oLinkAtt->GetLinkedClass();
$sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
- $aItemsFound = [];
- $aItemsNotFound = [];
-
- if (is_null($aLinkList)) {
+ $aItemsFound = array();
+ $aItemsNotFound = array();
+
+ if (is_null($aLinkList))
+ {
return $aItemsNotFound;
}
- foreach ($aLinkList as $aItemData) {
- if (!array_key_exists('class', $aItemData)) {
+ foreach ($aLinkList as $aItemData)
+ {
+ if (!array_key_exists('class', $aItemData))
+ {
$oRes->LogWarning("Parameter $sParamName: missing 'class' specification");
continue; // skip
}
$sTargetClass = $aItemData['class'];
- if (!MetaModel::IsValidClass($sTargetClass)) {
+ if (!MetaModel::IsValidClass($sTargetClass))
+ {
$oRes->LogError("Parameter $sParamName: invalid class '$sTargetClass'");
continue; // skip
}
- if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) {
+ if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass))
+ {
$oRes->LogError("Parameter $sParamName: '$sTargetClass' is not a child class of '$sLinkedClass'");
continue; // skip
}
$oReconFilter = new DBObjectSearch($sTargetClass);
- $aCIStringDesc = [];
+ $aCIStringDesc = array();
foreach ($aItemData['search'] as $sAttCode => $value) {
if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
$aCodes = MetaModel::GetFiltersList($sTargetClass);
@@ -429,42 +455,52 @@ protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $
// The attribute is one of our reconciliation key
$oReconFilter->AddCondition($sAttCode, $value, '=');
}
- if (count($aCIStringDesc) == 1) {
+ if (count($aCIStringDesc) == 1)
+ {
// take the last and unique value to describe the object
$sItemDesc = $value;
- } else {
+ }
+ else
+ {
// describe the object by the given keys
$sItemDesc = $sTargetClass.'('.implode('/', $aCIStringDesc).')';
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
- switch ($oExtObjects->Count()) {
- case 0:
- $oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
- $aItemsNotFound[] = $sItemDesc;
- break;
- case 1:
- $aItemsFound[] = [
- 'object' => $oExtObjects->Fetch(),
- 'link_values' => @$aItemData['link_values'],
- 'desc' => $sItemDesc,
- ];
- break;
- default:
- $oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
- $aItemsNotFound[] = $sItemDesc;
+ switch($oExtObjects->Count())
+ {
+ case 0:
+ $oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL(true)."')");
+ $aItemsNotFound[] = $sItemDesc;
+ break;
+ case 1:
+ $aItemsFound[] = array (
+ 'object' => $oExtObjects->Fetch(),
+ 'link_values' => @$aItemData['link_values'],
+ 'desc' => $sItemDesc,
+ );
+ break;
+ default:
+ $oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL(true)."')");
+ $aItemsNotFound[] = $sItemDesc;
}
}
- if (count($aItemsFound) > 0) {
- $aLinks = [];
- foreach ($aItemsFound as $aItemData) {
+ if (count($aItemsFound) > 0)
+ {
+ $aLinks = array();
+ foreach($aItemsFound as $aItemData)
+ {
$oLink = MetaModel::NewObject($sLinkClass);
$oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
- foreach ($aItemData['link_values'] as $sKey => $value) {
- if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) {
+ foreach($aItemData['link_values'] as $sKey => $value)
+ {
+ if(!MetaModel::IsValidAttCode($sLinkClass, $sKey))
+ {
$oRes->LogWarning("Parameter $sParamName: Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'");
- } else {
+ }
+ else
+ {
$oLink->Set($sKey, $value);
}
}
@@ -493,71 +529,77 @@ protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $
*/
protected function MyObjectInsert($oTargetObj, $sResultLabel, &$oRes)
{
- if ($oRes->IsOk()) {
+ if ($oRes->IsOk())
+ {
list($bRes, $aIssues) = $oTargetObj->CheckToWrite();
- if ($bRes) {
+ if ($bRes)
+ {
$iId = $oTargetObj->DBInsertNoReload();
$oRes->LogInfo("Created object ".get_class($oTargetObj)."::$iId");
$oRes->AddResultObject($sResultLabel, $oTargetObj);
- } else {
+ }
+ else
+ {
$oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)");
- foreach ($aIssues as $iIssue => $sIssue) {
+ foreach($aIssues as $iIssue => $sIssue)
+ {
$oRes->LogError("Issue #$iIssue: $sIssue");
}
}
}
}
- protected static function SoapStructToExternalKeySearch($oExternalKeySearch)
+
+ static protected function SoapStructToExternalKeySearch($oExternalKeySearch)
{
- if (is_null($oExternalKeySearch)) {
- return null;
- }
- if ($oExternalKeySearch->IsVoid()) {
- return null;
- }
+ if (is_null($oExternalKeySearch)) return null;
+ if ($oExternalKeySearch->IsVoid()) return null;
- $aRes = [];
- foreach ($oExternalKeySearch->conditions as $oSearchCondition) {
+ $aRes = array();
+ foreach($oExternalKeySearch->conditions as $oSearchCondition)
+ {
$aRes[$oSearchCondition->attcode] = $oSearchCondition->value;
}
return $aRes;
}
- protected static function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
+ static protected function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
{
- $aRes =
- [
+ $aRes = array
+ (
'class' => $oLinkCreationSpec->class,
- 'search' => [],
- 'link_values' => [],
- ];
+ 'search' => array(),
+ 'link_values' => array(),
+ );
- foreach ($oLinkCreationSpec->conditions as $oSearchCondition) {
+ foreach($oLinkCreationSpec->conditions as $oSearchCondition)
+ {
$aRes['search'][$oSearchCondition->attcode] = $oSearchCondition->value;
}
- foreach ($oLinkCreationSpec->attributes as $oAttributeValue) {
+ foreach($oLinkCreationSpec->attributes as $oAttributeValue)
+ {
$aRes['link_values'][$oAttributeValue->attcode] = $oAttributeValue->value;
}
return $aRes;
}
- protected static function SoapStructToAssociativeArray($aArrayOfAssocArray)
+ static protected function SoapStructToAssociativeArray($aArrayOfAssocArray)
{
- if (is_null($aArrayOfAssocArray)) {
- return [];
- }
-
- $aRes = [];
- foreach ($aArrayOfAssocArray as $aAssocArray) {
- $aRow = [];
- foreach ($aAssocArray as $oKeyValuePair) {
+ if (is_null($aArrayOfAssocArray)) return array();
+
+ $aRes = array();
+ foreach($aArrayOfAssocArray as $aAssocArray)
+ {
+ $aRow = array();
+ foreach ($aAssocArray as $oKeyValuePair)
+ {
$aRow[$oKeyValuePair->key] = $oKeyValuePair->value;
}
- $aRes[] = $aRow;
+ $aRes[] = $aRow;
}
return $aRes;
}
}
+?>