Add themed error pages

- Module init, post and rawContent-triggered HTTPException generate the classic bare HTTP status page
- Module content-triggered HTTPException generate themed error pages
- Trim System::httpExit to the bare minimum
This commit is contained in:
Hypolite Petovan 2019-05-01 21:33:33 -04:00
parent 8eba329111
commit 358baa9f62
5 changed files with 262 additions and 189 deletions

View file

@ -14,7 +14,7 @@ use Friendica\Core\Hook;
use Friendica\Core\Theme; use Friendica\Core\Theme;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Network\HTTPException;
use Friendica\Util\BaseURL; use Friendica\Util\BaseURL;
use Friendica\Util\Config\ConfigFileLoader; use Friendica\Util\Config\ConfigFileLoader;
use Friendica\Util\HTTPSignature; use Friendica\Util\HTTPSignature;
@ -572,7 +572,7 @@ class App
* @param string $origURL * @param string $origURL
* *
* @return string The cleaned url * @return string The cleaned url
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function removeBaseURL($origURL) public function removeBaseURL($origURL)
{ {
@ -593,7 +593,7 @@ class App
* Returns the current UserAgent as a String * Returns the current UserAgent as a String
* *
* @return string the UserAgent as a String * @return string the UserAgent as a String
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function getUserAgent() public function getUserAgent()
{ {
@ -723,7 +723,7 @@ class App
* @brief Checks if the minimal memory is reached * @brief Checks if the minimal memory is reached
* *
* @return bool Is the memory limit reached? * @return bool Is the memory limit reached?
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function isMinMemoryReached() public function isMinMemoryReached()
{ {
@ -768,7 +768,7 @@ class App
* @brief Checks if the maximum load is reached * @brief Checks if the maximum load is reached
* *
* @return bool Is the load reached? * @return bool Is the load reached?
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function isMaxLoadReached() public function isMaxLoadReached()
{ {
@ -801,7 +801,7 @@ class App
* *
* @param string $command The command to execute * @param string $command The command to execute
* @param array $args Arguments to pass to the command ( [ 'key' => value, 'key2' => value2, ... ] * @param array $args Arguments to pass to the command ( [ 'key' => value, 'key2' => value2, ... ]
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function proc_run($command, $args) public function proc_run($command, $args)
{ {
@ -842,7 +842,7 @@ class App
* Generates the site's default sender email address * Generates the site's default sender email address
* *
* @return string * @return string
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function getSenderEmailAddress() public function getSenderEmailAddress()
{ {
@ -863,7 +863,7 @@ class App
* Returns the current theme name. * Returns the current theme name.
* *
* @return string the name of the current theme * @return string the name of the current theme
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function getCurrentTheme() public function getCurrentTheme()
{ {
@ -944,7 +944,7 @@ class App
* Provide a sane default if nothing is chosen or the specified theme does not exist. * Provide a sane default if nothing is chosen or the specified theme does not exist.
* *
* @return string * @return string
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function getCurrentThemeStylesheetPath() public function getCurrentThemeStylesheetPath()
{ {
@ -1010,7 +1010,10 @@ class App
{ {
// Missing DB connection: ERROR // Missing DB connection: ERROR
if ($this->getMode()->has(App\Mode::LOCALCONFIGPRESENT) && !$this->getMode()->has(App\Mode::DBAVAILABLE)) { if ($this->getMode()->has(App\Mode::LOCALCONFIGPRESENT) && !$this->getMode()->has(App\Mode::DBAVAILABLE)) {
Core\System::httpExit(500, ['title' => 'Error 500 - Internal Server Error', 'description' => 'Apologies but the website is unavailable at the moment.']); echo Module\Special\HTTPException::rawContent(
new HTTPException\InternalServerErrorException('Apologies but the website is unavailable at the moment.')
);
exit;
} }
// Max Load Average reached: ERROR // Max Load Average reached: ERROR
@ -1018,11 +1021,17 @@ class App
header('Retry-After: 120'); header('Retry-After: 120');
header('Refresh: 120; url=' . $this->getBaseURL() . "/" . $this->query_string); header('Refresh: 120; url=' . $this->getBaseURL() . "/" . $this->query_string);
Core\System::httpExit(503, ['title' => 'Error 503 - Service Temporarily Unavailable', 'description' => 'Core\System is currently overloaded. Please try again later.']); echo Module\Special\HTTPException::rawContent(
new HTTPException\ServiceUnavaiableException('The node is currently overloaded. Please try again later.')
);
exit;
} }
if (strstr($this->query_string, '.well-known/host-meta') && ($this->query_string != '.well-known/host-meta')) { if (strstr($this->query_string, '.well-known/host-meta') && ($this->query_string != '.well-known/host-meta')) {
Core\System::httpExit(404); echo Module\Special\HTTPException::rawContent(
new HTTPException\NotFoundException()
);
exit;
} }
if (!$this->getMode()->isInstall()) { if (!$this->getMode()->isInstall()) {
@ -1073,7 +1082,10 @@ class App
// Someone came with an invalid parameter, maybe as a DDoS attempt // Someone came with an invalid parameter, maybe as a DDoS attempt
// We simply stop processing here // We simply stop processing here
Core\Logger::log("Invalid ZRL parameter " . $_GET['zrl'], Core\Logger::DEBUG); Core\Logger::log("Invalid ZRL parameter " . $_GET['zrl'], Core\Logger::DEBUG);
Core\System::httpExit(403, ['title' => '403 Forbidden']); echo Module\Special\HTTPException::rawContent(
new HTTPException\ForbiddenException()
);
exit;
} }
} }
} }
@ -1126,123 +1138,115 @@ class App
'title' => '' 'title' => ''
]; ];
if (strlen($this->module)) { // Compatibility with the Android Diaspora client
// Compatibility with the Android Diaspora client if ($this->module == 'stream') {
if ($this->module == 'stream') { $this->internalRedirect('network?f=&order=post');
$this->internalRedirect('network?f=&order=post'); }
}
if ($this->module == 'conversations') { if ($this->module == 'conversations') {
$this->internalRedirect('message'); $this->internalRedirect('message');
} }
if ($this->module == 'commented') { if ($this->module == 'commented') {
$this->internalRedirect('network?f=&order=comment'); $this->internalRedirect('network?f=&order=comment');
} }
if ($this->module == 'liked') { if ($this->module == 'liked') {
$this->internalRedirect('network?f=&order=comment'); $this->internalRedirect('network?f=&order=comment');
} }
if ($this->module == 'activity') { if ($this->module == 'activity') {
$this->internalRedirect('network/?f=&conv=1'); $this->internalRedirect('network/?f=&conv=1');
} }
if (($this->module == 'status_messages') && ($this->cmd == 'status_messages/new')) { if (($this->module == 'status_messages') && ($this->cmd == 'status_messages/new')) {
$this->internalRedirect('bookmarklet'); $this->internalRedirect('bookmarklet');
} }
if (($this->module == 'user') && ($this->cmd == 'user/edit')) { if (($this->module == 'user') && ($this->cmd == 'user/edit')) {
$this->internalRedirect('settings'); $this->internalRedirect('settings');
} }
if (($this->module == 'tag_followings') && ($this->cmd == 'tag_followings/manage')) { if (($this->module == 'tag_followings') && ($this->cmd == 'tag_followings/manage')) {
$this->internalRedirect('search'); $this->internalRedirect('search');
} }
// Compatibility with the Firefox App // Compatibility with the Firefox App
if (($this->module == "users") && ($this->cmd == "users/sign_in")) { if (($this->module == "users") && ($this->cmd == "users/sign_in")) {
$this->module = "login"; $this->module = "login";
} }
/* /*
* ROUTING * ROUTING
* *
* From the request URL, routing consists of obtaining the name of a BaseModule-extending class of which the * From the request URL, routing consists of obtaining the name of a BaseModule-extending class of which the
* post() and/or content() static methods can be respectively called to produce a data change or an output. * post() and/or content() static methods can be respectively called to produce a data change or an output.
*/ */
// First we try explicit routes defined in App\Router // First we try explicit routes defined in App\Router
$this->router->collectRoutes(); $this->router->collectRoutes();
$data = $this->router->getRouteCollector(); $data = $this->router->getRouteCollector();
Hook::callAll('route_collection', $data); Hook::callAll('route_collection', $data);
$this->module_class = $this->router->getModuleClass($this->cmd); $this->module_class = $this->router->getModuleClass($this->cmd);
// Then we try addon-provided modules that we wrap in the LegacyModule class // Then we try addon-provided modules that we wrap in the LegacyModule class
if (!$this->module_class && Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { if (!$this->module_class && Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) {
//Check if module is an app and if public access to apps is allowed or not //Check if module is an app and if public access to apps is allowed or not
$privateapps = $this->config->get('config', 'private_addons', false); $privateapps = $this->config->get('config', 'private_addons', false);
if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) {
info(Core\L10n::t("You must be logged in to use addons. ")); info(Core\L10n::t("You must be logged in to use addons. "));
} else { } else {
include_once "addon/{$this->module}/{$this->module}.php"; include_once "addon/{$this->module}/{$this->module}.php";
if (function_exists($this->module . '_module')) { if (function_exists($this->module . '_module')) {
LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php"); LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php");
$this->module_class = 'Friendica\\LegacyModule'; $this->module_class = LegacyModule::class;
}
} }
} }
// Then we try name-matching a Friendica\Module class
if (!$this->module_class && class_exists('Friendica\\Module\\' . ucfirst($this->module))) {
$this->module_class = 'Friendica\\Module\\' . ucfirst($this->module);
}
/* Finally, we look for a 'standard' program module in the 'mod' directory
* We emulate a Module class through the LegacyModule class
*/
if (!$this->module_class && file_exists("mod/{$this->module}.php")) {
LegacyModule::setModuleFile("mod/{$this->module}.php");
$this->module_class = 'Friendica\\LegacyModule';
}
/* The URL provided does not resolve to a valid module.
*
* On Dreamhost sites, quite often things go wrong for no apparent reason and they send us to '/internal_error.html'.
* We don't like doing this, but as it occasionally accounts for 10-20% or more of all site traffic -
* we are going to trap this and redirect back to the requested page. As long as you don't have a critical error on your page
* this will often succeed and eventually do the right thing.
*
* Otherwise we are going to emit a 404 not found.
*/
if (!$this->module_class) {
// Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit.
if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) {
exit();
}
if (!empty($_SERVER['QUERY_STRING']) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
Core\Logger::log('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']);
$this->internalRedirect($_SERVER['REQUEST_URI']);
}
Core\Logger::log('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], Core\Logger::DEBUG);
header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . Core\L10n::t('Not Found'));
$tpl = Core\Renderer::getMarkupTemplate("404.tpl");
$this->page['content'] = Core\Renderer::replaceMacros($tpl, [
'$message' => Core\L10n::t('Page not found.')
]);
}
} }
$content = ''; // Then we try name-matching a Friendica\Module class
if (!$this->module_class && class_exists('Friendica\\Module\\' . ucfirst($this->module))) {
$this->module_class = 'Friendica\\Module\\' . ucfirst($this->module);
}
/* Finally, we look for a 'standard' program module in the 'mod' directory
* We emulate a Module class through the LegacyModule class
*/
if (!$this->module_class && file_exists("mod/{$this->module}.php")) {
LegacyModule::setModuleFile("mod/{$this->module}.php");
$this->module_class = LegacyModule::class;
}
/* The URL provided does not resolve to a valid module.
*
* On Dreamhost sites, quite often things go wrong for no apparent reason and they send us to '/internal_error.html'.
* We don't like doing this, but as it occasionally accounts for 10-20% or more of all site traffic -
* we are going to trap this and redirect back to the requested page. As long as you don't have a critical error on your page
* this will often succeed and eventually do the right thing.
*
* Otherwise we are going to emit a 404 not found.
*/
if (!$this->module_class) {
// Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit.
if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) {
exit();
}
if (!empty($_SERVER['QUERY_STRING']) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
Core\Logger::log('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']);
$this->internalRedirect($_SERVER['REQUEST_URI']);
}
Core\Logger::log('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], Core\Logger::DEBUG);
$this->module_class = Module\PageNotFound::class;
}
// Initialize module that can set the current theme in the init() method, either directly or via App->profile_uid // Initialize module that can set the current theme in the init() method, either directly or via App->profile_uid
if ($this->module_class) { $this->page['page_title'] = $this->module;
$this->page['page_title'] = $this->module; try {
$placeholder = ''; $placeholder = '';
Core\Hook::callAll($this->module . '_mod_init', $placeholder); Core\Hook::callAll($this->module . '_mod_init', $placeholder);
@ -1251,35 +1255,42 @@ class App
// "rawContent" is especially meant for technical endpoints. // "rawContent" is especially meant for technical endpoints.
// This endpoint doesn't need any theme initialization or other comparable stuff. // This endpoint doesn't need any theme initialization or other comparable stuff.
call_user_func([$this->module_class, 'rawContent']); call_user_func([$this->module_class, 'rawContent']);
}
// Load current theme info after module has been initialized as theme could have been set in module // Load current theme info after module has been initialized as theme could have been set in module
$theme_info_file = 'view/theme/' . $this->getCurrentTheme() . '/theme.php'; $theme_info_file = 'view/theme/' . $this->getCurrentTheme() . '/theme.php';
if (file_exists($theme_info_file)) { if (file_exists($theme_info_file)) {
require_once $theme_info_file; require_once $theme_info_file;
} }
if (function_exists(str_replace('-', '_', $this->getCurrentTheme()) . '_init')) { if (function_exists(str_replace('-', '_', $this->getCurrentTheme()) . '_init')) {
$func = str_replace('-', '_', $this->getCurrentTheme()) . '_init'; $func = str_replace('-', '_', $this->getCurrentTheme()) . '_init';
$func($this); $func($this);
} }
if ($this->module_class) {
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
Core\Hook::callAll($this->module . '_mod_post', $_POST); Core\Hook::callAll($this->module . '_mod_post', $_POST);
call_user_func([$this->module_class, 'post']); call_user_func([$this->module_class, 'post']);
} }
Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder); Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder);
call_user_func([$this->module_class, 'afterpost']); call_user_func([$this->module_class, 'afterpost']);
} catch(HTTPException $e) {
echo Module\Special\HTTPException::rawContent($e);
exit;
}
$arr = ['content' => $content]; $content = '';
Core\Hook::callAll($this->module . '_mod_content', $arr);
$content = $arr['content']; try {
$arr = ['content' => call_user_func([$this->module_class, 'content'])]; $arr = ['content' => $content];
Core\Hook::callAll($this->module . '_mod_aftercontent', $arr); Core\Hook::callAll($this->module . '_mod_content', $arr);
$content .= $arr['content']; $content = $arr['content'];
$arr = ['content' => call_user_func([$this->module_class, 'content'])];
Core\Hook::callAll($this->module . '_mod_aftercontent', $arr);
$content .= $arr['content'];
} catch(HTTPException $e) {
$content = Module\Special\HTTPException::content($e);
} }
// initialise content region // initialise content region
@ -1303,13 +1314,6 @@ class App
*/ */
$this->initFooter(); $this->initFooter();
/* now that we've been through the module content, see if the page reported
* a permission problem and if so, a 403 response would seem to be in order.
*/
if (stristr(implode("", $_SESSION['sysmsg']), Core\L10n::t('Permission denied'))) {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 ' . Core\L10n::t('Permission denied.'));
}
if (!$this->isAjax()) { if (!$this->isAjax()) {
Core\Hook::callAll('page_end', $this->page['content']); Core\Hook::callAll('page_end', $this->page['content']);
} }
@ -1401,12 +1405,12 @@ class App
* @param string $toUrl The destination URL (Default is empty, which is the default page of the Friendica node) * @param string $toUrl The destination URL (Default is empty, which is the default page of the Friendica node)
* @param bool $ssl if true, base URL will try to get called with https:// (works just for relative paths) * @param bool $ssl if true, base URL will try to get called with https:// (works just for relative paths)
* *
* @throws InternalServerErrorException In Case the given URL is not relative to the Friendica node * @throws HTTPException\InternalServerErrorException In Case the given URL is not relative to the Friendica node
*/ */
public function internalRedirect($toUrl = '', $ssl = false) public function internalRedirect($toUrl = '', $ssl = false)
{ {
if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) { if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) {
throw new InternalServerErrorException("'$toUrl is not a relative path, please use System::externalRedirectTo"); throw new HTTPException\InternalServerErrorException("'$toUrl is not a relative path, please use System::externalRedirectTo");
} }
$redirectTo = $this->getBaseURL($ssl) . '/' . ltrim($toUrl, '/'); $redirectTo = $this->getBaseURL($ssl) . '/' . ltrim($toUrl, '/');
@ -1418,7 +1422,7 @@ class App
* Should only be used if it isn't clear if the URL is either internal or external * Should only be used if it isn't clear if the URL is either internal or external
* *
* @param string $toUrl The target URL * @param string $toUrl The target URL
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function redirect($toUrl) public function redirect($toUrl)
{ {

View file

@ -120,58 +120,17 @@ class System extends BaseObject
/** /**
* @brief Send HTTP status header and exit. * @brief Send HTTP status header and exit.
* *
* @param integer $val HTTP status result value * @param integer $val HTTP status result value
* @param array $description optional message * @param string $message Error message. Optional.
* 'title' => header title * @param string $content Response body. Optional.
* 'description' => optional message * @throws \Exception
* @throws InternalServerErrorException
*/ */
public static function httpExit($val, $description = []) public static function httpExit($val, $message = '', $content = '')
{ {
$err = '';
if ($val >= 400) {
if (!empty($description['title'])) {
$err = $description['title'];
} else {
$title = [
'400' => L10n::t('Error 400 - Bad Request'),
'401' => L10n::t('Error 401 - Unauthorized'),
'403' => L10n::t('Error 403 - Forbidden'),
'404' => L10n::t('Error 404 - Not Found'),
'500' => L10n::t('Error 500 - Internal Server Error'),
'503' => L10n::t('Error 503 - Service Unavailable'),
];
$err = defaults($title, $val, 'Error ' . $val);
$description['title'] = $err;
}
if (empty($description['description'])) {
// Explanations are taken from https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
$explanation = [
'400' => L10n::t('The server cannot or will not process the request due to an apparent client error.'),
'401' => L10n::t('Authentication is required and has failed or has not yet been provided.'),
'403' => L10n::t('The request was valid, but the server is refusing action. The user might not have the necessary permissions for a resource, or may need an account.'),
'404' => L10n::t('The requested resource could not be found but may be available in the future.'),
'500' => L10n::t('An unexpected condition was encountered and no more specific message is suitable.'),
'503' => L10n::t('The server is currently unavailable (because it is overloaded or down for maintenance). Please try again later.'),
];
if (!empty($explanation[$val])) {
$description['description'] = $explanation[$val];
}
}
}
if ($val >= 200 && $val < 300) {
$err = 'OK';
}
Logger::log('http_status_exit ' . $val); Logger::log('http_status_exit ' . $val);
header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $err); header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $message);
if (isset($description["title"])) { echo $content;
$tpl = Renderer::getMarkupTemplate('http_status.tpl');
echo Renderer::replaceMacros($tpl, ['$title' => $description["title"],
'$description' => defaults($description, 'description', '')]);
}
exit(); exit();
} }

View file

@ -0,0 +1,15 @@
<?php
namespace Friendica\Module;
use Friendica\BaseModule;
use Friendica\Core\L10n;
use Friendica\Network\HTTPException;
class PageNotFound extends BaseModule
{
public static function content()
{
throw new HTTPException\NotFoundException(L10n::t('Page not found.'));
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Friendica\Module\Special;
use Friendica\Core\L10n;
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Core\System;
/**
* This special module displays HTTPException when they are thrown in modules.
*
* @package Friendica\Module\Special
*/
class HTTPException
{
/**
* Generates the necessary template variables from the caught HTTPException.
*
* Fills in the blanks if title or descriptions aren't provided by the exception.
*
* @param \Friendica\Network\HTTPException $e
* @return array ['$title' => ..., '$description' => ...]
*/
private static function getVars(\Friendica\Network\HTTPException $e)
{
$message = $e->getMessage();
$titles = [
200 => 'OK',
400 => L10n::t('Bad Request'),
401 => L10n::t('Unauthorized'),
403 => L10n::t('Forbidden'),
404 => L10n::t('Not Found'),
500 => L10n::t('Internal Server Error'),
503 => L10n::t('Service Unavailable'),
];
$title = defaults($titles, $e->getCode(), 'Error ' . $e->getCode());
if (empty($message)) {
// Explanations are taken from https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
$explanation = [
400 => L10n::t('The server cannot or will not process the request due to an apparent client error.'),
401 => L10n::t('Authentication is required and has failed or has not yet been provided.'),
403 => L10n::t('The request was valid, but the server is refusing action. The user might not have the necessary permissions for a resource, or may need an account.'),
404 => L10n::t('The requested resource could not be found but may be available in the future.'),
500 => L10n::t('An unexpected condition was encountered and no more specific message is suitable.'),
503 => L10n::t('The server is currently unavailable (because it is overloaded or down for maintenance). Please try again later.'),
];
$message = defaults($explanation, $e->getCode(), '');
}
return ['$title' => $title, '$description' => $message];
}
/**
* Displays a bare message page with no theming at all.
*
* @param \Friendica\Network\HTTPException $e
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function rawContent(\Friendica\Network\HTTPException $e)
{
$content = '';
if ($e->getCode() >= 400) {
$tpl = Renderer::getMarkupTemplate('http_status.tpl');
$content = Renderer::replaceMacros($tpl, self::getVars($e));
}
System::httpExit($e->getCode(), $e->httpdesc, $content);
}
/**
* Returns a content string that can be integrated in the current theme.
*
* @param \Friendica\Network\HTTPException $e
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function content(\Friendica\Network\HTTPException $e)
{
header($_SERVER["SERVER_PROTOCOL"] . ' ' . $e->getCode() . ' ' . $e->httpdesc);
$tpl = Renderer::getMarkupTemplate('exception.tpl');
return Renderer::replaceMacros($tpl, self::getVars($e));
}
}

View file

@ -0,0 +1,4 @@
<div id="exception" class="generic-page-wrapper">
<h1>{{$title}}</h1>
<p>{{$message}}</p>
</div>