2020-06-24 05:27:02 +09:00
|
|
|
<?php
|
|
|
|
|
2021-10-10 17:26:18 +09:00
|
|
|
declare(strict_types = 1);
|
|
|
|
|
2020-06-24 05:27:02 +09:00
|
|
|
// {{{ License
|
|
|
|
// This file is part of GNU social - https://www.gnu.org/software/social
|
|
|
|
//
|
|
|
|
// GNU social is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// GNU social is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
/**
|
2021-07-22 01:42:33 +09:00
|
|
|
* Convert a Form from our declarative to Symfony's representation
|
2020-06-24 05:27:02 +09:00
|
|
|
*
|
|
|
|
* @package GNUsocial
|
|
|
|
* @category Wrapper
|
|
|
|
*
|
2021-02-20 08:29:43 +09:00
|
|
|
* @author Hugo Sales <hugo@hsal.es>
|
|
|
|
* @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org
|
2020-06-24 05:27:02 +09:00
|
|
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace App\Core;
|
|
|
|
|
2020-07-27 12:40:09 +09:00
|
|
|
use App\Core\DB\DB;
|
2021-08-19 02:35:03 +09:00
|
|
|
use App\Util\Exception\ServerException;
|
2020-07-27 12:40:09 +09:00
|
|
|
use App\Util\Formatting;
|
|
|
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
2020-06-24 05:27:02 +09:00
|
|
|
use Symfony\Component\Form\Form as SymfForm;
|
|
|
|
use Symfony\Component\Form\FormFactoryInterface;
|
2021-09-09 09:08:45 +09:00
|
|
|
use Symfony\Component\Form\FormInterface as SymfFormInterface;
|
2020-07-27 12:40:09 +09:00
|
|
|
use Symfony\Component\HttpFoundation\Request;
|
2020-06-24 05:27:02 +09:00
|
|
|
|
2021-07-22 01:42:33 +09:00
|
|
|
/**
|
|
|
|
* This class converts our own form representation to Symfony's
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* ```
|
|
|
|
* $form = Form::create([
|
|
|
|
* ['content', TextareaType::class, ['label' => ' ', 'data' => '', 'attr' => ['placeholder' => _m($placeholder_string[$rand_key])]]],
|
|
|
|
* ['attachments', FileType::class, ['label' => ' ', 'data' => null, 'multiple' => true, 'required' => false]],
|
|
|
|
* ['visibility', ChoiceType::class, ['label' => _m('Visibility:'), 'expanded' => true, 'data' => 'public', 'choices' => [_m('Public') => 'public', _m('Instance') => 'instance', _m('Private') => 'private']]],
|
|
|
|
* ['to', ChoiceType::class, ['label' => _m('To:'), 'multiple' => true, 'expanded' => true, 'choices' => $to_tags]],
|
|
|
|
* ['post', SubmitType::class, ['label' => _m('Post')]],
|
|
|
|
* ]);
|
|
|
|
* ```
|
|
|
|
* turns into
|
|
|
|
* ```
|
|
|
|
* \Symfony\Component\Form\Form {
|
|
|
|
* config: Symfony\Component\Form\FormBuilder { ... }
|
|
|
|
* ...
|
|
|
|
* children: Symfony\Component\Form\Util\OrderedHashMap {
|
|
|
|
* elements: array:5 [
|
|
|
|
* "content" => Symfony\Component\Form\Form { ... }
|
|
|
|
* "attachments" => Symfony\Component\Form\Form { ... }
|
|
|
|
* "visibility" => Symfony\Component\Form\Form { ... }
|
|
|
|
* "to" => Symfony\Component\Form\Form { ... }
|
|
|
|
* "post" => Symfony\Component\Form\SubmitButton { ... }
|
|
|
|
* ]
|
|
|
|
* ...
|
|
|
|
* }
|
|
|
|
* ...
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*/
|
2020-06-24 05:27:02 +09:00
|
|
|
abstract class Form
|
|
|
|
{
|
|
|
|
private static ?FormFactoryInterface $form_factory;
|
|
|
|
|
|
|
|
public static function setFactory($ff): void
|
|
|
|
{
|
|
|
|
self::$form_factory = $ff;
|
|
|
|
}
|
|
|
|
|
2020-11-07 04:47:15 +09:00
|
|
|
/**
|
|
|
|
* Create a form with the given associative array $form as fields
|
|
|
|
*/
|
2021-10-10 17:26:18 +09:00
|
|
|
public static function create(
|
|
|
|
array $form,
|
|
|
|
?object $target = null,
|
|
|
|
array $extra_data = [],
|
|
|
|
string $type = 'Symfony\Component\Form\Extension\Core\Type\FormType',
|
|
|
|
array $form_options = [],
|
|
|
|
): SymfFormInterface {
|
2020-09-05 04:35:41 +09:00
|
|
|
$name = $form[array_key_last($form)][0];
|
2021-08-17 02:43:36 +09:00
|
|
|
$fb = self::$form_factory->createNamedBuilder($name, $type, data: null, options: array_merge($form_options, ['translation_domain' => false]));
|
2021-07-22 01:42:33 +09:00
|
|
|
foreach ($form as [$key, $class, $options]) {
|
2021-10-10 17:26:18 +09:00
|
|
|
if ($class == SubmitType::class && \in_array($key, ['save', 'publish', 'post'])) {
|
2021-08-19 02:35:03 +09:00
|
|
|
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);
|
|
|
|
}
|
2021-10-10 17:26:18 +09:00
|
|
|
if ($target != null && empty($options['data']) && (mb_strstr($key, 'password') == false) && $class != SubmitType::class) {
|
2020-07-27 12:40:09 +09:00
|
|
|
if (isset($extra_data[$key])) {
|
2021-07-22 21:31:19 +09:00
|
|
|
// @codeCoverageIgnoreStart
|
2020-07-27 12:40:09 +09:00
|
|
|
$options['data'] = $extra_data[$key];
|
2021-07-22 21:31:19 +09:00
|
|
|
// @codeCoverageIgnoreEnd
|
2020-07-27 12:40:09 +09:00
|
|
|
} else {
|
|
|
|
$method = 'get' . ucfirst(Formatting::snakeCaseToCamelCase($key));
|
|
|
|
if (method_exists($target, $method)) {
|
|
|
|
$options['data'] = $target->{$method}();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unset($options['hide']);
|
|
|
|
if (isset($options['transformer'])) {
|
|
|
|
$transformer = $options['transformer'];
|
|
|
|
unset($options['transformer']);
|
|
|
|
}
|
|
|
|
$fb->add($key, $class, $options);
|
|
|
|
if (isset($transformer)) {
|
|
|
|
$fb->get($key)->addModelTransformer(new $transformer());
|
|
|
|
unset($transformer);
|
|
|
|
}
|
2020-06-24 05:27:02 +09:00
|
|
|
}
|
|
|
|
return $fb->getForm();
|
|
|
|
}
|
2020-07-26 02:50:23 +09:00
|
|
|
|
2020-11-07 04:47:15 +09:00
|
|
|
/**
|
|
|
|
* Whether the given $field of $form has the `required` property
|
|
|
|
* set, defaults to true
|
|
|
|
*/
|
2020-07-26 02:50:23 +09:00
|
|
|
public static function isRequired(array $form, string $field): bool
|
|
|
|
{
|
|
|
|
return $form[$field][2]['required'] ?? true;
|
|
|
|
}
|
2020-07-27 12:40:09 +09:00
|
|
|
|
2021-12-03 11:51:41 +09:00
|
|
|
|
2020-11-07 04:47:15 +09:00
|
|
|
/**
|
|
|
|
* Handle the full life cycle of a form. Creates it with @see
|
|
|
|
* self::create and inserts the submitted values into the database
|
2021-12-03 11:51:41 +09:00
|
|
|
*
|
|
|
|
* @param array $form_definition
|
|
|
|
* @param Request $request
|
|
|
|
* @param object|null $target
|
|
|
|
* @param array $extra_args
|
|
|
|
* @param callable|null $extra_step
|
|
|
|
* @param array $create_args
|
|
|
|
* @param SymfForm|null $testing_only_form
|
|
|
|
* @return mixed
|
|
|
|
* @throws ServerException
|
2020-11-07 04:47:15 +09:00
|
|
|
*/
|
2021-12-03 11:51:41 +09:00
|
|
|
public static function handle(array $form_definition, Request $request, ?object $target, array $extra_args = [], ?callable $extra_step = null, array $create_args = [], ?SymfForm $testing_only_form = null): mixed
|
2020-07-27 12:40:09 +09:00
|
|
|
{
|
2021-07-22 21:31:19 +09:00
|
|
|
$form = $testing_only_form ?? self::create($form_definition, $target, ...$create_args);
|
2020-07-27 12:40:09 +09:00
|
|
|
|
|
|
|
$form->handleRequest($request);
|
|
|
|
if ($form->isSubmitted() && $form->isValid()) {
|
2021-12-03 11:51:41 +09:00
|
|
|
|
2020-07-27 12:40:09 +09:00
|
|
|
$data = $form->getData();
|
2021-12-03 11:51:41 +09:00
|
|
|
if (is_null($target)) {
|
2020-07-27 12:40:09 +09:00
|
|
|
return $data;
|
|
|
|
}
|
2021-12-03 11:51:41 +09:00
|
|
|
|
2020-07-27 12:40:09 +09:00
|
|
|
unset($data['translation_domain'], $data['save']);
|
|
|
|
foreach ($data as $key => $val) {
|
|
|
|
$method = 'set' . ucfirst(Formatting::snakeCaseToCamelCase($key));
|
|
|
|
if (method_exists($target, $method)) {
|
|
|
|
if (isset($extra_args[$key])) {
|
2021-07-22 21:31:19 +09:00
|
|
|
// @codeCoverageIgnoreStart
|
2020-07-27 12:40:09 +09:00
|
|
|
$target->{$method}($val, $extra_args[$key]);
|
2021-12-03 11:51:41 +09:00
|
|
|
// @codeCoverageIgnoreEnd
|
2020-07-27 12:40:09 +09:00
|
|
|
} else {
|
|
|
|
$target->{$method}($val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-03 11:51:41 +09:00
|
|
|
|
2020-07-27 12:40:09 +09:00
|
|
|
if (isset($extra_step)) {
|
2021-07-22 21:31:19 +09:00
|
|
|
// @codeCoverageIgnoreStart
|
2020-07-27 12:40:09 +09:00
|
|
|
$extra_step($data, $extra_args);
|
2021-07-22 21:31:19 +09:00
|
|
|
// @codeCoverageIgnoreEnd
|
2020-07-27 12:40:09 +09:00
|
|
|
}
|
2021-12-03 11:51:41 +09:00
|
|
|
|
2020-07-27 12:40:09 +09:00
|
|
|
DB::flush();
|
|
|
|
}
|
2021-12-03 11:51:41 +09:00
|
|
|
|
2020-07-27 12:40:09 +09:00
|
|
|
return $form;
|
|
|
|
}
|
2020-06-24 05:27:02 +09:00
|
|
|
}
|