From 2563672f11f1664aec77a45890db14beae08f963 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 28 May 2021 16:40:30 +0200 Subject: [PATCH 1/3] css: Initialize `@iplWebAssets` variable --- public/css/icinga/main.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/css/icinga/main.less b/public/css/icinga/main.less index 005d4bb4f..c4a61bfae 100644 --- a/public/css/icinga/main.less +++ b/public/css/icinga/main.less @@ -1,5 +1,8 @@ /*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ +// Url for static ipl assets +@iplWebAssets: "../lib/icinga/icinga-php-library"; + // Width for the name column--th--of name-value-table @name-value-table-name-width: 38/3em; From cb102399d57c0779de9134a071e52b94759ca793 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 2 Jun 2021 14:53:53 +0200 Subject: [PATCH 2/3] doc: Mention ipl and icinga 3rdparty as requirements --- doc/02-Installation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/02-Installation.md b/doc/02-Installation.md index c4938b34b..b2639c7c4 100644 --- a/doc/02-Installation.md +++ b/doc/02-Installation.md @@ -15,6 +15,8 @@ chapter. * [Icinga 2](https://icinga.com/products/icinga-2/) with the IDO database backend (MySQL or PostgreSQL) * A web server, e.g. Apache or Nginx * PHP version >= 5.6.0 +* [Icinga PHP Library (ipl)](https://github.com/Icinga/icinga-php-library) (>= 0.6) +* [Icinga PHP Thirdparty](https://github.com/Icinga/icinga-php-thirdparty) (>= 0.10) * The following PHP modules must be installed: cURL, gettext, intl, mbstring, OpenSSL and xml * LDAP PHP library when using Active Directory or LDAP for authentication * MySQL or PostgreSQL PHP libraries From 0d6da2d8592cd047eb75d24188c281bce2965440 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 7 Jun 2021 09:40:40 +0200 Subject: [PATCH 3/3] error/error: Check web2's deps and enhance lib checks --- application/controllers/ErrorController.php | 8 +- application/views/scripts/error/error.phtml | 120 ++++++++++++------ .../Icinga/Application/Libraries/Library.php | 16 +++ 3 files changed, 105 insertions(+), 39 deletions(-) diff --git a/application/controllers/ErrorController.php b/application/controllers/ErrorController.php index df422d716..580015b25 100644 --- a/application/controllers/ErrorController.php +++ b/application/controllers/ErrorController.php @@ -21,7 +21,8 @@ class ErrorController extends ActionController /** * Regular expression to match exceptions resulting from missing functions/classes */ - const MISSING_DEP_ERROR = "/Uncaught Error:.*(?:undefined function (\S+)|Class '([^']+)' not found).* in ([^:]+)/"; + const MISSING_DEP_ERROR = + "/Uncaught Error:.*(?:undefined function (\S+)|Class ['\"]([^']+)['\"] not found).* in ([^:]+)/"; /** * {@inheritdoc} @@ -108,9 +109,10 @@ class ErrorController extends ActionController } } - if (preg_match('/^(?:Icinga\\\Module\\\(\w+)|(\w+)\\\)/', $match[1] ?: $match[2], $natch)) { + if (preg_match('/^(?:Icinga\\\Module\\\(\w+)|(\w+)\\\(\w+))/', $match[1] ?: $match[2], $natch)) { $this->view->requiredModule = isset($natch[1]) ? strtolower($natch[1]) : null; - $this->view->requiredLibrary = isset($natch[2]) ? $natch[2] : null; + $this->view->requiredVendor = isset($natch[2]) ? $natch[2] : null; + $this->view->requiredProject = isset($natch[3]) ? $natch[3] : null; } } diff --git a/application/views/scripts/error/error.phtml b/application/views/scripts/error/error.phtml index 51ad9143f..ecb1c2b48 100644 --- a/application/views/scripts/error/error.phtml +++ b/application/views/scripts/error/error.phtml @@ -16,42 +16,90 @@ if (isset($stackTraces)) { echo '

' . nl2br($this->escape($message)) . '

'; } } + +$libraries = \Icinga\Application\Icinga::app()->getLibraries(); +$coreReason = []; +$modReason = []; + +if (isset($requiredVendor, $requiredProject) && $requiredVendor && $requiredProject) { + // TODO: I don't like this, can we define requirements somewhere else? + $coreDeps = ['icinga-php-library' => '>= 0.6', 'icinga-php-thirdparty' => '>= 0.10']; + + foreach ($coreDeps as $libraryName => $requiredVersion) { + if (! $libraries->has($libraryName)) { + $coreReason[] = sprintf($this->translate( + 'Library "%s" is required and missing. Please install a version of it matching the required one: %s' + ), $libraryName, $requiredVersion); + } elseif (! $libraries->has($libraryName, $requiredVersion) && $libraries->get($libraryName)->isRequired($requiredVendor, $requiredProject)) { + $coreReason[] = sprintf($this->translate( + 'Library "%s" is required and installed, but its version (%s) does not satisfy the required one: %s' + ), $libraryName, $libraries->get($libraryName)->getVersion() ?: '-', $requiredVersion); + } + } + + if (! empty($coreReason)) { + array_unshift($coreReason, $this->translate('You have unmet dependencies. Please check Icinga Web 2\'s installation instructions.')); + } +} + +if (isset($module)) { + $manager = \Icinga\Application\Icinga::app()->getModuleManager(); + if ($manager->hasUnmetDependencies($module->getName())) { + if (isset($requiredModule) && $requiredModule && isset($module->getRequiredModules()[$requiredModule])) { + if (! $manager->hasInstalled($requiredModule)) { + $modReason[] = sprintf($this->translate( + 'Module "%s" is required and missing. Please install a version of it matching the required one: %s' + ), $requiredModule, $module->getRequiredModules()[$requiredModule]); + } elseif (! $manager->hasEnabled($requiredModule)) { + $modReason[] = sprintf($this->translate( + 'Module "%s" is required and installed, but not enabled. Please enable module "%1$s".' + ), $requiredModule); + } elseif (! $manager->has($requiredModule, $module->getRequiredModules()[$requiredModule])) { + $modReason[] = sprintf($this->translate( + 'Module "%s" is required and installed, but its version (%s) does not satisfy the required one: %s' + ), $requiredModule, $manager->getModule($requiredModule, false)->getVersion(), $module->getRequiredModules()[$requiredModule]); + } + } elseif (isset($requiredVendor, $requiredProject) && $requiredVendor && $requiredProject) { + foreach ($module->getRequiredLibraries() as $libraryName => $requiredVersion) { + if (! $libraries->has($libraryName)) { + $modReason[] = sprintf($this->translate( + 'Library "%s" is required and missing. Please install a version of it matching the required one: %s' + ), $libraryName, $requiredVersion); + } elseif (! $libraries->has($libraryName, $requiredVersion) && $libraries->get($libraryName)->isRequired($requiredVendor, $requiredProject)) { + $modReason[] = sprintf($this->translate( + 'Library "%s" is required and installed, but its version (%s) does not satisfy the required one: %s' + ), $libraryName, $libraries->get($libraryName)->getVersion() ?: '-', $requiredVersion); + } + } + } + + if (! empty($modReason)) { + array_unshift($modReason, sprintf($this->translate( + 'This error might have occurred because module "%s" has unmet dependencies.' + . ' Please check it\'s installation instructions and install missing dependencies.' + ), $module->getName())); + } + } +} + +// The following doesn't use ipl\Html because that's what the error possibly is about ?> - - getModuleManager(); ?> - hasUnmetDependencies($module->getName())): ?> -
-

translate( - 'This error might have occurred because module "%s" has unmet dependencies.' - . ' Please check it\'s installation instructions and install missing dependencies.' - ), $module->getName()) ?>

- getRequiredModules()[$requiredModule])): ?> - hasInstalled($requiredModule)): ?> -

translate( - 'Module "%s" is required and missing. Please install a version of it matching the required one: %s' - ), $requiredModule, $module->getRequiredModules()[$requiredModule]) ?>

- hasEnabled($requiredModule)): ?> -

translate( - 'Module "%s" is required and installed, but not enabled. Please enable module "%1$s".' - ), $requiredModule) ?>

- has($requiredModule, $module->getRequiredModules()[$requiredModule])): ?> -

translate( - 'Module "%s" is required and installed, but its version (%s) does not satisfy the required one: %s' - ), $requiredModule, $manager->getModule($requiredModule, false)->getVersion(), $module->getRequiredModules()[$requiredModule]) ?>

- - getRequiredLibraries()[$requiredLibrary])): ?> - getLibraries(); ?> - has($requiredLibrary)): ?> -

translate( - 'Library "%s" is required and missing. Please install a version of it matching the required one: %s' - ), $requiredLibrary, $module->getRequiredLibraries()[$requiredLibrary]) ?>

- has($requiredLibrary, $module->getRequiredLibraries()[$requiredLibrary])): ?> -

translate( - 'Library "%s" is required and installed, but its version (%s) does not satisfy the required one: %s' - ), $requiredLibrary, $libraries->get($requiredLibrary)->getVersion(), $module->getRequiredLibraries()[$requiredLibrary]) ?>

- - -
- + +
+ +

+ +
+ + + +
+ + +

+ + +
+ diff --git a/library/Icinga/Application/Libraries/Library.php b/library/Icinga/Application/Libraries/Library.php index b2e359b57..ece5efaf5 100644 --- a/library/Icinga/Application/Libraries/Library.php +++ b/library/Icinga/Application/Libraries/Library.php @@ -119,6 +119,22 @@ class Library return $this->version; } + /** + * Check whether the given package is required + * + * @param string $vendor The vendor of the project + * @param string $project The project's name + * + * @return bool + */ + public function isRequired($vendor, $project) + { + // Ensure the parts are lowercase and separated by dashes, not capital letters + $project = strtolower(join('-', preg_split('/\w(?=[A-Z])/', $project))); + + return isset($this->metaData()['require'][strtolower($vendor) . '/' . $project]); + } + /** * Get this library's JS assets *