Skip to content

Commit

Permalink
Add toolbar action for creating copy of current form to edit view (#316)
Browse files Browse the repository at this point in the history
* Implement controller action for copying existing form

* Remove trailing commas

* Move copy form to manager

* Throw exception when form not exist

* Increase required version for copy toolbar action

* Update phpstan

* Fix test

* Move lint to PHP 8.1

* Update baseline

* Update github action

Co-authored-by: Alexander Schranz <[email protected]>
  • Loading branch information
niklasnatter and alexander-schranz committed Apr 1, 2022
1 parent 1d84466 commit 8fc1492
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 48 deletions.
13 changes: 6 additions & 7 deletions .github/workflows/test-application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
database: mysql
dependency-versions: 'highest'
tools: 'composer:v2'
lint: true
lint: false
env:
SYMFONY_DEPRECATIONS_HELPER: weak
DATABASE_URL: mysql://root:[email protected]/sulu_form_test?serverVersion=5.7
Expand All @@ -53,7 +53,7 @@ jobs:
database: mysql
dependency-versions: 'highest'
tools: 'composer:v2'
lint: false
lint: true
env:
SYMFONY_DEPRECATIONS_HELPER: weak
DATABASE_URL: mysql://root:[email protected]/sulu_form_test?serverVersion=5.7
Expand Down Expand Up @@ -100,12 +100,11 @@ jobs:
run: composer bootstrap-test-environment
env: ${{ matrix.env }}

- name: Lint code
if: ${{ matrix.lint }}
run: composer lint
env: ${{ matrix.env }}

- name: Execute test cases
run: time composer test
env: ${{ matrix.env }}

- name: Lint code
if: ${{ matrix.lint }}
run: composer lint
env: ${{ matrix.env }}
13 changes: 11 additions & 2 deletions .php_cs.dist → .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,28 @@
EOF;

$finder = PhpCsFixer\Finder::create()
->exclude(['var/cache'])
->exclude(['var/cache', 'node_modules'])
->in(__DIR__);

return PhpCsFixer\Config::create()
$config = new PhpCsFixer\Config();
$config->setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
'class_definition' => false,
'concat_space' => ['spacing' => 'one'],
'function_declaration' => ['closure_function_spacing' => 'none'],
'header_comment' => ['header' => $header],
'native_constant_invocation' => true,
'native_function_casing' => true,
'native_function_invocation' => ['include' => ['@internal']],
'no_superfluous_phpdoc_tags' => ['allow_mixed' => true, 'remove_inheritdoc' => true],
'ordered_imports' => true,
'phpdoc_align' => ['align' => 'left'],
'phpdoc_types_order' => false,
'single_line_throw' => false,
'single_line_comment_spacing' => false,
])
->setFinder($finder);

return $config;
8 changes: 8 additions & 0 deletions Admin/FormAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Sulu\Bundle\AdminBundle\Admin\Admin;
use Sulu\Bundle\AdminBundle\Admin\Navigation\NavigationItem;
use Sulu\Bundle\AdminBundle\Admin\Navigation\NavigationItemCollection;
use Sulu\Bundle\AdminBundle\Admin\View\DropdownToolbarAction;
use Sulu\Bundle\AdminBundle\Admin\View\ToolbarAction;
use Sulu\Bundle\AdminBundle\Admin\View\ViewBuilderFactoryInterface;
use Sulu\Bundle\AdminBundle\Admin\View\ViewCollection;
Expand Down Expand Up @@ -90,6 +91,13 @@ function(Localization $localization) {
$formToolbarActions = [
new ToolbarAction('sulu_admin.save'),
new ToolbarAction('sulu_admin.delete'),
new DropdownToolbarAction(
'sulu_admin.edit',
'su-pen',
[
new ToolbarAction('sulu_admin.copy'),
]
),
];
$listToolbarActions = [
new ToolbarAction('sulu_admin.add'),
Expand Down
27 changes: 27 additions & 0 deletions Controller/FormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\RestBundle\View\ViewHandlerInterface;
use Sulu\Bundle\FormBundle\Entity\Form;
use Sulu\Bundle\FormBundle\Exception\FormNotFoundException;
use Sulu\Bundle\FormBundle\Manager\FormManager;
use Sulu\Component\Rest\AbstractRestController;
use Sulu\Component\Rest\Exception\RestException;
use Sulu\Component\Rest\ListBuilder\AbstractListBuilder;
use Sulu\Component\Rest\ListBuilder\Doctrine\DoctrineListBuilderFactoryInterface;
use Sulu\Component\Rest\ListBuilder\ListRepresentation;
Expand Down Expand Up @@ -174,6 +176,31 @@ public function postAction(Request $request): Response
return $this->handleView($this->view($this->getApiEntity($entity, $locale), 201));
}

public function postTriggerAction(Request $request, int $id): Response
{
$action = $request->query->get('action');
$locale = $this->getLocale($request);

try {
switch ($action) {
case 'copy':
try {
$copiedForm = $this->formManager->copy($id);
} catch (FormNotFoundException $e) {
throw new NotFoundHttpException(sprintf('No form with id "%s" was found!', $e->getFormEntityId()), $e);
}

return $this->handleView($this->view($this->getApiEntity($copiedForm, $locale)));
default:
throw new RestException(\sprintf('Unrecognized action: "%s"', $action));
}
} catch (RestException $ex) {
$view = $this->view($ex->toArray(), 400);

return $this->handleView($view);
}
}

public function putAction(Request $request, int $id): Response
{
$locale = $this->getLocale($request);
Expand Down
3 changes: 3 additions & 0 deletions Controller/FormWebsiteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Sulu\Bundle\FormBundle\Form\Type\AbstractType;
use Sulu\Bundle\WebsiteBundle\Controller\DefaultController;
use Sulu\Component\Content\Compat\StructureInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormRegistryInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand Down Expand Up @@ -192,6 +193,7 @@ protected function getErrors(): array
$errors = [];

$generalErrors = [];
/** @var FormError $error */
foreach ($this->form->getErrors() as $error) {
$generalErrors[] = $error->getMessage();
}
Expand All @@ -203,6 +205,7 @@ protected function getErrors(): array
foreach ($this->form->all() as $field) {
$fieldErrors = [];

/** @var FormError $error */
foreach ($field->getErrors() as $error) {
$fieldErrors[] = $error->getMessage();
}
Expand Down
2 changes: 1 addition & 1 deletion Exception/FormNotFoundException.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FormNotFoundException extends \Exception
private $formEntityId;

/**
* @param string $locale
* @param string|null $locale
*/
public function __construct(int $formEntityId, $locale)
{
Expand Down
4 changes: 4 additions & 0 deletions Form/Type/DynamicFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@ public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var Form $formEntity */
$formEntity = $options['formEntity'];
/** @var string $locale */
$locale = $options['locale'];
/** @var string $type */
$type = $options['type'];
/** @var string $typeId */
$typeId = $options['typeId'];
/** @var string $name */
$name = $options['name'];

if (!$translation = $formEntity->getTranslation($locale)) {
Expand Down
73 changes: 73 additions & 0 deletions Manager/FormManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
use Doctrine\ORM\EntityManagerInterface;
use Sulu\Bundle\FormBundle\Entity\Form;
use Sulu\Bundle\FormBundle\Entity\FormField;
use Sulu\Bundle\FormBundle\Entity\FormFieldTranslation;
use Sulu\Bundle\FormBundle\Entity\FormTranslation;
use Sulu\Bundle\FormBundle\Entity\FormTranslationReceiver;
use Sulu\Bundle\FormBundle\Exception\FormNotFoundException;
use Sulu\Bundle\FormBundle\Repository\FormRepository;

class FormManager
Expand Down Expand Up @@ -64,6 +66,77 @@ public function count(?string $locale = null, array $filters = []): int
return $this->formRepository->countByFilters($locale, $filters);
}

public function copy(int $id): Form
{
$form = $this->findById($id);

if (!$form) {
throw new FormNotFoundException($id, null);
}

$newForm = new Form();
$newForm->setDefaultLocale($form->getDefaultLocale());

foreach ($form->getTranslations() as $translation) {
/** @var FormTranslation $newFormTranslation */
$newFormTranslation = $newForm->getTranslation($translation->getLocale(), true);
$newFormTranslation->setTitle($translation->getTitle() . ' (2)');
$newFormTranslation->setSubject($translation->getSubject());
$newFormTranslation->setFromEmail($translation->getFromEmail());
$newFormTranslation->setFromName($translation->getFromName());
$newFormTranslation->setToEmail($translation->getToEmail());
$newFormTranslation->setToName($translation->getToName());
$newFormTranslation->setMailText($translation->getMailText());
$newFormTranslation->setSubmitLabel($translation->getSubmitLabel());
$newFormTranslation->setSuccessText($translation->getSuccessText());
$newFormTranslation->setSendAttachments($translation->getSendAttachments());
$newFormTranslation->setDeactivateAttachmentSave($translation->getDeactivateAttachmentSave());
$newFormTranslation->setDeactivateNotifyMails($translation->getDeactivateNotifyMails());
$newFormTranslation->setDeactivateCustomerMails($translation->getDeactivateCustomerMails());
$newFormTranslation->setReplyTo($translation->getReplyTo());
$newFormTranslation->setChanged(new \DateTime());
$newFormTranslation->setForm($newForm);
$newForm->addTranslation($newFormTranslation);

foreach ($translation->getReceivers() as $receiver) {
$newReceiver = new FormTranslationReceiver();
$newReceiver->setType($receiver->getType());
$newReceiver->setEmail($receiver->getEmail());
$newReceiver->setName($receiver->getName());
$newReceiver->setFormTranslation($newFormTranslation);
$newFormTranslation->addReceiver($newReceiver);
}
}

foreach ($form->getFields() as $field) {
$newField = new FormField();
$newField->setDefaultLocale($field->getDefaultLocale());
$newField->setKey($field->getKey());
$newField->setType($field->getType());
$newField->setOrder($field->getOrder());
$newField->setWidth($field->getWidth());
$newField->setRequired($field->getRequired());

foreach ($field->getTranslations() as $fieldTranslation) {
/** @var FormFieldTranslation $newFieldTranslation */
$newFieldTranslation = $newField->getTranslation($fieldTranslation->getLocale(), true);
$newFieldTranslation->setTitle($fieldTranslation->getTitle());
$newFieldTranslation->setPlaceholder($fieldTranslation->getPlaceholder());
$newFieldTranslation->setDefaultValue($fieldTranslation->getDefaultValue());
$newFieldTranslation->setShortTitle($fieldTranslation->getShortTitle());
$newFieldTranslation->setOptions($fieldTranslation->getOptions());
}

$newField->setForm($newForm);
$newForm->addField($newField);
}

$this->entityManager->persist($newForm);
$this->entityManager->flush();

return $newForm;
}

/**
* @param mixed[] $data
*/
Expand Down
7 changes: 7 additions & 0 deletions Resources/config/routing_api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ sulu_form.forms:
resource: sulu_form.form_controller
name_prefix: sulu_form.

sulu_form.post_form_trigger:
path: /forms/{id}.{_format}
methods: POST
defaults:
_controller: sulu_form.form_controller::postTriggerAction
_format: json

sulu_form.dynamic:
type: rest
resource: sulu_form.dynamic_controller
Expand Down
41 changes: 41 additions & 0 deletions Tests/Functional/Controller/FormControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,47 @@ public function testPostFull(): void
$this->assertFullForm($response);
}

public function testPostTriggerNotFound(): void
{
$this->client->request(
'POST',
'/admin/api/forms/2?action=copy'
);

$this->assertHttpStatusCode(404, $this->client->getResponse());
}

public function testPostTriggerInvalidAction(): void
{
$form = $this->createMinimalForm();

$this->client->request(
'POST',
'/admin/api/forms/' . $form->getId() . '?action=not-an-action'
);

$this->assertHttpStatusCode(400, $this->client->getResponse());
}

public function testPostTriggerCopy(): void
{
$form = $this->createFullForm();

$this->client->request(
'POST',
'/admin/api/forms/' . $form->getId() . '?action=copy'
);

$this->assertHttpStatusCode(200, $this->client->getResponse());
$response = json_decode($this->client->getResponse()->getContent(), true);

$this->assertEquals('Title (2)', $response['title']);
$this->assertNotEquals($form->getId(), $response['id']);
$response['title'] = str_replace(' (2)', '', $response['title']);

$this->assertFullForm($response);
}

public function testPutMinimal(): void
{
$form = $this->createFullForm();
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"doctrine/collections": "^1.0",
"doctrine/orm": "^2.5.3",
"doctrine/persistence": "^1.3 || ^2.0",
"sulu/sulu": "^2.2.0 || 2.3@dev",
"sulu/sulu": "^2.4.1 || ^2.4.1@dev",
"symfony/config": "^4.4 || ^5.0",
"symfony/console": "^4.4 || ^5.0",
"symfony/dependency-injection": "^4.4 || ^5.0",
Expand All @@ -39,7 +39,7 @@
"doctrine/doctrine-bundle": "^1.10 || ^2.0",
"drewm/mailchimp-api": "^2.2",
"excelwebzone/recaptcha-bundle": "^1.4.2",
"friendsofphp/php-cs-fixer": "^2.17",
"friendsofphp/php-cs-fixer": "^3.0",
"handcraftedinthealps/zendsearch": "^2.0",
"jackalope/jackalope-doctrine-dbal": "^1.3.2",
"jangregor/phpstan-prophecy": "^1.0",
Expand Down
Loading

0 comments on commit 8fc1492

Please sign in to comment.