diff --git a/core/config.class.inc.php b/core/config.class.inc.php
index 3622c41930..0025c9addd 100644
--- a/core/config.class.inc.php
+++ b/core/config.class.inc.php
@@ -2856,20 +2856,8 @@ public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
}
}
}
- if (isset($aModuleInfo['installer']))
- {
- $sModuleInstallerClass = $aModuleInfo['installer'];
- if (!class_exists($sModuleInstallerClass))
- {
- throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
- }
- if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
- {
- throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
- }
- $aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
- call_user_func_array($aCallSpec, array($this));
- }
+
+ RunTimeEnvironment::CallInstallerHandler($aModuleInfo, "BeforeWritingConfig", [$this]);
}
}
$this->SetAddOns($aAddOns);
diff --git a/lib/autoload.php b/lib/autoload.php
index 9ee03077e4..db10dc8675 100644
--- a/lib/autoload.php
+++ b/lib/autoload.php
@@ -14,7 +14,10 @@
echo $err;
}
}
- throw new RuntimeException($err);
+ trigger_error(
+ $err,
+ E_USER_ERROR
+ );
}
require_once __DIR__ . '/composer/autoload_real.php';
diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php
index eccb877e48..90782d2bb6 100644
--- a/lib/composer/autoload_classmap.php
+++ b/lib/composer/autoload_classmap.php
@@ -469,6 +469,44 @@
'Combodo\\iTop\\Form\\Validator\\NotEmptyExtKeyValidator' => $baseDir . '/sources/Form/Validator/NotEmptyExtKeyValidator.php',
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => $baseDir . '/sources/Form/Validator/SelectObjectValidator.php',
'Combodo\\iTop\\Kernel' => $baseDir . '/sources/Kernel.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\AbstractExprEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/AbstractExprEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ArrayDimFetchEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/ArrayDimFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ArrayEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/ArrayEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BinaryOpEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BinaryOpEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseAndEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BitwiseAndEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseNotEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BitwiseNotEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseOrEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BitwiseOrEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseXorEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BitwiseXorEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BooleanAndEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BooleanAndEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BooleanNotEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BooleanNotEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BooleanOrEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/BooleanOrEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\CastEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/CastEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ClassConstFetchEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/ClassConstFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\CoalesceEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/CoalesceEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ConcatEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/ConcatEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ConstFetchEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/ConstFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\EqualEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/EqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\FuncCallEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/FuncCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\GreaterEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/GreaterEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\GreaterOrEqualEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/GreaterOrEqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\IssetEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/IssetEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\MethodCallEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/MethodCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ModEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/ModEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\MulEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/MulEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\NotEqualEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/NotEqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\NullsafeMethodCallEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/NullsafeMethodCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\NullsafePropertyFetchEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/NullsafePropertyFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\PropertyFetchEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PropertyFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\SmallerEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/SmallerEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\SmallerOrEqualEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/SmallerOrEqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\StaticCallEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/StaticCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\StaticPropertyFetchEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/StaticPropertyFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\TernaryEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/TernaryEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\UnaryMinusEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/UnaryMinusEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\UnaryPlusEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/UnaryPlusEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\VariableEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/VariableEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\iExprEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/iExprEvaluator.php',
'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => $baseDir . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFormRenderer' => $baseDir . '/sources/Renderer/Bootstrap/BsFormRenderer.php',
diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php
index 18f7002d40..d57e598609 100644
--- a/lib/composer/autoload_psr4.php
+++ b/lib/composer/autoload_psr4.php
@@ -56,7 +56,7 @@
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'Pelago\\Emogrifier\\' => array($vendorDir . '/pelago/emogrifier/src'),
- 'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-google/src', $vendorDir . '/league/oauth2-client/src'),
+ 'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-client/src', $vendorDir . '/league/oauth2-google/src'),
'Laminas\\Validator\\' => array($vendorDir . '/laminas/laminas-validator/src'),
'Laminas\\Stdlib\\' => array($vendorDir . '/laminas/laminas-stdlib/src'),
'Laminas\\ServiceManager\\' => array($vendorDir . '/laminas/laminas-servicemanager/src'),
diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php
index 2d9aa046ed..ede77adf1d 100644
--- a/lib/composer/autoload_static.php
+++ b/lib/composer/autoload_static.php
@@ -317,8 +317,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
),
'League\\OAuth2\\Client\\' =>
array (
- 0 => __DIR__ . '/..' . '/league/oauth2-google/src',
- 1 => __DIR__ . '/..' . '/league/oauth2-client/src',
+ 0 => __DIR__ . '/..' . '/league/oauth2-client/src',
+ 1 => __DIR__ . '/..' . '/league/oauth2-google/src',
),
'Laminas\\Validator\\' =>
array (
@@ -847,6 +847,44 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Form\\Validator\\NotEmptyExtKeyValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/NotEmptyExtKeyValidator.php',
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/SelectObjectValidator.php',
'Combodo\\iTop\\Kernel' => __DIR__ . '/../..' . '/sources/Kernel.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\AbstractExprEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/AbstractExprEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ArrayDimFetchEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/ArrayDimFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ArrayEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/ArrayEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BinaryOpEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BinaryOpEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseAndEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BitwiseAndEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseNotEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BitwiseNotEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseOrEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BitwiseOrEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BitwiseXorEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BitwiseXorEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BooleanAndEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BooleanAndEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BooleanNotEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BooleanNotEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\BooleanOrEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/BooleanOrEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\CastEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/CastEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ClassConstFetchEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/ClassConstFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\CoalesceEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/CoalesceEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ConcatEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/ConcatEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ConstFetchEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/ConstFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\EqualEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/EqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\FuncCallEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/FuncCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\GreaterEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/GreaterEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\GreaterOrEqualEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/GreaterOrEqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\IssetEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/IssetEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\MethodCallEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/MethodCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\ModEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/ModEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\MulEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/MulEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\NotEqualEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/NotEqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\NullsafeMethodCallEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/NullsafeMethodCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\NullsafePropertyFetchEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/NullsafePropertyFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\PropertyFetchEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PropertyFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\SmallerEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/SmallerEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\SmallerOrEqualEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/SmallerOrEqualEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\StaticCallEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/StaticCallEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\StaticPropertyFetchEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/StaticPropertyFetchEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\TernaryEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/TernaryEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\UnaryMinusEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/UnaryMinusEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\UnaryPlusEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/UnaryPlusEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\VariableEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/VariableEvaluator.php',
+ 'Combodo\\iTop\\PhpParser\\Evaluation\\iExprEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/iExprEvaluator.php',
'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFormRenderer' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFormRenderer.php',
diff --git a/lib/composer/platform_check.php b/lib/composer/platform_check.php
index 72145773d0..dee74e1736 100644
--- a/lib/composer/platform_check.php
+++ b/lib/composer/platform_check.php
@@ -36,7 +36,8 @@
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
- throw new \RuntimeException(
- 'Composer detected issues in your platform: ' . implode(' ', $issues)
+ trigger_error(
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
+ E_USER_ERROR
);
}
diff --git a/lib/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php b/lib/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php
index 9526787142..c99d0f82d3 100644
--- a/lib/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php
+++ b/lib/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php
@@ -27,211 +27,211 @@
* affected by the LC_NUMERIC locale.
*/
class ConstExprEvaluator {
- /** @var callable|null */
- private $fallbackEvaluator;
-
- /**
- * Create a constant expression evaluator.
- *
- * The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
- * class doc comment for more information.
- *
- * @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
- */
- public function __construct(?callable $fallbackEvaluator = null) {
- $this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) {
- throw new ConstExprEvaluationException(
- "Expression of type {$expr->getType()} cannot be evaluated"
- );
- };
- }
-
- /**
- * Silently evaluates a constant expression into a PHP value.
- *
- * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
- * The original source of the exception is available through getPrevious().
- *
- * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
- * constructor will be invoked. By default, if no fallback is provided, an exception of type
- * ConstExprEvaluationException is thrown.
- *
- * See class doc comment for caveats and limitations.
- *
- * @param Expr $expr Constant expression to evaluate
- * @return mixed Result of evaluation
- *
- * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
- */
- public function evaluateSilently(Expr $expr) {
- set_error_handler(function ($num, $str, $file, $line) {
- throw new \ErrorException($str, 0, $num, $file, $line);
- });
-
- try {
- return $this->evaluate($expr);
- } catch (\Throwable $e) {
- if (!$e instanceof ConstExprEvaluationException) {
- $e = new ConstExprEvaluationException(
- "An error occurred during constant expression evaluation", 0, $e);
- }
- throw $e;
- } finally {
- restore_error_handler();
- }
- }
-
- /**
- * Directly evaluates a constant expression into a PHP value.
- *
- * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
- * into a ConstExprEvaluationException.
- *
- * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
- * constructor will be invoked. By default, if no fallback is provided, an exception of type
- * ConstExprEvaluationException is thrown.
- *
- * See class doc comment for caveats and limitations.
- *
- * @param Expr $expr Constant expression to evaluate
- * @return mixed Result of evaluation
- *
- * @throws ConstExprEvaluationException if the expression cannot be evaluated
- */
- public function evaluateDirectly(Expr $expr) {
- return $this->evaluate($expr);
- }
-
- /** @return mixed */
- private function evaluate(Expr $expr) {
- if ($expr instanceof Scalar\Int_
- || $expr instanceof Scalar\Float_
- || $expr instanceof Scalar\String_
- ) {
- return $expr->value;
- }
-
- if ($expr instanceof Expr\Array_) {
- return $this->evaluateArray($expr);
- }
-
- // Unary operators
- if ($expr instanceof Expr\UnaryPlus) {
- return +$this->evaluate($expr->expr);
- }
- if ($expr instanceof Expr\UnaryMinus) {
- return -$this->evaluate($expr->expr);
- }
- if ($expr instanceof Expr\BooleanNot) {
- return !$this->evaluate($expr->expr);
- }
- if ($expr instanceof Expr\BitwiseNot) {
- return ~$this->evaluate($expr->expr);
- }
-
- if ($expr instanceof Expr\BinaryOp) {
- return $this->evaluateBinaryOp($expr);
- }
-
- if ($expr instanceof Expr\Ternary) {
- return $this->evaluateTernary($expr);
- }
-
- if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
- return $this->evaluate($expr->var)[$this->evaluate($expr->dim)];
- }
-
- if ($expr instanceof Expr\ConstFetch) {
- return $this->evaluateConstFetch($expr);
- }
-
- return ($this->fallbackEvaluator)($expr);
- }
-
- private function evaluateArray(Expr\Array_ $expr): array {
- $array = [];
- foreach ($expr->items as $item) {
- if (null !== $item->key) {
- $array[$this->evaluate($item->key)] = $this->evaluate($item->value);
- } elseif ($item->unpack) {
- $array = array_merge($array, $this->evaluate($item->value));
- } else {
- $array[] = $this->evaluate($item->value);
- }
- }
- return $array;
- }
-
- /** @return mixed */
- private function evaluateTernary(Expr\Ternary $expr) {
- if (null === $expr->if) {
- return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
- }
-
- return $this->evaluate($expr->cond)
- ? $this->evaluate($expr->if)
- : $this->evaluate($expr->else);
- }
-
- /** @return mixed */
- private function evaluateBinaryOp(Expr\BinaryOp $expr) {
- if ($expr instanceof Expr\BinaryOp\Coalesce
- && $expr->left instanceof Expr\ArrayDimFetch
- ) {
- // This needs to be special cased to respect BP_VAR_IS fetch semantics
- return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
- ?? $this->evaluate($expr->right);
- }
-
- // The evaluate() calls are repeated in each branch, because some of the operators are
- // short-circuiting and evaluating the RHS in advance may be illegal in that case
- $l = $expr->left;
- $r = $expr->right;
- switch ($expr->getOperatorSigil()) {
- case '&': return $this->evaluate($l) & $this->evaluate($r);
- case '|': return $this->evaluate($l) | $this->evaluate($r);
- case '^': return $this->evaluate($l) ^ $this->evaluate($r);
- case '&&': return $this->evaluate($l) && $this->evaluate($r);
- case '||': return $this->evaluate($l) || $this->evaluate($r);
- case '??': return $this->evaluate($l) ?? $this->evaluate($r);
- case '.': return $this->evaluate($l) . $this->evaluate($r);
- case '/': return $this->evaluate($l) / $this->evaluate($r);
- case '==': return $this->evaluate($l) == $this->evaluate($r);
- case '>': return $this->evaluate($l) > $this->evaluate($r);
- case '>=': return $this->evaluate($l) >= $this->evaluate($r);
- case '===': return $this->evaluate($l) === $this->evaluate($r);
- case 'and': return $this->evaluate($l) and $this->evaluate($r);
- case 'or': return $this->evaluate($l) or $this->evaluate($r);
- case 'xor': return $this->evaluate($l) xor $this->evaluate($r);
- case '-': return $this->evaluate($l) - $this->evaluate($r);
- case '%': return $this->evaluate($l) % $this->evaluate($r);
- case '*': return $this->evaluate($l) * $this->evaluate($r);
- case '!=': return $this->evaluate($l) != $this->evaluate($r);
- case '!==': return $this->evaluate($l) !== $this->evaluate($r);
- case '+': return $this->evaluate($l) + $this->evaluate($r);
- case '**': return $this->evaluate($l) ** $this->evaluate($r);
- case '<<': return $this->evaluate($l) << $this->evaluate($r);
- case '>>': return $this->evaluate($l) >> $this->evaluate($r);
- case '<': return $this->evaluate($l) < $this->evaluate($r);
- case '<=': return $this->evaluate($l) <= $this->evaluate($r);
- case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
- case '|>':
- $lval = $this->evaluate($l);
- return $this->evaluate($r)($lval);
- }
-
- throw new \Exception('Should not happen');
- }
-
- /** @return mixed */
- private function evaluateConstFetch(Expr\ConstFetch $expr) {
- $name = $expr->name->toLowerString();
- switch ($name) {
- case 'null': return null;
- case 'false': return false;
- case 'true': return true;
- }
-
- return ($this->fallbackEvaluator)($expr);
- }
+ /** @var callable|null */
+ private $fallbackEvaluator;
+
+ /**
+ * Create a constant expression evaluator.
+ *
+ * The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
+ * class doc comment for more information.
+ *
+ * @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
+ */
+ public function __construct(?callable $fallbackEvaluator = null) {
+ $this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) {
+ throw new ConstExprEvaluationException(
+ "Expression of type {$expr->getType()} cannot be evaluated"
+ );
+ };
+ }
+
+ /**
+ * Silently evaluates a constant expression into a PHP value.
+ *
+ * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
+ * The original source of the exception is available through getPrevious().
+ *
+ * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
+ * constructor will be invoked. By default, if no fallback is provided, an exception of type
+ * ConstExprEvaluationException is thrown.
+ *
+ * See class doc comment for caveats and limitations.
+ *
+ * @param Expr $expr Constant expression to evaluate
+ * @return mixed Result of evaluation
+ *
+ * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
+ */
+ public function evaluateSilently(Expr $expr) {
+ set_error_handler(function ($num, $str, $file, $line) {
+ throw new \ErrorException($str, 0, $num, $file, $line);
+ });
+
+ try {
+ return $this->evaluate($expr);
+ } catch (\Throwable $e) {
+ if (!$e instanceof ConstExprEvaluationException) {
+ $e = new ConstExprEvaluationException(
+ "An error occurred during constant expression evaluation", 0, $e);
+ }
+ throw $e;
+ } finally {
+ restore_error_handler();
+ }
+ }
+
+ /**
+ * Directly evaluates a constant expression into a PHP value.
+ *
+ * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
+ * into a ConstExprEvaluationException.
+ *
+ * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
+ * constructor will be invoked. By default, if no fallback is provided, an exception of type
+ * ConstExprEvaluationException is thrown.
+ *
+ * See class doc comment for caveats and limitations.
+ *
+ * @param Expr $expr Constant expression to evaluate
+ * @return mixed Result of evaluation
+ *
+ * @throws ConstExprEvaluationException if the expression cannot be evaluated
+ */
+ public function evaluateDirectly(Expr $expr) {
+ return $this->evaluate($expr);
+ }
+
+ /** @return mixed */
+ private function evaluate(Expr $expr) {
+ if ($expr instanceof Scalar\Int_
+ || $expr instanceof Scalar\Float_
+ || $expr instanceof Scalar\String_
+ ) {
+ return $expr->value;
+ }
+
+ if ($expr instanceof Expr\Array_) {
+ return $this->evaluateArray($expr);
+ }
+
+ // Unary operators
+ if ($expr instanceof Expr\UnaryPlus) {
+ return +$this->evaluate($expr->expr);
+ }
+ if ($expr instanceof Expr\UnaryMinus) {
+ return -$this->evaluate($expr->expr);
+ }
+ if ($expr instanceof Expr\BooleanNot) {
+ return !$this->evaluate($expr->expr);
+ }
+ if ($expr instanceof Expr\BitwiseNot) {
+ return ~$this->evaluate($expr->expr);
+ }
+
+ if ($expr instanceof Expr\BinaryOp) {
+ return $this->evaluateBinaryOp($expr);
+ }
+
+ if ($expr instanceof Expr\Ternary) {
+ return $this->evaluateTernary($expr);
+ }
+
+ if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
+ return $this->evaluate($expr->var)[$this->evaluate($expr->dim)];
+ }
+
+ if ($expr instanceof Expr\ConstFetch) {
+ return $this->evaluateConstFetch($expr);
+ }
+
+ return ($this->fallbackEvaluator)($expr);
+ }
+
+ private function evaluateArray(Expr\Array_ $expr): array {
+ $array = [];
+ foreach ($expr->items as $item) {
+ if (null !== $item->key) {
+ $array[$this->evaluate($item->key)] = $this->evaluate($item->value);
+ } elseif ($item->unpack) {
+ $array = array_merge($array, $this->evaluate($item->value));
+ } else {
+ $array[] = $this->evaluate($item->value);
+ }
+ }
+ return $array;
+ }
+
+ /** @return mixed */
+ private function evaluateTernary(Expr\Ternary $expr) {
+ if (null === $expr->if) {
+ return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
+ }
+
+ return $this->evaluate($expr->cond)
+ ? $this->evaluate($expr->if)
+ : $this->evaluate($expr->else);
+ }
+
+ /** @return mixed */
+ private function evaluateBinaryOp(Expr\BinaryOp $expr) {
+ if ($expr instanceof Expr\BinaryOp\Coalesce
+ && $expr->left instanceof Expr\ArrayDimFetch
+ ) {
+ // This needs to be special cased to respect BP_VAR_IS fetch semantics
+ return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
+ ?? $this->evaluate($expr->right);
+ }
+
+ // The evaluate() calls are repeated in each branch, because some of the operators are
+ // short-circuiting and evaluating the RHS in advance may be illegal in that case
+ $l = $expr->left;
+ $r = $expr->right;
+ switch ($expr->getOperatorSigil()) {
+ case '&': return $this->evaluate($l) & $this->evaluate($r);
+ case '|': return $this->evaluate($l) | $this->evaluate($r);
+ case '^': return $this->evaluate($l) ^ $this->evaluate($r);
+ case '&&': return $this->evaluate($l) && $this->evaluate($r);
+ case '||': return $this->evaluate($l) || $this->evaluate($r);
+ case '??': return $this->evaluate($l) ?? $this->evaluate($r);
+ case '.': return $this->evaluate($l) . $this->evaluate($r);
+ case '/': return $this->evaluate($l) / $this->evaluate($r);
+ case '==': return $this->evaluate($l) == $this->evaluate($r);
+ case '>': return $this->evaluate($l) > $this->evaluate($r);
+ case '>=': return $this->evaluate($l) >= $this->evaluate($r);
+ case '===': return $this->evaluate($l) === $this->evaluate($r);
+ case 'and': return $this->evaluate($l) and $this->evaluate($r);
+ case 'or': return $this->evaluate($l) or $this->evaluate($r);
+ case 'xor': return $this->evaluate($l) xor $this->evaluate($r);
+ case '-': return $this->evaluate($l) - $this->evaluate($r);
+ case '%': return $this->evaluate($l) % $this->evaluate($r);
+ case '*': return $this->evaluate($l) * $this->evaluate($r);
+ case '!=': return $this->evaluate($l) != $this->evaluate($r);
+ case '!==': return $this->evaluate($l) !== $this->evaluate($r);
+ case '+': return $this->evaluate($l) + $this->evaluate($r);
+ case '**': return $this->evaluate($l) ** $this->evaluate($r);
+ case '<<': return $this->evaluate($l) << $this->evaluate($r);
+ case '>>': return $this->evaluate($l) >> $this->evaluate($r);
+ case '<': return $this->evaluate($l) < $this->evaluate($r);
+ case '<=': return $this->evaluate($l) <= $this->evaluate($r);
+ case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
+ case '|>':
+ $lval = $this->evaluate($l);
+ return $this->evaluate($r)($lval);
+ }
+
+ throw new \Exception('Should not happen');
+ }
+
+ /** @return mixed */
+ private function evaluateConstFetch(Expr\ConstFetch $expr) {
+ $name = $expr->name->toLowerString();
+ switch ($name) {
+ case 'null': return null;
+ case 'false': return false;
+ case 'true': return true;
+ }
+
+ return ($this->fallbackEvaluator)($expr);
+ }
}
diff --git a/setup/extensionsmap.class.inc.php b/setup/extensionsmap.class.inc.php
index 440608f12e..851a63bdd6 100644
--- a/setup/extensionsmap.class.inc.php
+++ b/setup/extensionsmap.class.inc.php
@@ -11,52 +11,52 @@ class iTopExtension
const SOURCE_WIZARD = 'datamodels';
const SOURCE_MANUAL = 'extensions';
const SOURCE_REMOTE = 'data';
-
+
/**
* @var string
*/
public $sCode;
-
+
/**
* @var string
*/
public $sVersion;
-
- /**
+
+ /**
* @var string
*/
public $sInstalledVersion;
-
-/**
+
+ /**
* @var string
*/
public $sLabel;
-
+
/**
* @var string
*/
public $sDescription;
-
+
/**
* @var string
*/
public $sSource;
-
+
/**
* @var bool
*/
public $bMandatory;
-
+
/**
* @var string
*/
public $sMoreInfoUrl;
-
+
/**
* @var bool
*/
public $bMarkedAsChosen;
-
+
/**
* @var bool
*/
@@ -87,7 +87,7 @@ class iTopExtension
* @var string[]
*/
public $aMissingDependencies;
-
+
public function __construct()
{
$this->sCode = '';
@@ -120,13 +120,13 @@ class iTopExtensionsMap
* @return void
*/
protected $aExtensions;
-
+
/**
* The list of directories browsed using the ReadDir method when building the map
* @var string[]
*/
protected $aScannedDirs;
-
+
public function __construct($sFromEnvironment = 'production', $bNormalizeOldExtensions = true, $aExtraDirs = array())
{
$this->aExtensions = array();
@@ -134,7 +134,7 @@ public function __construct($sFromEnvironment = 'production', $bNormalizeOldExte
$this->ScanDisk($sFromEnvironment);
foreach($aExtraDirs as $sDir)
{
- $this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE);
+ $this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE);
}
$this->CheckDependencies($sFromEnvironment);
if ($bNormalizeOldExtensions)
@@ -142,7 +142,7 @@ public function __construct($sFromEnvironment = 'production', $bNormalizeOldExte
$this->NormalizeOldExtensions();
}
}
-
+
/**
* Populate the list of available (pseudo)extensions by scanning the disk
* where the iTop files are located
@@ -158,7 +158,7 @@ protected function ScanDisk($sEnvironment)
$this->ReadDir(APPROOT.'/extensions', iTopExtension::SOURCE_MANUAL);
$this->ReadDir(APPROOT.'/data/'.$sEnvironment.'-modules', iTopExtension::SOURCE_REMOTE);
}
-
+
/**
* Read the information contained in the "installation.xml" file in the given directory
* and create pseudo extensions from the list of choices described in this file
@@ -168,7 +168,7 @@ protected function ScanDisk($sEnvironment)
protected function ReadInstallationWizard($sDir)
{
if (!is_readable($sDir.'/installation.xml')) return false;
-
+
$oXml = new XMLParameters($sDir.'/installation.xml');
foreach($oXml->Get('steps') as $aStepInfo)
{
@@ -183,7 +183,7 @@ protected function ReadInstallationWizard($sDir)
}
return true;
}
-
+
/**
* Helper to process a "choice" array read from the installation.xml file
* @param array $aChoices
@@ -218,7 +218,7 @@ protected function ProcessWizardChoices($aChoices)
}
}
}
-
+
/**
* Add an extension to the list of existing extensions, taking care of removing duplicates
* (only the latest/greatest version is kept)
@@ -248,7 +248,7 @@ protected function AddExtension(iTopExtension $oNewExtension)
// Finally it's not a duplicate, let's add it to the list
$this->aExtensions[$oNewExtension->sCode.'/'.$oNewExtension->sVersion] = $oNewExtension;
}
-
+
/**
* Read (recursively) a directory to find if it contains extensions (or modules)
*
@@ -264,11 +264,11 @@ protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
$hDir = opendir($sSearchDir);
if ($hDir !== false)
{
- if ($sParentExtensionId == null)
- {
- // We're not recursing, let's add the directory to the list of scanned dirs
- $this->aScannedDirs[] = $sSearchDir;
- }
+ if ($sParentExtensionId == null)
+ {
+ // We're not recursing, let's add the directory to the list of scanned dirs
+ $this->aScannedDirs[] = $sSearchDir;
+ }
$sExtensionId = null;
$aSubDirectories = array();
@@ -285,7 +285,7 @@ protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
$oExtension->sMoreInfoUrl = $oXml->Get('more_info_url');
$oExtension->sSource = $sSource;
$oExtension->sSourceDir = $sSearchDir;
-
+
$sParentExtensionId = $sExtensionId = $oExtension->sCode.'/'.$oExtension->sVersion;
$this->AddExtension($oExtension);
}
@@ -303,7 +303,11 @@ protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
{
// Found a module
- $aModuleInfo = $this->GetModuleInfo($sSearchDir.'/'.$sFile);
+ try {
+ $aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sSearchDir.'/'.$sFile);
+ } catch(ModuleFileReaderException $e){
+ continue;
+ }
// If we are not already inside a formal extension, then the module itself is considered
// as an extension, otherwise, the module is just added to the list of modules belonging
// to this extension
@@ -314,7 +318,7 @@ protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
// Provide a default module version since version is mandatory when recording ExtensionInstallation
$sModuleVersion = '0.0.1';
}
-
+
if (($sParentExtensionId !== null) && (array_key_exists($sParentExtensionId, $this->aExtensions)) && ($this->aExtensions[$sParentExtensionId] instanceof iTopExtension)) {
// Already inside an extension, let's add this module the list of modules belonging to this extension
$this->aExtensions[$sParentExtensionId]->aModules[] = $sModuleName;
@@ -324,15 +328,15 @@ protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
else
{
// Not already inside an folder containing an 'extension.xml' file
-
+
// Ignore non-visible modules and auto-select ones, since these are never prompted
// as a choice to the end-user
$bVisible = true;
if (!$aModuleInfo[2]['visible'] || isset($aModuleInfo[2]['auto_select']))
{
- $bVisible = false;
+ $bVisible = false;
}
-
+
// Let's create a "fake" extension from this module (containing just this module) for backwards compatibility
$oExtension = new iTopExtension();
$oExtension->sCode = $sModuleName;
@@ -366,7 +370,7 @@ protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
}
return false;
}
-
+
/**
* Check if some extension contains a module with missing dependencies...
* If so, populate the aMissingDepenencies array
@@ -376,7 +380,7 @@ protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null)
protected function CheckDependencies($sFromEnvironment)
{
$aSearchDirs = array();
-
+
if (is_dir(APPROOT.'/datamodels/2.x'))
{
$aSearchDirs[] = APPROOT.'/datamodels/2.x';
@@ -386,7 +390,7 @@ protected function CheckDependencies($sFromEnvironment)
$aSearchDirs[] = APPROOT.'/datamodels/1.x';
}
$aSearchDirs = array_merge($aSearchDirs, $this->aScannedDirs);
-
+
try
{
$aAllModules = ModuleDiscovery::GetAvailableModules($aSearchDirs, true);
@@ -404,7 +408,7 @@ protected function CheckDependencies($sFromEnvironment)
// This information is not available for pseudo modules defined in the installation wizard, but let's ignore them
$sVersion = $oExtension->aModuleVersion[$sModuleName];
$sModuleId = $sModuleName.'/'.$sVersion;
-
+
if (array_key_exists($sModuleId, $e->aModulesInfo))
{
// The extension actually contains a module which has unmet dependencies
@@ -416,61 +420,7 @@ protected function CheckDependencies($sFromEnvironment)
}
}
}
-
- /**
- * Read the information from a module file (module.xxx.php)
- * Closely inspired (almost copied/pasted !!) from ModuleDiscovery::ListModuleFiles
- * @param string $sModuleFile
- * @return array
- */
- protected function GetModuleInfo($sModuleFile)
- {
- static $iDummyClassIndex = 0;
-
- $aModuleInfo = array(); // will be filled by the "eval" line below...
- try
- {
- $aMatches = array();
- $sModuleFileContents = file_get_contents($sModuleFile);
- $sModuleFileContents = str_replace(array(''), '', $sModuleFileContents);
- $sModuleFileContents = str_replace('__FILE__', "'".addslashes($sModuleFile)."'", $sModuleFileContents);
- preg_match_all('/class ([A-Za-z0-9_]+) extends ([A-Za-z0-9_]+)/', $sModuleFileContents, $aMatches);
- //print_r($aMatches);
- $idx = 0;
- foreach($aMatches[1] as $sClassName)
- {
- if (class_exists($sClassName))
- {
- // rename any class declaration inside the code to prevent a "duplicate class" declaration
- // and change its parent class as well so that nobody will find it and try to execute it
- // Note: don't use the same naming scheme as ModuleDiscovery otherwise you 'll have the duplicate class error again !!
- $sModuleFileContents = str_replace($sClassName.' extends '.$aMatches[2][$idx], $sClassName.'_Ext_'.($iDummyClassIndex++).' extends DummyHandler', $sModuleFileContents);
- }
- $idx++;
- }
- // Replace the main function call by an assignment to a variable, as an array...
- $sModuleFileContents = str_replace(array('SetupWebPage::AddModule', 'ModuleDiscovery::AddModule'), '$aModuleInfo = array', $sModuleFileContents);
-
- eval($sModuleFileContents); // Assigns $aModuleInfo
-
- if (count($aModuleInfo) === 0)
- {
- SetupLog::Warning("Eval of $sModuleFile did not return the expected information...");
- }
- }
- catch(ParseError $e)
- {
- // Continue...
- SetupLog::Warning("Eval of $sModuleFile caused a parse error: ".$e->getMessage()." at line ".$e->getLine());
- }
- catch(Exception $e)
- {
- // Continue...
- SetupLog::Warning("Eval of $sModuleFile caused an exception: ".$e->getMessage());
- }
- return $aModuleInfo;
- }
-
+
/**
* Get all available extensions
* @return iTopExtension[]
@@ -479,7 +429,7 @@ public function GetAllExtensions()
{
return $this->aExtensions;
}
-
+
/**
* Mark the given extension as chosen
* @param string $sExtensionCode The code of the extension (code without verison number)
@@ -497,7 +447,7 @@ public function MarkAsChosen($sExtensionCode, $bMark = true)
}
}
}
-
+
/**
* Tells if a given extension(code) is marked as chosen
* @param string $sExtensionCode
@@ -532,7 +482,7 @@ protected function SetInstalledVersion($sExtensionCode, $sInstalledVersion)
}
}
}
-
+
/**
* Get the list of the "chosen" extensions
* @return iTopExtension[]
@@ -549,7 +499,7 @@ public function GetChoices()
}
return $aResult;
}
-
+
/**
* Load the choices (i.e. MarkedAsChosen) from the database defined in the supplied Config
* @param Config $oConfig
@@ -572,7 +522,7 @@ public function LoadChoicesFromDatabase(Config $oConfig)
// No database or erroneous information
return false;
}
-
+
foreach($aInstalledExtensions as $aDBInfo)
{
$this->MarkAsChosen($aDBInfo['code']);
@@ -580,7 +530,7 @@ public function LoadChoicesFromDatabase(Config $oConfig)
}
return true;
}
-
+
/**
* Find is a single-module extension is contained within another extension
* @param iTopExtension $oExtension
@@ -590,7 +540,7 @@ public function IsExtensionObsoletedByAnother(iTopExtension $oExtension)
{
// Complex extensions (more than 1 module) are never considered as obsolete
if (count($oExtension->aModules) != 1) return null;
-
+
foreach($this->GetAllExtensions() as $oOtherExtension)
{
if (($oOtherExtension->sSourceDir != $oExtension->sSourceDir) && ($oOtherExtension->sSource != iTopExtension::SOURCE_WIZARD))
@@ -603,12 +553,12 @@ public function IsExtensionObsoletedByAnother(iTopExtension $oExtension)
}
}
}
-
+
// No match at all
return null;
-
+
}
-
+
/**
* Search for multi-module extensions that are NOT deployed as an extension (i.e. shipped with an extension.xml file)
* but as a bunch of un-related modules based on the signature of some well-known extensions. If such an extension is found,
@@ -617,53 +567,53 @@ public function IsExtensionObsoletedByAnother(iTopExtension $oExtension)
*/
public function NormalizeOldExtensions($sInSourceOnly = iTopExtension::SOURCE_MANUAL)
{
- $aSignatures = $this->GetOldExtensionsSignatures();
- foreach($aSignatures as $sExtensionCode => $aExtensionSignatures)
- {
- $bFound = false;
- foreach($aExtensionSignatures['versions'] as $sVersion => $aModules)
- {
- $bInstalled = true;
- foreach($aModules as $sModuleId)
- {
- if(!$this->ModuleIsPresent($sModuleId, $sInSourceOnly))
- {
- $bFound = false;
- break; // One missing module is enough to determine that the extension/version is not present
- }
- else
- {
- $bInstalled = $bInstalled && (!$this->ModuleIsInstalled($sModuleId, $sInSourceOnly));
- $bFound = true;
- }
- }
- if ($bFound) break; // The current version matches the signature
- }
-
- if ($bFound)
- {
- $oExtension = new iTopExtension();
- $oExtension->sCode = $sExtensionCode;
- $oExtension->sLabel = $aExtensionSignatures['label'];
- $oExtension->sSource = $sInSourceOnly;
- $oExtension->sDescription = $aExtensionSignatures['description'];
- $oExtension->sVersion = $sVersion;
- $oExtension->aModules = array();
- if ($bInstalled)
- {
- $oExtension->sInstalledVersion = $sVersion;
- $oExtension->bMarkedAsChosen = true;
- }
- foreach($aModules as $sModuleId)
- {
- list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
- $oExtension->aModules[] = $sModuleName;
- }
- $this->ReplaceModulesByNormalizedExtension($aExtensionSignatures['versions'][$sVersion], $oExtension);
- }
- }
+ $aSignatures = $this->GetOldExtensionsSignatures();
+ foreach($aSignatures as $sExtensionCode => $aExtensionSignatures)
+ {
+ $bFound = false;
+ foreach($aExtensionSignatures['versions'] as $sVersion => $aModules)
+ {
+ $bInstalled = true;
+ foreach($aModules as $sModuleId)
+ {
+ if(!$this->ModuleIsPresent($sModuleId, $sInSourceOnly))
+ {
+ $bFound = false;
+ break; // One missing module is enough to determine that the extension/version is not present
+ }
+ else
+ {
+ $bInstalled = $bInstalled && (!$this->ModuleIsInstalled($sModuleId, $sInSourceOnly));
+ $bFound = true;
+ }
+ }
+ if ($bFound) break; // The current version matches the signature
+ }
+
+ if ($bFound)
+ {
+ $oExtension = new iTopExtension();
+ $oExtension->sCode = $sExtensionCode;
+ $oExtension->sLabel = $aExtensionSignatures['label'];
+ $oExtension->sSource = $sInSourceOnly;
+ $oExtension->sDescription = $aExtensionSignatures['description'];
+ $oExtension->sVersion = $sVersion;
+ $oExtension->aModules = array();
+ if ($bInstalled)
+ {
+ $oExtension->sInstalledVersion = $sVersion;
+ $oExtension->bMarkedAsChosen = true;
+ }
+ foreach($aModules as $sModuleId)
+ {
+ list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
+ $oExtension->aModules[] = $sModuleName;
+ }
+ $this->ReplaceModulesByNormalizedExtension($aExtensionSignatures['versions'][$sVersion], $oExtension);
+ }
+ }
}
-
+
/**
* Check if the given module-code/version is present on the disk
* @param string $sModuleIdToFind The module ID (code/version) to search for
@@ -672,9 +622,9 @@ public function NormalizeOldExtensions($sInSourceOnly = iTopExtension::SOURCE_MA
*/
protected function ModuleIsPresent($sModuleIdToFind, $sInSourceOnly)
{
- return (array_key_exists($sModuleIdToFind, $this->aExtensions) && ($this->aExtensions[$sModuleIdToFind]->sSource == $sInSourceOnly));
+ return (array_key_exists($sModuleIdToFind, $this->aExtensions) && ($this->aExtensions[$sModuleIdToFind]->sSource == $sInSourceOnly));
}
-
+
/**
* Check if the given module-code/version is currently installed
* @param string $sModuleIdToFind The module ID (code/version) to search for
@@ -683,11 +633,11 @@ protected function ModuleIsPresent($sModuleIdToFind, $sInSourceOnly)
*/
protected function ModuleIsInstalled($sModuleIdToFind, $sInSourceOnly)
{
- return (array_key_exists($sModuleIdToFind, $this->aExtensions) &&
- ($this->aExtensions[$sModuleIdToFind]->sSource == $sInSourceOnly) &&
- ($this->aExtensions[$sModuleIdToFind]->sInstalledVersion !== '') );
+ return (array_key_exists($sModuleIdToFind, $this->aExtensions) &&
+ ($this->aExtensions[$sModuleIdToFind]->sSource == $sInSourceOnly) &&
+ ($this->aExtensions[$sModuleIdToFind]->sInstalledVersion !== '') );
}
-
+
/**
* Tells if the given module name is "chosen" since it is part of a "chosen" extension (in the specified source dir)
* @param string $sModuleNameToFind
@@ -697,7 +647,7 @@ protected function ModuleIsInstalled($sModuleIdToFind, $sInSourceOnly)
public function ModuleIsChosenAsPartOfAnExtension($sModuleNameToFind, $sInSourceOnly = iTopExtension::SOURCE_REMOTE)
{
$bChosen = false;
-
+
foreach($this->GetAllExtensions() as $oExtension)
{
if (($oExtension->sSource == $sInSourceOnly) &&
@@ -709,7 +659,7 @@ public function ModuleIsChosenAsPartOfAnExtension($sModuleNameToFind, $sInSource
}
return false;
}
-
+
/**
* Replace a given set of stand-alone modules by one single "extension"
* @param string[] $aModules
@@ -717,13 +667,13 @@ public function ModuleIsChosenAsPartOfAnExtension($sModuleNameToFind, $sInSource
*/
protected function ReplaceModulesByNormalizedExtension($aModules, iTopExtension $oNewExtension)
{
- foreach($aModules as $sModuleId)
- {
- unset($this->aExtensions[$sModuleId]);
- }
- $this->AddExtension($oNewExtension);
+ foreach($aModules as $sModuleId)
+ {
+ unset($this->aExtensions[$sModuleId]);
+ }
+ $this->AddExtension($oNewExtension);
}
-
+
/**
* Get the list of signatures of some well-known multi-module extensions without extension.xml file (should not exist anymore)
*
@@ -731,637 +681,637 @@ protected function ReplaceModulesByNormalizedExtension($aModules, iTopExtension
*/
protected function GetOldExtensionsSignatures()
{
- // Generated by the Factory using the page export_component_versions_for_normalisation.php
- return array (
- 'combodo-approval-process-light' =>
- array (
- 'label' => 'Approval process light',
- 'description' => 'Approve a request via a simple email',
- 'versions' =>
- array (
- '1.0.1' =>
- array (
- 0 => 'approval-base/2.1.0',
- 1 => 'combodo-approval-light/1.0.1',
- ),
- '1.0.2' =>
- array (
- 0 => 'approval-base/2.1.1',
- 1 => 'combodo-approval-light/1.0.2',
- ),
- '1.0.3' =>
- array (
- 0 => 'approval-base/2.1.2',
- 1 => 'combodo-approval-light/1.0.2',
- ),
- '1.1.0' =>
- array (
- 0 => 'approval-base/2.2.2',
- 1 => 'combodo-approval-light/1.0.2',
- ),
- '1.1.1' =>
- array (
- 0 => 'approval-base/2.2.3',
- 1 => 'combodo-approval-light/1.0.2',
- ),
- '1.1.2' =>
- array (
- 0 => 'approval-base/2.2.6',
- 1 => 'combodo-approval-light/1.0.2',
- ),
- '1.1.3' =>
- array (
- 0 => 'approval-base/2.2.6',
- 1 => 'combodo-approval-light/1.0.3',
- ),
- '1.2.0' =>
- array (
- 0 => 'approval-base/2.3.0',
- 1 => 'combodo-approval-light/1.0.3',
- ),
- '1.2.1' =>
- array (
- 0 => 'approval-base/2.4.0',
- 1 => 'combodo-approval-light/1.0.4',
- ),
- '1.3.0' =>
- array (
- 0 => 'approval-base/2.4.2',
- 1 => 'combodo-approval-light/1.1.1',
- ),
- '1.3.1' =>
- array (
- 0 => 'approval-base/2.5.0',
- 1 => 'combodo-approval-light/1.1.1',
- ),
- '1.3.2' =>
- array (
- 0 => 'approval-base/2.5.0',
- 1 => 'combodo-approval-light/1.1.2',
- ),
- '1.2.2' =>
- array (
- 0 => 'approval-base/2.4.2',
- 1 => 'combodo-approval-light/1.0.5',
- ),
- '1.3.3' =>
- array (
- 0 => 'approval-base/2.5.1',
- 1 => 'combodo-approval-light/1.1.2',
- ),
- '1.3.4' =>
- array (
- 0 => 'approval-base/2.5.2',
- 1 => 'combodo-approval-light/1.1.2',
- ),
- '1.3.5' =>
- array (
- 0 => 'approval-base/2.5.3',
- 1 => 'combodo-approval-light/1.1.2',
- ),
- '1.4.0' =>
- array (
- 0 => 'approval-base/2.5.3',
- 1 => 'combodo-approval-light/1.1.2',
- 2 => 'itop-approval-portal/1.0.0',
- ),
- ),
- ),
- 'combodo-approval-process-automation' =>
- array (
- 'label' => 'Approval process automation',
- 'description' => 'Control your approval process with predefined rules based on service catalog',
- 'versions' =>
- array (
- '1.0.1' =>
- array (
- 0 => 'approval-base/2.1.0',
- 1 => 'combodo-approval-extended/1.0.2',
- ),
- '1.0.2' =>
- array (
- 0 => 'approval-base/2.1.1',
- 1 => 'combodo-approval-extended/1.0.4',
- ),
- '1.0.3' =>
- array (
- 0 => 'approval-base/2.1.2',
- 1 => 'combodo-approval-extended/1.0.4',
- ),
- '1.1.0' =>
- array (
- 0 => 'approval-base/2.2.2',
- 1 => 'combodo-approval-extended/1.0.4',
- ),
- '1.1.1' =>
- array (
- 0 => 'approval-base/2.2.3',
- 1 => 'combodo-approval-extended/1.0.4',
- ),
- '1.1.2' =>
- array (
- 0 => 'approval-base/2.2.6',
- 1 => 'combodo-approval-extended/1.0.5',
- ),
- '1.1.3' =>
- array (
- 0 => 'approval-base/2.2.6',
- 1 => 'combodo-approval-extended/1.0.6',
- ),
- '1.2.0' =>
- array (
- 0 => 'approval-base/2.3.0',
- 1 => 'combodo-approval-extended/1.0.7',
- ),
- '1.2.1' =>
- array (
- 0 => 'approval-base/2.4.0',
- 1 => 'combodo-approval-extended/1.0.8',
- ),
- '1.3.0' =>
- array (
- 0 => 'approval-base/2.4.2',
- 1 => 'combodo-approval-extended/1.2.1',
- ),
- '1.3.1' =>
- array (
- 0 => 'approval-base/2.5.0',
- 1 => 'combodo-approval-extended/1.2.1',
- ),
- '1.3.2' =>
- array (
- 0 => 'approval-base/2.5.0',
- 1 => 'combodo-approval-extended/1.2.2',
- ),
- '1.2.2' =>
- array (
- 0 => 'approval-base/2.4.2',
- 1 => 'combodo-approval-extended/1.0.9',
- ),
- '1.3.3' =>
- array (
- 0 => 'approval-base/2.5.1',
- 1 => 'combodo-approval-extended/1.2.3',
- ),
- '1.3.4' =>
- array (
- 0 => 'approval-base/2.5.2',
- 1 => 'combodo-approval-extended/1.2.3',
- ),
- '1.3.5' =>
- array (
- 0 => 'approval-base/2.5.3',
- 1 => 'combodo-approval-extended/1.2.3',
- ),
- '1.4.0' =>
- array (
- 0 => 'approval-base/2.5.3',
- 1 => 'combodo-approval-extended/1.2.3',
- 3 => 'itop-approval-portal/1.0.0',
- ),
- ),
- ),
- 'combodo-predefined-response-models' =>
- array (
- 'label' => 'Predefined response models',
- 'description' => 'Pick common answers from a list of predefined replies grouped by categories to update tickets log',
- 'versions' =>
- array (
- '1.0.0' =>
- array (
- 0 => 'precanned-replies/1.0.0',
- 1 => 'precanned-replies-pro/1.0.0',
- ),
- '1.0.1' =>
- array (
- 0 => 'precanned-replies/1.0.1',
- 1 => 'precanned-replies-pro/1.0.1',
- ),
- '1.0.2' =>
- array (
- 0 => 'precanned-replies/1.0.2',
- 1 => 'precanned-replies-pro/1.0.1',
- ),
- '1.0.3' =>
- array (
- 0 => 'precanned-replies/1.0.3',
- 1 => 'precanned-replies-pro/1.0.1',
- ),
- '1.0.4' =>
- array (
- 0 => 'precanned-replies/1.0.3',
- 1 => 'precanned-replies-pro/1.0.2',
- ),
- '1.0.5' =>
- array (
- 0 => 'precanned-replies/1.0.4',
- 1 => 'precanned-replies-pro/1.0.2',
- ),
- '1.1.0' =>
- array (
- 0 => 'precanned-replies/1.1.0',
- 1 => 'precanned-replies-pro/1.0.2',
- ),
- '1.1.1' =>
- array (
- 0 => 'precanned-replies/1.1.1',
- 1 => 'precanned-replies-pro/1.0.2',
- ),
- ),
- ),
- 'combodo-customized-request-forms' =>
- array (
- 'label' => 'Customized request forms',
- 'description' => 'Define personalized request forms based on the service catalog. Add extra fields for a given type of request.',
- 'versions' =>
- array (
- '1.0.1' =>
- array (
- 0 => 'templates-base/2.1.1',
- 1 => 'itop-request-template/1.0.0',
- ),
- '1.0.2' =>
- array (
- 0 => 'templates-base/2.1.2',
- 1 => 'itop-request-template/1.0.0',
- ),
- '1.0.3' =>
- array (
- 0 => 'templates-base/2.1.2',
- 1 => 'itop-request-template/1.0.1',
- ),
- '1.0.4' =>
- array (
- 0 => 'templates-base/2.1.3',
- 1 => 'itop-request-template/1.0.1',
- ),
- '1.0.5' =>
- array (
- 0 => 'templates-base/2.1.4',
- 1 => 'itop-request-template/1.0.1',
- ),
- '2.0.0' =>
- array (
- 0 => 'templates-base/3.0.0',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.1' =>
- array (
- 0 => 'templates-base/3.0.1',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.2' =>
- array (
- 0 => 'templates-base/3.0.2',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.3' =>
- array (
- 0 => 'templates-base/3.0.4',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.4' =>
- array (
- 0 => 'templates-base/3.0.5',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.5' =>
- array (
- 0 => 'templates-base/3.0.6',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.6' =>
- array (
- 0 => 'templates-base/3.0.8',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.7' =>
- array (
- 0 => 'templates-base/3.0.9',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- '2.0.8' =>
- array (
- 0 => 'templates-base/3.0.12',
- 1 => 'itop-request-template/2.0.0',
- 2 => 'itop-request-template-portal/1.0.0',
- ),
- ),
- ),
- 'combodo-sla-considering-business-hours' =>
- array (
- 'label' => 'SLA considering business hours',
- 'description' => 'Compute SLAs taking into account service coverage window and holidays',
- 'versions' =>
- array (
- '2.0.1' =>
- array (
- 0 => 'combodo-sla-computation/2.0.1',
- 1 => 'combodo-coverage-windows-computation/2.0.0',
- ),
- '2.1.0' =>
- array (
- 0 => 'combodo-sla-computation/2.1.0',
- 1 => 'combodo-coverage-windows-computation/2.0.0',
- ),
- '2.1.1' =>
- array (
- 0 => 'combodo-sla-computation/2.1.1',
- 1 => 'combodo-coverage-windows-computation/2.0.0',
- ),
- '2.1.2' =>
- array (
- 0 => 'combodo-sla-computation/2.1.2',
- 1 => 'combodo-coverage-windows-computation/2.0.0',
- ),
- '2.1.3' =>
- array (
- 0 => 'combodo-sla-computation/2.1.2',
- 1 => 'combodo-coverage-windows-computation/2.0.1',
- ),
- '2.0.2' =>
- array (
- 0 => 'combodo-sla-computation/2.0.1',
- 1 => 'combodo-coverage-windows-computation/2.0.1',
- ),
- '2.1.4' =>
- array (
- 0 => 'combodo-sla-computation/2.1.3',
- 1 => 'combodo-coverage-windows-computation/2.0.1',
- ),
- '2.1.5' =>
- array (
- 0 => 'combodo-sla-computation/2.1.5',
- 1 => 'combodo-coverage-windows-computation/2.0.1',
- ),
- '2.1.6' =>
- array (
- 0 => 'combodo-sla-computation/2.1.5',
- 1 => 'combodo-coverage-windows-computation/2.0.2',
- ),
- '2.1.7' =>
- array (
- 0 => 'combodo-sla-computation/2.1.6',
- 1 => 'combodo-coverage-windows-computation/2.0.2',
- ),
- '2.1.8' =>
- array (
- 0 => 'combodo-sla-computation/2.1.7',
- 1 => 'combodo-coverage-windows-computation/2.0.2',
- ),
- '2.1.9' =>
- array (
- 0 => 'combodo-sla-computation/2.1.8',
- 1 => 'combodo-coverage-windows-computation/2.0.2',
- ),
- ),
- ),
- 'combodo-mail-to-ticket-automation' =>
- array (
- 'label' => 'Mail to ticket automation',
- 'description' => 'Scan several mailboxes to create or update tickets.',
- 'versions' =>
- array (
- '2.6.0' =>
- array (
- 0 => 'combodo-email-synchro/2.6.0',
- 1 => 'itop-standard-email-synchro/2.6.0',
- ),
- '2.6.1' =>
- array (
- 0 => 'combodo-email-synchro/2.6.1',
- 1 => 'itop-standard-email-synchro/2.6.0',
- ),
- '2.6.2' =>
- array (
- 0 => 'combodo-email-synchro/2.6.2',
- 1 => 'itop-standard-email-synchro/2.6.0',
- ),
- '2.6.3' =>
- array (
- 0 => 'combodo-email-synchro/2.6.2',
- 1 => 'itop-standard-email-synchro/2.6.1',
- ),
- '2.6.4' =>
- array (
- 0 => 'combodo-email-synchro/2.6.3',
- 1 => 'itop-standard-email-synchro/2.6.2',
- ),
- '2.6.5' =>
- array (
- 0 => 'combodo-email-synchro/2.6.4',
- 1 => 'itop-standard-email-synchro/2.6.2',
- ),
- '2.6.6' =>
- array (
- 0 => 'combodo-email-synchro/2.6.5',
- 1 => 'itop-standard-email-synchro/2.6.3',
- ),
- '2.6.7' =>
- array (
- 0 => 'combodo-email-synchro/2.6.6',
- 1 => 'itop-standard-email-synchro/2.6.4',
- ),
- '2.6.8' =>
- array (
- 0 => 'combodo-email-synchro/2.6.7',
- 1 => 'itop-standard-email-synchro/2.6.4',
- ),
- '2.6.9' =>
- array (
- 0 => 'combodo-email-synchro/2.6.8',
- 1 => 'itop-standard-email-synchro/2.6.5',
- ),
- '2.6.10' =>
- array (
- 0 => 'combodo-email-synchro/2.6.9',
- 1 => 'itop-standard-email-synchro/2.6.6',
- ),
- '2.6.11' =>
- array (
- 0 => 'combodo-email-synchro/2.6.10',
- 1 => 'itop-standard-email-synchro/2.6.6',
- ),
- '2.6.12' =>
- array (
- 0 => 'combodo-email-synchro/2.6.11',
- 1 => 'itop-standard-email-synchro/2.6.6',
- ),
- '3.0.0' =>
- array (
- 0 => 'combodo-email-synchro/3.0.0',
- 1 => 'itop-standard-email-synchro/3.0.0',
- ),
- '3.0.1' =>
- array (
- 0 => 'combodo-email-synchro/3.0.1',
- 1 => 'itop-standard-email-synchro/3.0.1',
- ),
- '3.0.2' =>
- array (
- 0 => 'combodo-email-synchro/3.0.2',
- 1 => 'itop-standard-email-synchro/3.0.1',
- ),
- '3.0.3' =>
- array (
- 0 => 'combodo-email-synchro/3.0.3',
- 1 => 'itop-standard-email-synchro/3.0.3',
- ),
- '3.0.4' =>
- array (
- 0 => 'combodo-email-synchro/3.0.3',
- 1 => 'itop-standard-email-synchro/3.0.4',
- ),
- '3.0.5' =>
- array (
- 0 => 'combodo-email-synchro/3.0.4',
- 1 => 'itop-standard-email-synchro/3.0.4',
- ),
- '3.0.6' =>
- array (
- 0 => 'combodo-email-synchro/3.0.5',
- 1 => 'itop-standard-email-synchro/3.0.4',
- ),
- '3.0.7' =>
- array (
- 0 => 'combodo-email-synchro/3.0.5',
- 1 => 'itop-standard-email-synchro/3.0.5',
- ),
- ),
- ),
- 'combodo-configurator-for-automatic-object-creation' =>
- array (
- 'label' => 'Configurator for automatic object creation',
- 'description' => 'Templating based on existing objects.',
- 'versions' =>
- array (
- '1.0.13' =>
- array (
- 1 => 'itop-stencils/1.0.6',
- ),
- ),
- ),
- 'combodo-user-actions-configurator' =>
- array (
- 'label' => 'User actions configurator',
- 'description' => 'Configure user actions to simplify and automate processes (e.g. create an incident from a CI).',
- 'versions' =>
- array (
- '1.0.0' =>
- array (
- 0 => 'itop-object-copier/1.0.0',
- ),
- '1.0.1' =>
- array (
- 0 => 'itop-object-copier/1.0.1',
- ),
- '1.0.2' =>
- array (
- 0 => 'itop-object-copier/1.0.2',
- ),
- '1.0.3' =>
- array (
- 0 => 'itop-object-copier/1.0.3',
- ),
- '1.1.0' =>
- array (
- 0 => 'itop-object-copier/1.1.0',
- ),
- '1.1.1' =>
- array (
- 0 => 'itop-object-copier/1.1.1',
- ),
- '1.1.2' =>
- array (
- 0 => 'itop-object-copier/1.1.2',
- ),
- '1.1.3' =>
- array (
- 0 => 'itop-object-copier/1.1.3',
- ),
- '1.1.4' =>
- array (
- 0 => 'itop-object-copier/1.1.4',
- ),
- '1.1.5' =>
- array (
- 0 => 'itop-object-copier/1.1.5',
- ),
- '1.1.6' =>
- array (
- 0 => 'itop-object-copier/1.1.6',
- ),
- '1.1.7' =>
- array (
- 0 => 'itop-object-copier/1.1.7',
- ),
- '1.1.8' =>
- array (
- 0 => 'itop-object-copier/1.1.8',
- ),
- ),
- ),
- 'combodo-send-updates-by-email' =>
- array (
- 'label' => 'Send updates by email',
- 'description' => 'Send an email to pre-configured contacts when a ticket log is updated.',
- 'versions' =>
- array (
- '1.0.1' =>
- array (
- 0 => 'email-reply/1.0.1',
- ),
- '1.0.3' =>
- array (
- 0 => 'email-reply/1.0.3',
- ),
- '1.1.1' =>
- array (
- 0 => 'email-reply/1.1.1',
- ),
- '1.1.2' =>
- array (
- 0 => 'email-reply/1.1.2',
- ),
- '1.1.3' =>
- array (
- 0 => 'email-reply/1.1.3',
- ),
- '1.1.4' =>
- array (
- 0 => 'email-reply/1.1.4',
- ),
- '1.1.5' =>
- array (
- 0 => 'email-reply/1.1.5',
- ),
- '1.1.6' =>
- array (
- 0 => 'email-reply/1.1.6',
- ),
- '1.1.7' =>
- array (
- 0 => 'email-reply/1.1.7',
- ),
- // 1.1.8 was never released
- '1.1.9' =>
- array (
- 0 => 'email-reply/1.1.9',
- ),
- '1.1.10' =>
- array (
- 0 => 'email-reply/1.1.10',
- ),
- ),
- ),
- );
+ // Generated by the Factory using the page export_component_versions_for_normalisation.php
+ return array (
+ 'combodo-approval-process-light' =>
+ array (
+ 'label' => 'Approval process light',
+ 'description' => 'Approve a request via a simple email',
+ 'versions' =>
+ array (
+ '1.0.1' =>
+ array (
+ 0 => 'approval-base/2.1.0',
+ 1 => 'combodo-approval-light/1.0.1',
+ ),
+ '1.0.2' =>
+ array (
+ 0 => 'approval-base/2.1.1',
+ 1 => 'combodo-approval-light/1.0.2',
+ ),
+ '1.0.3' =>
+ array (
+ 0 => 'approval-base/2.1.2',
+ 1 => 'combodo-approval-light/1.0.2',
+ ),
+ '1.1.0' =>
+ array (
+ 0 => 'approval-base/2.2.2',
+ 1 => 'combodo-approval-light/1.0.2',
+ ),
+ '1.1.1' =>
+ array (
+ 0 => 'approval-base/2.2.3',
+ 1 => 'combodo-approval-light/1.0.2',
+ ),
+ '1.1.2' =>
+ array (
+ 0 => 'approval-base/2.2.6',
+ 1 => 'combodo-approval-light/1.0.2',
+ ),
+ '1.1.3' =>
+ array (
+ 0 => 'approval-base/2.2.6',
+ 1 => 'combodo-approval-light/1.0.3',
+ ),
+ '1.2.0' =>
+ array (
+ 0 => 'approval-base/2.3.0',
+ 1 => 'combodo-approval-light/1.0.3',
+ ),
+ '1.2.1' =>
+ array (
+ 0 => 'approval-base/2.4.0',
+ 1 => 'combodo-approval-light/1.0.4',
+ ),
+ '1.3.0' =>
+ array (
+ 0 => 'approval-base/2.4.2',
+ 1 => 'combodo-approval-light/1.1.1',
+ ),
+ '1.3.1' =>
+ array (
+ 0 => 'approval-base/2.5.0',
+ 1 => 'combodo-approval-light/1.1.1',
+ ),
+ '1.3.2' =>
+ array (
+ 0 => 'approval-base/2.5.0',
+ 1 => 'combodo-approval-light/1.1.2',
+ ),
+ '1.2.2' =>
+ array (
+ 0 => 'approval-base/2.4.2',
+ 1 => 'combodo-approval-light/1.0.5',
+ ),
+ '1.3.3' =>
+ array (
+ 0 => 'approval-base/2.5.1',
+ 1 => 'combodo-approval-light/1.1.2',
+ ),
+ '1.3.4' =>
+ array (
+ 0 => 'approval-base/2.5.2',
+ 1 => 'combodo-approval-light/1.1.2',
+ ),
+ '1.3.5' =>
+ array (
+ 0 => 'approval-base/2.5.3',
+ 1 => 'combodo-approval-light/1.1.2',
+ ),
+ '1.4.0' =>
+ array (
+ 0 => 'approval-base/2.5.3',
+ 1 => 'combodo-approval-light/1.1.2',
+ 2 => 'itop-approval-portal/1.0.0',
+ ),
+ ),
+ ),
+ 'combodo-approval-process-automation' =>
+ array (
+ 'label' => 'Approval process automation',
+ 'description' => 'Control your approval process with predefined rules based on service catalog',
+ 'versions' =>
+ array (
+ '1.0.1' =>
+ array (
+ 0 => 'approval-base/2.1.0',
+ 1 => 'combodo-approval-extended/1.0.2',
+ ),
+ '1.0.2' =>
+ array (
+ 0 => 'approval-base/2.1.1',
+ 1 => 'combodo-approval-extended/1.0.4',
+ ),
+ '1.0.3' =>
+ array (
+ 0 => 'approval-base/2.1.2',
+ 1 => 'combodo-approval-extended/1.0.4',
+ ),
+ '1.1.0' =>
+ array (
+ 0 => 'approval-base/2.2.2',
+ 1 => 'combodo-approval-extended/1.0.4',
+ ),
+ '1.1.1' =>
+ array (
+ 0 => 'approval-base/2.2.3',
+ 1 => 'combodo-approval-extended/1.0.4',
+ ),
+ '1.1.2' =>
+ array (
+ 0 => 'approval-base/2.2.6',
+ 1 => 'combodo-approval-extended/1.0.5',
+ ),
+ '1.1.3' =>
+ array (
+ 0 => 'approval-base/2.2.6',
+ 1 => 'combodo-approval-extended/1.0.6',
+ ),
+ '1.2.0' =>
+ array (
+ 0 => 'approval-base/2.3.0',
+ 1 => 'combodo-approval-extended/1.0.7',
+ ),
+ '1.2.1' =>
+ array (
+ 0 => 'approval-base/2.4.0',
+ 1 => 'combodo-approval-extended/1.0.8',
+ ),
+ '1.3.0' =>
+ array (
+ 0 => 'approval-base/2.4.2',
+ 1 => 'combodo-approval-extended/1.2.1',
+ ),
+ '1.3.1' =>
+ array (
+ 0 => 'approval-base/2.5.0',
+ 1 => 'combodo-approval-extended/1.2.1',
+ ),
+ '1.3.2' =>
+ array (
+ 0 => 'approval-base/2.5.0',
+ 1 => 'combodo-approval-extended/1.2.2',
+ ),
+ '1.2.2' =>
+ array (
+ 0 => 'approval-base/2.4.2',
+ 1 => 'combodo-approval-extended/1.0.9',
+ ),
+ '1.3.3' =>
+ array (
+ 0 => 'approval-base/2.5.1',
+ 1 => 'combodo-approval-extended/1.2.3',
+ ),
+ '1.3.4' =>
+ array (
+ 0 => 'approval-base/2.5.2',
+ 1 => 'combodo-approval-extended/1.2.3',
+ ),
+ '1.3.5' =>
+ array (
+ 0 => 'approval-base/2.5.3',
+ 1 => 'combodo-approval-extended/1.2.3',
+ ),
+ '1.4.0' =>
+ array (
+ 0 => 'approval-base/2.5.3',
+ 1 => 'combodo-approval-extended/1.2.3',
+ 3 => 'itop-approval-portal/1.0.0',
+ ),
+ ),
+ ),
+ 'combodo-predefined-response-models' =>
+ array (
+ 'label' => 'Predefined response models',
+ 'description' => 'Pick common answers from a list of predefined replies grouped by categories to update tickets log',
+ 'versions' =>
+ array (
+ '1.0.0' =>
+ array (
+ 0 => 'precanned-replies/1.0.0',
+ 1 => 'precanned-replies-pro/1.0.0',
+ ),
+ '1.0.1' =>
+ array (
+ 0 => 'precanned-replies/1.0.1',
+ 1 => 'precanned-replies-pro/1.0.1',
+ ),
+ '1.0.2' =>
+ array (
+ 0 => 'precanned-replies/1.0.2',
+ 1 => 'precanned-replies-pro/1.0.1',
+ ),
+ '1.0.3' =>
+ array (
+ 0 => 'precanned-replies/1.0.3',
+ 1 => 'precanned-replies-pro/1.0.1',
+ ),
+ '1.0.4' =>
+ array (
+ 0 => 'precanned-replies/1.0.3',
+ 1 => 'precanned-replies-pro/1.0.2',
+ ),
+ '1.0.5' =>
+ array (
+ 0 => 'precanned-replies/1.0.4',
+ 1 => 'precanned-replies-pro/1.0.2',
+ ),
+ '1.1.0' =>
+ array (
+ 0 => 'precanned-replies/1.1.0',
+ 1 => 'precanned-replies-pro/1.0.2',
+ ),
+ '1.1.1' =>
+ array (
+ 0 => 'precanned-replies/1.1.1',
+ 1 => 'precanned-replies-pro/1.0.2',
+ ),
+ ),
+ ),
+ 'combodo-customized-request-forms' =>
+ array (
+ 'label' => 'Customized request forms',
+ 'description' => 'Define personalized request forms based on the service catalog. Add extra fields for a given type of request.',
+ 'versions' =>
+ array (
+ '1.0.1' =>
+ array (
+ 0 => 'templates-base/2.1.1',
+ 1 => 'itop-request-template/1.0.0',
+ ),
+ '1.0.2' =>
+ array (
+ 0 => 'templates-base/2.1.2',
+ 1 => 'itop-request-template/1.0.0',
+ ),
+ '1.0.3' =>
+ array (
+ 0 => 'templates-base/2.1.2',
+ 1 => 'itop-request-template/1.0.1',
+ ),
+ '1.0.4' =>
+ array (
+ 0 => 'templates-base/2.1.3',
+ 1 => 'itop-request-template/1.0.1',
+ ),
+ '1.0.5' =>
+ array (
+ 0 => 'templates-base/2.1.4',
+ 1 => 'itop-request-template/1.0.1',
+ ),
+ '2.0.0' =>
+ array (
+ 0 => 'templates-base/3.0.0',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.1' =>
+ array (
+ 0 => 'templates-base/3.0.1',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.2' =>
+ array (
+ 0 => 'templates-base/3.0.2',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.3' =>
+ array (
+ 0 => 'templates-base/3.0.4',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.4' =>
+ array (
+ 0 => 'templates-base/3.0.5',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.5' =>
+ array (
+ 0 => 'templates-base/3.0.6',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.6' =>
+ array (
+ 0 => 'templates-base/3.0.8',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.7' =>
+ array (
+ 0 => 'templates-base/3.0.9',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ '2.0.8' =>
+ array (
+ 0 => 'templates-base/3.0.12',
+ 1 => 'itop-request-template/2.0.0',
+ 2 => 'itop-request-template-portal/1.0.0',
+ ),
+ ),
+ ),
+ 'combodo-sla-considering-business-hours' =>
+ array (
+ 'label' => 'SLA considering business hours',
+ 'description' => 'Compute SLAs taking into account service coverage window and holidays',
+ 'versions' =>
+ array (
+ '2.0.1' =>
+ array (
+ 0 => 'combodo-sla-computation/2.0.1',
+ 1 => 'combodo-coverage-windows-computation/2.0.0',
+ ),
+ '2.1.0' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.0',
+ 1 => 'combodo-coverage-windows-computation/2.0.0',
+ ),
+ '2.1.1' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.1',
+ 1 => 'combodo-coverage-windows-computation/2.0.0',
+ ),
+ '2.1.2' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.2',
+ 1 => 'combodo-coverage-windows-computation/2.0.0',
+ ),
+ '2.1.3' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.2',
+ 1 => 'combodo-coverage-windows-computation/2.0.1',
+ ),
+ '2.0.2' =>
+ array (
+ 0 => 'combodo-sla-computation/2.0.1',
+ 1 => 'combodo-coverage-windows-computation/2.0.1',
+ ),
+ '2.1.4' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.3',
+ 1 => 'combodo-coverage-windows-computation/2.0.1',
+ ),
+ '2.1.5' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.5',
+ 1 => 'combodo-coverage-windows-computation/2.0.1',
+ ),
+ '2.1.6' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.5',
+ 1 => 'combodo-coverage-windows-computation/2.0.2',
+ ),
+ '2.1.7' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.6',
+ 1 => 'combodo-coverage-windows-computation/2.0.2',
+ ),
+ '2.1.8' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.7',
+ 1 => 'combodo-coverage-windows-computation/2.0.2',
+ ),
+ '2.1.9' =>
+ array (
+ 0 => 'combodo-sla-computation/2.1.8',
+ 1 => 'combodo-coverage-windows-computation/2.0.2',
+ ),
+ ),
+ ),
+ 'combodo-mail-to-ticket-automation' =>
+ array (
+ 'label' => 'Mail to ticket automation',
+ 'description' => 'Scan several mailboxes to create or update tickets.',
+ 'versions' =>
+ array (
+ '2.6.0' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.0',
+ 1 => 'itop-standard-email-synchro/2.6.0',
+ ),
+ '2.6.1' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.1',
+ 1 => 'itop-standard-email-synchro/2.6.0',
+ ),
+ '2.6.2' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.2',
+ 1 => 'itop-standard-email-synchro/2.6.0',
+ ),
+ '2.6.3' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.2',
+ 1 => 'itop-standard-email-synchro/2.6.1',
+ ),
+ '2.6.4' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.3',
+ 1 => 'itop-standard-email-synchro/2.6.2',
+ ),
+ '2.6.5' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.4',
+ 1 => 'itop-standard-email-synchro/2.6.2',
+ ),
+ '2.6.6' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.5',
+ 1 => 'itop-standard-email-synchro/2.6.3',
+ ),
+ '2.6.7' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.6',
+ 1 => 'itop-standard-email-synchro/2.6.4',
+ ),
+ '2.6.8' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.7',
+ 1 => 'itop-standard-email-synchro/2.6.4',
+ ),
+ '2.6.9' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.8',
+ 1 => 'itop-standard-email-synchro/2.6.5',
+ ),
+ '2.6.10' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.9',
+ 1 => 'itop-standard-email-synchro/2.6.6',
+ ),
+ '2.6.11' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.10',
+ 1 => 'itop-standard-email-synchro/2.6.6',
+ ),
+ '2.6.12' =>
+ array (
+ 0 => 'combodo-email-synchro/2.6.11',
+ 1 => 'itop-standard-email-synchro/2.6.6',
+ ),
+ '3.0.0' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.0',
+ 1 => 'itop-standard-email-synchro/3.0.0',
+ ),
+ '3.0.1' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.1',
+ 1 => 'itop-standard-email-synchro/3.0.1',
+ ),
+ '3.0.2' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.2',
+ 1 => 'itop-standard-email-synchro/3.0.1',
+ ),
+ '3.0.3' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.3',
+ 1 => 'itop-standard-email-synchro/3.0.3',
+ ),
+ '3.0.4' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.3',
+ 1 => 'itop-standard-email-synchro/3.0.4',
+ ),
+ '3.0.5' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.4',
+ 1 => 'itop-standard-email-synchro/3.0.4',
+ ),
+ '3.0.6' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.5',
+ 1 => 'itop-standard-email-synchro/3.0.4',
+ ),
+ '3.0.7' =>
+ array (
+ 0 => 'combodo-email-synchro/3.0.5',
+ 1 => 'itop-standard-email-synchro/3.0.5',
+ ),
+ ),
+ ),
+ 'combodo-configurator-for-automatic-object-creation' =>
+ array (
+ 'label' => 'Configurator for automatic object creation',
+ 'description' => 'Templating based on existing objects.',
+ 'versions' =>
+ array (
+ '1.0.13' =>
+ array (
+ 1 => 'itop-stencils/1.0.6',
+ ),
+ ),
+ ),
+ 'combodo-user-actions-configurator' =>
+ array (
+ 'label' => 'User actions configurator',
+ 'description' => 'Configure user actions to simplify and automate processes (e.g. create an incident from a CI).',
+ 'versions' =>
+ array (
+ '1.0.0' =>
+ array (
+ 0 => 'itop-object-copier/1.0.0',
+ ),
+ '1.0.1' =>
+ array (
+ 0 => 'itop-object-copier/1.0.1',
+ ),
+ '1.0.2' =>
+ array (
+ 0 => 'itop-object-copier/1.0.2',
+ ),
+ '1.0.3' =>
+ array (
+ 0 => 'itop-object-copier/1.0.3',
+ ),
+ '1.1.0' =>
+ array (
+ 0 => 'itop-object-copier/1.1.0',
+ ),
+ '1.1.1' =>
+ array (
+ 0 => 'itop-object-copier/1.1.1',
+ ),
+ '1.1.2' =>
+ array (
+ 0 => 'itop-object-copier/1.1.2',
+ ),
+ '1.1.3' =>
+ array (
+ 0 => 'itop-object-copier/1.1.3',
+ ),
+ '1.1.4' =>
+ array (
+ 0 => 'itop-object-copier/1.1.4',
+ ),
+ '1.1.5' =>
+ array (
+ 0 => 'itop-object-copier/1.1.5',
+ ),
+ '1.1.6' =>
+ array (
+ 0 => 'itop-object-copier/1.1.6',
+ ),
+ '1.1.7' =>
+ array (
+ 0 => 'itop-object-copier/1.1.7',
+ ),
+ '1.1.8' =>
+ array (
+ 0 => 'itop-object-copier/1.1.8',
+ ),
+ ),
+ ),
+ 'combodo-send-updates-by-email' =>
+ array (
+ 'label' => 'Send updates by email',
+ 'description' => 'Send an email to pre-configured contacts when a ticket log is updated.',
+ 'versions' =>
+ array (
+ '1.0.1' =>
+ array (
+ 0 => 'email-reply/1.0.1',
+ ),
+ '1.0.3' =>
+ array (
+ 0 => 'email-reply/1.0.3',
+ ),
+ '1.1.1' =>
+ array (
+ 0 => 'email-reply/1.1.1',
+ ),
+ '1.1.2' =>
+ array (
+ 0 => 'email-reply/1.1.2',
+ ),
+ '1.1.3' =>
+ array (
+ 0 => 'email-reply/1.1.3',
+ ),
+ '1.1.4' =>
+ array (
+ 0 => 'email-reply/1.1.4',
+ ),
+ '1.1.5' =>
+ array (
+ 0 => 'email-reply/1.1.5',
+ ),
+ '1.1.6' =>
+ array (
+ 0 => 'email-reply/1.1.6',
+ ),
+ '1.1.7' =>
+ array (
+ 0 => 'email-reply/1.1.7',
+ ),
+ // 1.1.8 was never released
+ '1.1.9' =>
+ array (
+ 0 => 'email-reply/1.1.9',
+ ),
+ '1.1.10' =>
+ array (
+ 0 => 'email-reply/1.1.10',
+ ),
+ ),
+ ),
+ );
}
}
diff --git a/setup/modulediscovery.class.inc.php b/setup/modulediscovery.class.inc.php
index 96822a982d..1021c93b2e 100644
--- a/setup/modulediscovery.class.inc.php
+++ b/setup/modulediscovery.class.inc.php
@@ -19,6 +19,10 @@
*
*/
+use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
+
+require_once(APPROOT.'setup/modulediscovery/ModuleFileReader.php');
+
class MissingDependencyException extends CoreException
{
/**
@@ -105,6 +109,9 @@ protected static function SetModulePath($sModulePath)
*/
public static function AddModule($sFilePath, $sId, $aArgs)
{
+ if (is_null($aArgs)||! is_array($aArgs)){
+ throw new ModuleFileReaderException("Error parsing module file args", 0, null, $sFilePath);
+ }
if (!array_key_exists('itop_version', $aArgs))
{
// Assume 1.0.2
@@ -115,7 +122,7 @@ public static function AddModule($sFilePath, $sId, $aArgs)
if (!array_key_exists($sArgName, $aArgs))
{
throw new Exception("Module '$sId': missing argument '$sArgName'");
- }
+ }
}
$aArgs['root_dir'] = dirname($sFilePath);
@@ -218,7 +225,7 @@ protected static function GetModules($bAbortOnMissingDependency = false, $aModul
* @param array $aModulesToLoad List of modules to search for, defaults to all if omitted
* @return array
* @throws \MissingDependencyException
-*/
+ */
public static function OrderModulesByDependencies($aModules, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
{
// Order the modules to take into account their inter-dependencies
@@ -349,19 +356,19 @@ protected static function DependencyIsResolved($sDepString, $aOrderedModules, $a
if (version_compare($sCurrentVersion, $sExpectedVersion, $sOperator))
{
$aReplacements[$sModuleId] = '(true)'; // Add parentheses to protect against invalid condition causing
- // a function call that results in a runtime fatal error
+ // a function call that results in a runtime fatal error
}
else
{
$aReplacements[$sModuleId] = '(false)'; // Add parentheses to protect against invalid condition causing
- // a function call that results in a runtime fatal error
+ // a function call that results in a runtime fatal error
}
}
else
{
// module is not present
$aReplacements[$sModuleId] = '(false)'; // Add parentheses to protect against invalid condition causing
- // a function call that results in a runtime fatal error
+ // a function call that results in a runtime fatal error
}
}
}
@@ -385,10 +392,10 @@ protected static function DependencyIsResolved($sDepString, $aOrderedModules, $a
else
{
$sBooleanExpr = str_replace(array_keys($aReplacements), array_values($aReplacements), $sDepString);
- $bOk = @eval('$bResult = '.$sBooleanExpr.'; return true;');
- if ($bOk == false)
- {
- SetupLog::Warning("Eval of '$sBooleanExpr' returned false");
+ try{
+ $bResult = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($sBooleanExpr);
+ } catch(ModuleFileReaderException $e){
+ //logged already
echo "Failed to parse the boolean Expression = '$sBooleanExpr'
";
}
}
@@ -496,42 +503,12 @@ protected static function ListModuleFiles($sRelDir, $sRootDir)
else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
{
self::SetModulePath($sRelDir);
- try
- {
- $sModuleFileContents = file_get_contents($sDirectory.'/'.$sFile);
- $sModuleFileContents = str_replace(array(''), '', $sModuleFileContents);
- $sModuleFileContents = str_replace('__FILE__', "'".addslashes($sDirectory.'/'.$sFile)."'", $sModuleFileContents);
- preg_match_all('/class ([A-Za-z0-9_]+) extends ([A-Za-z0-9_]+)/', $sModuleFileContents, $aMatches);
- //print_r($aMatches);
- $idx = 0;
- foreach($aMatches[1] as $sClassName)
- {
- if (class_exists($sClassName))
- {
- // rename the class inside the code to prevent a "duplicate class" declaration
- // and change its parent class as well so that nobody will find it and try to execute it
- $sModuleFileContents = str_replace($sClassName.' extends '.$aMatches[2][$idx], $sClassName.'_'.($iDummyClassIndex++).' extends DummyHandler', $sModuleFileContents);
- }
- $idx++;
- }
- $bRet = eval($sModuleFileContents);
-
- if ($bRet === false)
- {
- SetupLog::Warning("Eval of $sRelDir/$sFile returned false");
- }
-
- //echo "
Done.
\n"; - } - catch(ParseError $e) - { - // PHP 7 - SetupLog::Warning("Eval of $sRelDir/$sFile caused an exception: ".$e->getMessage()." at line ".$e->getLine()); - } - catch(Exception $e) - { - // Continue... - SetupLog::Warning("Eval of $sRelDir/$sFile caused an exception: ".$e->getMessage()); + $sModuleFilePath = $sDirectory.'/'.$sFile; + try { + $aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sDirectory.'/'.$sFile); + SetupWebPage::AddModule($sModuleFilePath, $aModuleInfo[1], $aModuleInfo[2]); + } catch(ModuleFileReaderException $e){ + continue; } } } diff --git a/setup/modulediscovery/ModuleFileParser.php b/setup/modulediscovery/ModuleFileParser.php new file mode 100644 index 0000000000..5064c34818 --- /dev/null +++ b/setup/modulediscovery/ModuleFileParser.php @@ -0,0 +1,155 @@ +createForNewestSupportedVersion(); + return $oParser->parse($sPhpContent); + } + + /** + * @param string $sModuleFilePath + * @param \PhpParser\Node\Expr\Assign $oAssignation + * + * @return array|null + * @throws \ModuleFileReaderException + */ + public function GetModuleInformationFromAddModuleCall(string $sModuleFilePath, \PhpParser\Node\Stmt\Expression $oExpression) : ?array + { + /** @var Assign $oAssignation */ + $oAssignation = $oExpression->expr; + if (false === ($oAssignation instanceof PhpParser\Node\Expr\StaticCall)) { + return null; + } + + /** @var PhpParser\Node\Expr\StaticCall $oAssignation */ + + if ("SetupWebPage" !== $oAssignation?->class?->name) { + return null; + } + + if ("AddModule" !== $oAssignation?->name?->name) { + return null; + } + + $aArgs = $oAssignation?->args; + if (count($aArgs) != 3) { + throw new ModuleFileReaderException("Not enough parameters when calling SetupWebPage::AddModule", 0, null, $sModuleFilePath); + } + + $oModuleId = $aArgs[1]; + if (false === ($oModuleId instanceof PhpParser\Node\Arg)) { + throw new ModuleFileReaderException("2nd parameter to SetupWebPage::AddModule call issue: " . get_class($oModuleId), 0, null, $sModuleFilePath); + } + + /** @var PhpParser\Node\Arg $oModuleId */ + if (false === ($oModuleId->value instanceof PhpParser\Node\Scalar\String_)) { + throw new ModuleFileReaderException("2nd parameter to SetupWebPage::AddModule not a string: " . get_class($oModuleId->value), 0, null, $sModuleFilePath); + } + + $sModuleId = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oModuleId->value); + + $oModuleConfigInfo = $aArgs[2]; + if (false === ($oModuleConfigInfo instanceof PhpParser\Node\Arg)) { + throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule call issue: " . get_class($oModuleConfigInfo), 0, null, $sModuleFilePath); + } + + /** @var PhpParser\Node\Arg $oModuleConfigInfo */ + if (false === ($oModuleConfigInfo->value instanceof PhpParser\Node\Expr\Array_)) { + throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath); + } + + $aModuleConfig = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oModuleConfigInfo->value); + + if (! is_array($aModuleConfig)){ + throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath); + } + + return [ + $sModuleFilePath, + $sModuleId, + $aModuleConfig, + ]; + } + + /** + * @param string $sModuleFilePath + * @param \PhpParser\Node\Stmt\If_ $oNode + * + * @return array|null + * @throws \ModuleFileReaderException + */ + public function GetModuleInformationFromIf(string $sModuleFilePath, \PhpParser\Node\Stmt\If_ $oNode) : ?array + { + $bCondition = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oNode->cond); + if ($bCondition) { + foreach ($oNode->stmts as $oSubNode) { + if ($oSubNode instanceof \PhpParser\Node\Stmt\Expression) { + $aModuleConfig = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oSubNode); + if (!is_null($aModuleConfig)) { + return $aModuleConfig; + } + } + } + + return null; + } + + if (! is_null($oNode->elseifs)) { + foreach ($oNode->elseifs as $oElseIfSubNode) { + /** @var \PhpParser\Node\Stmt\ElseIf_ $oElseIfSubNode */ + $bCondition = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oElseIfSubNode->cond); + if ($bCondition) { + return $this->GetModuleConfigurationFromStatement($sModuleFilePath, $oElseIfSubNode->stmts); + } + } + } + + if (! is_null($oNode->else)) { + return $this->GetModuleConfigurationFromStatement($sModuleFilePath, $oNode->else->stmts); + } + + return null; + } + + public function GetModuleConfigurationFromStatement(string $sModuleFilePath, array $aStmts) : ?array + { + foreach ($aStmts as $oSubNode) { + if ($oSubNode instanceof \PhpParser\Node\Stmt\Expression) { + $aModuleConfig = $this->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oSubNode); + if (!is_null($aModuleConfig)) { + return $aModuleConfig; + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/setup/modulediscovery/ModuleFileReader.php b/setup/modulediscovery/ModuleFileReader.php new file mode 100644 index 0000000000..63ff26a109 --- /dev/null +++ b/setup/modulediscovery/ModuleFileReader.php @@ -0,0 +1,167 @@ +ParsePhpCode(file_get_contents($sModuleFilePath)); + } + catch (PhpParser\Error $e) { + throw new \ModuleFileReaderException($e->getMessage(), 0, $e, $sModuleFilePath); + } + + try { + foreach ($aNodes as $sKey => $oNode) { + if ($oNode instanceof \PhpParser\Node\Stmt\Expression) { + $aModuleInfo = ModuleFileParser::GetInstance()->GetModuleInformationFromAddModuleCall($sModuleFilePath, $oNode); + if (! is_null($aModuleInfo)){ + $this->CompleteModuleInfoWithFilePath($aModuleInfo); + return $aModuleInfo; + } + } + + if ($oNode instanceof PhpParser\Node\Stmt\If_) { + $aModuleInfo = ModuleFileParser::GetInstance()->GetModuleInformationFromIf($sModuleFilePath, $oNode); + if (! is_null($aModuleInfo)){ + $this->CompleteModuleInfoWithFilePath($aModuleInfo); + return $aModuleInfo; + } + } + } + } catch(ModuleFileReaderException $e) { + // Continue... + throw $e; + } catch(Exception $e) { + // Continue... + throw new ModuleFileReaderException("Eval of $sModuleFilePath caused an exception: ".$e->getMessage(), 0, $e, $sModuleFilePath); + } + + throw new ModuleFileReaderException("No proper call to SetupWebPage::AddModule found in module file", 0, null, $sModuleFilePath); + } + + /** + * Read the information from a module file (module.xxx.php) + * Warning: this method is using eval() function to load the ModuleInstallerAPI classes. + * Current method is never called at design/runtime. It is acceptable to use it during setup only. + * @param string $sModuleFile + * @return array + * @throws ModuleFileReaderException + */ + public function ReadModuleFileInformationUnsafe(string $sModuleFilePath) : array + { + $aModuleInfo = []; // will be filled by the "eval" line below... + try + { + $aMatches = []; + $sModuleFileContents = file_get_contents($sModuleFilePath); + $sModuleFileContents = str_replace([''], '', $sModuleFileContents); + $sModuleFileContents = str_replace('__FILE__', "'".addslashes($sModuleFilePath)."'", $sModuleFileContents); + preg_match_all('/class ([A-Za-z0-9_]+) extends ([A-Za-z0-9_]+)/', $sModuleFileContents, $aMatches); + //print_r($aMatches); + $idx = 0; + foreach($aMatches[1] as $sClassName) + { + if (class_exists($sClassName)) + { + // rename any class declaration inside the code to prevent a "duplicate class" declaration + // and change its parent class as well so that nobody will find it and try to execute it + // Note: don't use the same naming scheme as ModuleDiscovery otherwise you 'll have the duplicate class error again !! + $sModuleFileContents = str_replace($sClassName.' extends '.$aMatches[2][$idx], $sClassName.'_Ext_'.(ModuleFileReader::$iDummyClassIndex++).' extends DummyHandler', $sModuleFileContents); + } + $idx++; + } + // Replace the main function call by an assignment to a variable, as an array... + $sModuleFileContents = str_replace(['SetupWebPage::AddModule', 'ModuleDiscovery::AddModule'], '$aModuleInfo = array', $sModuleFileContents); + eval($sModuleFileContents); // Assigns $aModuleInfo + + if (count($aModuleInfo) === 0) + { + throw new ModuleFileReaderException("Eval of $sModuleFilePath did not return the expected information..."); + } + + $this->CompleteModuleInfoWithFilePath($aModuleInfo); + } + catch(ModuleFileReaderException $e) + { + // Continue... + throw $e; + } + catch(ParseError $e) + { + // Continue... + throw new ModuleFileReaderException("Eval of $sModuleFilePath caused a parse error: ".$e->getMessage()." at line ".$e->getLine()); + } + catch(Exception $e) + { + // Continue... + throw new ModuleFileReaderException("Eval of $sModuleFilePath caused an exception: ".$e->getMessage(), 0, $e); + } + return $aModuleInfo; + } + + /** + * + * Internal trick: additional path is added into the module info structure to handle ModuleInstallerAPI execution during setup + * @param array &$aModuleInfo + * + * @return void + */ + private function CompleteModuleInfoWithFilePath(array &$aModuleInfo) + { + if (count($aModuleInfo)==3) { + $aModuleInfo[2]['module_file_path'] = $aModuleInfo[0]; + } + } + + public function GetAndCheckModuleInstallerClass($aModuleInfo) : ?string + { + if (! isset($aModuleInfo['installer'])){ + return null; + } + + $sModuleInstallerClass = $aModuleInfo['installer']; + if (!class_exists($sModuleInstallerClass)) { + $sModuleFilePath = $aModuleInfo['module_file_path']; + $this->ReadModuleFileInformationUnsafe($sModuleFilePath); + } + + if (!class_exists($sModuleInstallerClass)) + { + throw new CoreException("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']); + } + if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI')) + { + throw new CoreException("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']); + } + + return $sModuleInstallerClass; + } +} diff --git a/setup/modulediscovery/ModuleFileReaderException.php b/setup/modulediscovery/ModuleFileReaderException.php new file mode 100644 index 0000000000..00f074819a --- /dev/null +++ b/setup/modulediscovery/ModuleFileReaderException.php @@ -0,0 +1,23 @@ + $oPrevious?->getMessage(), 'stack' => $e->getTraceAsString()]; + if (!is_null($sModuleFile)) { + $aContext['module_file'] = $sModuleFile; + } + SetupLog::Warning($sMessage, null, $aContext); + parent::__construct($sMessage, $iHttpCode, $oPrevious); + } +} \ No newline at end of file diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index bf5cc50c65..7cf0129b3d 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -24,6 +24,8 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ +use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator; + require_once APPROOT."setup/modulediscovery.class.inc.php"; require_once APPROOT.'setup/modelfactory.class.inc.php'; require_once APPROOT.'setup/compiler.class.inc.php'; @@ -202,10 +204,10 @@ public function AnalyzeInstallation($oConfig, $modulesPath, $bAbortOnMissingDepe if (!in_array($sModuleAppVersion, array('1.0.0', '1.0.1', '1.0.2'))) { // This module is NOT compatible with the current version - $aModuleInfo['install'] = array( - 'flag' => MODULE_ACTION_IMPOSSIBLE, - 'message' => 'the module is not compatible with the current version of the application' - ); + $aModuleInfo['install'] = array( + 'flag' => MODULE_ACTION_IMPOSSIBLE, + 'message' => 'the module is not compatible with the current version of the application' + ); } elseif ($aModuleInfo['mandatory']) { @@ -457,20 +459,16 @@ protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir) { if (!array_key_exists($oModule->GetName(), $aRet) && $oModule->IsAutoSelect()) { - try - { - $bSelected = false; - SetupInfo::SetSelectedModules($aRet); - eval('$bSelected = ('.$oModule->GetAutoSelect().');'); - } - catch(Exception $e) - { - $bSelected = false; - } - if ($bSelected) - { - $aRet[$oModule->GetName()] = $oModule; // store the Id of the selected module - $bModuleAdded = true; + SetupInfo::SetSelectedModules($aRet); + try{ + $bSelected = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($oModule->GetAutoSelect()); + if ($bSelected) + { + $aRet[$oModule->GetName()] = $oModule; // store the Id of the selected module + $bModuleAdded = true; + } + } catch(ModuleFileReaderException $e){ + //do nothing. logged already } } } @@ -977,8 +975,8 @@ public function Commit() $this->CommitDir( APPROOT.'env-'.$this->sTargetEnv, APPROOT.'env-'.$this->sFinalEnv, - true, - false + true, + false ); // Move the config file @@ -1045,7 +1043,7 @@ protected function CommitFile($sSource, $sDest, $bSourceMustExist = true) * @param $sSource * @param $sDest * @param boolean $bSourceMustExist - * @param boolean $bRemoveSource If true $sSource will be removed, otherwise $sSource will just be emptied + * @param boolean $bRemoveSource If true $sSource will be removed, otherwise $sSource will just be emptied * @throws Exception */ protected function CommitDir($sSource, $sDest, $bSourceMustExist = true, $bRemoveSource = true) @@ -1080,41 +1078,59 @@ public function Rollback() } } - /** - * Call the given handler method for all selected modules having an installation handler - * @param array[] $aAvailableModules - * @param string[] $aSelectedModules - * @param string $sHandlerName - * @throws CoreException - */ + /** + * Call the given handler method for all selected modules having an installation handler + * @param array[] $aAvailableModules + * @param string[] $aSelectedModules + * @param string $sHandlerName + * @throws CoreException + */ public function CallInstallerHandlers($aAvailableModules, $aSelectedModules, $sHandlerName) { - foreach($aAvailableModules as $sModuleId => $aModule) - { - if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) && - isset($aAvailableModules[$sModuleId]['installer']) ) - { - $sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer']; - SetupLog::Info("Calling Module Handler: $sModuleInstallerClass::$sHandlerName(oConfig, {$aModule['version_db']}, {$aModule['version_code']})"); - $aCallSpec = array($sModuleInstallerClass, $sHandlerName); - if (is_callable($aCallSpec)) - { - try { - call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code'])); - } catch (Exception $e) { - $sErrorMessage = "Module $sModuleId : error when calling module installer class $sModuleInstallerClass for $sHandlerName handler"; - $aExceptionContextData = [ - 'ModulelId' => $sModuleId, - 'ModuleInstallerClass' => $sModuleInstallerClass, - 'ModuleInstallerHandler' => $sHandlerName, - 'ExceptionClass' => get_class($e), - 'ExceptionMessage' => $e->getMessage(), - ]; - throw new CoreException($sErrorMessage, $aExceptionContextData, '', $e); - } - } - } - } + foreach($aAvailableModules as $sModuleId => $aModule) + { + if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules)) + { + $aArgs = [MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']]; + RunTimeEnvironment::CallInstallerHandler($aAvailableModules[$sModuleId], $sHandlerName, $aArgs); + } + } + } + + /** + * Call the given handler method for all selected modules having an installation handler + * + * @param array $aModuleInfo + * @param string $sHandlerName + * @param array $aArgs + * + * @throws CoreException + */ + public static function CallInstallerHandler(array $aModuleInfo, $sHandlerName, array $aArgs) + { + $sModuleInstallerClass = ModuleFileReader::GetInstance()->GetAndCheckModuleInstallerClass($aModuleInfo); + if (is_null($sModuleInstallerClass)){ + return; + } + + SetupLog::Info("Calling Module Handler: $sModuleInstallerClass::$sHandlerName", null, $aArgs); + $aCallSpec = [$sModuleInstallerClass, $sHandlerName]; + if (is_callable($aCallSpec)) + { + try { + call_user_func_array($aCallSpec, $aArgs); + } catch (Exception $e) { + $sErrorMessage = "Module $sModuleId : error when calling module installer class $sModuleInstallerClass for $sHandlerName handler"; + $aExceptionContextData = [ + 'ModulelId' => $sModuleId, + 'ModuleInstallerClass' => $sModuleInstallerClass, + 'ModuleInstallerHandler' => $sHandlerName, + 'ExceptionClass' => get_class($e), + 'ExceptionMessage' => $e->getMessage(), + ]; + throw new CoreException($sErrorMessage, $aExceptionContextData, '', $e); + } + } } /** @@ -1143,64 +1159,64 @@ public function LoadData($aAvailableModules, $aSelectedModules, $bSampleData) if ($aModule['version_db'] != '') { // Simulate the load of the previously loaded XML files to get the mapping of the keys if ($bSampleData) { - $aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); - $aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']); - } - else - { - // Load only structural data - $aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); - } - } - else - { - if ($bSampleData) - { - $aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); - $aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']); - } - else - { - // Load only structural data - $aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); - } - } - } - } - } - - // Simulate the load of the previously loaded files, in order to initialize - // the mapping between the identifiers in the XML and the actual identifiers - // in the current database - foreach($aPreviouslyLoadedFiles as $sFileRelativePath) - { - $sFileName = APPROOT.$sFileRelativePath; - SetupLog::Info("Loading file: $sFileName (just to get the keys mapping)"); - if (empty($sFileName) || !file_exists($sFileName)) - { - throw(new Exception("File $sFileName does not exist")); - } - - $oDataLoader->LoadFile($sFileName, true); - $sResult = sprintf("loading of %s done.", basename($sFileName)); - SetupLog::Info($sResult); - } - - foreach($aFiles as $sFileRelativePath) - { - $sFileName = APPROOT.$sFileRelativePath; - SetupLog::Info("Loading file: $sFileName"); - if (empty($sFileName) || !file_exists($sFileName)) - { - throw(new Exception("File $sFileName does not exist")); - } - - $oDataLoader->LoadFile($sFileName); - $sResult = sprintf("loading of %s done.", basename($sFileName)); - SetupLog::Info($sResult); - } - - $oDataLoader->EndSession(); + $aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); + $aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']); + } + else + { + // Load only structural data + $aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); + } + } + else + { + if ($bSampleData) + { + $aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); + $aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']); + } + else + { + // Load only structural data + $aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']); + } + } + } + } + } + + // Simulate the load of the previously loaded files, in order to initialize + // the mapping between the identifiers in the XML and the actual identifiers + // in the current database + foreach($aPreviouslyLoadedFiles as $sFileRelativePath) + { + $sFileName = APPROOT.$sFileRelativePath; + SetupLog::Info("Loading file: $sFileName (just to get the keys mapping)"); + if (empty($sFileName) || !file_exists($sFileName)) + { + throw(new Exception("File $sFileName does not exist")); + } + + $oDataLoader->LoadFile($sFileName, true); + $sResult = sprintf("loading of %s done.", basename($sFileName)); + SetupLog::Info($sResult); + } + + foreach($aFiles as $sFileRelativePath) + { + $sFileName = APPROOT.$sFileRelativePath; + SetupLog::Info("Loading file: $sFileName"); + if (empty($sFileName) || !file_exists($sFileName)) + { + throw(new Exception("File $sFileName does not exist")); + } + + $oDataLoader->LoadFile($sFileName); + $sResult = sprintf("loading of %s done.", basename($sFileName)); + SetupLog::Info($sResult); + } + + $oDataLoader->EndSession(); SetupLog::Info("ending data load session"); } @@ -1213,12 +1229,12 @@ public function LoadData($aAvailableModules, $aSelectedModules, $bSampleData) */ protected static function MergeWithRelativeDir($aSourceArray, $sBaseDir, $aFilesToMerge) { - $aToMerge = array(); - foreach($aFilesToMerge as $sFile) - { - $aToMerge[] = $sBaseDir.'/'.$sFile; - } - return array_merge($aSourceArray, $aToMerge); + $aToMerge = array(); + foreach($aFilesToMerge as $sFile) + { + $aToMerge[] = $sBaseDir.'/'.$sFile; + } + return array_merge($aSourceArray, $aToMerge); } /** @@ -1227,40 +1243,40 @@ protected static function MergeWithRelativeDir($aSourceArray, $sBaseDir, $aFiles * @throws Exception * @return string */ - public function CheckMetaModel() - { - $iCount = 0; - $fStart = microtime(true); - foreach(MetaModel::GetClasses() as $sClass) - { - if (false == MetaModel::HasTable($sClass) && MetaModel::IsAbstract($sClass)) - { - //if a class is not persisted and is abstract, the code below would crash. Needed by the class AbstractRessource. This is tolerable to skip this because we check the setup process integrity, not the datamodel integrity. - continue; - } - - $oSearch = new DBObjectSearch($sClass); - $oSearch->SetShowObsoleteData(false); - $oSQLQuery = $oSearch->GetSQLQueryStructure(null, false); - $sViewName = MetaModel::DBGetView($sClass); - if (strlen($sViewName) > 64) - { - throw new Exception("Class name too long for class: '$sClass'. The name of the corresponding view ($sViewName) would exceed MySQL's limit for the name of a table (64 characters)."); - } - $sTableName = MetaModel::DBGetTable($sClass); - if (strlen($sTableName) > 64) - { - throw new Exception("Table name too long for class: '$sClass'. The name of the corresponding MySQL table ($sTableName) would exceed MySQL's limit for the name of a table (64 characters)."); - } - $iTableCount = $oSQLQuery->CountTables(); - if ($iTableCount > 61) - { - throw new Exception("Class requiring too many tables: '$sClass'. The structure of the class ($sClass) would require a query with more than 61 JOINS (MySQL's limitation)."); - } - $iCount++; - } - $fDuration = microtime(true) - $fStart; - - return sprintf("Checked %d classes in %.1f ms. No error found.\n", $iCount, $fDuration*1000.0); - } + public function CheckMetaModel() + { + $iCount = 0; + $fStart = microtime(true); + foreach(MetaModel::GetClasses() as $sClass) + { + if (false == MetaModel::HasTable($sClass) && MetaModel::IsAbstract($sClass)) + { + //if a class is not persisted and is abstract, the code below would crash. Needed by the class AbstractRessource. This is tolerable to skip this because we check the setup process integrity, not the datamodel integrity. + continue; + } + + $oSearch = new DBObjectSearch($sClass); + $oSearch->SetShowObsoleteData(false); + $oSQLQuery = $oSearch->GetSQLQueryStructure(null, false); + $sViewName = MetaModel::DBGetView($sClass); + if (strlen($sViewName) > 64) + { + throw new Exception("Class name too long for class: '$sClass'. The name of the corresponding view ($sViewName) would exceed MySQL's limit for the name of a table (64 characters)."); + } + $sTableName = MetaModel::DBGetTable($sClass); + if (strlen($sTableName) > 64) + { + throw new Exception("Table name too long for class: '$sClass'. The name of the corresponding MySQL table ($sTableName) would exceed MySQL's limit for the name of a table (64 characters)."); + } + $iTableCount = $oSQLQuery->CountTables(); + if ($iTableCount > 61) + { + throw new Exception("Class requiring too many tables: '$sClass'. The structure of the class ($sClass) would require a query with more than 61 JOINS (MySQL's limitation)."); + } + $iCount++; + } + $fDuration = microtime(true) - $fStart; + + return sprintf("Checked %d classes in %.1f ms. No error found.\n", $iCount, $fDuration*1000.0); + } } // End of class diff --git a/setup/unattended-install/InstallationFileService.php b/setup/unattended-install/InstallationFileService.php index e44a8d0460..7cf0dfce40 100644 --- a/setup/unattended-install/InstallationFileService.php +++ b/setup/unattended-install/InstallationFileService.php @@ -1,5 +1,7 @@ GetAutoSelectModules() as $sModuleId => $aModule) { try { - $bSelected = false; SetupInfo::SetSelectedModules($this->aSelectedModules); - eval('$bSelected = ('.$aModule['auto_select'].');'); + $bSelected = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($aModule['auto_select']); if ($bSelected) { // Modules in data/production-modules/ are considered as mandatory and always installed $this->aSelectedModules[$sModuleId] = true; } } - catch (Exception $e) { + catch (ModuleFileReaderException $e) { + //logged already } } } diff --git a/setup/wizardsteps.class.inc.php b/setup/wizardsteps.class.inc.php index a6bbd1012f..b99111d465 100644 --- a/setup/wizardsteps.class.inc.php +++ b/setup/wizardsteps.class.inc.php @@ -40,6 +40,7 @@ */ use Combodo\iTop\Application\WebPage\WebPage; +use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator; require_once(APPROOT.'setup/setuputils.class.inc.php'); require_once(APPROOT.'setup/parameters.class.inc.php'); @@ -90,7 +91,7 @@ public function Display(WebPage $oPage) $oPage->add(""); $oPage->add_ready_script( -<<