[CORE][Form] Add facilities for automattically adding a _next field to all forms, which can be customized by the in Form::create and defaults to the current URL. Usage of RedirectedException should mostly be replaced with Form::forceRedirect
This commit is contained in:
parent
7c9b01c516
commit
94449c9153
|
@ -33,13 +33,17 @@ declare(strict_types = 1);
|
|||
namespace App\Core;
|
||||
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Router\Router;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\RedirectException;
|
||||
use App\Util\Exception\ServerException;
|
||||
use App\Util\Formatting;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Form as SymfForm;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\Form\FormInterface as SymfFormInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
|
@ -77,7 +81,6 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
abstract class Form
|
||||
{
|
||||
private static ?FormFactoryInterface $form_factory;
|
||||
|
||||
public static function setFactory($ff): void
|
||||
{
|
||||
self::$form_factory = $ff;
|
||||
|
@ -85,18 +88,24 @@ abstract class Form
|
|||
|
||||
/**
|
||||
* Create a form with the given associative array $form as fields
|
||||
*
|
||||
* If the current request has a GET parameter `next`, adds `_next` hidden. Default values gotten
|
||||
* from the request, but can be overriden by `$extra_data['_next']`
|
||||
*/
|
||||
public static function create(
|
||||
array $form,
|
||||
?object $target = null,
|
||||
array $extra_data = [],
|
||||
string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType',
|
||||
string $type = '\Symfony\Component\Form\Extension\Core\Type\FormType',
|
||||
array $form_options = [],
|
||||
): SymfFormInterface {
|
||||
$name = $form[array_key_last($form)][0];
|
||||
$fb = self::$form_factory->createNamedBuilder($name, $type, data: null, options: array_merge($form_options, ['translation_domain' => false]));
|
||||
$name = $form[array_key_last($form)][0];
|
||||
$r = Common::getRequest();
|
||||
$form[] = ['_next', HiddenType::class, ['data' => $r->get('next') ?? $r->get('_next') ?? $r->getRequestUri()]];
|
||||
|
||||
$fb = self::$form_factory->createNamedBuilder($name, $type, data: null, options: array_merge($form_options, ['translation_domain' => false]));
|
||||
foreach ($form as [$key, $class, $options]) {
|
||||
if ($class == SubmitType::class && \in_array($key, ['save', 'publish', 'post'])) {
|
||||
if ($class == SubmitType::class && \in_array($key, ['save', 'publish', 'post', 'next'])) {
|
||||
Log::critical($m = "It's generally a bad idea to use {$key} as a form name, because it can conflict with other forms in the same page");
|
||||
throw new ServerException($m);
|
||||
}
|
||||
|
@ -195,10 +204,33 @@ abstract class Form
|
|||
|
||||
DB::merge($target);
|
||||
DB::flush();
|
||||
throw new RedirectException(url: $request->getPathInfo());
|
||||
}
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a redirect to the `_next` form field, which is either a page to go after filling a
|
||||
* form, or the page where the form was (given we use form actions). This prevents the browser
|
||||
* from attempting to resubmit the form when the user merely ment to refresh the page
|
||||
*/
|
||||
public static function forceRedirect(SymfFormInterface $form, Request $request): RedirectResponse
|
||||
{
|
||||
$next = $form->get('_next')->getData();
|
||||
try {
|
||||
if ($pos = mb_strrpos($next, '#')) {
|
||||
$fragment = mb_substr($next, $pos);
|
||||
$next = mb_substr($next, 0, $pos);
|
||||
}
|
||||
Router::match($next);
|
||||
$next = $next . ($fragment ?? '');
|
||||
return new RedirectResponse(url: $next . ($fragment ?? ''));
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
$user = Common::user();
|
||||
$user_id = \is_null($user) ? $user->getId() : '(not logged in)';
|
||||
Log::warning("Suspicious activity: User with ID {$user_id} submitted a form where the `_next` parameter is not a valid local URL ({$next})");
|
||||
throw new ClientException(_m('Invalid form submission'), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user