error/error: Show a hint to check dependencies if there are unmet ones

This commit is contained in:
Johannes Meyer 2020-11-13 17:43:40 +01:00
parent 40c97d6a3e
commit 39e59422f4
3 changed files with 74 additions and 6 deletions

View File

@ -18,6 +18,11 @@ use Icinga\Web\Url;
*/
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 ([^:]+)/";
/**
* {@inheritdoc}
*/
@ -44,21 +49,23 @@ class ErrorController extends ActionController
$this->innerLayout = 'guest-error';
}
$modules = Icinga::app()->getModuleManager();
$sourcePath = ltrim($this->_request->get('PATH_INFO'), '/');
$pathParts = preg_split('~/~', $sourcePath);
$moduleName = array_shift($pathParts);
$module = null;
switch ($error->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
$modules = Icinga::app()->getModuleManager();
$path = ltrim($this->_request->get('PATH_INFO'), '/');
$path = preg_split('~/~', $path);
$path = array_shift($path);
$this->getResponse()->setHttpResponseCode(404);
$this->view->messages = array($this->translate('Page not found.'));
if ($isAuthenticated) {
if ($modules->hasInstalled($path) && ! $modules->hasEnabled($path)) {
if ($modules->hasInstalled($moduleName) && ! $modules->hasEnabled($moduleName)) {
$this->view->messages[0] .= ' ' . sprintf(
$this->translate('Enabling the "%s" module might help!'),
$path
$moduleName
);
}
}
@ -84,10 +91,29 @@ class ErrorController extends ActionController
break;
default:
$this->getResponse()->setHttpResponseCode(500);
$module = $modules->hasLoaded($moduleName) ? $modules->getModule($moduleName) : null;
Logger::error("%s\n%s", $exception, IcingaException::getConfidentialTraceAsString($exception));
break;
}
// Try to narrow down why the request has failed
if (preg_match(self::MISSING_DEP_ERROR, $exception->getMessage(), $match)) {
$sourcePath = $match[3];
foreach ($modules->listLoadedModules() as $name) {
$candidate = $modules->getModule($name);
$modulePath = $candidate->getBaseDir();
if (substr($sourcePath, 0, strlen($modulePath)) === $modulePath) {
$module = $candidate;
break;
}
}
if (preg_match('/^(?:Icinga\\\Module\\\(\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->messages = array();
if ($this->getInvokeArg('displayExceptions')) {
@ -114,6 +140,7 @@ class ErrorController extends ActionController
->sendResponse();
}
$this->view->module = $module;
$this->view->request = $error->request;
if (! $isAuthenticated) {
$this->view->hideControls = true;

View File

@ -17,4 +17,41 @@ if (isset($stackTraces)) {
}
}
?>
<?php if (isset($module)): ?>
<?php $manager = \Icinga\Application\Icinga::app()->getModuleManager(); ?>
<?php if ($manager->hasUnmetDependencies($module->getName())): ?>
<div class="error-reason">
<p><?= 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()) ?></p>
<?php if (isset($requiredModule) && $requiredModule && isset($module->getRequiredModules()[$requiredModule])): ?>
<?php if (! $manager->hasInstalled($requiredModule)): ?>
<p><?= sprintf($this->translate(
'Module "%s" is required and missing. Please install a version of it matching the required one: %s'
), $requiredModule, $module->getRequiredModules()[$requiredModule]) ?></p>
<?php elseif (! $manager->hasEnabled($requiredModule)): ?>
<p><?= sprintf($this->translate(
'Module "%s" is required and installed, but not enabled. Please enable module "%1$s".'
), $requiredModule) ?></p>
<?php elseif (! $manager->has($requiredModule, $module->getRequiredModules()[$requiredModule])): ?>
<p><?= 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]) ?></p>
<?php endif ?>
<?php elseif (isset($requiredLibrary) && $requiredLibrary && isset($module->getRequiredLibraries()[$requiredLibrary])): ?>
<?php $libraries = \Icinga\Application\Icinga::app()->getLibraries(); ?>
<?php if (! $libraries->has($requiredLibrary)): ?>
<p><?= sprintf($this->translate(
'Library "%s" is required and missing. Please install a version of it matching the required one: %s'
), $requiredLibrary, $module->getRequiredLibraries()[$requiredLibrary]) ?></p>
<?php elseif (! $libraries->has($requiredLibrary, $module->getRequiredLibraries()[$requiredLibrary])): ?>
<p><?= sprintf($this->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]) ?></p>
<?php endif ?>
<?php endif ?>
</div>
<?php endif ?>
<?php endif ?>
</div>

View File

@ -11,6 +11,10 @@
font-weight: @font-weight-bold;
}
.error-reason {
margin-top: 4em;
}
.large-icon {
font-size: 200%;
}