diff --git a/components/ILIAS/Init/classes/ErrorHandling/Http/ErrorPageResponder.php b/components/ILIAS/Init/classes/ErrorHandling/Http/ErrorPageResponder.php new file mode 100644 index 000000000000..e3e6c12a9f3a --- /dev/null +++ b/components/ILIAS/Init/classes/ErrorHandling/Http/ErrorPageResponder.php @@ -0,0 +1,120 @@ +globalScreen()->tool()->context()->claim()->external(); + + $dic->language()->loadLanguageModule('error'); + + $local_tpl = new ilGlobalTemplate('tpl.error.html', true, true); + + if ($error_lang_key !== null) { + $local_tpl->setCurrentBlock('msg_box'); + $local_tpl->setVariable( + 'MESSAGE_BOX', + $dic->ui()->renderer()->render( + $dic->ui()->factory()->messageBox()->failure( + $dic->language()->txt($error_lang_key) + ) + ) + ); + $local_tpl->parseCurrentBlock(); + } + + if ($link_url !== null && $link_lang_key !== null) { + $local_tpl->setCurrentBlock('ErrorLink'); + $local_tpl->setVariable('TXT_LINK', $dic->language()->txt($link_lang_key)); + $local_tpl->setVariable('LINK', ilUtil::secureUrl($link_url)); + $local_tpl->parseCurrentBlock(); + } + + $dic->http()->saveResponse( + $dic->http() + ->response() + ->withStatus($http_status_code) + ->withHeader(ResponseHeader::CONTENT_TYPE, 'text/html') + ); + + $dic->ui()->mainTemplate()->setContent($local_tpl->get()); + $dic->ui()->mainTemplate()->printToStdout(); + + $dic->http()->close(); + } + + public function sendFallbackResponse(Throwable $e): never + { + if (defined('DEVMODE') && DEVMODE) { + throw $e; + } + + if (!headers_sent()) { + http_response_code(StatusCode::HTTP_INTERNAL_SERVER_ERROR); + header('Content-Type: text/plain; charset=UTF-8'); + } + + $incident_id = session_id() . '_' . (new \Random\Randomizer())->getInt(1, 9999); + $timestamp = (new DateTimeImmutable()) + ->setTimezone(new DateTimeZone('UTC')) + ->format('Y-m-d\TH:i:s\Z'); + + echo "Internal Server Error\n"; + echo "Incident: $incident_id\n"; + echo "Timestamp: $timestamp\n"; + + if ($e instanceof PDOException) { + echo "Message: A database error occurred. Please contact the system administrator with the incident id.\n"; + } else { + echo "Message: {$e->getMessage()}\n"; + } + + error_log(sprintf( + "[%s] INCIDENT %s — Uncaught %s: %s in %s:%d\nStack trace:\n%s\n", + $timestamp, + $incident_id, + get_class($e), + $e->getMessage(), + $e->getFile(), + $e->getLine(), + $e->getTraceAsString() + )); + + exit(1); + } +} diff --git a/components/ILIAS/Init/resources/error.php b/components/ILIAS/Init/resources/error.php index 2f537c794b78..6aa4300d89bb 100644 --- a/components/ILIAS/Init/resources/error.php +++ b/components/ILIAS/Init/resources/error.php @@ -20,73 +20,27 @@ namespace ILIAS\Init; +use ilSession; +use Throwable; +use ILIAS\HTTP\StatusCode; +use ILIAS\Init\ErrorHandling\Http\ErrorPageResponder; + try { require_once '../vendor/composer/vendor/autoload.php'; require_once __DIR__ . '/../artifacts/bootstrap_default.php'; entry_point('ILIAS Legacy Initialisation Adapter'); - $DIC->globalScreen()->tool()->context()->claim()->external(); - - $lng->loadLanguageModule('error'); - $txt = $lng->txt('error_back_to_repository'); - - $local_tpl = new \ilGlobalTemplate('tpl.error.html', true, true); - $local_tpl->setCurrentBlock('ErrorLink'); - $local_tpl->setVariable('TXT_LINK', $txt); - $local_tpl->setVariable('LINK', \ilUtil::secureUrl(ILIAS_HTTP_PATH . '/ilias.php?baseClass=ilRepositoryGUI')); - $local_tpl->parseCurrentBlock(); - \ilSession::clear('referer'); \ilSession::clear('message'); - $DIC->http()->saveResponse( - $DIC->http() - ->response() - ->withStatus(500) - ->withHeader(\ILIAS\HTTP\Response\ResponseHeader::CONTENT_TYPE, 'text/html') + (new ErrorPageResponder())->handleError( + $DIC, + StatusCode::HTTP_INTERNAL_SERVER_ERROR, + null, + ILIAS_HTTP_PATH . '/ilias.php?baseClass=ilRepositoryGUI', + 'error_back_to_repository' ); - - $tpl->setContent($local_tpl->get()); - $tpl->printToStdout(); - - $DIC->http()->close(); -} catch (\Throwable $e) { - if (\defined('DEVMODE') && DEVMODE) { - throw $e; - } - - /* - * Since we are already in the `error.php` and an unexpected error occurred, we should not rely on the $DIC or any - * other components here and use "Vanilla PHP" instead to handle the error. - */ - if (!headers_sent()) { - http_response_code(500); - header('Content-Type: text/plain; charset=UTF-8'); - } - - $incident_id = session_id() . '_' . (new \Random\Randomizer())->getInt(1, 9999); - $timestamp = (new \DateTimeImmutable())->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d\TH:i:s\Z'); - - echo "Internal Server Error\n"; - echo "Incident: $incident_id\n"; - echo "Timestamp: $timestamp\n"; - if ($e instanceof \PDOException) { - echo "Message: A database error occurred. Please contact the system administrator with the incident id.\n"; - } else { - echo "Message: {$e->getMessage()}\n"; - } - - error_log(\sprintf( - "[%s] INCIDENT %s — Uncaught %s: %s in %s:%d\nStack trace:\n%s\n", - $timestamp, - $incident_id, - \get_class($e), - $e->getMessage(), - $e->getFile(), - $e->getLine(), - $e->getTraceAsString() - )); - - exit(1); +} catch (Throwable $e) { + (new ErrorPageResponder())->sendFallbackResponse($e); } diff --git a/components/ILIAS/Init/resources/ilias.php b/components/ILIAS/Init/resources/ilias.php index b331a6b6e2c1..be92ccbe69ef 100644 --- a/components/ILIAS/Init/resources/ilias.php +++ b/components/ILIAS/Init/resources/ilias.php @@ -18,6 +18,9 @@ declare(strict_types=1); +use ILIAS\HTTP\StatusCode; +use ILIAS\Init\ErrorHandling\Http\ErrorPageResponder; + if (!file_exists('../ilias.ini.php')) { die('The ILIAS setup is not completed. Please run the setup routine.'); } @@ -36,14 +39,16 @@ throw $e; } - if (!str_contains($e->getMessage(), 'not given a baseclass') && - !str_contains($e->getMessage(), 'not a baseclass')) { - throw new RuntimeException(sprintf('ilCtrl could not dispatch request: %s', $e->getMessage()), 0, $e); - } - $DIC->logger()->root()->error($e->getMessage()); $DIC->logger()->root()->error($e->getTraceAsString()); - $DIC->ctrl()->redirectToURL(ilUtil::_getHttpPath()); + + (new ErrorPageResponder())->handleError( + $DIC, + StatusCode::HTTP_NOT_FOUND, + 'http_404_not_found', + ILIAS_HTTP_PATH . '/ilias.php?baseClass=ilRepositoryGUI', + 'error_back_to_repository' + ); } $DIC['ilBench']->save(); diff --git a/components/ILIAS/UICore/classes/Path/class.ilCtrlExistingPath.php b/components/ILIAS/UICore/classes/Path/class.ilCtrlExistingPath.php index c777bdb1ed4f..f9566e9c18b0 100755 --- a/components/ILIAS/UICore/classes/Path/class.ilCtrlExistingPath.php +++ b/components/ILIAS/UICore/classes/Path/class.ilCtrlExistingPath.php @@ -18,6 +18,8 @@ declare(strict_types=1); +use ILIAS\UICore\Exceptions\ilCtrlPathException; + /** * Class ilCtrlExistingPath * @@ -56,7 +58,7 @@ protected function ensureValidCidPath(): void $child_class = $this->structure->getClassNameByCid($child_cid); $allowed_children = $this->structure->getChildrenByCid($parent_cid) ?? []; if (null === $child_class || !in_array($child_class, $allowed_children, true)) { - throw new RuntimeException('ilCtrl: invalid ' . ilCtrlInterface::PARAM_CID_PATH . ' parameter requested.'); + throw new ilCtrlPathException('ilCtrl: invalid ' . ilCtrlInterface::PARAM_CID_PATH . ' parameter requested.'); } } } diff --git a/components/ILIAS/UICore/exceptions/ilCtrlPathException.php b/components/ILIAS/UICore/exceptions/ilCtrlPathException.php new file mode 100644 index 000000000000..10a5c4d60259 --- /dev/null +++ b/components/ILIAS/UICore/exceptions/ilCtrlPathException.php @@ -0,0 +1,31 @@ + +{MESSAGE_BOX} +