From 1c929fcdf5c636a77a7ec3730895c0790e1a9c09 Mon Sep 17 00:00:00 2001 From: Ambroise Maupate Date: Mon, 3 Jun 2024 22:49:40 +0200 Subject: [PATCH] fix(UI): Fixed drag and drop custom-form fields, node-type fields and attribute-values by setting new position against previous or next element id --- .../AjaxAbstractFieldsController.php | 50 ++++++++++++++----- .../AjaxAttributeValuesController.php | 42 ++++++++++------ .../AjaxCustomFormFieldsController.php | 10 ++-- .../AjaxNodeTypeFieldsController.php | 10 ++-- .../AjaxNodeTypesController.php | 2 +- .../AttributeValuePosition.js | 12 +++-- .../CustomFormFieldsPosition.js | 13 ++--- .../NodeTypeFieldsPosition.js | 13 ++--- .../views/nodes/attributes/edit.html.twig | 2 +- 9 files changed, 100 insertions(+), 54 deletions(-) diff --git a/lib/Rozier/src/AjaxControllers/AjaxAbstractFieldsController.php b/lib/Rozier/src/AjaxControllers/AjaxAbstractFieldsController.php index 43146921..64f3941e 100644 --- a/lib/Rozier/src/AjaxControllers/AjaxAbstractFieldsController.php +++ b/lib/Rozier/src/AjaxControllers/AjaxAbstractFieldsController.php @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; abstract class AjaxAbstractFieldsController extends AbstractAjaxController { @@ -16,6 +17,11 @@ public function __construct(protected readonly HandlerFactoryInterface $handlerF { } + protected function findEntity(int|string $entityId): ?AbstractField + { + return $this->em()->find($this->getEntityClass(), (int) $entityId); + } + /** * Handle actions for any abstract fields. * @@ -70,11 +76,31 @@ protected function handleFieldActions(Request $request, AbstractField $field = n */ protected function updatePosition(array $parameters, AbstractField $field = null): array { - /* - * First, we set the new parent - */ - if (!empty($parameters['newPosition']) && null !== $field) { - $field->setPosition((float) $parameters['newPosition']); + if (!empty($parameters['afterFieldId']) && is_numeric($parameters['afterFieldId'])) { + $afterField = $this->findEntity((int) $parameters['afterFieldId']); + if (null === $afterField) { + throw new BadRequestHttpException('afterFieldId does not exist'); + } + $field->setPosition($afterField->getPosition() + 0.5); + // Apply position update before cleaning + $this->em()->flush(); + $handler = $this->handlerFactory->getHandler($field); + $handler->cleanPositions(); + $this->em()->flush(); + return [ + 'statusCode' => '200', + 'status' => 'success', + 'responseText' => $this->getTranslator()->trans('field.%name%.updated', [ + '%name%' => $field->getName(), + ]), + ]; + } + if (!empty($parameters['beforeFieldId']) && is_numeric($parameters['beforeFieldId'])) { + $beforeField = $this->findEntity((int) $parameters['beforeFieldId']); + if (null === $beforeField) { + throw new BadRequestHttpException('beforeFieldId does not exist'); + } + $field->setPosition($beforeField->getPosition() - 0.5); // Apply position update before cleaning $this->em()->flush(); $handler = $this->handlerFactory->getHandler($field); @@ -88,12 +114,12 @@ protected function updatePosition(array $parameters, AbstractField $field = null ]), ]; } - return [ - 'statusCode' => '400', - 'status' => 'error', - 'responseText' => $this->getTranslator()->trans('field.%name%.updated', [ - '%name%' => $field->getName(), - ]), - ]; + + throw new BadRequestHttpException('Cannot update position for Field. Missing parameters.'); } + + /** + * @return class-string + */ + abstract protected function getEntityClass(): string; } diff --git a/lib/Rozier/src/AjaxControllers/AjaxAttributeValuesController.php b/lib/Rozier/src/AjaxControllers/AjaxAttributeValuesController.php index f18004a5..a4bc2108 100644 --- a/lib/Rozier/src/AjaxControllers/AjaxAttributeValuesController.php +++ b/lib/Rozier/src/AjaxControllers/AjaxAttributeValuesController.php @@ -5,11 +5,11 @@ namespace Themes\Rozier\AjaxControllers; use RZ\Roadiz\CoreBundle\Entity\AttributeValue; -use RZ\Roadiz\CoreBundle\Entity\Node; use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; final class AjaxAttributeValuesController extends AbstractAjaxController { @@ -70,12 +70,31 @@ protected function updatePosition(array $parameters, AttributeValue $attributeVa '%name%' => $attributeValue->getAttribute()->getLabelOrCode(), '%nodeName%' => $attributable->getNodeName(), ]; - /* - * First, we set the new parent - */ - if (!empty($parameters['newPosition'])) { - $attributeValue->setPosition((float) $parameters['newPosition']); - // Apply position update before cleaning + + if (!empty($parameters['afterAttributeValueId']) && is_numeric($parameters['afterAttributeValueId'])) { + /** @var AttributeValue|null $afterAttributeValue */ + $afterAttributeValue = $this->em()->find(AttributeValue::class, (int) $parameters['afterAttributeValueId']); + if (null === $afterAttributeValue) { + throw new BadRequestHttpException('afterAttributeValueId does not exist'); + } + $attributeValue->setPosition($afterAttributeValue->getPosition() + 0.5); + $this->em()->flush(); + return [ + 'statusCode' => '200', + 'status' => 'success', + 'responseText' => $this->getTranslator()->trans( + 'attribute_value_translation.%name%.updated_from_node.%nodeName%', + $details + ), + ]; + } + if (!empty($parameters['beforeAttributeValueId']) && is_numeric($parameters['beforeAttributeValueId'])) { + /** @var AttributeValue|null $beforeAttributeValue */ + $beforeAttributeValue = $this->em()->find(AttributeValue::class, (int) $parameters['beforeAttributeValueId']); + if (null === $beforeAttributeValue) { + throw new BadRequestHttpException('beforeAttributeValueId does not exist'); + } + $attributeValue->setPosition($beforeAttributeValue->getPosition() - 0.5); $this->em()->flush(); return [ 'statusCode' => '200', @@ -87,13 +106,6 @@ protected function updatePosition(array $parameters, AttributeValue $attributeVa ]; } - return [ - 'statusCode' => '400', - 'status' => 'error', - 'responseText' => $this->getTranslator()->trans( - 'attribute_value_translation.%name%.updated_from_node.%nodeName%', - $details - ), - ]; + throw new BadRequestHttpException('Cannot update position for AttributeValue. Missing parameters.'); } } diff --git a/lib/Rozier/src/AjaxControllers/AjaxCustomFormFieldsController.php b/lib/Rozier/src/AjaxControllers/AjaxCustomFormFieldsController.php index fc2307d4..86dc7ebc 100644 --- a/lib/Rozier/src/AjaxControllers/AjaxCustomFormFieldsController.php +++ b/lib/Rozier/src/AjaxControllers/AjaxCustomFormFieldsController.php @@ -21,13 +21,10 @@ class AjaxCustomFormFieldsController extends AjaxAbstractFieldsController */ public function editAction(Request $request, int $customFormFieldId): Response { - /* - * Validate - */ $this->validateRequest($request); $this->denyAccessUnlessGranted('ROLE_ACCESS_CUSTOMFORMS_DELETE'); - $field = $this->em()->find(CustomFormField::class, (int) $customFormFieldId); + $field = $this->findEntity((int) $customFormFieldId); if (null !== $field && null !== $response = $this->handleFieldActions($request, $field)) { return $response; @@ -40,4 +37,9 @@ public function editAction(Request $request, int $customFormFieldId): Response ] )); } + + protected function getEntityClass(): string + { + return CustomFormField::class; + } } diff --git a/lib/Rozier/src/AjaxControllers/AjaxNodeTypeFieldsController.php b/lib/Rozier/src/AjaxControllers/AjaxNodeTypeFieldsController.php index 2d560a0b..d825f480 100644 --- a/lib/Rozier/src/AjaxControllers/AjaxNodeTypeFieldsController.php +++ b/lib/Rozier/src/AjaxControllers/AjaxNodeTypeFieldsController.php @@ -21,13 +21,10 @@ class AjaxNodeTypeFieldsController extends AjaxAbstractFieldsController */ public function editAction(Request $request, int $nodeTypeFieldId): Response { - /* - * Validate - */ $this->validateRequest($request); $this->denyAccessUnlessGranted('ROLE_ACCESS_NODEFIELDS_DELETE'); - $field = $this->em()->find(NodeTypeField::class, (int) $nodeTypeFieldId); + $field = $this->findEntity($nodeTypeFieldId); if (null !== $response = $this->handleFieldActions($request, $field)) { return $response; @@ -40,4 +37,9 @@ public function editAction(Request $request, int $nodeTypeFieldId): Response ] )); } + + protected function getEntityClass(): string + { + return NodeTypeField::class; + } } diff --git a/lib/Rozier/src/AjaxControllers/AjaxNodeTypesController.php b/lib/Rozier/src/AjaxControllers/AjaxNodeTypesController.php index 6fd2457e..e2f2a736 100644 --- a/lib/Rozier/src/AjaxControllers/AjaxNodeTypesController.php +++ b/lib/Rozier/src/AjaxControllers/AjaxNodeTypesController.php @@ -13,7 +13,7 @@ use Symfony\Component\Routing\Exception\InvalidParameterException; use Themes\Rozier\Models\NodeTypeModel; -class AjaxNodeTypesController extends AjaxAbstractFieldsController +class AjaxNodeTypesController extends AbstractAjaxController { /** * @param Request $request diff --git a/lib/Rozier/src/Resources/app/components/attribute-values/AttributeValuePosition.js b/lib/Rozier/src/Resources/app/components/attribute-values/AttributeValuePosition.js index 07a56dc1..ac058ac4 100644 --- a/lib/Rozier/src/Resources/app/components/attribute-values/AttributeValuePosition.js +++ b/lib/Rozier/src/Resources/app/components/attribute-values/AttributeValuePosition.js @@ -34,13 +34,14 @@ export default class AttributeValuePosition { let $element = $(element) let attributeValueId = parseInt($element.data('id')) let $sibling = $element.prev() - let newPosition = 0.0 + let beforeElementId = null + let afterElementId = null if ($sibling.length === 0) { $sibling = $element.next() - newPosition = parseInt($sibling.data('position')) - 0.5 + beforeElementId = parseInt($sibling.data('id')) } else { - newPosition = parseInt($sibling.data('position')) + 0.5 + afterElementId = parseInt($sibling.data('id')) } const route = window.Rozier.routes.attributeValueAjaxEdit if (!route || !attributeValueId) { @@ -57,7 +58,8 @@ export default class AttributeValuePosition { _token: window.Rozier.ajaxToken, _action: 'updatePosition', attributeValueId: attributeValueId, - newPosition: newPosition, + beforeAttributeValueId: beforeElementId, + afterAttributeValueId: afterElementId, }), }) if (!response.ok) { @@ -74,7 +76,7 @@ export default class AttributeValuePosition { } catch (response) { const data = await response.json() window.UIkit.notify({ - message: data.error_message || '', + message: data.title || '', status: 'danger', timeout: 3000, pos: 'top-center', diff --git a/lib/Rozier/src/Resources/app/components/custom-form-fields/CustomFormFieldsPosition.js b/lib/Rozier/src/Resources/app/components/custom-form-fields/CustomFormFieldsPosition.js index 45b94cf7..2392414d 100644 --- a/lib/Rozier/src/Resources/app/components/custom-form-fields/CustomFormFieldsPosition.js +++ b/lib/Rozier/src/Resources/app/components/custom-form-fields/CustomFormFieldsPosition.js @@ -26,20 +26,22 @@ export default class CustomFormFieldsPosition { let $element = $(element) let customFormFieldId = parseInt($element.data('field-id')) let $sibling = $element.prev() - let newPosition = 0.0 + let afterFieldId = null + let beforeFieldId = null if ($sibling.length === 0) { $sibling = $element.next() - newPosition = parseInt($sibling.data('position')) - 0.5 + beforeFieldId = parseInt($sibling.data('field-id')) } else { - newPosition = parseInt($sibling.data('position')) + 0.5 + afterFieldId = parseInt($sibling.data('field-id')) } const postData = { _token: window.Rozier.ajaxToken, _action: 'updatePosition', customFormFieldId: customFormFieldId, - newPosition: newPosition, + beforeFieldId: beforeFieldId, + afterFieldId: afterFieldId, } const response = await fetch( window.Rozier.routes.customFormsFieldAjaxEdit.replace('%customFormFieldId%', customFormFieldId), @@ -54,14 +56,13 @@ export default class CustomFormFieldsPosition { if (!response.ok) { const data = await response.json() window.UIkit.notify({ - message: data.error_message, + message: data.title, status: 'danger', timeout: 3000, pos: 'top-center', }) } else { const data = await response.json() - $element.attr('data-position', newPosition) window.UIkit.notify({ message: data.responseText, status: data.status, diff --git a/lib/Rozier/src/Resources/app/components/node-type-fields/NodeTypeFieldsPosition.js b/lib/Rozier/src/Resources/app/components/node-type-fields/NodeTypeFieldsPosition.js index 2eccc472..3d98b0fb 100644 --- a/lib/Rozier/src/Resources/app/components/node-type-fields/NodeTypeFieldsPosition.js +++ b/lib/Rozier/src/Resources/app/components/node-type-fields/NodeTypeFieldsPosition.js @@ -74,20 +74,22 @@ export default class NodeTypeFieldsPosition { let $element = $(element) let nodeTypeFieldId = parseInt($element.data('field-id')) let $sibling = $element.prev() - let newPosition = 0.0 + let beforeFieldId = null + let afterFieldId = null if ($sibling.length === 0) { $sibling = $element.next() - newPosition = parseInt($sibling.data('position')) - 0.5 + beforeFieldId = parseInt($sibling.data('field-id')) } else { - newPosition = parseInt($sibling.data('position')) + 0.5 + afterFieldId = parseInt($sibling.data('field-id')) } const postData = { _token: window.Rozier.ajaxToken, _action: 'updatePosition', nodeTypeFieldId: nodeTypeFieldId, - newPosition: newPosition, + beforeFieldId: beforeFieldId, + afterFieldId: afterFieldId, } const response = await fetch( window.Rozier.routes.nodeTypesFieldAjaxEdit.replace('%nodeTypeFieldId%', nodeTypeFieldId), @@ -102,14 +104,13 @@ export default class NodeTypeFieldsPosition { if (!response.ok) { const data = await response.json() window.UIkit.notify({ - message: data.error_message, + message: data.title, status: 'danger', timeout: 3000, pos: 'top-center', }) } else { const data = await response.json() - $element.attr('data-position', newPosition) window.UIkit.notify({ message: data.responseText, status: data.status, diff --git a/lib/Rozier/src/Resources/views/nodes/attributes/edit.html.twig b/lib/Rozier/src/Resources/views/nodes/attributes/edit.html.twig index b48fdf76..fe238ac6 100644 --- a/lib/Rozier/src/Resources/views/nodes/attributes/edit.html.twig +++ b/lib/Rozier/src/Resources/views/nodes/attributes/edit.html.twig @@ -23,7 +23,7 @@ {% for attribute_value_translation_form in attribute_value_translation_forms %} {% set attributeValue = attribute_value_translation_form.vars.data.attributeValue %} - + {{- attribute_value_translation_form.vars.data|attribute_label -}}