1
0
mirror of https://github.com/flarum/core.git synced 2025-08-13 11:54:32 +02:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Alexander Skvortsov
72f7c3cae9 Apply fixes from StyleCI
[ci skip] [skip ci]
2021-04-13 05:48:31 +00:00
Alexander Skvortsov
d7a5bf7546 Remove redundant class 2021-04-13 01:48:11 -04:00
Alexander Skvortsov
2c8cc0c92b Provide frontend string as param to ExecuteErrorToFrontend handler 2021-04-13 01:47:20 -04:00
Alexander Skvortsov
7cf0df88f3 Add content to error page, drop 404 page, move translations 2021-04-13 01:43:09 -04:00
Alexander Skvortsov
43d00fcb79 Update docblock 2021-04-13 01:22:36 -04:00
Alexander Skvortsov
897dd578b8 Fix URL changing to homepage on 404 2021-04-13 01:21:13 -04:00
Alexander Skvortsov
235dfebfd4 Re-implement via middleware pipe 2021-04-13 01:20:12 -04:00
Alexander Skvortsov
c8424689ef Simplify frontend error handling 2021-04-12 23:16:45 -04:00
Alexander Skvortsov
6b799028b5 Apply fixes from StyleCI
[ci skip] [skip ci]
2021-04-12 23:16:45 -04:00
Alexander Skvortsov
058f0c50d2 Use resolver instead of catch-all route for errors 2021-04-12 23:16:45 -04:00
Alexander Skvortsov
b5d2cbda9c Run frontend handler before route resolver 2021-04-12 23:16:45 -04:00
Alexander Skvortsov
827366fa9c More cleanup and fixes 2021-04-12 23:16:45 -04:00
Alexander Skvortsov
f5fbaf46a9 Cleanup 2021-04-12 23:16:45 -04:00
luceos
5eff7b010d Apply fixes from StyleCI
[ci skip] [skip ci]
2021-04-12 23:16:45 -04:00
Alexander Skvortsov
7035625899 Use content for error page 2021-04-12 23:16:45 -04:00
Alexander Skvortsov
bf20ac663d Add frontend component page for not found
Improve ErrorPage
2021-04-12 23:16:45 -04:00
Alexander Skvortsov
122f566e10 Fix missing imports for exceptions to process with frontend formatter 2021-04-12 23:16:45 -04:00
Alexander Skvortsov
194cf555db Add middleware in admin and forum to some errors via the frontend 2021-04-12 23:16:44 -04:00
Alexander Skvortsov
f879182ef7 Add a formatter for rendering errors via the frontend 2021-04-12 23:16:44 -04:00
Alexander Skvortsov
053b3fd96b Add errorClasses parameter to HandleErrors so that HandleErrors middleware can be used on only some exceptions if wanted 2021-04-12 23:16:44 -04:00
18 changed files with 292 additions and 23 deletions

View File

@@ -24,6 +24,7 @@ import { flattenDeep } from 'lodash-es';
import PageState from './states/PageState';
import ModalManagerState from './states/ModalManagerState';
import AlertManagerState from './states/AlertManagerState';
import ErrorPage from './components/ErrorPage';
/**
* The `App` class provides a container for an application, as well as various
@@ -199,6 +200,9 @@ export default class Application {
this.drawer = new Drawer();
// Needed so 404s don't get their URLs changed to the homepage.
// Won't affect extension routes since its added last.
this.routes.error = { path: '/:4xx...', component: ErrorPage };
m.route(document.getElementById('content'), basePath + '/', mapRoutes(this.routes, basePath));
// Add a class to the body which indicates that the page has been scrolled

View File

@@ -0,0 +1,40 @@
import Link from './Link';
import Page from './Page';
export default class ErrorPage extends Page {
oninit(vnode) {
super.oninit(vnode);
this.title = app.translator.trans(`core.forum.error.${this.attrs.errorCode}_title`);
}
oncreate(vnode) {
super.oncreate(vnode);
app.setTitle(this.title);
}
view() {
const links = {
404: '/',
};
const link = links[this.attrs.errorCode];
return (
<div className="ErrorPage">
<div className="container">
<h2>{this.title}</h2>
<p>{app.translator.trans(`core.forum.error.${this.attrs.errorCode}_text`)}</p>
<p>
{link && (
<Link href={link}>
{app.translator.trans(`core.forum.error.${this.attrs.errorCode}_link_text`, { forum: app.forum.attribute('title') })}
</Link>
)}
</p>
</div>
</div>
);
}
}

View File

@@ -1,4 +1,5 @@
import Mithril from 'mithril';
import ErrorPage from '../components/ErrorPage';
/**
* Generates a route resolver for a given component.
@@ -32,6 +33,12 @@ export default class DefaultResolver {
}
onmatch(args, requestedPath, route) {
const errorCode = app.data.errorCode;
if (errorCode) {
delete app.data.errorCode;
return { view: () => ErrorPage.component({ errorCode }) };
}
return this.component;
}

View File

@@ -303,6 +303,15 @@ core:
username_heading: => core.ref.username
username_label: => core.ref.username
error:
401_text: You do not have permission to access this page. Try again after logging in.
403_text: You do not have permission to access this page.
404_text: The page you requested could not be found.
401_title: Not Authenticated
403_title: Permission Denied
404_title: Not Found
404_link_text: "Return to {forum}"
# These translations are used in the Forgot Password modal dialog.
forgot_password:
dismiss_button: => core.ref.okay
@@ -517,10 +526,6 @@ core:
csrf_token_mismatch: You have been inactive for too long.
csrf_token_mismatch_return_link: Go back, to try again
invalid_confirmation_token: This confirmation link has already been used or is invalid.
not_authenticated: You do not have permission to access this page. Try again after logging in.
not_found: The page you requested could not be found.
not_found_return_link: "Return to {forum}"
permission_denied: You do not have permission to access this page.
unknown: An error occurred while trying to load this page.
# Translations in this namespace are displayed by the basic HTML discussion index.

View File

@@ -12,6 +12,7 @@ namespace Flarum\Admin;
use Flarum\Extension\Event\Disabled;
use Flarum\Extension\Event\Enabled;
use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Foundation\ErrorHandling\FrontendFormatter;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Flarum\Foundation\ErrorHandling\ViewFormatter;
@@ -66,6 +67,7 @@ class AdminServiceProvider extends AbstractServiceProvider
return new HttpMiddleware\HandleErrors(
$this->container->make(Registry::class),
$this->container['flarum.config']->inDebugMode() ? $this->container->make(WhoopsFormatter::class) : $this->container->make(ViewFormatter::class),
$this->container->make(FrontendFormatter::class),
$this->container->tagged(Reporter::class)
);
});

View File

@@ -73,9 +73,12 @@ class ApiServiceProvider extends AbstractServiceProvider
});
$this->container->bind('flarum.api.error_handler', function () {
$jsonFormatter = new JsonApiFormatter($this->container['flarum.config']->inDebugMode());
return new HttpMiddleware\HandleErrors(
$this->container->make(Registry::class),
new JsonApiFormatter($this->container['flarum.config']->inDebugMode()),
$jsonFormatter,
$jsonFormatter, // This won't be used.
$this->container->tagged(Reporter::class)
);
});

View File

@@ -13,6 +13,8 @@ use Flarum\Extension\Event\Disabled;
use Flarum\Extension\Event\Enabled;
use Flarum\Formatter\Formatter;
use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Foundation\ErrorHandling\FrontendFormatter;
use Flarum\Foundation\ErrorHandling\Middleware\ExecuteErrorToFrontend;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Flarum\Foundation\ErrorHandling\ViewFormatter;
@@ -32,6 +34,7 @@ use Flarum\Settings\Event\Saved;
use Flarum\Settings\Event\Saving;
use Flarum\Settings\SettingsRepositoryInterface;
use Laminas\Stratigility\MiddlewarePipe;
use Laminas\Stratigility\MiddlewarePipeInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ForumServiceProvider extends AbstractServiceProvider
@@ -77,6 +80,7 @@ class ForumServiceProvider extends AbstractServiceProvider
return new HttpMiddleware\HandleErrors(
$this->container->make(Registry::class),
$this->container['flarum.config']->inDebugMode() ? $this->container->make(WhoopsFormatter::class) : $this->container->make(ViewFormatter::class),
$this->container->make(FrontendFormatter::class),
$this->container->tagged(Reporter::class)
);
});
@@ -124,6 +128,30 @@ class ForumServiceProvider extends AbstractServiceProvider
$this->container->bind('flarum.frontend.forum', function () {
return $this->container->make('flarum.frontend.factory')('forum');
});
$this->container->when(FrontendFormatter::class)
->needs(MiddlewarePipeInterface::class)
->give(function () {
$middleware = $this->container->make('flarum.forum.middleware');
$pipe = new MiddlewarePipe;
foreach ($middleware as $middlewareClass) {
if (! in_array($middlewareClass, [
'flarum.forum.error_handler',
HttpMiddleware\InjectActorReference::class,
HttpMiddleware\RememberFromCookie::class,
HttpMiddleware\AuthenticateWithSession::class,
'flarum.forum.route_resolver',
])) {
$pipe->pipe($this->container->make($middlewareClass));
}
}
$pipe->pipe(new ExecuteErrorToFrontend('forum', $this->container->make(RouteHandlerFactory::class)));
return $pipe;
});
}
/**

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Foundation\ErrorHandling;
use Laminas\Stratigility\MiddlewarePipeInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* This formatter will route errors to the SPA frontend.
*/
class FrontendFormatter implements HttpFormatter
{
/**
* @var MiddlewarePipeInterface
*/
protected $pipe;
public function __construct(MiddlewarePipeInterface $pipe)
{
$this->pipe = $pipe;
}
public function format(HandledError $error, Request $request): Response
{
return $this->pipe->handle($request->withAttribute('error', $error));
}
}

View File

@@ -23,6 +23,7 @@ class HandledError
private $error;
private $type;
private $statusCode;
private $frontendContentClass;
private $details = [];
@@ -31,11 +32,12 @@ class HandledError
return new static($error, 'unknown', 500);
}
public function __construct(Throwable $error, $type, $statusCode)
public function __construct(Throwable $error, $type, $statusCode, $frontendContentClass = null)
{
$this->error = $error;
$this->type = $type;
$this->statusCode = $statusCode;
$this->frontendContentClass = $frontendContentClass;
}
public function withDetails(array $details): self
@@ -65,6 +67,11 @@ class HandledError
return $this->type === 'unknown';
}
public function contentClass()
{
return $this->frontendContentClass;
}
public function getDetails(): array
{
return $this->details;

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Foundation\ErrorHandling\Middleware;
use Flarum\Http\RouteHandlerFactory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class ExecuteErrorToFrontend implements MiddlewareInterface
{
/**
* @var string
*/
protected $frontend;
/**
* @var RouteHandlerFactory
*/
protected $handlerFactory;
public function __construct(string $frontend, RouteHandlerFactory $handlerFactory)
{
$this->frontend = $frontend;
$this->handlerFactory = $handlerFactory;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$error = $request->getAttribute('error');
$contentClass = $error->contentClass();
$controller = $this->handlerFactory->toFrontend($this->frontend, new $contentClass);
return $controller($request, [])->withStatus($error->getStatusCode());
}
}

View File

@@ -10,6 +10,7 @@
namespace Flarum\Foundation\ErrorHandling;
use Flarum\Foundation\KnownError;
use Illuminate\Support\Arr;
use Throwable;
/**
@@ -24,12 +25,14 @@ class Registry
private $statusMap;
private $classMap;
private $handlerMap;
private $contentMap;
public function __construct(array $statusMap, array $classMap, array $handlerMap)
public function __construct(array $statusMap, array $classMap, array $handlerMap, array $contentMap)
{
$this->statusMap = $statusMap;
$this->classMap = $classMap;
$this->handlerMap = $handlerMap;
$this->contentMap = $contentMap;
}
/**
@@ -55,20 +58,23 @@ class Registry
{
$errorType = null;
$errorClass = get_class($error);
if ($error instanceof KnownError) {
$errorType = $error->getType();
} else {
$errorClass = get_class($error);
if (isset($this->classMap[$errorClass])) {
$errorType = $this->classMap[$errorClass];
}
}
$errorContent = Arr::get($this->contentMap, $errorClass);
if ($errorType) {
return new HandledError(
$error,
$errorType,
$this->statusMap[$errorType] ?? 500
$this->statusMap[$errorType] ?? 500,
$errorContent
);
}

View File

@@ -17,6 +17,12 @@ use Flarum\Foundation\ErrorHandling\ExceptionHandler;
use Flarum\Foundation\ErrorHandling\LogReporter;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Flarum\Http\Content\NotAuthenticated;
use Flarum\Http\Content\NotFound;
use Flarum\Http\Content\PermissionDenied;
use Flarum\Http\Exception\RouteNotFoundException;
use Flarum\User\Exception\NotAuthenticatedException;
use Flarum\User\Exception\PermissionDeniedException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Validation\ValidationException as IlluminateValidationException;
use Tobscure\JsonApi\Exception\InvalidParameterException;
@@ -57,6 +63,15 @@ class ErrorServiceProvider extends AbstractServiceProvider
];
});
$this->container->singleton('flarum.error.contents', function () {
return [
NotAuthenticatedException::class => NotAuthenticated::class,
PermissionDeniedException::class => PermissionDenied::class,
ModelNotFoundException::class => NotFound::class,
RouteNotFoundException::class => NotFound::class,
];
});
$this->container->singleton('flarum.error.handlers', function () {
return [
IlluminateValidationException::class => ExceptionHandler\IlluminateValidationExceptionHandler::class,
@@ -70,7 +85,8 @@ class ErrorServiceProvider extends AbstractServiceProvider
return new Registry(
$this->container->make('flarum.error.statuses'),
$this->container->make('flarum.error.classes'),
$this->container->make('flarum.error.handlers')
$this->container->make('flarum.error.handlers'),
$this->container->make('flarum.error.contents'),
);
});

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Http\Content;
use Flarum\Frontend\Document;
use Psr\Http\Message\ServerRequestInterface as Request;
class NotAuthenticated
{
public function __invoke(Document $document, Request $request)
{
$document->title = 'Not Authenticated';
$document->payload['errorCode'] = 401;
return $document;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Http\Content;
use Flarum\Frontend\Document;
use Psr\Http\Message\ServerRequestInterface as Request;
class NotFound
{
public function __invoke(Document $document, Request $request)
{
$document->title = 'Not Found';
$document->payload['errorCode'] = 404;
return $document;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Http\Content;
use Flarum\Frontend\Document;
use Psr\Http\Message\ServerRequestInterface as Request;
class PermissionDenied
{
public function __invoke(Document $document, Request $request)
{
$document->title = 'Permission Denied';
$document->payload['errorCode'] = 403;
return $document;
}
}

View File

@@ -58,6 +58,7 @@ class HttpServiceProvider extends AbstractServiceProvider
return $compiledDrivers;
});
$this->container->bind(SlugManager::class, function () {
return new SlugManager($this->container->make('flarum.http.selectedSlugDrivers'));
});

View File

@@ -36,15 +36,21 @@ class HandleErrors implements Middleware
*/
protected $formatter;
/**
* @var HttpFormatter
*/
protected $frontendFormatter;
/**
* @var \Flarum\Foundation\ErrorHandling\Reporter[]
*/
protected $reporters;
public function __construct(Registry $registry, HttpFormatter $formatter, iterable $reporters)
public function __construct(Registry $registry, HttpFormatter $formatter, HttpFormatter $frontendFormatter, iterable $reporters)
{
$this->registry = $registry;
$this->formatter = $formatter;
$this->frontendFormatter = $frontendFormatter;
$this->reporters = $reporters;
}
@@ -64,6 +70,10 @@ class HandleErrors implements Middleware
}
}
if ($error->contentClass()) {
return $this->frontendFormatter->format($error, $request);
}
return $this->formatter->format($error, $request);
}
}

View File

@@ -1,12 +0,0 @@
@extends('flarum.forum::layouts.basic')
@section('content')
<p>
{{ $message }}
</p>
<p>
<a href="{{ $url->to('forum')->base() }}">
{{ $translator->trans('core.views.error.not_found_return_link', ['{forum}' => $settings->get('forum_title')]) }}
</a>
</p>
@endsection