diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml index 189e3a55..3b17d56e 100644 --- a/.github/workflows/run-test.yml +++ b/.github/workflows/run-test.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: ['8.1', '8.2', '8.3'] + php-version: ['8.0', '8.1'] steps: - uses: shivammathur/setup-php@v2 with: diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..5346b623 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,75 @@ +node_js: + - "14" +php: + - 8.0 + - 8.1 + - nightly +dist: bionic +stages: + - "PHP lint tests" + - "Backoffice assets tests" +branches: + except: + - l10n_develop + +jobs: + allow_failures: + - php: nightly + + include: + - stage: "Backoffice assets tests" + language: node_js + node_js: "14" + script: sh .travis/backoffice_assets.sh + + - stage: "PHP lint tests" + language: php + sudo: required + services: + - mysql + env: + - DB=mysql + - MYSQL_VERSION=5.7 + - MYSQL_PASSWORD= + php: 7.4 + install: sh .travis/composer_install.sh + script: sh .travis/php_lint.sh + - stage: "PHP lint tests" + language: php + sudo: required + services: + - mysql + env: + - DB=mysql + - MYSQL_VERSION=5.7 + - MYSQL_PASSWORD= + php: 8.0 + install: sh .travis/composer_install.sh + script: sh .travis/php_lint.sh + - stage: "PHP lint tests" + language: php + sudo: required + services: + - mysql + env: + - DB=mysql + - MYSQL_VERSION=5.7 + - MYSQL_PASSWORD= + php: 8.1 + install: sh .travis/composer_install.sh + script: sh .travis/php_lint.sh + - stage: "PHP lint tests" + language: php + sudo: required + services: + - mysql + env: + - DB=mysql + - MYSQL_VERSION=5.7 + - MYSQL_PASSWORD= + php: nightly + install: sh .travis/composer_install.sh + script: sh .travis/php_lint.sh + + + diff --git a/composer.json b/composer.json index 0d18956c..e388e7d5 100644 --- a/composer.json +++ b/composer.json @@ -28,26 +28,25 @@ "role": "Frontend developer" } ], - "prefer-stable": true, "require": { - "php": ">=8.1", + "php": ">=8.0", "ext-zip": "*", - "doctrine/orm": "~2.17.0", + "doctrine/orm": "<2.17", "guzzlehttp/guzzle": "^7.2.0", "jms/serializer": "^3.9.0", "league/flysystem": "^3.0", "pimple/pimple": "^3.3.1", "ramsey/uuid": "^4.7", - "roadiz/compat-bundle": "2.2.*", - "roadiz/core-bundle": "2.2.*", - "roadiz/doc-generator": "2.2.*", - "roadiz/documents": "2.2.*", - "roadiz/dts-generator": "2.2.*", - "roadiz/markdown": "2.2.*", - "roadiz/models": "2.2.*", + "roadiz/compat-bundle": "^2.1", + "roadiz/core-bundle": "^2.1", + "roadiz/doc-generator": "^2.1", + "roadiz/documents": "^2.1", + "roadiz/dts-generator": "^2.1", + "roadiz/markdown": "^2.1", + "roadiz/models": "^2.1", "roadiz/nodetype-contracts": "~1.1.2", - "roadiz/openid": "2.2.*", - "roadiz/rozier-bundle": "2.2.*", + "roadiz/openid": "^2.1", + "roadiz/rozier-bundle": "^2.1", "symfony/asset": "5.4.*", "symfony/filesystem": "5.4.*", "symfony/form": "5.4.*", @@ -67,9 +66,9 @@ "php-coveralls/php-coveralls": "^2.4", "phpstan/phpstan": "^1.5.3", "phpstan/phpstan-doctrine": "^1.3", - "roadiz/entity-generator": "2.2.*", - "roadiz/random": "2.2.*", - "roadiz/jwt": "2.2.*", + "roadiz/entity-generator": "^2.1", + "roadiz/random": "^2.1", + "roadiz/jwt": "^2.1", "squizlabs/php_codesniffer": "^3.5" }, "autoload": { @@ -95,8 +94,8 @@ }, "extra": { "branch-alias": { - "dev-main": "2.2.x-dev", - "dev-develop": "2.3.x-dev" + "dev-main": "2.1.x-dev", + "dev-develop": "2.2.x-dev" } } } diff --git a/phpstan.neon b/phpstan.neon index 10dbac08..353be6b9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -28,8 +28,6 @@ parameters: - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' - - '#but returns Doctrine\\Common\\Collections\\ReadableCollection]+>#' - - '#does not accept Doctrine\\Common\\Collections\\ReadableCollection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false diff --git a/src/.babelrc b/src/.babelrc index b007a11d..2595c191 100644 --- a/src/.babelrc +++ b/src/.babelrc @@ -1,4 +1,4 @@ { - "presets": ["env", "stage-0"], + "presets": ["es2015", "stage-0"], "plugins": ["transform-runtime", "lodash"] } diff --git a/src/.editorconfig b/src/.editorconfig index cd986d42..c2778daa 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -1,6 +1,7 @@ # TheatreTheme editor config for contributors # Root is false as your theme is inside Roadiz filetree # http://editorconfig.org/ +root = false [*] indent_style = space diff --git a/src/.eslintrc.js b/src/.eslintrc.js index 020a951c..536f9132 100644 --- a/src/.eslintrc.js +++ b/src/.eslintrc.js @@ -11,7 +11,7 @@ module.exports = { }, // https://github.com/standard/standard/blob/master/docs/RULES-en.md //extends: 'standard', - extends: ['prettier', 'plugin:prettier/recommended', 'plugin:vue/base'], + extends: ['prettier', 'plugin:prettier/recommended', 'plugin:vue/essential'], // required to lint *.vue files plugins: ['html', 'prettier'], // add your custom rules here diff --git a/src/AjaxControllers/AbstractAjaxController.php b/src/AjaxControllers/AbstractAjaxController.php index 694d7f6a..037c80bb 100644 --- a/src/AjaxControllers/AbstractAjaxController.php +++ b/src/AjaxControllers/AbstractAjaxController.php @@ -24,7 +24,7 @@ abstract class AbstractAjaxController extends RozierApp * @param string $method * @param bool $requestCsrfToken * - * @return bool Return true if request is valid, else throw exception + * @return boolean Return true if request is valid, else throw exception */ protected function validateRequest(Request $request, $method = 'POST', $requestCsrfToken = true) { diff --git a/src/AjaxControllers/AjaxAbstractFieldsController.php b/src/AjaxControllers/AjaxAbstractFieldsController.php index 832660ba..0c597186 100644 --- a/src/AjaxControllers/AjaxAbstractFieldsController.php +++ b/src/AjaxControllers/AjaxAbstractFieldsController.php @@ -10,6 +10,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +/** + * @package Themes\Rozier\AjaxControllers + */ abstract class AjaxAbstractFieldsController extends AbstractAjaxController { private HandlerFactoryInterface $handlerFactory; diff --git a/src/AjaxControllers/AjaxAttributeValuesController.php b/src/AjaxControllers/AjaxAttributeValuesController.php index a10af158..73f5adf1 100644 --- a/src/AjaxControllers/AjaxAttributeValuesController.php +++ b/src/AjaxControllers/AjaxAttributeValuesController.php @@ -6,7 +6,6 @@ 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; @@ -26,41 +25,40 @@ final class AjaxAttributeValuesController extends AbstractAjaxController * * @return Response JSON response */ - public function editAction(Request $request, int $attributeValueId): Response + public function editAction(Request $request, int $attributeValueId) { /* * Validate */ $this->validateRequest($request, 'POST', false); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODE_ATTRIBUTES'); /** @var AttributeValue|null $attributeValue */ $attributeValue = $this->em()->find(AttributeValue::class, (int) $attributeValueId); - if ($attributeValue === null) { - throw $this->createNotFoundException($this->getTranslator()->trans( - 'attribute_value.%attributeValueId%.not_exists', - [ - '%attributeValueId%' => $attributeValueId - ] - )); - } - - $this->denyAccessUnlessGranted(NodeVoter::EDIT_ATTRIBUTE, $attributeValue->getAttributable()); + if ($attributeValue !== null) { + $responseArray = []; + /* + * Get the right update method against "_action" parameter + */ + switch ($request->get('_action')) { + case 'updatePosition': + $responseArray = $this->updatePosition($request->request->all(), $attributeValue); + break; + } - $responseArray = []; - /* - * Get the right update method against "_action" parameter - */ - switch ($request->get('_action')) { - case 'updatePosition': - $responseArray = $this->updatePosition($request->request->all(), $attributeValue); - break; + return new JsonResponse( + $responseArray, + Response::HTTP_PARTIAL_CONTENT + ); } - return new JsonResponse( - $responseArray, - Response::HTTP_PARTIAL_CONTENT - ); + throw $this->createNotFoundException($this->getTranslator()->trans( + 'attribute_value.%attributeValueId%.not_exists', + [ + '%attributeValueId%' => $attributeValueId + ] + )); } /** diff --git a/src/AjaxControllers/AjaxCustomFormFieldsController.php b/src/AjaxControllers/AjaxCustomFormFieldsController.php index fe2e10e8..f6075f8d 100644 --- a/src/AjaxControllers/AjaxCustomFormFieldsController.php +++ b/src/AjaxControllers/AjaxCustomFormFieldsController.php @@ -8,6 +8,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxCustomFormFieldsController extends AjaxAbstractFieldsController { /** diff --git a/src/AjaxControllers/AjaxCustomFormsExplorerController.php b/src/AjaxControllers/AjaxCustomFormsExplorerController.php index d4e3e152..901e6004 100644 --- a/src/AjaxControllers/AjaxCustomFormsExplorerController.php +++ b/src/AjaxControllers/AjaxCustomFormsExplorerController.php @@ -13,6 +13,9 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Themes\Rozier\Models\CustomFormModel; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxCustomFormsExplorerController extends AbstractAjaxController { private UrlGeneratorInterface $urlGenerator; @@ -27,9 +30,9 @@ public function __construct(UrlGeneratorInterface $urlGenerator) * * @return Response JSON response */ - public function indexAction(Request $request): Response + public function indexAction(Request $request) { - $this->denyAccessUnlessGranted('ROLE_ACCESS_CUSTOMFORMS'); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); $arrayFilter = []; /* @@ -65,15 +68,15 @@ public function indexAction(Request $request): Response * Get a CustomForm list from an array of id. * * @param Request $request - * @return Response + * @return JsonResponse */ - public function listAction(Request $request): Response + public function listAction(Request $request) { if (!$request->query->has('ids')) { throw new InvalidParameterException('Ids should be provided within an array'); } - $this->denyAccessUnlessGranted('ROLE_ACCESS_CUSTOMFORMS'); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); $cleanCustomFormsIds = array_filter($request->query->filter('ids', [], \FILTER_DEFAULT, [ 'flags' => \FILTER_FORCE_ARRAY diff --git a/src/AjaxControllers/AjaxDocumentsExplorerController.php b/src/AjaxControllers/AjaxDocumentsExplorerController.php index dfc32b9b..c816d16a 100644 --- a/src/AjaxControllers/AjaxDocumentsExplorerController.php +++ b/src/AjaxControllers/AjaxDocumentsExplorerController.php @@ -16,6 +16,9 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Themes\Rozier\Models\DocumentModel; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxDocumentsExplorerController extends AbstractAjaxController { private RendererInterface $renderer; diff --git a/src/AjaxControllers/AjaxEntitiesExplorerController.php b/src/AjaxControllers/AjaxEntitiesExplorerController.php index d22ae0c1..4557121d 100644 --- a/src/AjaxControllers/AjaxEntitiesExplorerController.php +++ b/src/AjaxControllers/AjaxEntitiesExplorerController.php @@ -26,6 +26,9 @@ use Themes\Rozier\Explorer\SettingExplorerItem; use Themes\Rozier\Explorer\UserExplorerItem; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxEntitiesExplorerController extends AbstractAjaxController { private RendererInterface $renderer; @@ -134,7 +137,7 @@ public function listAction(Request $request): JsonResponse throw new InvalidParameterException('Ids should be provided within an array'); } - $this->denyAccessUnlessGranted('ROLE_BACKEND_USER'); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); /** @var EntityManager $em */ $em = $this->em(); diff --git a/src/AjaxControllers/AjaxExplorerProviderController.php b/src/AjaxControllers/AjaxExplorerProviderController.php index c09b77cd..b9ee2e04 100644 --- a/src/AjaxControllers/AjaxExplorerProviderController.php +++ b/src/AjaxControllers/AjaxExplorerProviderController.php @@ -4,9 +4,7 @@ namespace Themes\Rozier\AjaxControllers; -use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; -use Psr\Container\NotFoundExceptionInterface; use RZ\Roadiz\CoreBundle\Explorer\AbstractExplorerProvider; use RZ\Roadiz\CoreBundle\Explorer\ExplorerItemInterface; use RZ\Roadiz\CoreBundle\Explorer\ExplorerProviderInterface; @@ -15,6 +13,9 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Exception\InvalidParameterException; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxExplorerProviderController extends AbstractAjaxController { private ContainerInterface $psrContainer; @@ -25,10 +26,10 @@ public function __construct(ContainerInterface $psrContainer) } /** - * @param class-string $providerClass + * @param class-string $providerClass * @return ExplorerProviderInterface - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface */ protected function getProvider(string $providerClass): ExplorerProviderInterface { @@ -37,47 +38,27 @@ protected function getProvider(string $providerClass): ExplorerProviderInterface } return new $providerClass(); } - /** - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface + * @param Request $request + * @return Response JSON response */ - protected function getProviderFromRequest(Request $request): ExplorerProviderInterface + public function indexAction(Request $request) { - /** @var class-string|null $providerClass */ - $providerClass = $request->query->get('providerClass'); + $this->denyAccessUnlessGranted('ROLE_BACKEND_USER'); - if (!\is_string($providerClass)) { + if (!$request->query->has('providerClass')) { throw new InvalidParameterException('providerClass parameter is missing.'); } - if (!\class_exists($providerClass)) { - throw new InvalidParameterException('providerClass is not a valid class.'); - } - $reflection = new \ReflectionClass($providerClass); - if (!$reflection->implementsInterface(ExplorerProviderInterface::class)) { - throw new InvalidParameterException('providerClass is not a valid ExplorerProviderInterface class.'); + $providerClass = $request->query->get('providerClass'); + if (!class_exists($providerClass)) { + throw new InvalidParameterException('providerClass is not a valid class.'); } $provider = $this->getProvider($providerClass); if ($provider instanceof AbstractExplorerProvider) { $provider->setContainer($this->psrContainer); } - - return $provider; - } - - /** - * @param Request $request - * @return JsonResponse - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - public function indexAction(Request $request): JsonResponse - { - $this->denyAccessUnlessGranted('ROLE_BACKEND_USER'); - - $provider = $this->getProviderFromRequest($request); $options = [ 'page' => $request->query->get('page') ?: 1, 'itemPerPage' => $request->query->get('itemPerPage') ?: 30, @@ -118,14 +99,28 @@ public function indexAction(Request $request): JsonResponse * * @param Request $request * @return JsonResponse - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ - public function listAction(Request $request): JsonResponse + public function listAction(Request $request) { + if (!$request->query->has('providerClass')) { + throw new InvalidParameterException('providerClass parameter is missing.'); + } + + $providerClass = $request->query->get('providerClass'); + if (!class_exists($providerClass)) { + throw new InvalidParameterException('providerClass is not a valid class.'); + } + + if (!$request->query->has('ids')) { + throw new InvalidParameterException('Ids should be provided within an array'); + } + $this->denyAccessUnlessGranted('ROLE_BACKEND_USER'); - $provider = $this->getProviderFromRequest($request); + $provider = $this->getProvider($providerClass); + if ($provider instanceof AbstractExplorerProvider) { + $provider->setContainer($this->psrContainer); + } $entitiesArray = []; $cleanNodeIds = array_filter($request->query->filter('ids', [], \FILTER_DEFAULT, [ 'flags' => \FILTER_FORCE_ARRAY diff --git a/src/AjaxControllers/AjaxFolderTreeController.php b/src/AjaxControllers/AjaxFolderTreeController.php index c6601439..9644b1e6 100644 --- a/src/AjaxControllers/AjaxFolderTreeController.php +++ b/src/AjaxControllers/AjaxFolderTreeController.php @@ -10,6 +10,9 @@ use Themes\Rozier\Widgets\FolderTreeWidget; use Themes\Rozier\Widgets\TreeWidgetFactory; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxFolderTreeController extends AbstractAjaxController { private TreeWidgetFactory $treeWidgetFactory; diff --git a/src/AjaxControllers/AjaxFoldersController.php b/src/AjaxControllers/AjaxFoldersController.php index 1b5586e2..3162ecc7 100644 --- a/src/AjaxControllers/AjaxFoldersController.php +++ b/src/AjaxControllers/AjaxFoldersController.php @@ -11,6 +11,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxFoldersController extends AbstractAjaxController { private HandlerFactoryInterface $handlerFactory; diff --git a/src/AjaxControllers/AjaxFoldersExplorerController.php b/src/AjaxControllers/AjaxFoldersExplorerController.php index b4a72be2..3617a29a 100644 --- a/src/AjaxControllers/AjaxFoldersExplorerController.php +++ b/src/AjaxControllers/AjaxFoldersExplorerController.php @@ -9,6 +9,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxFoldersExplorerController extends AbstractAjaxController { /** diff --git a/src/AjaxControllers/AjaxNodeTreeController.php b/src/AjaxControllers/AjaxNodeTreeController.php index ece280b4..32533a4e 100644 --- a/src/AjaxControllers/AjaxNodeTreeController.php +++ b/src/AjaxControllers/AjaxNodeTreeController.php @@ -18,6 +18,9 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxNodeTreeController extends AbstractAjaxController { private NodeChrootResolver $nodeChrootResolver; @@ -121,7 +124,7 @@ public function getTreeAction(Request $request): JsonResponse $parent = $this->nodeChrootResolver->getChroot($this->getUser()); } - $nodeTree = $this->treeWidgetFactory->createRootNodeTree($parent, $translation); + $nodeTree = $this->treeWidgetFactory->createNodeTree($parent, $translation); $this->assignation['mainNodeTree'] = true; break; } diff --git a/src/AjaxControllers/AjaxNodeTypeFieldsController.php b/src/AjaxControllers/AjaxNodeTypeFieldsController.php index 060a1ed8..672627ad 100644 --- a/src/AjaxControllers/AjaxNodeTypeFieldsController.php +++ b/src/AjaxControllers/AjaxNodeTypeFieldsController.php @@ -8,6 +8,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxNodeTypeFieldsController extends AjaxAbstractFieldsController { /** diff --git a/src/AjaxControllers/AjaxNodeTypesController.php b/src/AjaxControllers/AjaxNodeTypesController.php index 2fa6ffa4..3f45891e 100644 --- a/src/AjaxControllers/AjaxNodeTypesController.php +++ b/src/AjaxControllers/AjaxNodeTypesController.php @@ -5,7 +5,6 @@ namespace Themes\Rozier\AjaxControllers; use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Exception\NotSupported; use RZ\Roadiz\CoreBundle\Entity\NodeType; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -13,6 +12,9 @@ use Symfony\Component\Routing\Exception\InvalidParameterException; use Themes\Rozier\Models\NodeTypeModel; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxNodeTypesController extends AjaxAbstractFieldsController { /** @@ -20,7 +22,7 @@ class AjaxNodeTypesController extends AjaxAbstractFieldsController * * @return Response JSON response */ - public function indexAction(Request $request): Response + public function indexAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); $arrayFilter = []; @@ -57,9 +59,8 @@ public function indexAction(Request $request): Response * * @param Request $request * @return JsonResponse - * @throws NotSupported */ - public function listAction(Request $request): Response + public function listAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); diff --git a/src/AjaxControllers/AjaxNodesController.php b/src/AjaxControllers/AjaxNodesController.php index bc0ee5c3..802fa451 100644 --- a/src/AjaxControllers/AjaxNodesController.php +++ b/src/AjaxControllers/AjaxNodesController.php @@ -19,14 +19,15 @@ use RZ\Roadiz\CoreBundle\Node\NodeMover; use RZ\Roadiz\CoreBundle\Node\NodeNamePolicyInterface; use RZ\Roadiz\CoreBundle\Node\UniqueNodeGenerator; -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; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Workflow\Registry; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxNodesController extends AbstractAjaxController { private NodeNamePolicyInterface $nodeNamePolicy; @@ -57,16 +58,12 @@ public function __construct( * @param int $nodeId * @return JsonResponse */ - public function getTagsAction(Request $request, int $nodeId): JsonResponse + public function getTagsAction(Request $request, int $nodeId) { + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); $tags = []; - /** @var Node|null $node */ + /** @var Node $node */ $node = $this->em()->find(Node::class, (int) $nodeId); - if (null === $node) { - throw new NotFoundHttpException('Node not found'); - } - - $this->denyAccessUnlessGranted(NodeVoter::READ, $node); /** @var Tag $tag */ foreach ($node->getTags() as $tag) { @@ -83,77 +80,83 @@ public function getTagsAction(Request $request, int $nodeId): JsonResponse * such as coming from node-tree widgets. * * @param Request $request - * @param int|string $nodeId + * @param int $nodeId * * @return Response JSON response */ - public function editAction(Request $request, int|string $nodeId): Response + public function editAction(Request $request, $nodeId) { + /* + * Validate + */ $this->validateRequest($request); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); /** @var Node|null $node */ $node = $this->em()->find(Node::class, (int) $nodeId); - if (null === $node) { - throw $this->createNotFoundException($this->getTranslator()->trans('node.%nodeId%.not_exists', [ - '%nodeId%' => $nodeId, - ])); - } - /* - * Get the right update method against "_action" parameter - */ - switch ($request->get('_action')) { - case 'updatePosition': - $this->denyAccessUnlessGranted(NodeVoter::EDIT_SETTING, $node); - $this->updatePosition($request->request->all(), $node); + if ($node !== null) { + $responseArray = null; + + /* + * Get the right update method against "_action" parameter + */ + switch ($request->get('_action')) { + case 'updatePosition': + $this->updatePosition($request->request->all(), $node); + break; + case 'duplicate': + $duplicator = new NodeDuplicator( + $node, + $this->em(), + $this->nodeNamePolicy + ); + $newNode = $duplicator->duplicate(); + /* + * Dispatch event + */ + $this->dispatchEvent(new NodeCreatedEvent($newNode)); + $this->dispatchEvent(new NodeDuplicatedEvent($newNode)); + + $msg = $this->getTranslator()->trans('duplicated.node.%name%', [ + '%name%' => $node->getNodeName(), + ]); + $this->logger->info($msg, ['source' => $newNode->getNodeSources()->first()]); + + $responseArray = [ + 'statusCode' => '200', + 'status' => 'success', + 'responseText' => $msg, + ]; + break; + } + + if ($responseArray === null) { $responseArray = [ 'statusCode' => '200', 'status' => 'success', - 'responseText' => $this->getTranslator()->trans('node.%name%.was_moved', [ + 'responseText' => $this->getTranslator()->trans('node.%name%.updated', [ '%name%' => $node->getNodeName(), ]), ]; - break; - case 'duplicate': - $this->denyAccessUnlessGranted(NodeVoter::DUPLICATE, $node); - $duplicator = new NodeDuplicator( - $node, - $this->em(), - $this->nodeNamePolicy - ); - $newNode = $duplicator->duplicate(); - /* - * Dispatch event - */ - $this->dispatchEvent(new NodeCreatedEvent($newNode)); - $this->dispatchEvent(new NodeDuplicatedEvent($newNode)); - - $msg = $this->getTranslator()->trans('duplicated.node.%name%', [ - '%name%' => $node->getNodeName(), - ]); - $this->logger->info($msg, ['entity' => $newNode->getNodeSources()->first()]); + } - $responseArray = [ - 'statusCode' => '200', - 'status' => 'success', - 'responseText' => $msg, - ]; - break; - default: - throw new BadRequestHttpException('Action is not defined.'); + return new JsonResponse( + $responseArray, + Response::HTTP_PARTIAL_CONTENT + ); } - return new JsonResponse( - $responseArray, - Response::HTTP_PARTIAL_CONTENT - ); + throw $this->createNotFoundException($this->getTranslator()->trans('node.%nodeId%.not_exists', [ + '%nodeId%' => $nodeId, + ])); } /** * @param array $parameters * @param Node $node */ - protected function updatePosition(array $parameters, Node $node): void + protected function updatePosition($parameters, Node $node): void { if ($node->isLocked()) { throw new BadRequestHttpException('Locked node cannot be moved.'); @@ -192,11 +195,6 @@ protected function updatePosition(array $parameters, Node $node): void $this->dispatchEvent(new NodesSourcesUpdatedEvent($nodeSource)); } - $msg = $this->getTranslator()->trans('node.%name%.was_moved', [ - '%name%' => $node->getNodeName(), - ]); - $this->logger->info($msg, ['entity' => $node->getNodeSources()->first() ?: $node]); - $this->em()->flush(); } @@ -256,6 +254,7 @@ protected function parsePosition(array $parameters, float $default = 0.0): float public function statusesAction(Request $request): JsonResponse { $this->validateRequest($request); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); if ($request->get('nodeId', 0) <= 0) { throw new BadRequestHttpException($this->getTranslator()->trans('node.id.not_specified')); @@ -269,8 +268,6 @@ public function statusesAction(Request $request): JsonResponse ])); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_STATUS, $node); - $availableStatuses = [ 'visible' => 'setVisible', 'locked' => 'setLocked', @@ -309,14 +306,14 @@ public function statusesAction(Request $request): JsonResponse '%name%' => $node->getNodeName(), '%visible%' => $node->isVisible() ? $this->getTranslator()->trans('visible') : $this->getTranslator()->trans('invisible'), ]); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: $node); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); $this->dispatchEvent(new NodeVisibilityChangedEvent($node)); } else { $msg = $this->getTranslator()->trans('node.%name%.%field%.updated', [ '%name%' => $node->getNodeName(), '%field%' => $request->get('statusName'), ]); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: $node); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); } $this->dispatchEvent(new NodeUpdatedEvent($node)); $this->em()->flush(); @@ -360,7 +357,7 @@ protected function changeNodeStatus(Node $node, string $transition): JsonRespons '%name%' => $node->getNodeName(), '%status%' => $this->getTranslator()->trans(Node::getStatusLabel($node->getStatus())), ]); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: $node); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); return new JsonResponse( [ @@ -384,9 +381,9 @@ public function quickAddAction(Request $request): JsonResponse * Validate */ $this->validateRequest($request); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); try { - // Access security is handled by UniqueNodeGenerator $source = $this->uniqueNodeGenerator->generateFromRequest($request); /* diff --git a/src/AjaxControllers/AjaxNodesExplorerController.php b/src/AjaxControllers/AjaxNodesExplorerController.php index 9911551e..834fc240 100644 --- a/src/AjaxControllers/AjaxNodesExplorerController.php +++ b/src/AjaxControllers/AjaxNodesExplorerController.php @@ -14,39 +14,34 @@ use RZ\Roadiz\CoreBundle\EntityApi\NodeTypeApi; use RZ\Roadiz\CoreBundle\SearchEngine\ClientRegistry; use RZ\Roadiz\CoreBundle\SearchEngine\NodeSourceSearchHandlerInterface; -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\Routing\Exception\InvalidParameterException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; use Themes\Rozier\Models\NodeModel; use Themes\Rozier\Models\NodeSourceModel; -final class AjaxNodesExplorerController extends AbstractAjaxController +class AjaxNodesExplorerController extends AbstractAjaxController { private SerializerInterface $serializer; private ClientRegistry $clientRegistry; private NodeSourceSearchHandlerInterface $nodeSourceSearchHandler; private NodeTypeApi $nodeTypeApi; private UrlGeneratorInterface $urlGenerator; - private Security $security; public function __construct( SerializerInterface $serializer, ClientRegistry $clientRegistry, NodeSourceSearchHandlerInterface $nodeSourceSearchHandler, NodeTypeApi $nodeTypeApi, - UrlGeneratorInterface $urlGenerator, - Security $security, + UrlGeneratorInterface $urlGenerator ) { $this->nodeSourceSearchHandler = $nodeSourceSearchHandler; $this->nodeTypeApi = $nodeTypeApi; $this->serializer = $serializer; $this->urlGenerator = $urlGenerator; $this->clientRegistry = $clientRegistry; - $this->security = $security; } protected function getItemPerPage(): int @@ -66,8 +61,7 @@ protected function isSearchEngineAvailable(Request $request): bool */ public function indexAction(Request $request): Response { - // Only requires Search permission for nodes - $this->denyAccessUnlessGranted(NodeVoter::SEARCH); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); $criteria = $this->parseFilterFromRequest($request); $sorting = $this->parseSortingFromRequest($request); @@ -227,8 +221,7 @@ protected function getSolrSearchResults( */ public function listAction(Request $request): JsonResponse { - // Only requires Search permission for nodes - $this->denyAccessUnlessGranted(NodeVoter::SEARCH); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); if (!$request->query->has('ids')) { throw new InvalidParameterException('Ids should be provided within an array'); @@ -263,10 +256,10 @@ public function listAction(Request $request): JsonResponse /** * Normalize response Node list result. * - * @param iterable $nodes + * @param array|\Traversable $nodes * @return array */ - private function normalizeNodes(iterable $nodes): array + private function normalizeNodes($nodes) { $nodesArray = []; @@ -274,12 +267,12 @@ private function normalizeNodes(iterable $nodes): array if (null !== $node) { if ($node instanceof NodesSources) { if (!key_exists($node->getNode()->getId(), $nodesArray)) { - $nodeModel = new NodeSourceModel($node, $this->urlGenerator, $this->security); + $nodeModel = new NodeSourceModel($node, $this->urlGenerator); $nodesArray[$node->getNode()->getId()] = $nodeModel->toArray(); } } else { if (!key_exists($node->getId(), $nodesArray)) { - $nodeModel = new NodeModel($node, $this->urlGenerator, $this->security); + $nodeModel = new NodeModel($node, $this->urlGenerator); $nodesArray[$node->getId()] = $nodeModel->toArray(); } } diff --git a/src/AjaxControllers/AjaxSearchNodesSourcesController.php b/src/AjaxControllers/AjaxSearchNodesSourcesController.php index 298d3083..3757ab14 100644 --- a/src/AjaxControllers/AjaxSearchNodesSourcesController.php +++ b/src/AjaxControllers/AjaxSearchNodesSourcesController.php @@ -8,35 +8,36 @@ use RZ\Roadiz\CoreBundle\Entity\NodesSourcesDocuments; use RZ\Roadiz\CoreBundle\Entity\Translation; use RZ\Roadiz\CoreBundle\SearchEngine\GlobalNodeSourceSearchHandler; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use RZ\Roadiz\Documents\UrlGenerators\DocumentUrlGeneratorInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\Security\Core\Security; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxSearchNodesSourcesController extends AbstractAjaxController { - public const RESULT_COUNT = 10; + public const RESULT_COUNT = 8; + private DocumentUrlGeneratorInterface $documentUrlGenerator; - public function __construct( - private DocumentUrlGeneratorInterface $documentUrlGenerator, - private Security $security - ) { + public function __construct(DocumentUrlGeneratorInterface $documentUrlGenerator) + { + $this->documentUrlGenerator = $documentUrlGenerator; } /** * Handle AJAX edition requests for Node - * such as coming from node-tree widgets. + * such as coming from nodetree widgets. * * @param Request $request * * @return Response JSON response */ - public function searchAction(Request $request): Response + public function searchAction(Request $request) { - $this->denyAccessUnlessGranted(NodeVoter::SEARCH); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); if (!$request->query->has('searchTerms') || $request->query->get('searchTerms') == '') { throw new BadRequestHttpException('searchTerms parameter is missing.'); @@ -45,13 +46,13 @@ public function searchAction(Request $request): Response $searchHandler = new GlobalNodeSourceSearchHandler($this->em()); $searchHandler->setDisplayNonPublishedNodes(true); - /** @var array $nodesSources */ + /** @var array $nodesSources */ $nodesSources = $searchHandler->getNodeSourcesBySearchTerm( $request->get('searchTerms'), static::RESULT_COUNT ); - if (count($nodesSources) > 0) { + if (null !== $nodesSources && count($nodesSources) > 0) { $responseArray = [ 'statusCode' => Response::HTTP_OK, 'status' => 'success', @@ -62,7 +63,6 @@ public function searchAction(Request $request): Response foreach ($nodesSources as $source) { if ( $source instanceof NodesSources && - $this->security->isGranted(NodeVoter::READ, $source) && !key_exists($source->getNode()->getId(), $responseArray['data']) ) { $responseArray['data'][$source->getNode()->getId()] = $this->getNodeSourceData($source); diff --git a/src/AjaxControllers/AjaxTagTreeController.php b/src/AjaxControllers/AjaxTagTreeController.php index f5d4598d..304a1c90 100644 --- a/src/AjaxControllers/AjaxTagTreeController.php +++ b/src/AjaxControllers/AjaxTagTreeController.php @@ -10,6 +10,9 @@ use Themes\Rozier\Widgets\TagTreeWidget; use Themes\Rozier\Widgets\TreeWidgetFactory; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxTagTreeController extends AbstractAjaxController { private TreeWidgetFactory $treeWidgetFactory; diff --git a/src/AjaxControllers/AjaxTagsController.php b/src/AjaxControllers/AjaxTagsController.php index 20f2e836..6b4b0a91 100644 --- a/src/AjaxControllers/AjaxTagsController.php +++ b/src/AjaxControllers/AjaxTagsController.php @@ -19,6 +19,9 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Themes\Rozier\Models\TagModel; +/** + * @package Themes\Rozier\AjaxControllers + */ class AjaxTagsController extends AbstractAjaxController { private HandlerFactoryInterface $handlerFactory; diff --git a/src/Controllers/AbstractAdminController.php b/src/Controllers/AbstractAdminController.php index ce321c36..f27a505a 100644 --- a/src/Controllers/AbstractAdminController.php +++ b/src/Controllers/AbstractAdminController.php @@ -4,7 +4,6 @@ namespace Themes\Rozier\Controllers; -use Doctrine\Persistence\ObjectRepository; use JMS\Serializer\SerializationContext; use JMS\Serializer\SerializerInterface; use RZ\Roadiz\Core\AbstractEntities\PersistableInterface; @@ -63,39 +62,6 @@ protected function prepareWorkingItem(PersistableInterface $item): void // Add or modify current working item. } - protected function getRepository(): ObjectRepository - { - return $this->em()->getRepository($this->getEntityClass()); - } - - /** - * @return string - */ - protected function getRequiredDeletionRole(): string - { - return $this->getRequiredRole(); - } - - protected function getRequiredListingRole(): string - { - return $this->getRequiredRole(); - } - - protected function getRequiredCreationRole(): string - { - return $this->getRequiredRole(); - } - - protected function getRequiredEditionRole(): string - { - return $this->getRequiredRole(); - } - - protected function getRequiredExportRole(): string - { - return $this->getRequiredRole(); - } - /** * @param Request $request * @return Response|null @@ -103,7 +69,7 @@ protected function getRequiredExportRole(): string */ public function defaultAction(Request $request) { - $this->denyAccessUnlessGranted($this->getRequiredListingRole()); + $this->denyAccessUnlessGranted($this->getRequiredRole()); $this->additionalAssignation($request); $elm = $this->createEntityListManager( @@ -137,7 +103,7 @@ public function defaultAction(Request $request) */ public function addAction(Request $request) { - $this->denyAccessUnlessGranted($this->getRequiredCreationRole()); + $this->denyAccessUnlessGranted($this->getRequiredRole()); $this->additionalAssignation($request); $item = $this->createEmptyItem($request); @@ -166,7 +132,7 @@ public function addAction(Request $request) '%namespace%' => $this->getTranslator()->trans($this->getNamespace()) ] ); - $this->publishConfirmMessage($request, $msg, $item); + $this->publishConfirmMessage($request, $msg); return $this->getPostSubmitResponse($item, true, $request); } @@ -190,7 +156,7 @@ public function addAction(Request $request) */ public function editAction(Request $request, $id) { - $this->denyAccessUnlessGranted($this->getRequiredEditionRole()); + $this->denyAccessUnlessGranted($this->getRequiredRole()); $this->additionalAssignation($request); /** @var mixed|object|null $item */ @@ -227,7 +193,7 @@ public function editAction(Request $request, $id) '%namespace%' => $this->getTranslator()->trans($this->getNamespace()) ] ); - $this->publishConfirmMessage($request, $msg, $item); + $this->publishConfirmMessage($request, $msg); return $this->getPostSubmitResponse($item, false, $request); } @@ -245,10 +211,10 @@ public function editAction(Request $request, $id) public function exportAction(Request $request): JsonResponse { - $this->denyAccessUnlessGranted($this->getRequiredExportRole()); + $this->denyAccessUnlessGranted($this->getRequiredRole()); $this->additionalAssignation($request); - $items = $this->getRepository()->findAll(); + $items = $this->em()->getRepository($this->getEntityClass())->findAll(); return new JsonResponse( $this->serializer->serialize( @@ -312,7 +278,7 @@ public function deleteAction(Request $request, $id) '%namespace%' => $this->getTranslator()->trans($this->getNamespace()) ] ); - $this->publishConfirmMessage($request, $msg, $item); + $this->publishConfirmMessage($request, $msg); return $this->getPostDeleteResponse($item); } @@ -356,7 +322,15 @@ abstract protected function getTemplateFolder(): string; abstract protected function getRequiredRole(): string; /** - * @return class-string + * @return string + */ + protected function getRequiredDeletionRole(): string + { + return $this->getRequiredRole(); + } + + /** + * @return class-string */ abstract protected function getEntityClass(): string; @@ -424,26 +398,14 @@ protected function getPostSubmitResponse( bool $forceDefaultEditRoute = false, ?Request $request = null ): Response { - if (null === $request) { - // Redirect to default route if no request provided - return $this->redirect($this->urlGenerator->generate( - $this->getEditRouteName(), - $this->getEditRouteParameters($item) - )); - } - - $route = $request->attributes->get('_route'); - $referrer = $request->query->get('referer'); - /* * Force redirect to avoid resending form when refreshing page */ if ( - \is_string($referrer) && - $referrer !== '' && - (new UnicodeString($referrer))->trim()->startsWith('/') + null !== $request && $request->query->has('referer') && + (new UnicodeString($request->query->get('referer')))->startsWith('/') ) { - return $this->redirect($referrer); + return $this->redirect($request->query->get('referer')); } /* @@ -451,8 +413,8 @@ protected function getPostSubmitResponse( */ if ( false === $forceDefaultEditRoute && - \is_string($route) && - $route !== '' + null !== $request && + null !== $route = $request->attributes->get('_route') ) { return $this->redirect($this->urlGenerator->generate( $route, @@ -487,27 +449,21 @@ protected function getPostDeleteResponse(PersistableInterface $item): Response } /** - * @template T of object|Event - * @param T|iterable|array|null $event - * @return T|iterable|array|null + * @param Event|Event[]|mixed|null $event + * @return object|object[]|null */ - protected function dispatchSingleOrMultipleEvent(mixed $event): mixed + protected function dispatchSingleOrMultipleEvent($event) { if (null === $event) { return null; } if ($event instanceof Event) { - // @phpstan-ignore-next-line return $this->dispatchEvent($event); } - if (\is_iterable($event)) { + if (is_iterable($event)) { $events = []; - /** @var T|null $singleEvent */ foreach ($event as $singleEvent) { - $returningEvent = $this->dispatchSingleOrMultipleEvent($singleEvent); - if ($returningEvent instanceof Event) { - $events[] = $returningEvent; - } + $events[] = $this->dispatchSingleOrMultipleEvent($singleEvent); } return $events; } diff --git a/src/Controllers/AbstractAdminWithBulkController.php b/src/Controllers/AbstractAdminWithBulkController.php deleted file mode 100644 index d7d77f91..00000000 --- a/src/Controllers/AbstractAdminWithBulkController.php +++ /dev/null @@ -1,285 +0,0 @@ -formFactory = $formFactory; - } - - protected function additionalAssignation(Request $request): void - { - parent::additionalAssignation($request); - - $this->assignation['hasBulkActions'] = true; - - if (null !== $this->getBulkDeleteRouteName()) { - $bulkDeleteForm = $this->createDeleteBulkForm(true); - $this->assignation['bulkDeleteForm'] = $bulkDeleteForm->createView(); - $this->assignation['hasBulkActions'] = true; - } - - if (null !== $this->getBulkPublishRouteName()) { - $bulkPublishForm = $this->createPublishBulkForm(true); - $this->assignation['bulkPublishForm'] = $bulkPublishForm->createView(); - $this->assignation['hasBulkActions'] = true; - } - - if (null !== $this->getBulkUnpublishRouteName()) { - $bulkUnpublishForm = $this->createUnpublishBulkForm(true); - $this->assignation['bulkUnpublishForm'] = $bulkUnpublishForm->createView(); - $this->assignation['hasBulkActions'] = true; - } - } - - protected function setPublishedAt(PersistableInterface $item, ?\DateTimeInterface $dateTime): void - { - } - - protected function removeItem(PersistableInterface $item): void - { - $this->em()->remove($item); - } - - protected function getBulkPublishRouteName(): ?string - { - return null; - } - protected function getBulkUnpublishRouteName(): ?string - { - return null; - } - protected function getBulkDeleteRouteName(): ?string - { - return null; - } - - /** - * @param FormInterface|null $form - * @return array - */ - protected function parseFormBulkIds(?FormInterface $form): array - { - if (null === $form) { - return []; - } - $ids = \json_decode($form->getData() ?? '[]'); - return \array_filter($ids, function ($id) { - // Allow int or UUID identifiers - return is_numeric($id) || is_string($id); - }); - } - - protected function bulkAction( - Request $request, - string $requiredRole, - FormInterface $bulkForm, - FormInterface $form, - callable $createBulkFormWithIds, - string $templatePath, - string $confirmMessageTemplate, - callable $alterItemCallable, - string $bulkFormName - ): Response { - $this->denyAccessUnlessGranted($requiredRole); - $bulkForm->handleRequest($request); - $form->handleRequest($request); - - if ($bulkForm->isSubmitted() && $bulkForm->isValid()) { - $ids = $this->parseFormBulkIds($bulkForm->get('id')); - if (count($ids) < 1) { - $bulkForm->addError(new FormError('No item selected.')); - } else { - $items = $this->getRepository()->findBy([ - 'id' => $ids, - ]); - $formWithIds = $createBulkFormWithIds(json_encode($ids)); - if (!$formWithIds instanceof FormInterface) { - throw new \RuntimeException('Invalid form returned.'); - } - $this->assignation['items'] = $items; - $this->assignation['filters'] = ['itemCount' => count($items)]; - $this->assignation['form'] = $formWithIds->createView(); - } - } - - if ($form->isSubmitted() && $form->isValid()) { - $ids = $this->parseFormBulkIds($form->get('id')); - if (count($ids) < 1) { - $form->addError(new FormError('No item selected.')); - } else { - /** @var PersistableInterface[] $items */ - $items = $this->getRepository()->findBy([ - 'id' => $ids, - ]); - foreach ($items as $item) { - if ($this->supports($item)) { - $alterItemCallable($item); - $msg = $this->getTranslator()->trans( - $confirmMessageTemplate, - [ - '%item%' => $this->getEntityName($item), - '%namespace%' => $this->getTranslator()->trans($this->getNamespace()) - ] - ); - $this->publishConfirmMessage($request, $msg, $item); - } - } - $this->em()->flush(); - return $this->redirect($this->urlGenerator->generate($this->getDefaultRouteName())); - } - } - - $this->assignation[$bulkFormName] = $bulkForm->createView(); - - return $this->render( - $templatePath, - $this->assignation, - null, - $this->getTemplateNamespace() - ); - } - - public function bulkDeleteAction(Request $request): Response - { - return $this->bulkAction( - $request, - $this->getRequiredDeletionRole(), - $this->createDeleteBulkForm(true), - $this->createDeleteBulkForm(), - function (string $ids) { - return $this->createDeleteBulkForm(false, [ - 'id' => $ids, - ]); - }, - $this->getTemplateFolder() . '/bulk_delete.html.twig', - '%namespace%.%item%.was_deleted', - function (PersistableInterface $item) { - $this->removeItem($item); - }, - 'bulkDeleteForm' - ); - } - - public function bulkPublishAction(Request $request): Response - { - return $this->bulkAction( - $request, - $this->getRequiredRole(), - $this->createPublishBulkForm(true), - $this->createPublishBulkForm(), - function (string $ids) { - return $this->createPublishBulkForm(false, [ - 'id' => $ids, - ]); - }, - $this->getTemplateFolder() . '/bulk_publish.html.twig', - '%namespace%.%item%.was_published', - function (PersistableInterface $item) { - $this->setPublishedAt($item, new \DateTime('now')); - }, - 'bulkPublishForm' - ); - } - - public function bulkUnpublishAction(Request $request): Response - { - return $this->bulkAction( - $request, - $this->getRequiredRole(), - $this->createUnpublishBulkForm(true), - $this->createUnpublishBulkForm(), - function (string $ids) { - return $this->createUnpublishBulkForm(false, [ - 'id' => $ids, - ]); - }, - $this->getTemplateFolder() . '/bulk_unpublish.html.twig', - '%namespace%.%item%.was_unpublished', - function (PersistableInterface $item) { - $this->setPublishedAt($item, null); - }, - 'bulkUnpublishForm' - ); - } - - protected function createBulkForm( - ?string $routeName, - string $formName, - bool $get = false, - ?array $data = null - ): FormInterface { - if (null === $routeName) { - throw new \RuntimeException('Bulk delete route name is not defined.'); - } - - if ($get) { - $options = [ - 'action' => $this->generateUrl($routeName), - 'method' => 'GET', - ]; - } else { - $options = [ - 'action' => $this->generateUrl($routeName), - 'method' => 'POST', - ]; - } - return $this->formFactory->createNamedBuilder($formName, FormType::class, $data, $options) - ->add('id', HiddenType::class, [ - 'attr' => [ - 'class' => 'bulk-form-value' - ] - ])->getForm(); - } - - protected function createDeleteBulkForm(bool $get = false, ?array $data = null): FormInterface - { - return $this->createBulkForm( - $this->getBulkDeleteRouteName(), - 'bulk-delete', - $get, - $data - ); - } - - protected function createPublishBulkForm(bool $get = false, ?array $data = null): FormInterface - { - return $this->createBulkForm( - $this->getBulkPublishRouteName(), - 'bulk-publish', - $get, - $data - ); - } - - protected function createUnpublishBulkForm(bool $get = false, ?array $data = null): FormInterface - { - return $this->createBulkForm( - $this->getBulkUnpublishRouteName(), - 'bulk-unpublish', - $get, - $data - ); - } -} diff --git a/src/Controllers/Attributes/AttributeController.php b/src/Controllers/Attributes/AttributeController.php index 2cd71934..dcfe7c0d 100644 --- a/src/Controllers/Attributes/AttributeController.php +++ b/src/Controllers/Attributes/AttributeController.php @@ -11,25 +11,22 @@ use RZ\Roadiz\CoreBundle\Form\AttributeType; use RZ\Roadiz\CoreBundle\Importer\AttributeImporter; use Symfony\Component\Form\FormError; -use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Themes\Rozier\Controllers\AbstractAdminWithBulkController; -use Twig\Error\RuntimeError; +use Themes\Rozier\Controllers\AbstractAdminController; -class AttributeController extends AbstractAdminWithBulkController +class AttributeController extends AbstractAdminController { private AttributeImporter $attributeImporter; public function __construct( AttributeImporter $attributeImporter, - FormFactoryInterface $formFactory, SerializerInterface $serializer, UrlGeneratorInterface $urlGenerator ) { - parent::__construct($formFactory, $serializer, $urlGenerator); + parent::__construct($serializer, $urlGenerator); $this->attributeImporter = $attributeImporter; } @@ -42,11 +39,6 @@ protected function supports(PersistableInterface $item): bool return $item instanceof Attribute; } - protected function getBulkDeleteRouteName(): ?string - { - return 'attributesBulkDeletePage'; - } - /** * @inheritDoc */ @@ -144,9 +136,8 @@ protected function getEntityName(PersistableInterface $item): string /** * @param Request $request * @return Response - * @throws RuntimeError */ - public function importAction(Request $request): Response + public function importAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_ACCESS_ATTRIBUTES'); @@ -159,9 +150,6 @@ public function importAction(Request $request): Response if ($file->isValid()) { $serializedData = file_get_contents($file->getPathname()); - if (false === $serializedData) { - throw new \RuntimeException('Cannot read uploaded file.'); - } $this->attributeImporter->import($serializedData); $this->em()->flush(); diff --git a/src/Controllers/Attributes/AttributeGroupController.php b/src/Controllers/Attributes/AttributeGroupController.php index 5fc541c3..69eeb70f 100644 --- a/src/Controllers/Attributes/AttributeGroupController.php +++ b/src/Controllers/Attributes/AttributeGroupController.php @@ -10,6 +10,9 @@ use Symfony\Component\HttpFoundation\Request; use Themes\Rozier\Controllers\AbstractAdminController; +/** + * @package Themes\Rozier\Controllers\Attributes + */ class AttributeGroupController extends AbstractAdminController { /** diff --git a/src/Controllers/CustomForms/CustomFormAnswersController.php b/src/Controllers/CustomForms/CustomFormAnswersController.php index 7aea9cfe..6a015479 100644 --- a/src/Controllers/CustomForms/CustomFormAnswersController.php +++ b/src/Controllers/CustomForms/CustomFormAnswersController.php @@ -16,6 +16,9 @@ use Themes\Rozier\RozierApp; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers + */ class CustomFormAnswersController extends RozierApp { /** @@ -84,7 +87,7 @@ public function deleteAction(Request $request, int $customFormAnswerId): Respons $this->em()->flush(); $msg = $this->getTranslator()->trans('customFormAnswer.%id%.deleted', ['%id%' => $customFormAnswer->getId()]); - $this->publishConfirmMessage($request, $msg, $customFormAnswer); + $this->publishConfirmMessage($request, $msg); /* * Redirect to update schema page */ diff --git a/src/Controllers/CustomForms/CustomFormFieldAttributesController.php b/src/Controllers/CustomForms/CustomFormFieldAttributesController.php index e5747a84..a6fac366 100644 --- a/src/Controllers/CustomForms/CustomFormFieldAttributesController.php +++ b/src/Controllers/CustomForms/CustomFormFieldAttributesController.php @@ -11,6 +11,9 @@ use Themes\Rozier\RozierApp; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers + */ class CustomFormFieldAttributesController extends RozierApp { /** @@ -51,8 +54,8 @@ protected function getAnswersByGroups(iterable $answers): array /** @var CustomFormFieldAttribute $answer */ foreach ($answers as $answer) { $groupName = $answer->getCustomFormField()->getGroupName(); - if (\is_string($groupName) && $groupName !== '') { - if (!isset($fieldsArray[$groupName]) || !\is_array($fieldsArray[$groupName])) { + if ($groupName != '') { + if (!isset($fieldsArray[$groupName])) { $fieldsArray[$groupName] = []; } $fieldsArray[$groupName][] = $answer; diff --git a/src/Controllers/CustomForms/CustomFormFieldsController.php b/src/Controllers/CustomForms/CustomFormFieldsController.php index 66e63255..0897579d 100644 --- a/src/Controllers/CustomForms/CustomFormFieldsController.php +++ b/src/Controllers/CustomForms/CustomFormFieldsController.php @@ -18,6 +18,9 @@ use Themes\Rozier\RozierApp; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers + */ class CustomFormFieldsController extends RozierApp { /** @@ -61,7 +64,6 @@ public function editAction(Request $request, int $customFormFieldId): Response /** @var CustomFormField|null $field */ $field = $this->em()->find(CustomFormField::class, $customFormFieldId); - if ($field === null) { throw new ResourceNotFoundException(); } @@ -75,7 +77,7 @@ public function editAction(Request $request, int $customFormFieldId): Response $this->em()->flush(); $msg = $this->getTranslator()->trans('customFormField.%name%.updated', ['%name%' => $field->getName()]); - $this->publishConfirmMessage($request, $msg, $field); + $this->publishConfirmMessage($request, $msg); /* * Redirect to update schema page @@ -106,14 +108,14 @@ public function addAction(Request $request, int $customFormId): Response { $this->denyAccessUnlessGranted('ROLE_ACCESS_CUSTOMFORMS'); + $field = new CustomFormField(); $customForm = $this->em()->find(CustomForm::class, $customFormId); + $field->setCustomForm($customForm); + if ($customForm === null) { throw new ResourceNotFoundException(); } - $field = new CustomFormField(); - $field->setCustomForm($customForm); - $this->assignation['customForm'] = $customForm; $this->assignation['field'] = $field; $form = $this->createForm(CustomFormFieldType::class, $field); @@ -128,7 +130,7 @@ public function addAction(Request $request, int $customFormId): Response 'customFormField.%name%.created', ['%name%' => $field->getName()] ); - $this->publishConfirmMessage($request, $msg, $field); + $this->publishConfirmMessage($request, $msg); /* * Redirect to update schema page @@ -141,7 +143,7 @@ public function addAction(Request $request, int $customFormId): Response ); } catch (Exception $e) { $msg = $e->getMessage(); - $this->publishErrorMessage($request, $msg, $field); + $this->publishErrorMessage($request, $msg); /* * Redirect to add page */ diff --git a/src/Controllers/CustomForms/CustomFormsController.php b/src/Controllers/CustomForms/CustomFormsController.php index 42537ebf..7956bec1 100644 --- a/src/Controllers/CustomForms/CustomFormsController.php +++ b/src/Controllers/CustomForms/CustomFormsController.php @@ -8,60 +8,96 @@ use RZ\Roadiz\CoreBundle\Entity\CustomForm; use RZ\Roadiz\RozierBundle\Form\CustomFormType; use Symfony\Component\HttpFoundation\Request; -use Themes\Rozier\Controllers\AbstractAdminWithBulkController; +use Themes\Rozier\Controllers\AbstractAdminController; -class CustomFormsController extends AbstractAdminWithBulkController +/** + * @package Themes\Rozier\Controllers + */ +class CustomFormsController extends AbstractAdminController { + /** + * @inheritDoc + */ protected function supports(PersistableInterface $item): bool { return $item instanceof CustomForm; } + /** + * @inheritDoc + */ protected function getNamespace(): string { return 'custom-form'; } + /** + * @inheritDoc + */ protected function createEmptyItem(Request $request): PersistableInterface { return new CustomForm(); } + /** + * @inheritDoc + */ protected function getTemplateFolder(): string { return '@RoadizRozier/custom-forms'; } + /** + * @inheritDoc + */ protected function getRequiredRole(): string { return 'ROLE_ACCESS_CUSTOMFORMS'; } + /** + * @inheritDoc + */ protected function getEntityClass(): string { return CustomForm::class; } + /** + * @inheritDoc + */ protected function getFormType(): string { return CustomFormType::class; } + /** + * @inheritDoc + */ protected function getDefaultOrder(Request $request): array { return ['createdAt' => 'DESC']; } + /** + * @inheritDoc + */ protected function getDefaultRouteName(): string { return 'customFormsHomePage'; } + /** + * @inheritDoc + */ protected function getEditRouteName(): string { return 'customFormsEditPage'; } + /** + * @inheritDoc + */ protected function getEntityName(PersistableInterface $item): string { if ($item instanceof CustomForm) { @@ -69,9 +105,4 @@ protected function getEntityName(PersistableInterface $item): string } throw new \InvalidArgumentException('Item should be instance of ' . $this->getEntityClass()); } - - protected function getBulkDeleteRouteName(): ?string - { - return 'customFormsBulkDeletePage'; - } } diff --git a/src/Controllers/CustomForms/CustomFormsUtilsController.php b/src/Controllers/CustomForms/CustomFormsUtilsController.php index 11516b8a..ba24ba04 100644 --- a/src/Controllers/CustomForms/CustomFormsUtilsController.php +++ b/src/Controllers/CustomForms/CustomFormsUtilsController.php @@ -14,6 +14,9 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Themes\Rozier\RozierApp; +/** + * @package Themes\Rozier\Controllers + */ class CustomFormsUtilsController extends RozierApp { private CustomFormAnswerSerializer $customFormAnswerSerializer; @@ -120,7 +123,7 @@ public function duplicateAction(Request $request, int $id): Response '%name%' => $existingCustomForm->getDisplayName(), ]); - $this->publishConfirmMessage($request, $msg, $newCustomForm); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'customFormsEditPage', @@ -131,10 +134,9 @@ public function duplicateAction(Request $request, int $id): Response $request, $this->getTranslator()->trans("impossible.duplicate.custom.form.%name%", [ '%name%' => $existingCustomForm->getDisplayName(), - ]), - $newCustomForm + ]) ); - $this->publishErrorMessage($request, $e->getMessage(), $existingCustomForm); + $this->publishErrorMessage($request, $e->getMessage()); return $this->redirectToRoute( 'customFormsEditPage', diff --git a/src/Controllers/DashboardController.php b/src/Controllers/DashboardController.php index 5a28614c..81178887 100644 --- a/src/Controllers/DashboardController.php +++ b/src/Controllers/DashboardController.php @@ -4,12 +4,15 @@ namespace Themes\Rozier\Controllers; -use RZ\Roadiz\CoreBundle\Logger\Entity\Log; +use RZ\Roadiz\CoreBundle\Entity\Log; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Themes\Rozier\RozierApp; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers + */ class DashboardController extends RozierApp { /** diff --git a/src/Controllers/Documents/DocumentTranslationsController.php b/src/Controllers/Documents/DocumentTranslationsController.php index 0fcb8cdc..70e81f4d 100644 --- a/src/Controllers/Documents/DocumentTranslationsController.php +++ b/src/Controllers/Documents/DocumentTranslationsController.php @@ -22,6 +22,9 @@ use Themes\Rozier\Traits\VersionedControllerTrait; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers\Documents + */ class DocumentTranslationsController extends RozierApp { use VersionedControllerTrait; @@ -176,13 +179,13 @@ public function deleteAction(Request $request, int $documentId, int $translation 'document.translation.%name%.deleted', ['%name%' => (string) $document] ); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); } catch (Exception $e) { $msg = $this->getTranslator()->trans( 'document.translation.%name%.cannot_delete', ['%name%' => (string) $document] ); - $this->publishErrorMessage($request, $msg, $document); + $this->publishErrorMessage($request, $msg); } /* * Force redirect to avoid resending form when refreshing page @@ -236,7 +239,7 @@ protected function onPostUpdate(PersistableInterface $entity, Request $request): $msg = $this->getTranslator()->trans('document.translation.%name%.updated', [ '%name%' => (string) $entity->getDocument(), ]); - $this->publishConfirmMessage($request, $msg, $entity); + $this->publishConfirmMessage($request, $msg); } } diff --git a/src/Controllers/Documents/DocumentsController.php b/src/Controllers/Documents/DocumentsController.php index b049b5d3..1acffaed 100644 --- a/src/Controllers/Documents/DocumentsController.php +++ b/src/Controllers/Documents/DocumentsController.php @@ -7,6 +7,7 @@ use GuzzleHttp\Exception\RequestException; use League\Flysystem\FilesystemException; use League\Flysystem\FilesystemOperator; +use League\Flysystem\UnableToMoveFile; use Psr\Log\LoggerInterface; use RZ\Roadiz\Core\Handlers\HandlerFactoryInterface; use RZ\Roadiz\CoreBundle\Document\DocumentFactory; @@ -47,7 +48,6 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\String\UnicodeString; use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; @@ -141,16 +141,20 @@ public function indexAction(Request $request, ?int $folderId = null): Response $this->assignation['folder'] = $folder; } - $type = $request->query->get('type', null); - if (\is_string($type) && trim($type) !== '') { - $prefilters['mimeType'] = trim($type); - $this->assignation['mimeType'] = trim($type); + if ( + $request->query->has('type') && + $request->query->get('type', '') !== '' + ) { + $prefilters['mimeType'] = trim($request->query->get('type', '')); + $this->assignation['mimeType'] = trim($request->query->get('type', '')); } - $embedPlatform = $request->query->get('embedPlatform', null); - if (\is_string($embedPlatform) && trim($embedPlatform) !== '') { - $prefilters['embedPlatform'] = trim($embedPlatform); - $this->assignation['embedPlatform'] = trim($embedPlatform); + if ( + $request->query->has('embedPlatform') && + $request->query->get('embedPlatform', '') !== '' + ) { + $prefilters['embedPlatform'] = trim($request->query->get('embedPlatform', '')); + $this->assignation['embedPlatform'] = trim($request->query->get('embedPlatform', '')); } $this->assignation['availablePlatforms'] = $this->documentPlatforms; @@ -336,13 +340,13 @@ public function editAction(Request $request, int $documentId): Response $this->dispatchEvent( new DocumentFileUpdatedEvent($document) ); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); } $msg = $this->getTranslator()->trans('document.%name%.updated', [ '%name%' => (string) $document, ]); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); $this->em()->flush(); // Event must be dispatched AFTER flush for async concurrency matters $this->dispatchEvent( @@ -415,13 +419,13 @@ public function deleteAction(Request $request, int $documentId): Response $msg = $this->getTranslator()->trans('document.%name%.deleted', [ '%name%' => (string) $document ]); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); } catch (\Exception $e) { $msg = $this->getTranslator()->trans('document.%name%.cannot_delete', [ '%name%' => (string) $document ]); $this->logger->error($e->getMessage()); - $this->publishErrorMessage($request, $msg, $document); + $this->publishErrorMessage($request, $msg); } /* * Force redirect to avoid resending form when refreshing page @@ -473,7 +477,7 @@ public function bulkDeleteAction(Request $request): Response 'document.%name%.deleted', ['%name%' => (string) $document] ); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); } $this->em()->flush(); @@ -519,18 +523,18 @@ public function embedAction(Request $request, ?int $folderId = null): Response if (is_iterable($document)) { foreach ($document as $singleDocument) { $msg = $this->getTranslator()->trans('document.%name%.uploaded', [ - '%name%' => (new UnicodeString((string) $singleDocument))->truncate(50, '...')->toString(), + '%name%' => (string) $singleDocument, ]); - $this->publishConfirmMessage($request, $msg, $singleDocument); + $this->publishConfirmMessage($request, $msg); $this->dispatchEvent( new DocumentCreatedEvent($singleDocument) ); } } else { $msg = $this->getTranslator()->trans('document.%name%.uploaded', [ - '%name%' => (new UnicodeString((string) $document))->truncate(50, '...')->toString(), + '%name%' => (string) $document, ]); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); $this->dispatchEvent( new DocumentCreatedEvent($document) ); @@ -578,9 +582,9 @@ public function randomAction(Request $request, ?int $folderId = null): Response $document = $this->randomDocument($folderId); $msg = $this->getTranslator()->trans('document.%name%.uploaded', [ - '%name%' => (new UnicodeString((string) $document))->truncate(50, '...')->toString(), + '%name%' => (string) $document, ]); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); $this->dispatchEvent( new DocumentCreatedEvent($document) @@ -622,31 +626,6 @@ public function downloadAction(Request $request, int $documentId): Response throw new ResourceNotFoundException(); } - /** - * Download document file inline. - * - * @param Request $request - * @param int $documentId - * @return Response - * @throws FilesystemException - */ - public function downloadInlineAction(Request $request, int $documentId): Response - { - $this->denyAccessUnlessGranted('ROLE_ACCESS_DOCUMENTS'); - - /** @var Document|null $document */ - $document = $this->em()->find(Document::class, $documentId); - - if ($document !== null) { - /** @var DocumentHandler $handler */ - $handler = $this->handlerFactory->getHandler($document); - - return $handler->getDownloadResponse(false); - } - - throw new ResourceNotFoundException(); - } - /** * @param Request $request * @param int|null $folderId @@ -676,9 +655,9 @@ public function uploadAction(Request $request, ?int $folderId = null, string $_f if (null !== $document) { $msg = $this->getTranslator()->trans('document.%name%.uploaded', [ - '%name%' => (new UnicodeString((string) $document))->truncate(50, '...')->toString(), + '%name%' => (string) $document, ]); - $this->publishConfirmMessage($request, $msg, $document); + $this->publishConfirmMessage($request, $msg); // Event must be dispatched AFTER flush for async concurrency matters $this->dispatchEvent( @@ -702,7 +681,7 @@ public function uploadAction(Request $request, ?int $folderId = null, string $_f } } else { $msg = $this->getTranslator()->trans('document.cannot_persist'); - $this->publishErrorMessage($request, $msg, $document); + $this->publishErrorMessage($request, $msg); if ($_format === 'json' || $request->isXmlHttpRequest()) { throw $this->createNotFoundException($msg); @@ -718,7 +697,6 @@ public function uploadAction(Request $request, ?int $folderId = null, string $_f /** @var Form $child */ foreach ($form as $child) { if ($child->isSubmitted() && !$child->isValid()) { - /** @var FormError $error */ foreach ($child->getErrors() as $error) { $errorPerForm[$child->getName()][] = $this->getTranslator()->trans($error->getMessage()); } diff --git a/src/Controllers/FoldersController.php b/src/Controllers/FoldersController.php index 92b122db..44f671d1 100644 --- a/src/Controllers/FoldersController.php +++ b/src/Controllers/FoldersController.php @@ -86,7 +86,7 @@ public function addAction(Request $request, ?int $parentFolderId = null): Respon 'folder.%name%.created', ['%name%' => $folder->getFolderName()] ); - $this->publishConfirmMessage($request, $msg, $folder); + $this->publishConfirmMessage($request, $msg); /* * Dispatch event @@ -95,7 +95,7 @@ public function addAction(Request $request, ?int $parentFolderId = null): Respon new FolderCreatedEvent($folder) ); } catch (\RuntimeException $e) { - $this->publishErrorMessage($request, $e->getMessage(), $folder); + $this->publishErrorMessage($request, $e->getMessage()); } return $this->redirectToRoute('foldersHomePage'); @@ -137,7 +137,7 @@ public function deleteAction(Request $request, int $folderId): Response 'folder.%name%.deleted', ['%name%' => $folder->getFolderName()] ); - $this->publishConfirmMessage($request, $msg, $folder); + $this->publishConfirmMessage($request, $msg); /* * Dispatch event @@ -146,7 +146,7 @@ public function deleteAction(Request $request, int $folderId): Response new FolderDeletedEvent($folder) ); } catch (\RuntimeException $e) { - $this->publishErrorMessage($request, $e->getMessage(), $folder); + $this->publishErrorMessage($request, $e->getMessage()); } return $this->redirectToRoute('foldersHomePage'); @@ -193,7 +193,7 @@ public function editAction(Request $request, int $folderId): Response 'folder.%name%.updated', ['%name%' => $folder->getFolderName()] ); - $this->publishConfirmMessage($request, $msg, $folder); + $this->publishConfirmMessage($request, $msg); /* * Dispatch event */ @@ -201,7 +201,7 @@ public function editAction(Request $request, int $folderId): Response new FolderUpdatedEvent($folder) ); } catch (\RuntimeException $e) { - $this->publishErrorMessage($request, $e->getMessage(), $folder); + $this->publishErrorMessage($request, $e->getMessage()); } return $this->redirectToRoute('foldersEditPage', ['folderId' => $folderId]); @@ -277,7 +277,7 @@ public function editTranslationAction(Request $request, int $folderId, int $tran 'folder.%name%.updated', ['%name%' => $folder->getFolderName()] ); - $this->publishConfirmMessage($request, $msg, $folder); + $this->publishConfirmMessage($request, $msg); /* * Dispatch event */ @@ -285,7 +285,7 @@ public function editTranslationAction(Request $request, int $folderId, int $tran new FolderUpdatedEvent($folder) ); } catch (\RuntimeException $e) { - $this->publishErrorMessage($request, $e->getMessage(), $folder); + $this->publishErrorMessage($request, $e->getMessage()); } return $this->redirectToRoute('foldersEditTranslationPage', [ diff --git a/src/Controllers/GroupsController.php b/src/Controllers/GroupsController.php index c4b9e949..61cbc768 100644 --- a/src/Controllers/GroupsController.php +++ b/src/Controllers/GroupsController.php @@ -20,6 +20,9 @@ use Themes\Rozier\Forms\GroupType; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers + */ class GroupsController extends AbstractAdminController { /** @@ -147,7 +150,7 @@ public function editRolesAction(Request $request, int $id): Response '%group%' => $item->getName(), '%role%' => $role->getRole(), ]); - $this->publishConfirmMessage($request, $msg, $role); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'groupsEditRolesPage', @@ -203,7 +206,7 @@ public function removeRolesAction(Request $request, int $id, int $roleId): Respo '%role%' => $role->getRole(), '%group%' => $item->getName(), ]); - $this->publishConfirmMessage($request, $msg, $role); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'groupsEditRolesPage', @@ -251,7 +254,7 @@ public function editUsersAction(Request $request, int $id): Response '%group%' => $item->getName(), '%user%' => $user->getUserName(), ]); - $this->publishConfirmMessage($request, $msg, $user); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'groupsEditUsersPage', @@ -306,7 +309,7 @@ public function removeUsersAction(Request $request, int $id, int $userId): Respo '%user%' => $user->getUserName(), '%group%' => $item->getName(), ]); - $this->publishConfirmMessage($request, $msg, $user); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'groupsEditUsersPage', diff --git a/src/Controllers/GroupsUtilsController.php b/src/Controllers/GroupsUtilsController.php index e090be63..f15b6499 100644 --- a/src/Controllers/GroupsUtilsController.php +++ b/src/Controllers/GroupsUtilsController.php @@ -120,9 +120,6 @@ public function importJsonFileAction(Request $request): Response if ($file->isValid()) { $serializedData = file_get_contents($file->getPathname()); - if (false === $serializedData) { - throw new RuntimeError('Cannot read uploaded file.'); - } if (null !== \json_decode($serializedData)) { $this->groupsImporter->import($serializedData); diff --git a/src/Controllers/HistoryController.php b/src/Controllers/HistoryController.php index acc480ca..56e7a727 100644 --- a/src/Controllers/HistoryController.php +++ b/src/Controllers/HistoryController.php @@ -4,12 +4,12 @@ namespace Themes\Rozier\Controllers; -use Monolog\Logger; +use RZ\Roadiz\CoreBundle\Entity\Log; use RZ\Roadiz\CoreBundle\Entity\User; -use RZ\Roadiz\CoreBundle\Logger\Entity\Log; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Themes\Rozier\RozierApp; use Twig\Error\RuntimeError; @@ -19,14 +19,14 @@ class HistoryController extends RozierApp { public static array $levelToHuman = [ - Logger::EMERGENCY => "emergency", - Logger::CRITICAL => "critical", - Logger::ALERT => "alert", - Logger::ERROR => "error", - Logger::WARNING => "warning", - Logger::NOTICE => "notice", - Logger::INFO => "info", - Logger::DEBUG => "debug", + Log::EMERGENCY => "emergency", + Log::CRITICAL => "critical", + Log::ALERT => "alert", + Log::ERROR => "error", + Log::WARNING => "warning", + Log::NOTICE => "notice", + Log::INFO => "info", + Log::DEBUG => "debug", ]; /** @@ -64,20 +64,20 @@ public function indexAction(Request $request): Response * List user logs action. * * @param Request $request - * @param int|string $userId + * @param int $userId * * @return Response * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Doctrine\ORM\TransactionRequiredException */ - public function userAction(Request $request, int|string $userId): Response + public function userAction(Request $request, int $userId): Response { $this->denyAccessUnlessGranted(['ROLE_BACKEND_USER', 'ROLE_ACCESS_LOGS']); if ( !($this->isGranted(['ROLE_ACCESS_USERS', 'ROLE_ACCESS_LOGS']) - || ($this->getUser() instanceof User && $this->getUser()->getId() === $userId)) + || ($this->getUser() instanceof User && $this->getUser()->getId() == $userId)) ) { throw $this->createAccessDeniedException("You don't have access to this page: ROLE_ACCESS_USERS"); } @@ -94,7 +94,7 @@ public function userAction(Request $request, int|string $userId): Response */ $listManager = $this->createEntityListManager( Log::class, - ['userId' => $user->getId()], + ['user' => $user], ['datetime' => 'DESC'] ); $listManager->setDisplayingNotPublishedNodes(true); diff --git a/src/Controllers/NodeTypeFieldsController.php b/src/Controllers/NodeTypeFieldsController.php index fad5a557..5596ab58 100644 --- a/src/Controllers/NodeTypeFieldsController.php +++ b/src/Controllers/NodeTypeFieldsController.php @@ -12,7 +12,6 @@ use Symfony\Component\Form\FormError; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -23,12 +22,10 @@ class NodeTypeFieldsController extends RozierApp { private MessageBusInterface $messageBus; - private KernelInterface $kernel; - public function __construct(KernelInterface $kernel, MessageBusInterface $messageBus) + public function __construct(MessageBusInterface $messageBus) { $this->messageBus = $messageBus; - $this->kernel = $kernel; } /** @@ -82,25 +79,21 @@ public function editAction(Request $request, int $nodeTypeFieldId): Response $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - if (!$this->kernel->isDebug()) { - $form->addError(new FormError('You cannot edit node-type fields in production.')); - } else { - $this->em()->flush(); + $this->em()->flush(); - /** @var NodeType $nodeType */ - $nodeType = $field->getNodeType(); - $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeType->getId()))); + /** @var NodeType $nodeType */ + $nodeType = $field->getNodeType(); + $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeType->getId()))); - $msg = $this->getTranslator()->trans('nodeTypeField.%name%.updated', ['%name%' => $field->getName()]); - $this->publishConfirmMessage($request, $msg, $field); + $msg = $this->getTranslator()->trans('nodeTypeField.%name%.updated', ['%name%' => $field->getName()]); + $this->publishConfirmMessage($request, $msg); - return $this->redirectToRoute( - 'nodeTypeFieldsEditPage', - [ - 'nodeTypeFieldId' => $nodeTypeFieldId, - ] - ); - } + return $this->redirectToRoute( + 'nodeTypeFieldsEditPage', + [ + 'nodeTypeFieldId' => $nodeTypeFieldId, + ] + ); } $this->assignation['form'] = $form->createView(); @@ -137,37 +130,31 @@ public function addAction(Request $request, int $nodeTypeId): Response $this->assignation['nodeType'] = $nodeType; $this->assignation['field'] = $field; - $form = $this->createForm(NodeTypeFieldType::class, $field, [ - 'disabled' => !$this->kernel->isDebug() - ]); + $form = $this->createForm(NodeTypeFieldType::class, $field); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - if (!$this->kernel->isDebug()) { - $form->addError(new FormError('You cannot add node-type fields in production.')); - } else { - try { - $this->em()->persist($field); - $this->em()->flush(); - $this->em()->refresh($nodeType); - - $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeType->getId()))); - - $msg = $this->getTranslator()->trans( - 'nodeTypeField.%name%.created', - ['%name%' => $field->getName()] - ); - $this->publishConfirmMessage($request, $msg, $field); - - return $this->redirectToRoute( - 'nodeTypeFieldsListPage', - [ - 'nodeTypeId' => $nodeTypeId, - ] - ); - } catch (Exception $e) { - $form->addError(new FormError($e->getMessage())); - } + try { + $this->em()->persist($field); + $this->em()->flush(); + $this->em()->refresh($nodeType); + + $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeType->getId()))); + + $msg = $this->getTranslator()->trans( + 'nodeTypeField.%name%.created', + ['%name%' => $field->getName()] + ); + $this->publishConfirmMessage($request, $msg); + + return $this->redirectToRoute( + 'nodeTypeFieldsListPage', + [ + 'nodeTypeId' => $nodeTypeId, + ] + ); + } catch (Exception $e) { + $form->addError(new FormError($e->getMessage())); } } @@ -198,30 +185,26 @@ public function deleteAction(Request $request, int $nodeTypeFieldId): Response $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - if (!$this->kernel->isDebug()) { - $form->addError(new FormError('You cannot delete node-type fields in production.')); - } else { - /** @var NodeType $nodeType */ - $nodeType = $field->getNodeType(); - $nodeTypeId = $nodeType->getId(); - $this->em()->remove($field); - $this->em()->flush(); - - $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeTypeId))); - - $msg = $this->getTranslator()->trans( - 'nodeTypeField.%name%.deleted', - ['%name%' => $field->getName()] - ); - $this->publishConfirmMessage($request, $msg, $field); - - return $this->redirectToRoute( - 'nodeTypeFieldsListPage', - [ - 'nodeTypeId' => $nodeTypeId, - ] - ); - } + /** @var NodeType $nodeType */ + $nodeType = $field->getNodeType(); + $nodeTypeId = $nodeType->getId(); + $this->em()->remove($field); + $this->em()->flush(); + + $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeTypeId))); + + $msg = $this->getTranslator()->trans( + 'nodeTypeField.%name%.deleted', + ['%name%' => $field->getName()] + ); + $this->publishConfirmMessage($request, $msg); + + return $this->redirectToRoute( + 'nodeTypeFieldsListPage', + [ + 'nodeTypeId' => $nodeTypeId, + ] + ); } $this->assignation['field'] = $field; diff --git a/src/Controllers/NodeTypes/NodeTypesController.php b/src/Controllers/NodeTypes/NodeTypesController.php index 3926d683..c4de8822 100644 --- a/src/Controllers/NodeTypes/NodeTypesController.php +++ b/src/Controllers/NodeTypes/NodeTypesController.php @@ -12,23 +12,22 @@ use Symfony\Component\Form\FormError; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; use Themes\Rozier\Forms\NodeTypeType; use Themes\Rozier\RozierApp; use Themes\Rozier\Utils\SessionListFilters; -use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers\NodeTypes + */ class NodeTypesController extends RozierApp { private MessageBusInterface $messageBus; - private KernelInterface $kernel; - public function __construct(KernelInterface $kernel, MessageBusInterface $messageBus) + public function __construct(MessageBusInterface $messageBus) { $this->messageBus = $messageBus; - $this->kernel = $kernel; } public function indexAction(Request $request): Response @@ -59,10 +58,12 @@ public function indexAction(Request $request): Response } /** + * Return an edition form for requested node-type. + * * @param Request $request - * @param int $nodeTypeId + * @param int $nodeTypeId + * * @return Response - * @throws RuntimeError */ public function editAction(Request $request, int $nodeTypeId): Response { @@ -81,10 +82,11 @@ public function editAction(Request $request, int $nodeTypeId): Response if ($form->isSubmitted() && $form->isValid()) { try { $this->em()->flush(); + $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeType->getId()))); $msg = $this->getTranslator()->trans('nodeType.%name%.updated', ['%name%' => $nodeType->getName()]); - $this->publishConfirmMessage($request, $msg, $nodeType); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute('nodeTypesEditPage', [ 'nodeTypeId' => $nodeTypeId @@ -101,40 +103,35 @@ public function editAction(Request $request, int $nodeTypeId): Response } /** + * Return an creation form for requested node-type. + * * @param Request $request * * @return Response - * @throws RuntimeError */ public function addAction(Request $request): Response { $this->denyAccessUnlessGranted('ROLE_ACCESS_NODETYPES'); $nodeType = new NodeType(); - $form = $this->createForm(NodeTypeType::class, $nodeType, [ - 'disabled' => !$this->kernel->isDebug() - ]); + $form = $this->createForm(NodeTypeType::class, $nodeType); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - if (!$this->kernel->isDebug()) { - $form->addError(new FormError('You cannot create a node-type in production mode.')); - } else { - try { - $this->em()->persist($nodeType); - $this->em()->flush(); - - $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeType->getId()))); - - $msg = $this->getTranslator()->trans('nodeType.%name%.created', ['%name%' => $nodeType->getName()]); - $this->publishConfirmMessage($request, $msg, $nodeType); - - return $this->redirectToRoute('nodeTypesEditPage', [ - 'nodeTypeId' => $nodeType->getId() - ]); - } catch (EntityAlreadyExistsException $e) { - $form->addError(new FormError($e->getMessage())); - } + try { + $this->em()->persist($nodeType); + $this->em()->flush(); + + $this->messageBus->dispatch(new Envelope(new UpdateNodeTypeSchemaMessage($nodeType->getId()))); + + $msg = $this->getTranslator()->trans('nodeType.%name%.created', ['%name%' => $nodeType->getName()]); + $this->publishConfirmMessage($request, $msg); + + return $this->redirectToRoute('nodeTypesEditPage', [ + 'nodeTypeId' => $nodeType->getId() + ]); + } catch (EntityAlreadyExistsException $e) { + $form->addError(new FormError($e->getMessage())); } } @@ -146,10 +143,9 @@ public function addAction(Request $request): Response /** * @param Request $request - * @param int $nodeTypeId + * @param int $nodeTypeId * * @return Response - * @throws RuntimeError */ public function deleteAction(Request $request, int $nodeTypeId): Response { @@ -166,16 +162,12 @@ public function deleteAction(Request $request, int $nodeTypeId): Response $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - if (!$this->kernel->isDebug()) { - $form->addError(new FormError('You cannot delete a node-type in production mode.')); - } else { - $this->messageBus->dispatch(new Envelope(new DeleteNodeTypeMessage($nodeType->getId()))); + $this->messageBus->dispatch(new Envelope(new DeleteNodeTypeMessage($nodeType->getId()))); - $msg = $this->getTranslator()->trans('nodeType.%name%.deleted', ['%name%' => $nodeType->getName()]); - $this->publishConfirmMessage($request, $msg, $nodeType); + $msg = $this->getTranslator()->trans('nodeType.%name%.deleted', ['%name%' => $nodeType->getName()]); + $this->publishConfirmMessage($request, $msg); - return $this->redirectToRoute('nodeTypesHomePage'); - } + return $this->redirectToRoute('nodeTypesHomePage'); } $this->assignation['form'] = $form->createView(); diff --git a/src/Controllers/NodeTypes/NodeTypesUtilsController.php b/src/Controllers/NodeTypes/NodeTypesUtilsController.php index b7c07e63..f0e656ca 100644 --- a/src/Controllers/NodeTypes/NodeTypesUtilsController.php +++ b/src/Controllers/NodeTypes/NodeTypesUtilsController.php @@ -52,9 +52,9 @@ public function __construct( * @param Request $request * @param int $nodeTypeId * - * @return JsonResponse + * @return Response */ - public function exportJsonFileAction(Request $request, int $nodeTypeId): JsonResponse + public function exportJsonFileAction(Request $request, int $nodeTypeId): Response { $this->denyAccessUnlessGranted('ROLE_ACCESS_NODETYPES'); @@ -71,7 +71,7 @@ public function exportJsonFileAction(Request $request, int $nodeTypeId): JsonRes 'json', SerializationContext::create()->setGroups(['node_type', 'position']) ), - Response::HTTP_OK, + JsonResponse::HTTP_OK, [ 'Content-Disposition' => sprintf('attachment; filename="%s"', $nodeType->getName() . '.json'), ], @@ -83,7 +83,6 @@ public function exportJsonFileAction(Request $request, int $nodeTypeId): JsonRes * @param Request $request * * @return BinaryFileResponse - * @throws RuntimeError */ public function exportDocumentationAction(Request $request): BinaryFileResponse { @@ -92,10 +91,6 @@ public function exportDocumentationAction(Request $request): BinaryFileResponse $documentationGenerator = new DocumentationGenerator($this->nodeTypesBag, $this->getTranslator()); $tmpfname = tempnam(sys_get_temp_dir(), date('Y-m-d-H-i-s') . '.zip'); - if (false === $tmpfname) { - throw new RuntimeError('Unable to create temporary file.'); - } - unlink($tmpfname); // Deprecated: ZipArchive::open(): Using empty file as ZipArchive is deprecated $zipArchive = new ZipArchive(); $zipArchive->open($tmpfname, ZipArchive::CREATE); @@ -166,9 +161,6 @@ public function exportAllAction(Request $request): BinaryFileResponse $zipArchive = new ZipArchive(); $tmpfname = tempnam(sys_get_temp_dir(), date('Y-m-d-H-i-s') . '.zip'); - if (false === $tmpfname) { - throw new RuntimeError('Unable to create temporary file.'); - } unlink($tmpfname); // Deprecated: ZipArchive::open(): Using empty file as ZipArchive is deprecated $zipArchive->open($tmpfname, ZipArchive::CREATE); @@ -220,9 +212,6 @@ public function importJsonFileAction(Request $request): Response if ($file->isValid()) { $serializedData = file_get_contents($file->getPathname()); - if (false === $serializedData) { - throw new RuntimeError('Unable to read uploaded file.'); - } if (null !== json_decode($serializedData)) { $this->nodeTypesImporter->import($serializedData); @@ -249,7 +238,7 @@ public function importJsonFileAction(Request $request): Response /** * @return FormInterface */ - private function buildImportJsonFileForm(): FormInterface + private function buildImportJsonFileForm() { $builder = $this->createFormBuilder() ->add('node_type_file', FileType::class, [ diff --git a/src/Controllers/Nodes/ExportController.php b/src/Controllers/Nodes/ExportController.php index 15549a66..61be7980 100644 --- a/src/Controllers/Nodes/ExportController.php +++ b/src/Controllers/Nodes/ExportController.php @@ -4,11 +4,9 @@ namespace Themes\Rozier\Controllers\Nodes; -use PhpOffice\PhpSpreadsheet\Writer\Exception; use RZ\Roadiz\CoreBundle\Entity\Node; use RZ\Roadiz\CoreBundle\Entity\NodesSources; use RZ\Roadiz\CoreBundle\Entity\Translation; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use RZ\Roadiz\CoreBundle\Xlsx\NodeSourceXlsxSerializer; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -36,10 +34,12 @@ public function __construct(NodeSourceXlsxSerializer $xlsxSerializer) * * @return Response * @throws \PhpOffice\PhpSpreadsheet\Exception - * @throws Exception + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ public function exportAllXlsxAction(Request $request, int $translationId, ?int $parentNodeId = null): Response { + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); + /* * Get translation */ @@ -61,11 +61,8 @@ public function exportAllXlsxAction(Request $request, int $translationId, ?int $ if (null === $parentNode) { throw $this->createNotFoundException(); } - $this->denyAccessUnlessGranted(NodeVoter::READ, $parentNode); $criteria['node.parent'] = $parentNode; $filename = $parentNode->getNodeName() . '-' . date("YmdHis") . '.' . $translation->getLocale() . '.xlsx'; - } else { - $this->denyAccessUnlessGranted(NodeVoter::READ_AT_ROOT); } $sources = $this->em() diff --git a/src/Controllers/Nodes/HistoryController.php b/src/Controllers/Nodes/HistoryController.php index 0ed1677c..54ef618d 100644 --- a/src/Controllers/Nodes/HistoryController.php +++ b/src/Controllers/Nodes/HistoryController.php @@ -4,12 +4,9 @@ namespace Themes\Rozier\Controllers\Nodes; -use Doctrine\ORM\QueryBuilder; +use RZ\Roadiz\CoreBundle\Entity\Log; use RZ\Roadiz\CoreBundle\Entity\Node; use RZ\Roadiz\CoreBundle\Entity\Translation; -use RZ\Roadiz\CoreBundle\ListManager\QueryBuilderListManager; -use RZ\Roadiz\CoreBundle\Logger\Entity\Log; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -17,6 +14,9 @@ use Themes\Rozier\Utils\SessionListFilters; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers\Nodes + */ class HistoryController extends RozierApp { /** @@ -27,26 +27,21 @@ class HistoryController extends RozierApp */ public function historyAction(Request $request, int $nodeId): Response { + $this->denyAccessUnlessGranted(['ROLE_ACCESS_NODES', 'ROLE_ACCESS_LOGS']); /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); if (null === $node) { throw new ResourceNotFoundException(); } - $this->denyAccessUnlessGranted(NodeVoter::READ_LOGS, $node); - $qb = $this->em() - ->getRepository(Log::class) - ->getAllRelatedToNodeQueryBuilder($node); - - $listManager = new QueryBuilderListManager($request, $qb, 'obj'); - $listManager->setSearchingCallable(function (QueryBuilder $queryBuilder, string $search) { - $queryBuilder->andWhere($queryBuilder->expr()->orX( - $queryBuilder->expr()->like('obj.message', ':search'), - $queryBuilder->expr()->like('obj.channel', ':search') - )); - $queryBuilder->setParameter('search', '%' . $search . '%'); - }); + $listManager = $this->createEntityListManager( + Log::class, + [ + 'nodeSource' => $node->getNodeSources()->toArray(), + ], + ['datetime' => 'DESC'] + ); $listManager->setDisplayingNotPublishedNodes(true); $listManager->setDisplayingAllNodesStatuses(true); /* diff --git a/src/Controllers/Nodes/NodesAttributesController.php b/src/Controllers/Nodes/NodesAttributesController.php index 5f11f8bd..fffaead9 100644 --- a/src/Controllers/Nodes/NodesAttributesController.php +++ b/src/Controllers/Nodes/NodesAttributesController.php @@ -9,12 +9,10 @@ use RZ\Roadiz\CoreBundle\Entity\AttributeValueTranslation; use RZ\Roadiz\CoreBundle\Entity\Node; use RZ\Roadiz\CoreBundle\Entity\NodesSources; -use RZ\Roadiz\CoreBundle\Entity\NodeType; use RZ\Roadiz\CoreBundle\Entity\Translation; use RZ\Roadiz\CoreBundle\Event\NodesSources\NodesSourcesUpdatedEvent; use RZ\Roadiz\CoreBundle\Form\AttributeValueTranslationType; use RZ\Roadiz\CoreBundle\Form\AttributeValueType; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -46,6 +44,8 @@ public function __construct(FormFactoryInterface $formFactory) */ public function editAction(Request $request, int $nodeId, int $translationId): Response { + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODE_ATTRIBUTES'); + /** @var Translation|null $translation */ $translation = $this->em()->find(Translation::class, $translationId); /** @var Node|null $node */ @@ -55,8 +55,6 @@ public function editAction(Request $request, int $nodeId, int $translationId): R throw $this->createNotFoundException('Node-source does not exist'); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_ATTRIBUTE, $node); - /** @var NodesSources|null $nodeSource */ $nodeSource = $this->em() ->getRepository(NodesSources::class) @@ -68,22 +66,10 @@ public function editAction(Request $request, int $nodeId, int $translationId): R throw $this->createNotFoundException('Node-source does not exist'); } - if (!$this->isAttributable($node)) { - throw $this->createNotFoundException('Node type is not attributable'); - } - if (null !== $response = $this->handleAddAttributeForm($request, $node, $translation)) { return $response; } - $isJson = - $request->isXmlHttpRequest() || - $request->getRequestFormat('html') === 'json' || - \in_array( - 'application/json', - $request->getAcceptableContentTypes() - ); - $this->assignation['attribute_value_translation_forms'] = []; $attributeValues = $node->getAttributeValues(); /** @var AttributeValue $attributeValue */ @@ -121,11 +107,11 @@ public function editAction(Request $request, int $nodeId, int $translationId): R ); $this->publishConfirmMessage($request, $msg, $nodeSource); - if ($isJson) { + if ($request->isXmlHttpRequest() || $request->getRequestFormat('html') === 'json') { return new JsonResponse([ 'status' => 'success', 'message' => $msg, - ], Response::HTTP_ACCEPTED); + ], JsonResponse::HTTP_ACCEPTED); } return $this->redirectToRoute('nodesEditAttributesPage', [ 'nodeId' => $node->getId(), @@ -136,12 +122,12 @@ public function editAction(Request $request, int $nodeId, int $translationId): R /* * Handle errors when Ajax POST requests */ - if ($isJson) { + if ($request->isXmlHttpRequest() || $request->getRequestFormat('html') === 'json') { return new JsonResponse([ 'status' => 'fail', 'errors' => $errors, 'message' => $this->getTranslator()->trans('form_has_errors.check_you_fields'), - ], Response::HTTP_BAD_REQUEST); + ], JsonResponse::HTTP_BAD_REQUEST); } foreach ($errors as $error) { $this->publishErrorMessage($request, $error); @@ -168,15 +154,6 @@ protected function hasAttributes(): bool return $this->em()->getRepository(Attribute::class)->countBy([]) > 0; } - protected function isAttributable(Node $node): bool - { - $nodeType = $node->getNodeType(); - if ($nodeType instanceof NodeType) { - return $nodeType->isAttributable(); - } - return false; - } - /** * @param Request $request * @param Node $node @@ -186,9 +163,6 @@ protected function isAttributable(Node $node): bool */ protected function handleAddAttributeForm(Request $request, Node $node, Translation $translation): ?RedirectResponse { - if (!$this->isAttributable($node)) { - return null; - } if (!$this->hasAttributes()) { return null; } @@ -216,15 +190,16 @@ protected function handleAddAttributeForm(Request $request, Node $node, Translat /** * @param Request $request - * @param int $nodeId - * @param int $translationId - * @param int $attributeValueId + * @param int $nodeId + * @param int $translationId + * @param int $attributeValueId * * @return Response - * @throws RuntimeError */ - public function deleteAction(Request $request, int $nodeId, int $translationId, int $attributeValueId): Response + public function deleteAction(Request $request, $nodeId, $translationId, $attributeValueId): Response { + $this->denyAccessUnlessGranted('ROLE_ACCESS_ATTRIBUTES_DELETE'); + /** @var AttributeValue|null $item */ $item = $this->em()->find(AttributeValue::class, $attributeValueId); if ($item === null) { @@ -239,8 +214,6 @@ public function deleteAction(Request $request, int $nodeId, int $translationId, throw $this->createNotFoundException('Node-source does not exist'); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_ATTRIBUTE, $node); - /** @var NodesSources|null $nodeSource */ $nodeSource = $this->em() ->getRepository(NodesSources::class) @@ -267,9 +240,9 @@ public function deleteAction(Request $request, int $nodeId, int $translationId, '%nodeName%' => $nodeSource->getTitle(), ] ); - $this->publishConfirmMessage($request, $msg, $item); + $this->publishConfirmMessage($request, $msg); } catch (\RuntimeException $e) { - $this->publishErrorMessage($request, $e->getMessage(), $item); + $this->publishErrorMessage($request, $e->getMessage()); } return $this->redirectToRoute('nodesEditAttributesPage', [ @@ -293,10 +266,11 @@ public function deleteAction(Request $request, int $nodeId, int $translationId, * @param int $translationId * @param int $attributeValueId * @return Response - * @throws RuntimeError */ public function resetAction(Request $request, int $nodeId, int $translationId, int $attributeValueId): Response { + $this->denyAccessUnlessGranted('ROLE_ACCESS_ATTRIBUTES_DELETE'); + /** @var AttributeValueTranslation|null $item */ $item = $this->em() ->getRepository(AttributeValueTranslation::class) @@ -316,8 +290,6 @@ public function resetAction(Request $request, int $nodeId, int $translationId, i throw $this->createNotFoundException('Node-source does not exist'); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_ATTRIBUTE, $node); - /** @var NodesSources|null $nodeSource */ $nodeSource = $this->em() ->getRepository(NodesSources::class) @@ -344,9 +316,9 @@ public function resetAction(Request $request, int $nodeId, int $translationId, i '%nodeName%' => $nodeSource->getTitle(), ] ); - $this->publishConfirmMessage($request, $msg, $item); + $this->publishConfirmMessage($request, $msg); } catch (\RuntimeException $e) { - $this->publishErrorMessage($request, $e->getMessage(), $item); + $this->publishErrorMessage($request, $e->getMessage()); } return $this->redirectToRoute('nodesEditAttributesPage', [ diff --git a/src/Controllers/Nodes/NodesController.php b/src/Controllers/Nodes/NodesController.php index d2763db6..e304e24f 100644 --- a/src/Controllers/Nodes/NodesController.php +++ b/src/Controllers/Nodes/NodesController.php @@ -4,8 +4,6 @@ namespace Themes\Rozier\Controllers\Nodes; -use Doctrine\ORM\OptimisticLockException; -use Doctrine\ORM\ORMException; use RZ\Roadiz\Core\Handlers\HandlerFactoryInterface; use RZ\Roadiz\CoreBundle\Entity\Node; use RZ\Roadiz\CoreBundle\Entity\NodeType; @@ -23,7 +21,6 @@ use RZ\Roadiz\CoreBundle\Node\NodeMover; use RZ\Roadiz\CoreBundle\Node\UniqueNodeGenerator; use RZ\Roadiz\CoreBundle\Security\Authorization\Chroot\NodeChrootResolver; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormError; @@ -38,6 +35,9 @@ use Themes\Rozier\Utils\SessionListFilters; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers\Nodes + */ class NodesController extends RozierApp { use NodesTrait; @@ -188,14 +188,14 @@ public function indexAction(Request $request, ?string $filter = null): Response */ public function editAction(Request $request, int $nodeId, ?int $translationId = null): Response { + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES_SETTING', $nodeId); + /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); if (null === $node) { throw new ResourceNotFoundException(sprintf('Node #%s does not exist.', $nodeId)); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_SETTING, $node); - $this->em()->refresh($node); /* * Handle StackTypes form @@ -213,7 +213,7 @@ public function editAction(Request $request, int $nodeId, ?int $translationId = '%type%' => $type->getDisplayName(), ] ); - $this->publishConfirmMessage($request, $msg, $node); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'nodesEditPage', ['nodeId' => $node->getId()] @@ -254,7 +254,7 @@ public function editAction(Request $request, int $nodeId, ?int $translationId = $msg = $this->getTranslator()->trans('node.%name%.updated', [ '%name%' => $node->getNodeName(), ]); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: $node); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); return $this->redirectToRoute( 'nodesEditPage', ['nodeId' => $node->getId()] @@ -289,13 +289,13 @@ public function editAction(Request $request, int $nodeId, ?int $translationId = */ public function removeStackTypeAction(Request $request, int $nodeId, int $typeId): Response { + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); + /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); if (null === $node) { throw new ResourceNotFoundException(sprintf('Node #%s does not exist.', $nodeId)); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_SETTING, $node); - /** @var NodeType|null $type */ $type = $this->em()->find(NodeType::class, $typeId); if (null === $type) { @@ -312,7 +312,7 @@ public function removeStackTypeAction(Request $request, int $nodeId, int $typeId '%type%' => $type->getDisplayName(), ] ); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: null); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); return $this->redirectToRoute('nodesEditPage', ['nodeId' => $node->getId()]); } @@ -321,13 +321,12 @@ public function removeStackTypeAction(Request $request, int $nodeId, int $typeId * Handle node creation pages. * * @param Request $request - * @param int $nodeTypeId + * @param int $nodeTypeId * @param int|null $translationId * * @return Response - * @throws RuntimeError - * @throws ORMException - * @throws OptimisticLockException + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException */ public function addAction(Request $request, int $nodeTypeId, ?int $translationId = null): Response { @@ -350,6 +349,7 @@ public function addAction(Request $request, int $nodeTypeId, ?int $translationId } $node = new Node($type); + $chroot = $this->nodeChrootResolver->getChroot($this->getUser()); if (null !== $chroot) { // If user is jailed in a node, prevent moving nodes out. @@ -374,7 +374,7 @@ public function addAction(Request $request, int $nodeTypeId, ?int $translationId 'node.%name%.created', ['%name%' => $node->getNodeName()] ); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: null); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); return $this->redirectToRoute( 'nodesEditSourcePage', @@ -406,12 +406,15 @@ public function addAction(Request $request, int $nodeTypeId, ?int $translationId * @param int|null $translationId * * @return Response - * @throws ORMException - * @throws OptimisticLockException - * @throws RuntimeError + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Twig\Error\RuntimeError */ public function addChildAction(Request $request, ?int $nodeId = null, ?int $translationId = null): Response { + // include CHRoot to enable creating node in it + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES', $nodeId, true); + /** @var Translation|null $translation */ $translation = $this->em()->getRepository(Translation::class)->findDefault(); @@ -425,19 +428,15 @@ public function addChildAction(Request $request, ?int $nodeId = null, ?int $tran } if (null === $translation) { - throw new ResourceNotFoundException('Translation does not exist'); + throw new ResourceNotFoundException(sprintf('Translation does not exist')); } if (null !== $nodeId && $nodeId > 0) { - /** @var Node|null $parentNode */ - $parentNode = $this->em()->find(Node::class, $nodeId); - if (null === $parentNode) { - throw new ResourceNotFoundException(sprintf('Node #%s does not exist.', $nodeId)); - } - $this->denyAccessUnlessGranted(NodeVoter::CREATE, $parentNode); + /** @var Node $parentNode */ + $parentNode = $this->em() + ->find(Node::class, $nodeId); } else { $parentNode = null; - $this->denyAccessUnlessGranted(NodeVoter::CREATE_AT_ROOT); } $node = new Node(); @@ -464,7 +463,7 @@ public function addChildAction(Request $request, ?int $nodeId = null, ?int $tran 'child_node.%name%.created', ['%name%' => $node->getNodeName()] ); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: null); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); return $this->redirectToRoute( 'nodesEditSourcePage', @@ -495,10 +494,12 @@ public function addChildAction(Request $request, ?int $nodeId = null, ?int $tran * @param int $nodeId * * @return Response - * @throws RuntimeError + * @throws \Twig\Error\RuntimeError */ public function deleteAction(Request $request, int $nodeId): Response { + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES_DELETE', $nodeId); + /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); @@ -506,11 +507,9 @@ public function deleteAction(Request $request, int $nodeId): Response throw new ResourceNotFoundException(sprintf('Node #%s does not exist.', $nodeId)); } - $this->denyAccessUnlessGranted(NodeVoter::DELETE, $node); - $workflow = $this->workflowRegistry->get($node); if (!$workflow->can($node, 'delete')) { - $this->publishErrorMessage($request, sprintf('Node #%s cannot be deleted.', $nodeId), $node); + $this->publishErrorMessage($request, sprintf('Node #%s cannot be deleted.', $nodeId)); return $this->redirectToRoute( 'nodesEditSourcePage', [ @@ -545,14 +544,13 @@ public function deleteAction(Request $request, int $nodeId): Response 'node.%name%.deleted', ['%name%' => $node->getNodeName()] ); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: $node); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); - $referrer = $request->query->get('referer'); if ( - \is_string($referrer) && - (new UnicodeString($referrer))->trim()->startsWith('/') + $request->query->has('referer') && + (new UnicodeString($request->query->get('referer')))->startsWith('/') ) { - return $this->redirect($referrer); + return $this->redirect($request->query->get('referer')); } if (null !== $parent) { return $this->redirectToRoute( @@ -575,11 +573,11 @@ public function deleteAction(Request $request, int $nodeId): Response * @param Request $request * * @return Response - * @throws RuntimeError + * @throws \Twig\Error\RuntimeError */ public function emptyTrashAction(Request $request): Response { - $this->denyAccessUnlessGranted(NodeVoter::EMPTY_TRASH); + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES_DELETE'); $form = $this->buildEmptyTrashForm(); $form->handleRequest($request); @@ -630,10 +628,12 @@ public function emptyTrashAction(Request $request): Response * @param int $nodeId * * @return Response - * @throws RuntimeError + * @throws \Twig\Error\RuntimeError */ public function undeleteAction(Request $request, int $nodeId): Response { + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES_DELETE', $nodeId); + /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); @@ -641,11 +641,9 @@ public function undeleteAction(Request $request, int $nodeId): Response throw new ResourceNotFoundException(sprintf('Node #%s does not exist.', $nodeId)); } - $this->denyAccessUnlessGranted(NodeVoter::DELETE, $node); - $workflow = $this->workflowRegistry->get($node); if (!$workflow->can($node, 'undelete')) { - $this->publishErrorMessage($request, sprintf('Node #%s cannot be undeleted.', $nodeId), $node); + $this->publishErrorMessage($request, sprintf('Node #%s cannot be undeleted.', $nodeId)); return $this->redirectToRoute( 'nodesEditSourcePage', [ @@ -671,7 +669,7 @@ public function undeleteAction(Request $request, int $nodeId): Response 'node.%name%.undeleted', ['%name%' => $node->getNodeName()] ); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: $node); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); /* * Force redirect to avoid resending form when refreshing page */ @@ -714,26 +712,24 @@ public function generateAndAddNodeAction(Request $request): RedirectResponse throw new ResourceNotFoundException($msg); } } - /** - * @param Request $request - * @param int $nodeId + * @param Request $request + * @param int $nodeId * @return Response - * @throws RuntimeError */ public function publishAllAction(Request $request, int $nodeId): Response { + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES_STATUS'); /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); if (null === $node) { throw new ResourceNotFoundException(sprintf('Node #%s does not exist.', $nodeId)); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_STATUS, $node); $workflow = $this->workflowRegistry->get($node); if (!$workflow->can($node, 'publish')) { - $this->publishErrorMessage($request, sprintf('Node #%s cannot be published.', $nodeId), $node); + $this->publishErrorMessage($request, sprintf('Node #%s cannot be published.', $nodeId)); return $this->redirectToRoute( 'nodesEditSourcePage', [ @@ -752,13 +748,11 @@ public function publishAllAction(Request $request, int $nodeId): Response $this->em()->flush(); $msg = $this->getTranslator()->trans('node.offspring.published'); - $this->publishConfirmMessage($request, $msg, $node); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute('nodesEditSourcePage', [ 'nodeId' => $nodeId, - 'translationId' => $node->getNodeSources()->first() ? - $node->getNodeSources()->first()->getTranslation()->getId() : - null, + 'translationId' => $node->getNodeSources()->first()->getTranslation()->getId(), ]); } diff --git a/src/Controllers/Nodes/NodesSourcesController.php b/src/Controllers/Nodes/NodesSourcesController.php index ce28da25..84012609 100644 --- a/src/Controllers/Nodes/NodesSourcesController.php +++ b/src/Controllers/Nodes/NodesSourcesController.php @@ -13,7 +13,6 @@ use RZ\Roadiz\CoreBundle\Event\NodesSources\NodesSourcesUpdatedEvent; use RZ\Roadiz\CoreBundle\Form\Error\FormErrorSerializer; use RZ\Roadiz\CoreBundle\Routing\NodeRouter; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use RZ\Roadiz\CoreBundle\TwigExtension\JwtExtension; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormError; @@ -55,6 +54,8 @@ public function __construct(JwtExtension $jwtExtension, FormErrorSerializer $for */ public function editSourceAction(Request $request, int $nodeId, int $translationId): Response { + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES', $nodeId); + /** @var Translation|null $translation */ $translation = $this->em()->find(Translation::class, $translationId); @@ -72,8 +73,6 @@ public function editSourceAction(Request $request, int $nodeId, int $translation throw new ResourceNotFoundException('Node does not exist'); } - $this->denyAccessUnlessGranted(NodeVoter::EDIT_CONTENT, $gNode); - /** @var NodesSources|null $source */ $source = $this->em() ->getRepository(NodesSources::class) @@ -110,16 +109,12 @@ public function editSourceAction(Request $request, int $nodeId, int $translation ] ); $form->handleRequest($request); - $isJsonRequest = - $request->isXmlHttpRequest() || - \in_array('application/json', $request->getAcceptableContentTypes()) - ; if ($form->isSubmitted()) { if ($form->isValid() && !$this->isReadOnly) { $this->onPostUpdate($source, $request); - if (!$isJsonRequest) { + if (!$request->isXmlHttpRequest()) { return $this->getPostUpdateRedirection($source); } @@ -172,7 +167,7 @@ public function editSourceAction(Request $request, int $nodeId, int $translation /* * Handle errors when Ajax POST requests */ - if ($isJsonRequest) { + if ($request->isXmlHttpRequest()) { $errors = $this->formErrorSerializer->getErrorsAsArray($form); return new JsonResponse([ 'status' => 'fail', @@ -212,10 +207,12 @@ public function removeAction(Request $request, int $nodeSourceId): Response if (null === $ns) { throw new ResourceNotFoundException('Node source does not exist'); } - $this->denyAccessUnlessGranted(NodeVoter::DELETE, $ns); + /** @var Node $node */ $node = $ns->getNode(); $this->em()->refresh($ns->getNode()); + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES_DELETE', $node->getId()); + /* * Prevent deleting last node-source available in node. */ @@ -251,18 +248,14 @@ public function removeAction(Request $request, int $nodeSourceId): Response $this->em()->remove($ns); $this->em()->flush(); - $ns = $node->getNodeSources()->first() ?: null; - - if (null === $ns) { - throw new ResourceNotFoundException('No more node-source available for this node.'); - } + $ns = $node->getNodeSources()->first(); $msg = $this->getTranslator()->trans('node_source.%node_source%.deleted.%translation%', [ '%node_source%' => $node->getNodeName(), '%translation%' => $ns->getTranslation()->getName(), ]); - $this->publishConfirmMessage($request, $msg, $node); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'nodesEditSourcePage', diff --git a/src/Controllers/Nodes/NodesTagsController.php b/src/Controllers/Nodes/NodesTagsController.php new file mode 100644 index 00000000..87f69398 --- /dev/null +++ b/src/Controllers/Nodes/NodesTagsController.php @@ -0,0 +1,104 @@ +nodeFactory = $nodeFactory; + } + + protected function getNodeFactory(): NodeFactory + { + return $this->nodeFactory; + } + + /** + * Return tags form for requested node. + * + * @param Request $request + * @param int $nodeId + * + * @return Response + * @throws \Twig\Error\RuntimeError + */ + public function editTagsAction(Request $request, int $nodeId): Response + { + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES', $nodeId); + + /** @var NodesSources|null $source */ + $source = $this->em() + ->getRepository(NodesSources::class) + ->setDisplayingAllNodesStatuses(true) + ->setDisplayingNotPublishedNodes(true) + ->findOneBy([ + 'node.id' => $nodeId, + 'translation' => $this->em()->getRepository(Translation::class)->findDefault() + ]); + if (null === $source) { + /** @var NodesSources|null $source */ + $source = $this->em() + ->getRepository(NodesSources::class) + ->setDisplayingAllNodesStatuses(true) + ->setDisplayingNotPublishedNodes(true) + ->findOneBy([ + 'node.id' => $nodeId, + ]); + } + + if (null !== $source) { + $node = $source->getNode(); + $form = $this->createForm(NodeTagsType::class, $node); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + /* + * Dispatch event + */ + $this->dispatchEvent(new NodeTaggedEvent($node)); + $this->em()->flush(); + + $msg = $this->getTranslator()->trans('node.%node%.linked.tags', [ + '%node%' => $node->getNodeName(), + ]); + $this->publishConfirmMessage($request, $msg, $source); + + return $this->redirectToRoute( + 'nodesEditTagsPage', + ['nodeId' => $node->getId()] + ); + } + + $this->assignation['translation'] = $source->getTranslation(); + $this->assignation['node'] = $node; + $this->assignation['source'] = $source; + $this->assignation['form'] = $form->createView(); + + return $this->render('@RoadizRozier/nodes/editTags.html.twig', $this->assignation); + } + + throw new ResourceNotFoundException(); + } +} diff --git a/src/Controllers/Nodes/NodesTreesController.php b/src/Controllers/Nodes/NodesTreesController.php index 085d4410..0e2f157f 100644 --- a/src/Controllers/Nodes/NodesTreesController.php +++ b/src/Controllers/Nodes/NodesTreesController.php @@ -10,7 +10,6 @@ use RZ\Roadiz\CoreBundle\Entity\Translation; use RZ\Roadiz\CoreBundle\EntityHandler\NodeHandler; use RZ\Roadiz\CoreBundle\Security\Authorization\Chroot\NodeChrootResolver; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\Form\ClickableInterface; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; @@ -28,8 +27,10 @@ use Symfony\Component\Workflow\Registry; use Themes\Rozier\RozierApp; use Themes\Rozier\Widgets\TreeWidgetFactory; -use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers\Nodes + */ class NodesTreesController extends RozierApp { private NodeChrootResolver $nodeChrootResolver; @@ -65,29 +66,25 @@ public function __construct( * @param int|null $translationId * * @return Response - * @throws RuntimeError */ - public function treeAction(Request $request, ?int $nodeId = null, ?int $translationId = null): Response + public function treeAction(Request $request, ?int $nodeId = null, ?int $translationId = null) { if (null !== $nodeId) { + $this->validateNodeAccessForRole('ROLE_ACCESS_NODES', $nodeId, true); /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); + if (null === $node) { throw new ResourceNotFoundException(); } + $this->em()->refresh($node); - } elseif (null !== $user = $this->getUser()) { - $node = $this->nodeChrootResolver->getChroot($user); + } elseif (null !== $this->getUser()) { + $node = $this->nodeChrootResolver->getChroot($this->getUser()); } else { $node = null; } - if (null !== $node) { - $this->denyAccessUnlessGranted(NodeVoter::READ, $node); - } else { - $this->denyAccessUnlessGranted(NodeVoter::READ_AT_ROOT); - } - if (null !== $translationId) { /** @var Translation $translation */ $translation = $this->em() @@ -179,131 +176,120 @@ public function treeAction(Request $request, ?int $nodeId = null, ?int $translat /** * @param Request $request * @return Response - * @throws RuntimeError */ - public function bulkDeleteAction(Request $request): Response + public function bulkDeleteAction(Request $request) { - if (empty($request->get('deleteForm')['nodesIds'])) { - throw new ResourceNotFoundException(); - } + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES_DELETE'); - $nodesIds = trim($request->get('deleteForm')['nodesIds']); - $nodesIds = explode(',', $nodesIds); - array_filter($nodesIds); + if (!empty($request->get('deleteForm')['nodesIds'])) { + $nodesIds = trim($request->get('deleteForm')['nodesIds']); + $nodesIds = explode(',', $nodesIds); + array_filter($nodesIds); - /** @var Node[] $nodes */ - $nodes = $this->em() - ->getRepository(Node::class) - ->setDisplayingNotPublishedNodes(true) - ->findBy([ - 'id' => $nodesIds, - ]); + /** @var Node[] $nodes */ + $nodes = $this->em() + ->getRepository(Node::class) + ->setDisplayingNotPublishedNodes(true) + ->findBy([ + 'id' => $nodesIds, + ]); - if (count($nodes) === 0) { - throw new ResourceNotFoundException(); - } + if (count($nodes) > 0) { + $form = $this->buildBulkDeleteForm( + $request->get('deleteForm')['referer'], + $nodesIds + ); + $form->handleRequest($request); + if ($request->get('confirm') && $form->isSubmitted() && $form->isValid()) { + $msg = $this->bulkDeleteNodes($form->getData()); + $this->publishConfirmMessage($request, $msg); + + if (!empty($form->getData()['referer'])) { + return $this->redirect($form->getData()['referer']); + } else { + return $this->redirectToRoute('nodesHomePage'); + } + } - foreach ($nodes as $node) { - $this->denyAccessUnlessGranted(NodeVoter::DELETE, $node); - } + $this->assignation['nodes'] = $nodes; + $this->assignation['form'] = $form->createView(); - $form = $this->buildBulkDeleteForm( - $request->get('deleteForm')['referer'], - $nodesIds - ); - $form->handleRequest($request); - if ($request->get('confirm') && $form->isSubmitted() && $form->isValid()) { - $msg = $this->bulkDeleteNodes($form->getData()); - $this->publishConfirmMessage($request, $msg); + if (!empty($request->get('deleteForm')['referer'])) { + $this->assignation['referer'] = $request->get('deleteForm')['referer']; + } - if (!empty($form->getData()['referer'])) { - return $this->redirect($form->getData()['referer']); - } else { - return $this->redirectToRoute('nodesHomePage'); + return $this->render('@RoadizRozier/nodes/bulkDelete.html.twig', $this->assignation); } } - $this->assignation['nodes'] = $nodes; - $this->assignation['form'] = $form->createView(); - - if (!empty($request->get('deleteForm')['referer'])) { - $this->assignation['referer'] = $request->get('deleteForm')['referer']; - } - - return $this->render('@RoadizRozier/nodes/bulkDelete.html.twig', $this->assignation); + throw new ResourceNotFoundException(); } /** * @param Request $request * * @return Response - * @throws RuntimeError */ - public function bulkStatusAction(Request $request): Response + public function bulkStatusAction(Request $request) { - if (empty($request->get('statusForm')['nodesIds'])) { - throw new ResourceNotFoundException(); - } + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES_STATUS'); - $nodesIds = trim($request->get('statusForm')['nodesIds']); - $nodesIds = explode(',', $nodesIds); - array_filter($nodesIds); + if (!empty($request->get('statusForm')['nodesIds'])) { + $nodesIds = trim($request->get('statusForm')['nodesIds']); + $nodesIds = explode(',', $nodesIds); + array_filter($nodesIds); - /** @var Node[] $nodes */ - $nodes = $this->em() - ->getRepository(Node::class) - ->setDisplayingNotPublishedNodes(true) - ->findBy([ - 'id' => $nodesIds, - ]); + /** @var Node[] $nodes */ + $nodes = $this->em() + ->getRepository(Node::class) + ->setDisplayingNotPublishedNodes(true) + ->findBy([ + 'id' => $nodesIds, + ]); + if (count($nodes) > 0) { + $form = $this->buildBulkStatusForm( + $request->get('statusForm')['referer'], + $nodesIds, + (string) $request->get('statusForm')['status'] + ); - if (count($nodes) === 0) { - throw new ResourceNotFoundException(); - } + $form->handleRequest($request); - foreach ($nodes as $node) { - $this->denyAccessUnlessGranted(NodeVoter::EDIT_STATUS, $node); - } + if ($form->isSubmitted() && $form->isValid()) { + $msg = $this->bulkStatusNodes($form->getData()); + $this->publishConfirmMessage($request, $msg); - $form = $this->buildBulkStatusForm( - $request->get('statusForm')['referer'], - $nodesIds, - (string) $request->get('statusForm')['status'] - ); + if (!empty($form->getData()['referer'])) { + return $this->redirect($form->getData()['referer']); + } else { + return $this->redirectToRoute('nodesHomePage'); + } + } - $form->handleRequest($request); + $this->assignation['nodes'] = $nodes; + $this->assignation['form'] = $form->createView(); - if ($form->isSubmitted() && $form->isValid()) { - $msg = $this->bulkStatusNodes($form->getData()); - $this->publishConfirmMessage($request, $msg); + if (!empty($request->get('statusForm')['referer'])) { + $this->assignation['referer'] = $request->get('statusForm')['referer']; + } - if (!empty($form->getData()['referer'])) { - return $this->redirect($form->getData()['referer']); - } else { - return $this->redirectToRoute('nodesHomePage'); + return $this->render('@RoadizRozier/nodes/bulkStatus.html.twig', $this->assignation); } } - $this->assignation['nodes'] = $nodes; - $this->assignation['form'] = $form->createView(); - - if (!empty($request->get('statusForm')['referer'])) { - $this->assignation['referer'] = $request->get('statusForm')['referer']; - } - - return $this->render('@RoadizRozier/nodes/bulkStatus.html.twig', $this->assignation); + throw new ResourceNotFoundException(); } /** - * @param null|string $referer + * @param false|string $referer * @param array $nodesIds * * @return FormInterface */ private function buildBulkDeleteForm( - ?string $referer = null, - array $nodesIds = [] - ): FormInterface { + $referer = false, + $nodesIds = [] + ) { /** @var FormBuilder $builder */ $builder = $this->formFactory ->createNamedBuilder('deleteForm') @@ -316,7 +302,7 @@ private function buildBulkDeleteForm( ], ]); - if (null !== $referer && (new UnicodeString($referer))->startsWith('/')) { + if (false !== $referer && (new UnicodeString($referer))->startsWith('/')) { $builder->add('referer', HiddenType::class, [ 'data' => $referer, ]); @@ -529,17 +515,17 @@ private function untagNodes(array $data) } /** - * @param null|string $referer + * @param false|string $referer * @param array $nodesIds * @param string $status * * @return FormInterface */ private function buildBulkStatusForm( - ?string $referer = null, - array $nodesIds = [], - string $status = 'reject' - ): FormInterface { + $referer = false, + $nodesIds = [], + $status = 'reject' + ) { /** @var FormBuilder $builder */ $builder = $this->formFactory ->createNamedBuilder('statusForm') @@ -567,7 +553,7 @@ private function buildBulkStatusForm( ]) ; - if (null !== $referer && (new UnicodeString($referer))->startsWith('/')) { + if (false !== $referer && (new UnicodeString($referer))->startsWith('/')) { $builder->add('referer', HiddenType::class, [ 'data' => $referer, ]); diff --git a/src/Controllers/Nodes/NodesUtilsController.php b/src/Controllers/Nodes/NodesUtilsController.php index e2e293a9..a25fa9bd 100644 --- a/src/Controllers/Nodes/NodesUtilsController.php +++ b/src/Controllers/Nodes/NodesUtilsController.php @@ -9,11 +9,13 @@ use RZ\Roadiz\CoreBundle\Event\Node\NodeDuplicatedEvent; use RZ\Roadiz\CoreBundle\Node\NodeDuplicator; use RZ\Roadiz\CoreBundle\Node\NodeNamePolicyInterface; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Themes\Rozier\RozierApp; +/** + * @package Themes\Rozier\Controllers\Nodes + */ class NodesUtilsController extends RozierApp { private NodeNamePolicyInterface $nodeNamePolicy; @@ -34,16 +36,12 @@ public function __construct(NodeNamePolicyInterface $nodeNamePolicy) * * @return Response */ - public function duplicateAction(Request $request, int $nodeId): Response + public function duplicateAction(Request $request, int $nodeId) { - /** @var Node|null $existingNode */ - $existingNode = $this->em()->find(Node::class, $nodeId); - - if (null === $existingNode) { - throw $this->createNotFoundException(); - } + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); - $this->denyAccessUnlessGranted(NodeVoter::DUPLICATE, $existingNode); + /** @var Node $existingNode */ + $existingNode = $this->em()->find(Node::class, $nodeId); try { $duplicator = new NodeDuplicator( @@ -63,7 +61,7 @@ public function duplicateAction(Request $request, int $nodeId): Response '%name%' => $existingNode->getNodeName(), ]); - $this->publishConfirmMessage($request, $msg, $newNode->getNodeSources()->first() ?: $newNode); + $this->publishConfirmMessage($request, $msg, $newNode->getNodeSources()->first()); return $this->redirectToRoute( 'nodesEditPage', @@ -74,8 +72,7 @@ public function duplicateAction(Request $request, int $nodeId): Response $request, $this->getTranslator()->trans("impossible.duplicate.node.%name%", [ '%name%' => $existingNode->getNodeName(), - ]), - $existingNode + ]) ); return $this->redirectToRoute( diff --git a/src/Controllers/Nodes/TranstypeController.php b/src/Controllers/Nodes/TranstypeController.php index c8b51d90..de1a7f3b 100644 --- a/src/Controllers/Nodes/TranstypeController.php +++ b/src/Controllers/Nodes/TranstypeController.php @@ -9,7 +9,6 @@ use RZ\Roadiz\CoreBundle\Event\Node\NodeUpdatedEvent; use RZ\Roadiz\CoreBundle\Event\NodesSources\NodesSourcesUpdatedEvent; use RZ\Roadiz\CoreBundle\Node\NodeTranstyper; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -18,6 +17,9 @@ use Themes\Rozier\RozierApp; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers\Nodes + */ class TranstypeController extends RozierApp { private NodeTranstyper $nodeTranstyper; @@ -34,12 +36,14 @@ public function __construct(NodeTranstyper $nodeTranstyper) * @param Request $request * @param int $nodeId * - * @return Response + * @return RedirectResponse|Response * @throws RuntimeError * @throws \Exception */ - public function transtypeAction(Request $request, int $nodeId): Response + public function transtypeAction(Request $request, int $nodeId) { + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); + /** @var Node|null $node */ $node = $this->em()->find(Node::class, $nodeId); $this->em()->refresh($node); @@ -48,11 +52,6 @@ public function transtypeAction(Request $request, int $nodeId): Response throw new ResourceNotFoundException(); } - /* - * Transtype is only available for higher rank users - */ - $this->denyAccessUnlessGranted(NodeVoter::EDIT_SETTING, $node); - $form = $this->createForm(TranstypeType::class, null, [ 'currentType' => $node->getNodeType(), ]); @@ -92,15 +91,13 @@ public function transtypeAction(Request $request, int $nodeId): Response '%node%' => $node->getNodeName(), '%type%' => $newNodeType->getName(), ]); - $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first() ?: $node); + $this->publishConfirmMessage($request, $msg, $node->getNodeSources()->first()); return $this->redirectToRoute( 'nodesEditSourcePage', [ 'nodeId' => $node->getId(), - 'translationId' => $node->getNodeSources()->first() ? - $node->getNodeSources()->first()->getTranslation()->getId() : - null, + 'translationId' => $node->getNodeSources()->first()->getTranslation()->getId(), ] ); } diff --git a/src/Controllers/Nodes/UrlAliasesController.php b/src/Controllers/Nodes/UrlAliasesController.php new file mode 100644 index 00000000..a9966545 --- /dev/null +++ b/src/Controllers/Nodes/UrlAliasesController.php @@ -0,0 +1,380 @@ +formFactory = $formFactory; + } + + /** + * Return aliases form for requested node. + * + * @param Request $request + * @param int $nodeId + * @param int|null $translationId + * + * @return Response + * @throws RuntimeError + */ + public function editAliasesAction(Request $request, int $nodeId, ?int $translationId = null): Response + { + $this->denyAccessUnlessGranted('ROLE_ACCESS_NODES'); + + if (null === $translationId || $translationId < 1) { + $translation = $this->em()->getRepository(Translation::class)->findDefault(); + } else { + $translation = $this->em()->find(Translation::class, $translationId); + } + /** @var NodesSources|null $source */ + $source = $this->em() + ->getRepository(NodesSources::class) + ->setDisplayingAllNodesStatuses(true) + ->setDisplayingNotPublishedNodes(true) + ->findOneBy(['translation' => $translation, 'node.id' => $nodeId]); + + if ($source !== null && null !== $node = $source->getNode()) { + $redirections = $this->em() + ->getRepository(Redirection::class) + ->findBy([ + 'redirectNodeSource' => $node->getNodeSources()->toArray() + ]); + $uas = $this->em() + ->getRepository(UrlAlias::class) + ->findAllFromNode($node->getId()); + $availableTranslations = $this->em() + ->getRepository(Translation::class) + ->findAvailableTranslationsForNode($node); + + $this->assignation['node'] = $node; + $this->assignation['source'] = $source; + $this->assignation['aliases'] = []; + $this->assignation['redirections'] = []; + $this->assignation['translation'] = $translation; + $this->assignation['available_translations'] = $availableTranslations; + + /* + * SEO Form + */ + $seoForm = $this->createForm(NodeSourceSeoType::class, $source); + $seoForm->handleRequest($request); + if ($seoForm->isSubmitted() && $seoForm->isValid()) { + $this->em()->flush(); + $msg = $this->getTranslator()->trans('node.seo.updated'); + $this->publishConfirmMessage($request, $msg, $source); + /* + * Dispatch event + */ + $this->dispatchEvent(new NodesSourcesUpdatedEvent($source)); + return $this->redirectToRoute( + 'nodesEditSEOPage', + ['nodeId' => $node->getId(), 'translationId' => $translationId] + ); + } + + if (null !== $response = $this->handleAddRedirection($source, $request)) { + return $response; + } + /* + * each url alias edit form + */ + /** @var UrlAlias $alias */ + foreach ($uas as $alias) { + if (null !== $response = $this->handleSingleUrlAlias($alias, $request)) { + return $response; + } + } + + /** @var Redirection $redirection */ + foreach ($redirections as $redirection) { + if (null !== $response = $this->handleSingleRedirection($redirection, $request)) { + return $response; + } + } + + /* + * Main ADD url alias form + */ + $alias = new UrlAlias(); + $addAliasForm = $this->formFactory->createNamed( + 'add_urlalias_' . $node->getId(), + UrlAliasType::class, + $alias, + [ + 'with_translation' => true + ] + ); + $addAliasForm->handleRequest($request); + if ($addAliasForm->isSubmitted() && $addAliasForm->isValid()) { + try { + $alias = $this->addNodeUrlAlias($alias, $node, $addAliasForm->get('translation')->getData()); + $msg = $this->getTranslator()->trans('url_alias.%alias%.created.%translation%', [ + '%alias%' => $alias->getAlias(), + '%translation%' => $alias->getNodeSource()->getTranslation()->getName(), + ]); + $this->publishConfirmMessage($request, $msg, $source); + /* + * Dispatch event + */ + $this->dispatchEvent(new UrlAliasCreatedEvent($alias)); + + return $this->redirect($this->generateUrl( + 'nodesEditSEOPage', + ['nodeId' => $node->getId(), 'translationId' => $translationId] + ) . '#manage-aliases'); + } catch (EntityAlreadyExistsException $e) { + $addAliasForm->addError(new FormError($e->getMessage())); + } catch (NoTranslationAvailableException $e) { + $addAliasForm->addError(new FormError($e->getMessage())); + } + } + + $this->assignation['form'] = $addAliasForm->createView(); + $this->assignation['seoForm'] = $seoForm->createView(); + + return $this->render('@RoadizRozier/nodes/editAliases.html.twig', $this->assignation); + } + + throw new ResourceNotFoundException(); + } + + /** + * @param UrlAlias $alias + * @param Node $node + * @param Translation $translation + * @return UrlAlias + */ + private function addNodeUrlAlias(UrlAlias $alias, Node $node, Translation $translation): UrlAlias + { + /** @var NodesSources|null $nodeSource */ + $nodeSource = $this->em() + ->getRepository(NodesSources::class) + ->setDisplayingAllNodesStatuses(true) + ->setDisplayingNotPublishedNodes(true) + ->findOneBy(['node' => $node, 'translation' => $translation]); + + if ($nodeSource !== null) { + $alias->setNodeSource($nodeSource); + $this->em()->persist($alias); + $this->em()->flush(); + + return $alias; + } else { + $msg = $this->getTranslator()->trans('url_alias.no_translation.%translation%', [ + '%translation%' => $translation->getName() + ]); + throw new NoTranslationAvailableException($msg); + } + } + + /** + * @param UrlAlias $alias + * @param Request $request + * + * @return RedirectResponse|null + */ + private function handleSingleUrlAlias(UrlAlias $alias, Request $request): ?RedirectResponse + { + $editForm = $this->formFactory->createNamed( + 'edit_urlalias_' . $alias->getId(), + UrlAliasType::class, + $alias + ); + $deleteForm = $this->formFactory->createNamed('delete_urlalias_' . $alias->getId()); + // Match edit + $editForm->handleRequest($request); + if ($editForm->isSubmitted() && $editForm->isValid()) { + try { + try { + $this->em()->flush(); + $msg = $this->getTranslator()->trans( + 'url_alias.%alias%.updated', + ['%alias%' => $alias->getAlias()] + ); + $this->publishConfirmMessage($request, $msg, $alias->getNodeSource()); + /* + * Dispatch event + */ + $this->dispatchEvent(new UrlAliasUpdatedEvent($alias)); + /** @var Translation $translation */ + $translation = $alias->getNodeSource()->getTranslation(); + + return $this->redirect($this->generateUrl( + 'nodesEditSEOPage', + [ + 'nodeId' => $alias->getNodeSource()->getNode()->getId(), + 'translationId' => $translation->getId() + ] + ) . '#manage-aliases'); + } catch (\RuntimeException $exception) { + $editForm->addError(new FormError($exception->getMessage())); + } + } catch (EntityAlreadyExistsException $e) { + $editForm->addError(new FormError($e->getMessage())); + } + } + + // Match delete + $deleteForm->handleRequest($request); + if ($deleteForm->isSubmitted() && $deleteForm->isValid()) { + $this->em()->remove($alias); + $this->em()->flush(); + $msg = $this->getTranslator()->trans('url_alias.%alias%.deleted', ['%alias%' => $alias->getAlias()]); + $this->publishConfirmMessage($request, $msg, $alias->getNodeSource()); + + /* + * Dispatch event + */ + $this->dispatchEvent(new UrlAliasDeletedEvent($alias)); + + /** @var Translation $translation */ + $translation = $alias->getNodeSource()->getTranslation(); + + return $this->redirect($this->generateUrl( + 'nodesEditSEOPage', + [ + 'nodeId' => $alias->getNodeSource()->getNode()->getId(), + 'translationId' => $translation->getId() + ] + ) . '#manage-aliases'); + } + + $this->assignation['aliases'][] = [ + 'alias' => $alias, + 'editForm' => $editForm->createView(), + 'deleteForm' => $deleteForm->createView(), + ]; + + return null; + } + + /** + * @param NodesSources $source + * @param Request $request + * @return RedirectResponse|null + */ + private function handleAddRedirection(NodesSources $source, Request $request): ?RedirectResponse + { + $redirection = new Redirection(); + $redirection->setRedirectNodeSource($source); + $redirection->setType(Response::HTTP_MOVED_PERMANENTLY); + + $addForm = $this->formFactory->createNamed( + 'add_redirection', + RedirectionType::class, + $redirection, + [ + 'placeholder' => $this->generateUrl($source), + 'only_query' => true + ] + ); + + $addForm->handleRequest($request); + if ($addForm->isSubmitted() && $addForm->isValid()) { + $this->em()->persist($redirection); + $this->em()->flush(); + + /** @var Translation $translation */ + $translation = $redirection->getRedirectNodeSource()->getTranslation(); + + return $this->redirect($this->generateUrl( + 'nodesEditSEOPage', + [ + 'nodeId' => $redirection->getRedirectNodeSource()->getNode()->getId(), + 'translationId' => $translation->getId() + ] + ) . '#manage-redirections'); + } + + $this->assignation['addRedirection'] = $addForm->createView(); + + return null; + } + + /** + * @param Redirection $redirection + * @param Request $request + * @return RedirectResponse|null + */ + private function handleSingleRedirection(Redirection $redirection, Request $request): ?RedirectResponse + { + $editForm = $this->formFactory->createNamed( + 'edit_redirection_' . $redirection->getId(), + RedirectionType::class, + $redirection, + [ + 'only_query' => true + ] + ); + + /** @var Translation $translation */ + $translation = $redirection->getRedirectNodeSource()->getTranslation(); + + $deleteForm = $this->formFactory->createNamed('delete_redirection_' . $redirection->getId()); + + $editForm->handleRequest($request); + if ($editForm->isSubmitted() && $editForm->isValid()) { + $this->em()->flush(); + return $this->redirect($this->generateUrl( + 'nodesEditSEOPage', + [ + 'nodeId' => $redirection->getRedirectNodeSource()->getNode()->getId(), + 'translationId' => $translation->getId() + ] + ) . '#manage-redirections'); + } + + // Match delete + $deleteForm->handleRequest($request); + if ($deleteForm->isSubmitted() && $deleteForm->isValid()) { + $this->em()->remove($redirection); + $this->em()->flush(); + return $this->redirect($this->generateUrl( + 'nodesEditSEOPage', + [ + 'nodeId' => $redirection->getRedirectNodeSource()->getNode()->getId(), + 'translationId' => $translation->getId() + ] + ) . '#manage-redirections'); + } + $this->assignation['redirections'][] = [ + 'redirection' => $redirection, + 'editForm' => $editForm->createView(), + 'deleteForm' => $deleteForm->createView(), + ]; + + return null; + } +} diff --git a/src/Controllers/PingController.php b/src/Controllers/PingController.php new file mode 100644 index 00000000..898e2a31 --- /dev/null +++ b/src/Controllers/PingController.php @@ -0,0 +1,26 @@ +denyAccessUnlessGranted('ROLE_BACKEND_USER'); + return $this->renderJson(['Pong']); + } +} diff --git a/src/Controllers/RedirectionsController.php b/src/Controllers/RedirectionsController.php index b2819ef5..cd787e20 100644 --- a/src/Controllers/RedirectionsController.php +++ b/src/Controllers/RedirectionsController.php @@ -6,14 +6,10 @@ use RZ\Roadiz\Core\AbstractEntities\PersistableInterface; use RZ\Roadiz\CoreBundle\Entity\Redirection; -use RZ\Roadiz\CoreBundle\Event\Redirection\PostCreatedRedirectionEvent; -use RZ\Roadiz\CoreBundle\Event\Redirection\PostDeletedRedirectionEvent; -use RZ\Roadiz\CoreBundle\Event\Redirection\PostUpdatedRedirectionEvent; -use RZ\Roadiz\CoreBundle\Event\Redirection\RedirectionEvent; use Symfony\Component\HttpFoundation\Request; use Themes\Rozier\Forms\RedirectionType; -class RedirectionsController extends AbstractAdminWithBulkController +class RedirectionsController extends AbstractAdminController { /** * @inheritDoc @@ -105,33 +101,4 @@ protected function getDefaultOrder(Request $request): array { return ['query' => 'ASC']; } - - protected function createPostCreateEvent(PersistableInterface $item): RedirectionEvent - { - if (!($item instanceof Redirection)) { - throw new \InvalidArgumentException('Item should be instance of ' . Redirection::class); - } - return new PostCreatedRedirectionEvent($item); - } - - protected function createPostUpdateEvent(PersistableInterface $item): RedirectionEvent - { - if (!($item instanceof Redirection)) { - throw new \InvalidArgumentException('Item should be instance of ' . Redirection::class); - } - return new PostUpdatedRedirectionEvent($item); - } - - protected function createDeleteEvent(PersistableInterface $item): RedirectionEvent - { - if (!($item instanceof Redirection)) { - throw new \InvalidArgumentException('Item should be instance of ' . Redirection::class); - } - return new PostDeletedRedirectionEvent($item); - } - - protected function getBulkDeleteRouteName(): ?string - { - return 'redirectionsBulkDeletePage'; - } } diff --git a/src/Controllers/RolesUtilsController.php b/src/Controllers/RolesUtilsController.php index f0a5907e..382baaf3 100644 --- a/src/Controllers/RolesUtilsController.php +++ b/src/Controllers/RolesUtilsController.php @@ -90,9 +90,6 @@ public function importJsonFileAction(Request $request): Response if ($file->isValid()) { $serializedData = file_get_contents($file->getPathname()); - if (false === $serializedData) { - throw new RuntimeError('Cannot read uploaded file.'); - } if (null !== \json_decode($serializedData)) { if ($this->rolesImporter->import($serializedData)) { diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php index 8a57e58d..81284ef6 100644 --- a/src/Controllers/SettingsController.php +++ b/src/Controllers/SettingsController.php @@ -116,13 +116,6 @@ protected function commonSettingList(Request $request, SettingGroup $settingGrou $this->assignation['filters'] = $listManager->getAssignation(); $settings = $listManager->getEntities(); $this->assignation['settings'] = []; - $isJson = - $request->isXmlHttpRequest() || - $request->getRequestFormat('html') === 'json' || - \in_array( - 'application/json', - $request->getAcceptableContentTypes() - ); /** @var Setting $setting */ foreach ($settings as $setting) { @@ -140,9 +133,9 @@ protected function commonSettingList(Request $request, SettingGroup $settingGrou 'setting.%name%.updated', ['%name%' => $setting->getName()] ); - $this->publishConfirmMessage($request, $msg, $setting); + $this->publishConfirmMessage($request, $msg); - if ($isJson) { + if ($request->isXmlHttpRequest() || $request->getRequestFormat('html') === 'json') { return new JsonResponse([ 'status' => 'success', 'message' => $msg, @@ -169,7 +162,7 @@ protected function commonSettingList(Request $request, SettingGroup $settingGrou /* * Do not publish any message, it may lead to flushing invalid form */ - if ($isJson) { + if ($request->isXmlHttpRequest() || $request->getRequestFormat('html') === 'json') { return new JsonResponse([ 'status' => 'failed', 'errors' => $errors, @@ -225,7 +218,7 @@ public function editAction(Request $request, int $settingId): Response $this->dispatchEvent(new SettingUpdatedEvent($setting)); $this->em()->flush(); $msg = $this->getTranslator()->trans('setting.%name%.updated', ['%name%' => $setting->getName()]); - $this->publishConfirmMessage($request, $msg, $setting); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page */ @@ -280,7 +273,7 @@ public function addAction(Request $request): Response $this->em()->persist($setting); $this->em()->flush(); $msg = $this->getTranslator()->trans('setting.%name%.created', ['%name%' => $setting->getName()]); - $this->publishConfirmMessage($request, $msg, $setting); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute('settingsHomePage'); } catch (EntityAlreadyExistsException $e) { @@ -326,7 +319,7 @@ public function deleteAction(Request $request, int $settingId): Response $this->em()->flush(); $msg = $this->getTranslator()->trans('setting.%name%.deleted', ['%name%' => $setting->getName()]); - $this->publishConfirmMessage($request, $msg, $setting); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page diff --git a/src/Controllers/SettingsUtilsController.php b/src/Controllers/SettingsUtilsController.php index dff29f0e..7ddb148c 100644 --- a/src/Controllers/SettingsUtilsController.php +++ b/src/Controllers/SettingsUtilsController.php @@ -103,10 +103,6 @@ public function importJsonFileAction(Request $request): Response if ($file->isValid()) { $serializedData = file_get_contents($file->getPathname()); - if (!\is_string($serializedData)) { - throw new RuntimeError('Imported file is not a string.'); - } - if (null !== \json_decode($serializedData)) { if ($this->settingsImporter->import($serializedData)) { $msg = $this->getTranslator()->trans('setting.imported'); diff --git a/src/Controllers/Tags/TagMultiCreationController.php b/src/Controllers/Tags/TagMultiCreationController.php index f71ddb4a..630fba1c 100644 --- a/src/Controllers/Tags/TagMultiCreationController.php +++ b/src/Controllers/Tags/TagMultiCreationController.php @@ -16,6 +16,9 @@ use Themes\Rozier\Forms\MultiTagType; use Themes\Rozier\RozierApp; +/** + * @package Themes\Rozier\Controllers\Tags + */ class TagMultiCreationController extends RozierApp { private TagFactory $tagFactory; @@ -75,7 +78,7 @@ public function addChildAction(Request $request, int $parentTagId) */ $this->dispatchEvent(new TagCreatedEvent($tag)); $msg = $this->getTranslator()->trans('child.tag.%name%.created', ['%name%' => $tag->getTagName()]); - $this->publishConfirmMessage($request, $msg, $tag); + $this->publishConfirmMessage($request, $msg); } return $this->redirectToRoute('tagsTreePage', ['tagId' => $parentTagId]); diff --git a/src/Controllers/Tags/TagsController.php b/src/Controllers/Tags/TagsController.php index a32b2daa..659c18a5 100644 --- a/src/Controllers/Tags/TagsController.php +++ b/src/Controllers/Tags/TagsController.php @@ -35,6 +35,9 @@ use Themes\Rozier\Widgets\TreeWidgetFactory; use Twig\Error\RuntimeError; +/** + * @package Themes\Rozier\Controllers\Tags + */ class TagsController extends RozierApp { use VersionedControllerTrait; @@ -65,7 +68,7 @@ public function __construct( * * @return Response */ - public function indexAction(Request $request): Response + public function indexAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -102,7 +105,7 @@ public function indexAction(Request $request): Response * @return Response * @throws RuntimeError */ - public function editTranslatedAction(Request $request, int $tagId, ?int $translationId = null): Response + public function editTranslatedAction(Request $request, int $tagId, ?int $translationId = null) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -163,10 +166,6 @@ public function editTranslatedAction(Request $request, int $tagId, ?int $transla 'disabled' => $this->isReadOnly, ]); $form->handleRequest($request); - $isJsonRequest = - $request->isXmlHttpRequest() || - \in_array('application/json', $request->getAcceptableContentTypes()) - ; if ($form->isSubmitted()) { if ($form->isValid()) { @@ -195,31 +194,24 @@ public function editTranslatedAction(Request $request, int $tagId, ?int $transla $msg = $this->getTranslator()->trans('tag.%name%.updated', [ '%name%' => $tagTranslation->getName(), ]); - $this->publishConfirmMessage($request, $msg, $tag); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page */ - if (!$isJsonRequest) { - return $this->getPostUpdateRedirection($tagTranslation); - } - - return new JsonResponse([ - 'status' => 'success', - 'errors' => [], - ], Response::HTTP_PARTIAL_CONTENT); + return $this->getPostUpdateRedirection($tagTranslation); } /* * Handle errors when Ajax POST requests */ - if ($isJsonRequest) { + if ($request->isXmlHttpRequest()) { $errors = $this->getErrorsAsArray($form); return new JsonResponse([ 'status' => 'fail', 'errors' => $errors, 'message' => $this->getTranslator()->trans('form_has_errors.check_you_fields'), - ], Response::HTTP_BAD_REQUEST); + ], JsonResponse::HTTP_BAD_REQUEST); } } /** @var TranslationRepository $translationRepository */ @@ -254,7 +246,7 @@ protected function tagNameExists(string $name): bool * @return Response * @throws RuntimeError */ - public function bulkDeleteAction(Request $request): Response + public function bulkDeleteAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS_DELETE'); @@ -307,7 +299,7 @@ public function bulkDeleteAction(Request $request): Response * * @return Response */ - public function addAction(Request $request): Response + public function addAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -341,7 +333,7 @@ public function addAction(Request $request): Response $this->dispatchEvent(new TagCreatedEvent($tag)); $msg = $this->getTranslator()->trans('tag.%name%.created', ['%name%' => $tag->getTagName()]); - $this->publishConfirmMessage($request, $msg, $tag); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page */ @@ -362,7 +354,7 @@ public function addAction(Request $request): Response * * @return Response */ - public function editSettingsAction(Request $request, int $tagId): Response + public function editSettingsAction(Request $request, int $tagId) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -380,10 +372,6 @@ public function editSettingsAction(Request $request, int $tagId): Response ]); $form->handleRequest($request); - $isJsonRequest = - $request->isXmlHttpRequest() || - \in_array('application/json', $request->getAcceptableContentTypes()) - ; if ($form->isSubmitted()) { if ($form->isValid()) { @@ -394,7 +382,7 @@ public function editSettingsAction(Request $request, int $tagId): Response $this->dispatchEvent(new TagUpdatedEvent($tag)); $msg = $this->getTranslator()->trans('tag.%name%.updated', ['%name%' => $tag->getTagName()]); - $this->publishConfirmMessage($request, $msg, $tag); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page @@ -407,7 +395,7 @@ public function editSettingsAction(Request $request, int $tagId): Response /* * Handle errors when Ajax POST requests */ - if ($isJsonRequest) { + if ($request->isXmlHttpRequest()) { $errors = $this->getErrorsAsArray($form); return new JsonResponse([ 'status' => 'fail', @@ -431,7 +419,7 @@ public function editSettingsAction(Request $request, int $tagId): Response * * @return Response */ - public function treeAction(Request $request, int $tagId, ?int $translationId = null): Response + public function treeAction(Request $request, int $tagId, ?int $translationId = null) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -465,7 +453,7 @@ public function treeAction(Request $request, int $tagId, ?int $translationId = n * * @return Response */ - public function deleteAction(Request $request, int $tagId): Response + public function deleteAction(Request $request, int $tagId) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS_DELETE'); @@ -494,12 +482,8 @@ public function deleteAction(Request $request, int $tagId): Response $this->em()->remove($tag); $this->em()->flush(); - $msg = $this->getTranslator()->trans('tag.%name%.deleted', [ - '%name%' => $tag->getTranslatedTags()->first() ? - $tag->getTranslatedTags()->first()->getName() : - $tag->getTagName(), - ]); - $this->publishConfirmMessage($request, $msg, $tag); + $msg = $this->getTranslator()->trans('tag.%name%.deleted', ['%name%' => $tag->getTranslatedTags()->first()->getName()]); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page @@ -524,7 +508,7 @@ public function deleteAction(Request $request, int $tagId): Response * * @return Response */ - public function addChildAction(Request $request, int $tagId, ?int $translationId = null): Response + public function addChildAction(Request $request, int $tagId, ?int $translationId = null) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -566,7 +550,7 @@ public function addChildAction(Request $request, int $tagId, ?int $translationId $this->dispatchEvent(new TagCreatedEvent($tag)); $msg = $this->getTranslator()->trans('child.tag.%name%.created', ['%name%' => $tag->getTagName()]); - $this->publishConfirmMessage($request, $msg, $tag); + $this->publishConfirmMessage($request, $msg); return $this->redirectToRoute( 'tagsEditPage', @@ -596,7 +580,7 @@ public function addChildAction(Request $request, int $tagId, ?int $translationId * @return Response * @throws RuntimeError */ - public function editNodesAction(Request $request, int $tagId): Response + public function editNodesAction(Request $request, int $tagId) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -634,7 +618,7 @@ public function editNodesAction(Request $request, int $tagId): Response * * @return FormInterface */ - private function buildDeleteForm(Tag $tag): FormInterface + private function buildDeleteForm(Tag $tag) { $builder = $this->createFormBuilder() ->add('tagId', HiddenType::class, [ @@ -657,7 +641,7 @@ private function buildDeleteForm(Tag $tag): FormInterface private function buildBulkDeleteForm( $referer = false, array $tagsIds = [] - ): FormInterface { + ) { $builder = $this->formFactory ->createNamedBuilder('deleteForm') ->add('tagsIds', HiddenType::class, [ @@ -683,7 +667,7 @@ private function buildBulkDeleteForm( * * @return string */ - private function bulkDeleteTags(array $data): string + private function bulkDeleteTags(array $data) { if (!empty($data['tagsIds'])) { $tagsIds = trim($data['tagsIds']); @@ -727,7 +711,7 @@ protected function onPostUpdate(PersistableInterface $entity, Request $request): $msg = $this->getTranslator()->trans('tag.%name%.updated', [ '%name%' => $entity->getName(), ]); - $this->publishConfirmMessage($request, $msg, $entity); + $this->publishConfirmMessage($request, $msg); } } diff --git a/src/Controllers/Tags/TagsUtilsController.php b/src/Controllers/Tags/TagsUtilsController.php index 1108a87d..c60cfb7a 100644 --- a/src/Controllers/Tags/TagsUtilsController.php +++ b/src/Controllers/Tags/TagsUtilsController.php @@ -12,6 +12,9 @@ use Symfony\Component\HttpFoundation\Response; use Themes\Rozier\RozierApp; +/** + * @package Themes\Rozier\Controllers\Tags + */ class TagsUtilsController extends RozierApp { private SerializerInterface $serializer; @@ -28,10 +31,11 @@ public function __construct(SerializerInterface $serializer) * Export a Tag in a Json file * * @param Request $request - * @param int $tagId - * @return JsonResponse + * @param int $tagId + * + * @return Response */ - public function exportAction(Request $request, int $tagId): JsonResponse + public function exportAction(Request $request, int $tagId) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -43,7 +47,7 @@ public function exportAction(Request $request, int $tagId): JsonResponse 'json', SerializationContext::create()->setGroups(['tag', 'position']) ), - Response::HTTP_OK, + JsonResponse::HTTP_OK, [ 'Content-Disposition' => sprintf( 'attachment; filename="%s"', @@ -60,9 +64,9 @@ public function exportAction(Request $request, int $tagId): JsonResponse * @param Request $request * @param int $tagId * - * @return JsonResponse + * @return Response */ - public function exportAllAction(Request $request, int $tagId): JsonResponse + public function exportAllAction(Request $request, int $tagId) { $this->denyAccessUnlessGranted('ROLE_ACCESS_TAGS'); @@ -76,7 +80,7 @@ public function exportAllAction(Request $request, int $tagId): JsonResponse 'json', SerializationContext::create()->setGroups(['tag', 'position']) ), - Response::HTTP_OK, + JsonResponse::HTTP_OK, [ 'Content-Disposition' => sprintf( 'attachment; filename="%s"', diff --git a/src/Controllers/TranslationsController.php b/src/Controllers/TranslationsController.php index 968a268e..94aacc18 100644 --- a/src/Controllers/TranslationsController.php +++ b/src/Controllers/TranslationsController.php @@ -61,7 +61,7 @@ public function indexAction(Request $request): Response $handler = $this->handlerFactory->getHandler($translation); $handler->makeDefault(); $msg = $this->getTranslator()->trans('translation.%name%.made_default', ['%name%' => $translation->getName()]); - $this->publishConfirmMessage($request, $msg, $translation); + $this->publishConfirmMessage($request, $msg); $this->dispatchEvent(new TranslationUpdatedEvent($translation)); /* * Force redirect to avoid resending form when refreshing page @@ -106,7 +106,7 @@ public function editAction(Request $request, int $translationId): Response if ($form->isSubmitted() && $form->isValid()) { $this->em()->flush(); $msg = $this->getTranslator()->trans('translation.%name%.updated', ['%name%' => $translation->getName()]); - $this->publishConfirmMessage($request, $msg, $translation); + $this->publishConfirmMessage($request, $msg); $this->dispatchEvent(new TranslationUpdatedEvent($translation)); /* @@ -144,7 +144,7 @@ public function addAction(Request $request): Response $this->em()->flush(); $msg = $this->getTranslator()->trans('translation.%name%.created', ['%name%' => $translation->getName()]); - $this->publishConfirmMessage($request, $msg, $translation); + $this->publishConfirmMessage($request, $msg); $this->dispatchEvent(new TranslationCreatedEvent($translation)); /* @@ -184,7 +184,7 @@ public function deleteAction(Request $request, int $translationId): Response $this->em()->remove($translation); $this->em()->flush(); $msg = $this->getTranslator()->trans('translation.%name%.deleted', ['%name%' => $translation->getName()]); - $this->publishConfirmMessage($request, $msg, $translation); + $this->publishConfirmMessage($request, $msg); $this->dispatchEvent(new TranslationDeletedEvent($translation)); return $this->redirectToRoute('translationsHomePage'); diff --git a/src/Controllers/Users/UsersController.php b/src/Controllers/Users/UsersController.php index 81f6cac7..62732744 100644 --- a/src/Controllers/Users/UsersController.php +++ b/src/Controllers/Users/UsersController.php @@ -4,292 +4,239 @@ namespace Themes\Rozier\Controllers\Users; -use RZ\Roadiz\Core\AbstractEntities\PersistableInterface; use RZ\Roadiz\CoreBundle\Entity\Role; use RZ\Roadiz\CoreBundle\Entity\User; -use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Themes\Rozier\Controllers\AbstractAdminWithBulkController; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Themes\Rozier\Forms\AddUserType; use Themes\Rozier\Forms\UserDetailsType; use Themes\Rozier\Forms\UserType; +use Themes\Rozier\RozierApp; +use Themes\Rozier\Utils\SessionListFilters; use Twig\Error\RuntimeError; -class UsersController extends AbstractAdminWithBulkController +class UsersController extends RozierApp { - protected function supports(PersistableInterface $item): bool - { - return $item instanceof User; - } - - protected function getNamespace(): string - { - return 'user'; - } - - protected function createEmptyItem(Request $request): User - { - $user = new User(); - $user->sendCreationConfirmationEmail(true); - return $user; - } - - protected function getTemplateFolder(): string - { - return '@RoadizRozier/users'; - } - - protected function getRequiredRole(): string - { - return 'ROLE_ACCESS_USERS'; - } - - protected function getRequiredEditionRole(): string - { - // Allow any backoffice user to access user edition before - // checking if current editing item is the same as current user. - return 'ROLE_BACKEND_USER'; - } - - protected function getRequiredDeletionRole(): string - { - return 'ROLE_ACCESS_USERS_DELETE'; - } - - protected function getEntityClass(): string - { - return User::class; - } - - protected function getFormType(): string + /** + * @param Request $request + * + * @return Response + * @throws RuntimeError + */ + public function indexAction(Request $request): Response { - return UserType::class; - } + $this->denyAccessUnlessGranted('ROLE_ACCESS_USERS'); - protected function getDefaultRouteName(): string - { - return 'usersHomePage'; - } + /* + * Manage get request to filter list + */ + $listManager = $this->createEntityListManager( + User::class, + [], + ['username' => 'ASC'] + ); + $listManager->setDisplayingNotPublishedNodes(true); + /* + * Stored in session + */ + $sessionListFilter = new SessionListFilters('user_item_per_page'); + $sessionListFilter->handleItemPerPage($request, $listManager); + $listManager->handle(); - protected function getEditRouteName(): string - { - return 'usersEditPage'; - } + $this->assignation['filters'] = $listManager->getAssignation(); + $this->assignation['users'] = $listManager->getEntities(); - protected function getBulkDeleteRouteName(): ?string - { - return 'usersBulkDeletePage'; + return $this->render('@RoadizRozier/users/list.html.twig', $this->assignation); } - protected function denyAccessUnlessItemGranted(PersistableInterface $item): void + /** + * @param Request $request + * @param int $userId + * + * @return Response + * @throws RuntimeError + */ + public function editAction(Request $request, int $userId): Response { - parent::denyAccessUnlessItemGranted($item); + $this->denyAccessUnlessGranted('ROLE_BACKEND_USER'); - if (!$item instanceof User) { - throw new \RuntimeException('Invalid item type.'); - } - $requestUser = $this->getUser(); if ( !( - $this->isGranted('ROLE_ACCESS_USERS') || - ($requestUser instanceof User && $requestUser->getId() === $item->getId()) + $this->isGranted('ROLE_ACCESS_USERS') || + ($this->getUser() instanceof User && $this->getUser()->getId() == $userId) ) ) { throw $this->createAccessDeniedException("You don't have access to this page: ROLE_ACCESS_USERS"); } - if (!$this->isGranted(Role::ROLE_SUPERADMIN) && $item->isSuperAdmin()) { - throw $this->createAccessDeniedException("You cannot edit a super admin."); + $user = $this->em()->find(User::class, $userId); + if ($user === null) { + throw new ResourceNotFoundException(); } - } - - protected function getEntityName(PersistableInterface $item): string - { - if (!$item instanceof User) { - throw new \RuntimeException('Invalid item type.'); + if (!$this->isGranted(Role::ROLE_SUPERADMIN) && $user->isSuperAdmin()) { + throw $this->createAccessDeniedException("You cannot edit a super admin."); } - return $item->getUsername(); - } - protected function getDefaultOrder(Request $request): array - { - return ['username' => 'ASC']; - } + $form = $this->createForm(UserType::class, $user); + $form->handleRequest($request); - protected function createUpdateEvent(PersistableInterface $item) - { - if (!$item instanceof User) { - throw new \RuntimeException('Invalid item type.'); - } - /* - * If pictureUrl is empty, use default Gravatar image. - */ - if ($item->getPictureUrl() == '') { - $item->setPictureUrl($item->getGravatarUrl()); + if ($form->isSubmitted() && $form->isValid()) { + $this->em()->flush(); + $msg = $this->getTranslator()->trans( + 'user.%name%.updated', + ['%name%' => $user->getUsername()] + ); + $this->publishConfirmMessage($request, $msg); + /* + * Force redirect to avoid resending form when refreshing page + */ + return $this->redirectToRoute( + 'usersEditPage', + ['userId' => $user->getId()] + ); } - return parent::createUpdateEvent($item); // TODO: Change the autogenerated stub + $this->assignation['user'] = $user; + $this->assignation['form'] = $form->createView(); + + return $this->render('@RoadizRozier/users/edit.html.twig', $this->assignation); } /** * @param Request $request - * @param int $id + * @param int $userId * * @return Response * @throws RuntimeError */ - public function editDetailsAction(Request $request, int $id): Response + public function editDetailsAction(Request $request, int $userId): Response { - $this->denyAccessUnlessGranted($this->getRequiredEditionRole()); - $this->additionalAssignation($request); + $this->denyAccessUnlessGranted('ROLE_BACKEND_USER'); - /** @var mixed|object|null $item */ - $item = $this->em()->find($this->getEntityClass(), $id); - if (!($item instanceof PersistableInterface)) { - throw $this->createNotFoundException(); + if ( + !( + $this->isGranted('ROLE_ACCESS_USERS') || + ($this->getUser() instanceof User && $this->getUser()->getId() === $userId) + ) + ) { + throw $this->createAccessDeniedException("You don't have access to this page: ROLE_ACCESS_USERS"); } + $user = $this->em()->find(User::class, $userId); - $this->prepareWorkingItem($item); - $this->denyAccessUnlessItemGranted($item); + if ($user === null) { + throw new ResourceNotFoundException(); + } + if (!$this->isGranted(Role::ROLE_SUPERADMIN) && $user->isSuperAdmin()) { + throw $this->createAccessDeniedException("You cannot edit a super admin."); + } - $form = $this->createForm(UserDetailsType::class, $item); + $form = $this->createForm(UserDetailsType::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { /* - * Events are dispatched before entity manager is flushed - * to be able to throw exceptions before it is persisted. + * If pictureUrl is empty, use default Gravatar image. */ - $event = $this->createUpdateEvent($item); - $this->dispatchSingleOrMultipleEvent($event); - $this->em()->flush(); + if ($user->getPictureUrl() == '') { + $user->setPictureUrl($user->getGravatarUrl()); + } - /* - * Event that requires that EM is flushed - */ - $postEvent = $this->createPostUpdateEvent($item); - $this->dispatchSingleOrMultipleEvent($postEvent); + $this->em()->flush(); $msg = $this->getTranslator()->trans( - '%namespace%.%item%.was_updated', - [ - '%item%' => $this->getEntityName($item), - '%namespace%' => $this->getTranslator()->trans($this->getNamespace()) - ] + 'user.%name%.updated', + ['%name%' => $user->getUsername()] ); - $this->publishConfirmMessage($request, $msg, $item); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page */ return $this->redirectToRoute( 'usersEditDetailsPage', - ['id' => $item->getId()] + ['userId' => $user->getId()] ); } + $this->assignation['user'] = $user; $this->assignation['form'] = $form->createView(); - $this->assignation['item'] = $item; - return $this->render( - $this->getTemplateFolder() . '/editDetails.html.twig', - $this->assignation, - null, - $this->getTemplateNamespace() - ); + return $this->render('@RoadizRozier/users/editDetails.html.twig', $this->assignation); } + /** + * @param Request $request + * + * @return Response + * @throws RuntimeError + */ + public function addAction(Request $request): Response + { + $this->denyAccessUnlessGranted('ROLE_ACCESS_USERS'); + $user = new User(); + $user->sendCreationConfirmationEmail(true); + $this->assignation['user'] = $user; - protected function additionalAssignation(Request $request): void - { - parent::additionalAssignation($request); + $form = $this->createForm(AddUserType::class, $user); + $form->handleRequest($request); - if (null !== $this->getBulkEnableRouteName()) { - $bulkEnableForm = $this->createEnableBulkForm(true); - $this->assignation['bulkEnableForm'] = $bulkEnableForm->createView(); - $this->assignation['hasBulkActions'] = true; - } + if ($form->isSubmitted() && $form->isValid()) { + $this->em()->persist($user); + $this->em()->flush(); + + $msg = $this->getTranslator()->trans('user.%name%.created', ['%name%' => $user->getUsername()]); + $this->publishConfirmMessage($request, $msg); - if (null !== $this->getBulkDisableRouteName()) { - $bulkDisableForm = $this->createDisableBulkForm(true); - $this->assignation['bulkDisableForm'] = $bulkDisableForm->createView(); - $this->assignation['hasBulkActions'] = true; + return $this->redirectToRoute('usersHomePage'); } - } - /* - * User specific bulk actions - */ + $this->assignation['form'] = $form->createView(); - protected function createEnableBulkForm(bool $get = false, ?array $data = null): FormInterface - { - return $this->createBulkForm( - $this->getBulkEnableRouteName(), - 'bulk-enable', - $get, - $data - ); + return $this->render('@RoadizRozier/users/add.html.twig', $this->assignation); } - protected function createDisableBulkForm(bool $get = false, ?array $data = null): FormInterface + /** + * @param Request $request + * @param int $userId + * + * @return Response + * @throws RuntimeError + */ + public function deleteAction(Request $request, int $userId): Response { - return $this->createBulkForm( - $this->getBulkDisableRouteName(), - 'bulk-disable', - $get, - $data - ); - } + $this->denyAccessUnlessGranted('ROLE_ACCESS_USERS_DELETE'); + $user = $this->em()->find(User::class, (int) $userId); - private function getBulkEnableRouteName(): string - { - return 'usersBulkEnablePage'; - } + if ($user === null) { + throw new ResourceNotFoundException(); + } - private function getBulkDisableRouteName(): string - { - return 'usersBulkDisablePage'; - } + if (!$this->isGranted(Role::ROLE_SUPERADMIN) && $user->isSuperAdmin()) { + throw $this->createAccessDeniedException("You cannot edit a super admin."); + } - public function bulkEnableAction(Request $request): Response - { - return $this->bulkAction( - $request, - $this->getRequiredRole(), - $this->createEnableBulkForm(true), - $this->createEnableBulkForm(), - function (string $ids) { - return $this->createEnableBulkForm(false, [ - 'id' => $ids, - ]); - }, - $this->getTemplateFolder() . '/bulk_enable.html.twig', - '%namespace%.%item%.was_enabled', - function (User $item) { - $item->setEnabled(true); - }, - 'bulkEnableForm' - ); - } + $form = $this->createForm(FormType::class); + $form->handleRequest($request); - public function bulkDisableAction(Request $request): Response - { - return $this->bulkAction( - $request, - $this->getRequiredRole(), - $this->createDisableBulkForm(true), - $this->createDisableBulkForm(), - function (string $ids) { - return $this->createDisableBulkForm(false, [ - 'id' => $ids, - ]); - }, - $this->getTemplateFolder() . '/bulk_disable.html.twig', - '%namespace%.%item%.was_disabled', - function (User $item) { - $item->setEnabled(false); - }, - 'bulkDisableForm' - ); + if ($form->isSubmitted() && $form->isValid()) { + $this->em()->remove($user); + $this->em()->flush(); + $msg = $this->getTranslator()->trans( + 'user.%name%.deleted', + ['%name%' => $user->getUsername()] + ); + $this->publishConfirmMessage($request, $msg); + /* + * Force redirect to avoid resending form when refreshing page + */ + return $this->redirectToRoute('usersHomePage'); + } + + $this->assignation['user'] = $user; + $this->assignation['form'] = $form->createView(); + + return $this->render('@RoadizRozier/users/delete.html.twig', $this->assignation); } } diff --git a/src/Controllers/Users/UsersGroupsController.php b/src/Controllers/Users/UsersGroupsController.php index 62103ffc..4c5ba264 100644 --- a/src/Controllers/Users/UsersGroupsController.php +++ b/src/Controllers/Users/UsersGroupsController.php @@ -27,52 +27,53 @@ public function editGroupsAction(Request $request, int $userId): Response /** @var User|null $user */ $user = $this->em()->find(User::class, $userId); - if ($user === null) { - throw new ResourceNotFoundException(); - } - - $this->assignation['user'] = $user; - - $form = $this->buildEditGroupsForm($user); - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $data = $form->getData(); - if ($data['userId'] == $user->getId()) { - if (array_key_exists('group', $data) && $data['group'][0] instanceof Group) { - $group = $data['group'][0]; - } elseif (array_key_exists('group', $data) && is_numeric($data['group'])) { - $group = $this->em()->find(Group::class, $data['group']); - } else { - $group = null; - } - if ($group !== null) { - $user->addGroup($group); - $this->em()->flush(); - - $this->dispatchEvent(new UserJoinedGroupEvent($user, $group)); - - $msg = $this->getTranslator()->trans('user.%user%.group.%group%.linked', [ - '%user%' => $user->getUserName(), - '%group%' => $group->getName(), - ]); - $this->publishConfirmMessage($request, $msg, $user); - - /* - * Force redirect to avoid resending form when refreshing page - */ - return $this->redirectToRoute( - 'usersEditGroupsPage', - ['userId' => $user->getId()] - ); + if ($user !== null) { + $this->assignation['user'] = $user; + + $form = $this->buildEditGroupsForm($user); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $data = $form->getData(); + if ($data['userId'] == $user->getId()) { + if (array_key_exists('group', $data) && $data['group'][0] instanceof Group) { + $group = $data['group'][0]; + } elseif (array_key_exists('group', $data) && is_numeric($data['group'])) { + $group = $this->em()->find(Group::class, $data['group']); + } else { + $group = null; + } + + if ($group !== null) { + $user->addGroup($group); + $this->em()->flush(); + + $this->dispatchEvent(new UserJoinedGroupEvent($user, $group)); + + $msg = $this->getTranslator()->trans('user.%user%.group.%group%.linked', [ + '%user%' => $user->getUserName(), + '%group%' => $group->getName(), + ]); + $this->publishConfirmMessage($request, $msg); + + /* + * Force redirect to avoid resending form when refreshing page + */ + return $this->redirectToRoute( + 'usersEditGroupsPage', + ['userId' => $user->getId()] + ); + } } } - } - $this->assignation['form'] = $form->createView(); + $this->assignation['form'] = $form->createView(); + + return $this->render('@RoadizRozier/users/groups.html.twig', $this->assignation); + } - return $this->render('@RoadizRozier/users/groups.html.twig', $this->assignation); + throw new ResourceNotFoundException(); } public function removeGroupAction(Request $request, int $userId, int $groupId): Response @@ -84,47 +85,44 @@ public function removeGroupAction(Request $request, int $userId, int $groupId): /** @var Group|null $group */ $group = $this->em()->find(Group::class, $groupId); - if ($user === null) { - throw new ResourceNotFoundException(); - } - if ($group === null) { - throw new ResourceNotFoundException(); - } - if (!$this->isGranted($group)) { throw $this->createAccessDeniedException(); } - $this->assignation['user'] = $user; - $this->assignation['group'] = $group; + if ($user !== null) { + $this->assignation['user'] = $user; + $this->assignation['group'] = $group; - $form = $this->createForm(FormType::class); - $form->handleRequest($request); + $form = $this->createForm(FormType::class); + $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - $user->removeGroup($group); - $this->em()->flush(); + if ($form->isSubmitted() && $form->isValid()) { + $user->removeGroup($group); + $this->em()->flush(); - $this->dispatchEvent(new UserLeavedGroupEvent($user, $group)); + $this->dispatchEvent(new UserLeavedGroupEvent($user, $group)); - $msg = $this->getTranslator()->trans('user.%user%.group.%group%.removed', [ - '%user%' => $user->getUserName(), - '%group%' => $group->getName(), - ]); - $this->publishConfirmMessage($request, $msg, $user); + $msg = $this->getTranslator()->trans('user.%user%.group.%group%.removed', [ + '%user%' => $user->getUserName(), + '%group%' => $group->getName(), + ]); + $this->publishConfirmMessage($request, $msg); - /* - * Force redirect to avoid resending form when refreshing page - */ - return $this->redirectToRoute( - 'usersEditGroupsPage', - ['userId' => $user->getId()] - ); - } + /* + * Force redirect to avoid resending form when refreshing page + */ + return $this->redirectToRoute( + 'usersEditGroupsPage', + ['userId' => $user->getId()] + ); + } + + $this->assignation['form'] = $form->createView(); - $this->assignation['form'] = $form->createView(); + return $this->render('@RoadizRozier/users/removeGroup.html.twig', $this->assignation); + } - return $this->render('@RoadizRozier/users/removeGroup.html.twig', $this->assignation); + throw new ResourceNotFoundException(); } /** diff --git a/src/Controllers/Users/UsersRolesController.php b/src/Controllers/Users/UsersRolesController.php index ba80f0af..04b0a57a 100644 --- a/src/Controllers/Users/UsersRolesController.php +++ b/src/Controllers/Users/UsersRolesController.php @@ -53,7 +53,7 @@ public function editRolesAction(Request $request, int $userId): Response '%role%' => $role->getRole(), ]); - $this->publishConfirmMessage($request, $msg, $user); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page @@ -114,7 +114,7 @@ public function removeRoleAction(Request $request, int $userId, int $roleId): Re 'user.%name%.role_removed', ['%name%' => $role->getRole()] ); - $this->publishConfirmMessage($request, $msg, $user); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page diff --git a/src/Controllers/Users/UsersSecurityController.php b/src/Controllers/Users/UsersSecurityController.php index 35949690..27c178c4 100644 --- a/src/Controllers/Users/UsersSecurityController.php +++ b/src/Controllers/Users/UsersSecurityController.php @@ -45,7 +45,7 @@ public function securityAction(Request $request, int $userId): Response ['%name%' => $user->getUsername()] ); - $this->publishConfirmMessage($request, $msg, $user); + $this->publishConfirmMessage($request, $msg); /* * Force redirect to avoid resending form when refreshing page diff --git a/src/Controllers/WebhookController.php b/src/Controllers/WebhookController.php index ff1186ec..807ba1bc 100644 --- a/src/Controllers/WebhookController.php +++ b/src/Controllers/WebhookController.php @@ -12,22 +12,20 @@ use RZ\Roadiz\CoreBundle\Webhook\WebhookDispatcher; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormError; -use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -final class WebhookController extends AbstractAdminWithBulkController +final class WebhookController extends AbstractAdminController { private WebhookDispatcher $webhookDispatcher; public function __construct( WebhookDispatcher $webhookDispatcher, - FormFactoryInterface $formFactory, SerializerInterface $serializer, UrlGeneratorInterface $urlGenerator ) { - parent::__construct($formFactory, $serializer, $urlGenerator); + parent::__construct($serializer, $urlGenerator); $this->webhookDispatcher = $webhookDispatcher; } @@ -59,7 +57,7 @@ public function triggerAction(Request $request, string $id): Response '%seconds%' => $item->getThrottleSeconds(), ] ); - $this->publishConfirmMessage($request, $msg, $item); + $this->publishConfirmMessage($request, $msg); return $this->redirect($this->urlGenerator->generate($this->getDefaultRouteName())); } catch (TooManyWebhookTriggeredException $e) { @@ -132,8 +130,4 @@ protected function getEntityName(PersistableInterface $item): string } return ''; } - protected function getBulkDeleteRouteName(): ?string - { - return 'webhooksBulkDeletePage'; - } } diff --git a/src/Event/UserActionsMenuEvent.php b/src/Event/UserActionsMenuEvent.php deleted file mode 100644 index f5284c61..00000000 --- a/src/Event/UserActionsMenuEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -actions[] = [ - 'label' => $label, - 'path' => $path, - 'icon' => $icon, - ]; - } - - public function getActions(): array - { - return $this->actions; - } -} diff --git a/src/Explorer/ConfigurableExplorerItem.php b/src/Explorer/ConfigurableExplorerItem.php index bc86157e..4fb67bfa 100644 --- a/src/Explorer/ConfigurableExplorerItem.php +++ b/src/Explorer/ConfigurableExplorerItem.php @@ -55,12 +55,9 @@ public function getAlternativeDisplayable(): ?string { $alt = $this->configuration['classname']; if (!empty($this->configuration['alt_displayable'])) { - $altDisplayableCallable = [$this->entity, $this->configuration['alt_displayable']]; - if (\is_callable($altDisplayableCallable)) { - $alt = call_user_func($altDisplayableCallable); - if ($alt instanceof \DateTimeInterface) { - $alt = $alt->format('c'); - } + $alt = call_user_func([$this->entity, $this->configuration['alt_displayable']]); + if ($alt instanceof \DateTimeInterface) { + $alt = $alt->format('c'); } } return (new UnicodeString($alt ?? ''))->truncate(30, '…')->toString(); @@ -71,12 +68,9 @@ public function getAlternativeDisplayable(): ?string */ public function getDisplayable(): string { - $displayableCallable = [$this->entity, $this->configuration['displayable']]; - if (\is_callable($displayableCallable)) { - $displayable = call_user_func($displayableCallable); - if ($displayable instanceof \DateTimeInterface) { - $displayable = $displayable->format('c'); - } + $displayable = call_user_func([$this->entity, $this->configuration['displayable']]); + if ($displayable instanceof \DateTimeInterface) { + $displayable = $displayable->format('c'); } return (new UnicodeString($displayable ?? ''))->truncate(30, '…')->toString(); } @@ -94,14 +88,11 @@ protected function getThumbnail(): ?array /** @var DocumentInterface|null $thumbnail */ $thumbnail = null; if (!empty($this->configuration['thumbnail'])) { - $thumbnailCallable = [$this->entity, $this->configuration['thumbnail']]; - if (\is_callable($thumbnailCallable)) { - $thumbnail = call_user_func($thumbnailCallable); - if ($thumbnail instanceof Collection && $thumbnail->count() > 0 && $thumbnail->first() instanceof DocumentInterface) { - $thumbnail = $thumbnail->first(); - } elseif (is_array($thumbnail) && count($thumbnail) > 0 && $thumbnail[0] instanceof DocumentInterface) { - $thumbnail = $thumbnail[0]; - } + $thumbnail = call_user_func([$this->entity, $this->configuration['thumbnail']]); + if ($thumbnail instanceof Collection && $thumbnail->count() > 0 && $thumbnail->first() instanceof DocumentInterface) { + $thumbnail = $thumbnail->first(); + } elseif (is_array($thumbnail) && count($thumbnail) > 0 && $thumbnail[0] instanceof DocumentInterface) { + $thumbnail = $thumbnail[0]; } } diff --git a/src/Explorer/FolderExplorerItem.php b/src/Explorer/FolderExplorerItem.php index 9a85e7ae..10588ae0 100644 --- a/src/Explorer/FolderExplorerItem.php +++ b/src/Explorer/FolderExplorerItem.php @@ -35,9 +35,7 @@ public function getAlternativeDisplayable(): ?string /** @var Folder|null $parent */ $parent = $this->folder->getParent(); if (null !== $parent) { - return $parent->getTranslatedFolders()->first() ? - $parent->getTranslatedFolders()->first()->getName() : - $parent->getName(); + return $parent->getTranslatedFolders()->first()->getName(); } return ''; } @@ -47,9 +45,7 @@ public function getAlternativeDisplayable(): ?string */ public function getDisplayable(): string { - return $this->folder->getTranslatedFolders()->first() ? - $this->folder->getTranslatedFolders()->first()->getName() : - $this->folder->getName(); + return $this->folder->getTranslatedFolders()->first()->getName(); } /** diff --git a/src/Explorer/UserExplorerItem.php b/src/Explorer/UserExplorerItem.php index b7012f16..eb63dc4c 100644 --- a/src/Explorer/UserExplorerItem.php +++ b/src/Explorer/UserExplorerItem.php @@ -62,7 +62,7 @@ public function getOriginal(): User protected function getEditItemPath(): ?string { return $this->urlGenerator->generate('usersEditPage', [ - 'id' => $this->user->getId() + 'userId' => $this->user->getId() ]); } } diff --git a/src/Forms/AddUserType.php b/src/Forms/AddUserType.php index 2cbff0d7..debb57c3 100644 --- a/src/Forms/AddUserType.php +++ b/src/Forms/AddUserType.php @@ -7,6 +7,9 @@ use RZ\Roadiz\CoreBundle\Form\GroupsType; use Symfony\Component\Form\FormBuilderInterface; +/** + * @package Themes\Rozier\Forms + */ class AddUserType extends UserType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Forms/CustomFormFieldType.php b/src/Forms/CustomFormFieldType.php index 0fc5675a..998ad6f6 100644 --- a/src/Forms/CustomFormFieldType.php +++ b/src/Forms/CustomFormFieldType.php @@ -9,11 +9,13 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @package Themes\Rozier\Forms + */ class CustomFormFieldType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void @@ -48,9 +50,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]) ->add( 'defaultValues', - TextareaType::class, + TextType::class, [ - 'label' => 'customFormField.defaultValues', + 'label' => 'defaultValues', 'required' => false, 'attr' => [ 'placeholder' => 'enter_values_comma_separated', diff --git a/src/Forms/DataTransformer/TagTransformer.php b/src/Forms/DataTransformer/TagTransformer.php index f90162b2..a5519023 100644 --- a/src/Forms/DataTransformer/TagTransformer.php +++ b/src/Forms/DataTransformer/TagTransformer.php @@ -4,11 +4,16 @@ namespace Themes\Rozier\Forms\DataTransformer; +use Doctrine\Common\Collections\Collection; use Doctrine\Persistence\ObjectManager; +use RZ\Roadiz\Core\AbstractEntities\AbstractEntity; use RZ\Roadiz\CoreBundle\Entity\Tag; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; +/** + * @package Themes\Rozier\Forms\DataTransformer + */ class TagTransformer implements DataTransformerInterface { private ObjectManager $manager; diff --git a/src/Forms/DocumentEditType.php b/src/Forms/DocumentEditType.php index 047dc8d6..09bf4c1b 100644 --- a/src/Forms/DocumentEditType.php +++ b/src/Forms/DocumentEditType.php @@ -118,14 +118,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]); } - if ($document->isProcessable()) { - $builder->add('imageCropAlignment', ImageCropAlignmentType::class, [ - 'label' => 'document.imageCropAlignment', - 'help' => 'document.imageCropAlignment.help', - 'required' => false, - ]); - } - /* * Display thumbnails only if current Document is original. */ diff --git a/src/Forms/DynamicType.php b/src/Forms/DynamicType.php index dbaf6999..3a65e897 100644 --- a/src/Forms/DynamicType.php +++ b/src/Forms/DynamicType.php @@ -9,6 +9,11 @@ use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * Class DynamicType + * + * @package Themes\Rozier\Forms + */ class DynamicType extends AbstractType { /** diff --git a/src/Forms/FolderTranslationType.php b/src/Forms/FolderTranslationType.php index ba030249..16b25967 100644 --- a/src/Forms/FolderTranslationType.php +++ b/src/Forms/FolderTranslationType.php @@ -10,6 +10,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @package Themes\Rozier\Forms + */ class FolderTranslationType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Forms/FolderType.php b/src/Forms/FolderType.php index d05469dc..83ab4f95 100644 --- a/src/Forms/FolderType.php +++ b/src/Forms/FolderType.php @@ -12,6 +12,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @package Themes\Rozier\Forms + */ class FolderType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Forms/ImageCropAlignmentType.php b/src/Forms/ImageCropAlignmentType.php deleted file mode 100644 index 5a79bd2d..00000000 --- a/src/Forms/ImageCropAlignmentType.php +++ /dev/null @@ -1,44 +0,0 @@ -setDefaults([ - 'label' => 'image_crop_alignment', - 'required' => false, - 'placeholder' => 'image_crop_alignment.none', - 'expanded' => true, - 'choices' => [ - 'image_crop_alignment.top-left' => 'top-left', - 'image_crop_alignment.top' => 'top', - 'image_crop_alignment.top-right' => 'top-right', - 'image_crop_alignment.left' => 'left', - 'image_crop_alignment.center' => 'center', - 'image_crop_alignment.right' => 'right', - 'image_crop_alignment.bottom-left' => 'bottom-left', - 'image_crop_alignment.bottom' => 'bottom', - 'image_crop_alignment.bottom-right' => 'bottom-right', - ] - ]); - } - - public function getBlockPrefix(): string - { - return 'image_crop_alignment'; - } - - - public function getParent(): string - { - return ChoiceType::class; - } -} diff --git a/src/Forms/LoginType.php b/src/Forms/LoginType.php index 3f88811c..37b0804e 100644 --- a/src/Forms/LoginType.php +++ b/src/Forms/LoginType.php @@ -65,7 +65,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ], ]); - if ($this->requestStack->getMainRequest()?->query->has('_home')) { + if ($this->requestStack->getMasterRequest()->query->has('_home')) { $builder->add('_target_path', HiddenType::class, [ 'data' => $this->urlGenerator->generate('adminHomePage') ]); diff --git a/src/Forms/Node/AddNodeType.php b/src/Forms/Node/AddNodeType.php index ffc863f6..336d4dea 100644 --- a/src/Forms/Node/AddNodeType.php +++ b/src/Forms/Node/AddNodeType.php @@ -9,17 +9,22 @@ use RZ\Roadiz\CoreBundle\Form\DataTransformer\NodeTypeTransformer; use RZ\Roadiz\CoreBundle\Form\NodeTypesType; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Event\SubmitEvent; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Event\PostSubmitEvent; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\Event\SubmitEvent; +/** + * @package Themes\Rozier\Forms\Node + */ class AddNodeType extends AbstractType { protected ManagerRegistry $managerRegistry; diff --git a/src/Forms/NodeSource/NodeSourceJoinType.php b/src/Forms/NodeSource/NodeSourceJoinType.php index b7810c44..27b90477 100644 --- a/src/Forms/NodeSource/NodeSourceJoinType.php +++ b/src/Forms/NodeSource/NodeSourceJoinType.php @@ -55,9 +55,8 @@ public function buildView(FormView $view, FormInterface $form, array $options): $configuration = $this->getFieldConfiguration($options); $displayableData = []; - /** @var callable $callable */ - $callable = [$options['nodeSource'], $options['nodeTypeField']->getGetterName()]; - $entities = call_user_func($callable); + + $entities = call_user_func([$options['nodeSource'], $options['nodeTypeField']->getGetterName()]); if ($entities instanceof \Traversable) { /** @var PersistableInterface $entity */ @@ -69,9 +68,8 @@ public function buildView(FormView $view, FormInterface $form, array $options): 'id' => $entity->getId(), 'classname' => $configuration['classname'], ]; - $displayableCallable = [$entity, $configuration['displayable']]; - if (\is_callable($displayableCallable)) { - $data['name'] = call_user_func($displayableCallable); + if (is_callable([$entity, $configuration['displayable']])) { + $data['name'] = call_user_func([$entity, $configuration['displayable']]); } $displayableData[] = $data; } @@ -83,9 +81,8 @@ public function buildView(FormView $view, FormInterface $form, array $options): 'id' => $entities->getId(), 'classname' => $configuration['classname'], ]; - $displayableCallable = [$entities, $configuration['displayable']]; - if (\is_callable($displayableCallable)) { - $data['name'] = call_user_func($displayableCallable); + if (is_callable([$entities, $configuration['displayable']])) { + $data['name'] = call_user_func([$entities, $configuration['displayable']]); } $displayableData[] = $data; } diff --git a/src/Forms/NodeSource/NodeSourceProviderType.php b/src/Forms/NodeSource/NodeSourceProviderType.php index 9132793c..3471cfdb 100644 --- a/src/Forms/NodeSource/NodeSourceProviderType.php +++ b/src/Forms/NodeSource/NodeSourceProviderType.php @@ -105,9 +105,7 @@ public function buildView(FormView $view, FormInterface $form, array $options): $provider = $this->getProvider($configuration, $options); $displayableData = []; - /** @var callable $callable */ - $callable = [$options['nodeSource'], $options['nodeTypeField']->getGetterName()]; - $ids = call_user_func($callable); + $ids = call_user_func([$options['nodeSource'], $options['nodeTypeField']->getGetterName()]); if (!is_array($ids)) { $entities = $provider->getItemsById([$ids]); } else { diff --git a/src/Forms/NodeTagsType.php b/src/Forms/NodeTagsType.php new file mode 100644 index 00000000..cecf98ac --- /dev/null +++ b/src/Forms/NodeTagsType.php @@ -0,0 +1,63 @@ +managerRegistry = $managerRegistry; + } + + /** + * {@inheritdoc} + * + * @param FormBuilderInterface $builder + * @param array $options + */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder->add('tags', TagsType::class, [ + 'by_reference' => false + ]); + $builder->get('tags') + ->addModelTransformer(new TagTransformer($this->managerRegistry->getManager())); + } + + /** + * {@inheritdoc} + * + * @param OptionsResolver $resolver + */ + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefault('data_class', Node::class); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix(): string + { + return 'node_tags'; + } +} diff --git a/src/Forms/NodeType.php b/src/Forms/NodeType.php index c85a0b4f..d3585452 100644 --- a/src/Forms/NodeType.php +++ b/src/Forms/NodeType.php @@ -5,6 +5,7 @@ namespace Themes\Rozier\Forms; use RZ\Roadiz\CoreBundle\Entity\Node; +use RZ\Roadiz\CoreBundle\Form\Constraint\UniqueNodeName; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -21,6 +22,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'nodeName', 'empty_data' => '', 'help' => 'node.nodeName.help', + 'constraints' => [ + new UniqueNodeName([ + 'currentValue' => $options['nodeName'], + ]), + ] ]) ->add('dynamicNodeName', CheckboxType::class, [ 'label' => 'node.dynamicNodeName', @@ -29,10 +35,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]) ; - /** @var Node|null $node */ - $node = $builder->getData(); - $isReachable = null !== $node && $node->getNodeType()?->isReachable(); - if ($isReachable) { + if (null !== $builder->getData() && $builder->getData()->getNodeType()->isReachable()) { $builder->add('home', CheckboxType::class, [ 'label' => 'node.isHome', 'required' => false, @@ -53,7 +56,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]) ; - if ($isReachable) { + if (null !== $builder->getData() && $builder->getData()->getNodeType()->isReachable()) { $builder->add('ttl', IntegerType::class, [ 'label' => 'node.ttl', 'help' => 'node_time_to_live_cache_on_front_controller', @@ -77,6 +80,7 @@ public function configureOptions(OptionsResolver $resolver): void 'class' => 'uk-form node-form', ], ]); - $resolver->setAllowedTypes('nodeName', ['string', 'null']); + + $resolver->setAllowedTypes('nodeName', 'string'); } } diff --git a/src/Forms/NodeTypeFieldType.php b/src/Forms/NodeTypeFieldType.php index 2d779998..46d15fa1 100644 --- a/src/Forms/NodeTypeFieldType.php +++ b/src/Forms/NodeTypeFieldType.php @@ -13,6 +13,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @package Themes\Rozier\Forms + */ class NodeTypeFieldType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void @@ -72,7 +75,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'required' => false, ]) ->add('defaultValues', DynamicType::class, [ - 'label' => 'nodeTypeField.defaultValues', + 'label' => 'defaultValues', 'required' => false, 'help' => 'for_children_node_and_node_references_enter_node_type_names_comma_separated', 'attr' => [ diff --git a/src/Forms/NodeTypeType.php b/src/Forms/NodeTypeType.php index 6f0a6681..3601c424 100644 --- a/src/Forms/NodeTypeType.php +++ b/src/Forms/NodeTypeType.php @@ -41,11 +41,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'required' => false, 'help' => 'enables_published_at_field_for_time_based_publication', ]) - ->add('attributable', CheckboxType::class, [ - 'label' => 'attributable', - 'required' => false, - 'help' => 'enables_node_attributes_for_this_type', - ]) ->add('reachable', CheckboxType::class, [ 'label' => 'reachable', 'required' => false, diff --git a/src/Forms/RedirectionType.php b/src/Forms/RedirectionType.php index 1839e8ce..b0d22a9b 100644 --- a/src/Forms/RedirectionType.php +++ b/src/Forms/RedirectionType.php @@ -13,6 +13,9 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @package Themes\Rozier\Forms + */ class RedirectionType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Forms/TranstypeType.php b/src/Forms/TranstypeType.php index bb32d3b5..2ba0eba5 100644 --- a/src/Forms/TranstypeType.php +++ b/src/Forms/TranstypeType.php @@ -14,6 +14,9 @@ use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; +/** + * @package Themes\Rozier\Forms + */ class TranstypeType extends AbstractType { protected ManagerRegistry $managerRegistry; diff --git a/src/Forms/UserType.php b/src/Forms/UserType.php index 0f4f8c9b..65bd4b39 100644 --- a/src/Forms/UserType.php +++ b/src/Forms/UserType.php @@ -12,6 +12,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** + * @package Themes\Rozier\Forms + */ class UserType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Models/CustomFormModel.php b/src/Models/CustomFormModel.php index 7724af37..fe4f00f5 100644 --- a/src/Models/CustomFormModel.php +++ b/src/Models/CustomFormModel.php @@ -8,6 +8,9 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @package Themes\Rozier\Models + */ final class CustomFormModel implements ModelInterface { private CustomForm $customForm; diff --git a/src/Models/DocumentModel.php b/src/Models/DocumentModel.php index ad4e6f98..237c4376 100644 --- a/src/Models/DocumentModel.php +++ b/src/Models/DocumentModel.php @@ -13,6 +13,9 @@ use RZ\Roadiz\Documents\UrlGenerators\DocumentUrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +/** + * @package Themes\Rozier\Models + */ final class DocumentModel implements ModelInterface { public static array $thumbnailArray = [ @@ -109,10 +112,10 @@ public function toArray(): array $editUrl = null; } - $embedFinder = $this->embedFinderFactory?->createForPlatform( + $embedFinder = $this->embedFinderFactory->createForPlatform( $this->document->getEmbedPlatform(), $this->document->getEmbedId() - ) ?? null; + ); return [ 'id' => $id, @@ -140,7 +143,7 @@ public function toArray(): array : $this->document->getShortType(), 'shortMimeType' => $this->document->getShortMimeType(), 'thumbnail_80' => $thumbnail80Url, - 'url' => $previewUrl ?? $thumbnail80Url, + 'url' => $previewUrl ?? $thumbnail80Url ?? null, ]; } } diff --git a/src/Models/ModelInterface.php b/src/Models/ModelInterface.php index 9670e57c..60726121 100644 --- a/src/Models/ModelInterface.php +++ b/src/Models/ModelInterface.php @@ -4,6 +4,9 @@ namespace Themes\Rozier\Models; +/** + * @package Themes\Rozier\Models + */ interface ModelInterface { /** diff --git a/src/Models/NodeModel.php b/src/Models/NodeModel.php index d7ad14e8..f90bb280 100644 --- a/src/Models/NodeModel.php +++ b/src/Models/NodeModel.php @@ -9,20 +9,25 @@ use RZ\Roadiz\CoreBundle\Entity\NodesSources; use RZ\Roadiz\CoreBundle\Entity\NodesSourcesDocuments; use RZ\Roadiz\CoreBundle\Entity\Translation; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; /** + * @package Themes\Rozier\Models * @Serializer\ExclusionPolicy("all") */ final class NodeModel implements ModelInterface { - public function __construct( - private Node $node, - private UrlGeneratorInterface $urlGenerator, - private Security $security - ) { + private Node $node; + private UrlGeneratorInterface $urlGenerator; + + /** + * @param Node $node + * @param UrlGeneratorInterface $urlGenerator + */ + public function __construct(Node $node, UrlGeneratorInterface $urlGenerator) + { + $this->node = $node; + $this->urlGenerator = $urlGenerator; } public function toArray(): array @@ -31,21 +36,18 @@ public function toArray(): array $nodeSource = $this->node->getNodeSources()->first(); if (false === $nodeSource) { - $result = [ + return [ 'id' => $this->node->getId(), 'title' => $this->node->getNodeName(), 'nodeName' => $this->node->getNodeName(), 'isPublished' => $this->node->isPublished(), + 'nodesEditPage' => $this->urlGenerator->generate('nodesEditPage', [ + 'nodeId' => $this->node->getId(), + ]), 'nodeType' => [ - 'color' => $this->node->getNodeType()?->getColor() ?? '#000000', + 'color' => $this->node->getNodeType()->getColor() ] ]; - if ($this->security->isGranted(NodeVoter::EDIT_SETTING, $this->node)) { - $result['nodesEditPage'] = $this->urlGenerator->generate('nodesEditPage', [ - 'nodeId' => $this->node->getId(), - ]); - } - return $result; } /** @var NodesSourcesDocuments|false $thumbnail */ @@ -59,32 +61,25 @@ public function toArray(): array 'thumbnail' => $thumbnail ? $thumbnail->getDocument() : null, 'nodeName' => $this->node->getNodeName(), 'isPublished' => $this->node->isPublished(), + 'nodesEditPage' => $this->urlGenerator->generate('nodesEditSourcePage', [ + 'nodeId' => $this->node->getId(), + 'translationId' => $translation->getId(), + ]), 'nodeType' => [ - 'color' => $this->node->getNodeType()?->getColor() ?? '#000000', + 'color' => $this->node->getNodeType()->getColor() ] ]; - if ($this->security->isGranted(NodeVoter::EDIT_CONTENT, $nodeSource)) { - $result['nodesEditPage'] = $this->urlGenerator->generate('nodesEditSourcePage', [ - 'nodeId' => $this->node->getId(), - 'translationId' => $translation->getId(), - ]); - } - $parent = $this->node->getParent(); if ($parent instanceof Node) { $result['parent'] = [ - 'title' => $parent->getNodeSources()->first() ? - $parent->getNodeSources()->first()->getTitle() : - $parent->getNodeName() + 'title' => $parent->getNodeSources()->first()->getTitle() ]; $subParent = $parent->getParent(); if ($subParent instanceof Node) { $result['subparent'] = [ - 'title' => $subParent->getNodeSources()->first() ? - $subParent->getNodeSources()->first()->getTitle() : - $subParent->getNodeName() + 'title' => $subParent->getNodeSources()->first()->getTitle() ]; } } diff --git a/src/Models/NodeSourceModel.php b/src/Models/NodeSourceModel.php index 476cc011..73fef8c1 100644 --- a/src/Models/NodeSourceModel.php +++ b/src/Models/NodeSourceModel.php @@ -8,25 +8,28 @@ use RZ\Roadiz\CoreBundle\Entity\NodesSources; use RZ\Roadiz\CoreBundle\Entity\NodesSourcesDocuments; use RZ\Roadiz\CoreBundle\Entity\Translation; -use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Security; /** * @Serializer\ExclusionPolicy("all") */ final class NodeSourceModel implements ModelInterface { - public function __construct( - private NodesSources $nodeSource, - private UrlGeneratorInterface $urlGenerator, - private Security $security - ) { + private NodesSources $nodeSource; + private UrlGeneratorInterface $urlGenerator; + + public function __construct(NodesSources $nodeSource, UrlGeneratorInterface $urlGenerator) + { + $this->nodeSource = $nodeSource; + $this->urlGenerator = $urlGenerator; } public function toArray(): array { $node = $this->nodeSource->getNode(); + if (null === $node) { + throw new \RuntimeException('Node-source does not have a Node.'); + } /** @var NodesSourcesDocuments|false $thumbnail */ $thumbnail = $this->nodeSource->getDocumentsByFields()->first(); @@ -39,18 +42,15 @@ public function toArray(): array 'nodeName' => $node->getNodeName(), 'thumbnail' => $thumbnail ? $thumbnail->getDocument() : null, 'isPublished' => $node->isPublished(), + 'nodesEditPage' => $this->urlGenerator->generate('nodesEditSourcePage', [ + 'nodeId' => $node->getId(), + 'translationId' => $translation->getId(), + ]), 'nodeType' => [ - 'color' => $node->getNodeType()?->getColor() ?? '#000000', + 'color' => $node->getNodeType()->getColor() ] ]; - if ($this->security->isGranted(NodeVoter::EDIT_CONTENT, $node)) { - $result['nodesEditPage'] = $this->urlGenerator->generate('nodesEditSourcePage', [ - 'nodeId' => $node->getId(), - 'translationId' => $translation->getId(), - ]); - } - $parent = $this->nodeSource->getParent(); if ($parent instanceof NodesSources) { diff --git a/src/Models/NodeTypeModel.php b/src/Models/NodeTypeModel.php index 6c26967f..bf9e93b0 100644 --- a/src/Models/NodeTypeModel.php +++ b/src/Models/NodeTypeModel.php @@ -6,6 +6,9 @@ use RZ\Roadiz\CoreBundle\Entity\NodeType; +/** + * @package Themes\Rozier\Models + */ final class NodeTypeModel implements ModelInterface { private NodeType $nodeType; diff --git a/src/Models/TagModel.php b/src/Models/TagModel.php index d22864d9..10fe026c 100644 --- a/src/Models/TagModel.php +++ b/src/Models/TagModel.php @@ -7,6 +7,9 @@ use RZ\Roadiz\CoreBundle\Entity\Tag; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +/** + * @package Themes\Rozier\Models + */ final class TagModel implements ModelInterface { private Tag $tag; diff --git a/src/Resources/app/Lazyload.js b/src/Resources/app/Lazyload.js index 1b6ddfe9..0e1f3d36 100644 --- a/src/Resources/app/Lazyload.js +++ b/src/Resources/app/Lazyload.js @@ -6,8 +6,10 @@ import TagsBulk from './components/bulk-edits/TagsBulk' import DocumentUploader from './components/documents/DocumentUploader' import NodeTypeFieldsPosition from './components/node-type-fields/NodeTypeFieldsPosition' import AttributeValuePosition from './components/attribute-values/AttributeValuePosition' +import NodeTypeFieldEdit from './components/node-type-fields/NodeTypeFieldEdit' import CustomFormFieldsPosition from './components/custom-form-fields/CustomFormFieldsPosition' import NodeTreeContextActions from './components/trees/NodeTreeContextActions' +import Import from './components/import/Import' import NodeEditSource from './components/node/NodeEditSource' import InputLengthWatcher from './widgets/InputLengthWatcher' import ChildrenNodesField from './widgets/ChildrenNodesField' @@ -27,6 +29,9 @@ import MultiLeafletGeotagField from './widgets/MultiLeafletGeotagField' import TagEdit from './components/tag/TagEdit' import MainTreeTabs from './components/tabs/MainTreeTabs' +/** + * Lazyload + */ export default class Lazyload { constructor() { this.$linksSelector = null @@ -49,6 +54,7 @@ export default class Lazyload { this.attributeValuesPosition = null this.customFormFieldsPosition = null this.settingsSaveButtons = null + this.nodeTypeFieldEdit = null this.nodeEditSource = null this.tagEdit = null this.markdownEditors = [] @@ -110,10 +116,14 @@ export default class Lazyload { ) { event.preventDefault() - window.requestAnimationFrame(() => { + if (this.clickTimeout) { + clearTimeout(this.clickTimeout) + } + + this.clickTimeout = window.setTimeout(() => { window.history.pushState({}, null, $link.attr('href')) this.onPopState(null) - }) + }, 0) return false } @@ -150,10 +160,10 @@ export default class Lazyload { * Delay loading if user is click like devil */ if (this.currentTimeout) { - window.cancelAnimationFrame(this.currentTimeout) + clearTimeout(this.currentTimeout) } - this.currentTimeout = window.requestAnimationFrame(async () => { + this.currentTimeout = window.setTimeout(() => { /* * Trigger event on window to notify open * widgets to close. @@ -161,71 +171,48 @@ export default class Lazyload { let pageChangeEvent = new CustomEvent('pagechange') window.dispatchEvent(pageChangeEvent) - try { - let url = '' - const path = location.href.split('?')[0] - const params = new URLSearchParams(location.href.split('?')[1]) - if (state.headerData) { - /** - * @param {string} key - * @param {string|number|Array} value - */ - for (let [key, value] of Object.entries(state.headerData)) { - if (Array.isArray(value)) { - value.forEach((v, i) => { - params.append(key + '[' + i + ']', v) + this.currentRequest = $.ajax({ + url: location.href, + type: 'get', + dataType: 'html', + cache: false, + data: state.headerData, + beforeSend: (xhr) => { + xhr.setRequestHeader('X-Partial', true) + }, + }) + .done((data) => { + this.applyContent(data) + this.canvasLoader.hide() + let pageLoadEvent = new CustomEvent('pageload', { detail: data }) + window.dispatchEvent(pageLoadEvent) + }) + .fail((data) => { + if (typeof data.responseText !== 'undefined') { + try { + let exception = JSON.parse(data.responseText) + window.UIkit.notify({ + message: exception.message, + status: 'danger', + timeout: 3000, + pos: 'top-center', }) - } else { - params.set(key, value) + } catch (e) { + // No valid JsonResponse, need to refresh page + window.location.href = location.href } - } - } - if (params.toString() !== '') { - url = path + '?' + params.toString() - } else { - url = path - } - - const response = await fetch(url, { - method: 'GET', - headers: { - 'X-Partial': true, - Accept: 'text/html', - }, - }) - if (!response.ok) { - throw response - } - const data = await response.text() - this.applyContent(data) - let pageLoadEvent = new CustomEvent('pageload', { detail: data }) - window.dispatchEvent(pageLoadEvent) - } catch (response) { - const data = await response.text() - if (data) { - try { - let exception = JSON.parse(data) + } else { window.UIkit.notify({ - message: exception.message, + message: window.Rozier.messages.forbiddenPage, status: 'danger', timeout: 3000, pos: 'top-center', }) - } catch (e) { - // No valid JsonResponse, need to refresh page - window.location.href = location.href } - } else { - window.UIkit.notify({ - message: window.Rozier.messages.forbiddenPage, - status: 'danger', - timeout: 3000, - pos: 'top-center', - }) - } - } - this.canvasLoader.hide() - }) + + this.canvasLoader.hide() + }) + }, 100) } refreshCodemirrorEditor() { @@ -277,10 +264,10 @@ export default class Lazyload { $tempData = $container.find('.new-content-global') - $old.eq(0).fadeOut(100, () => { + $old.fadeOut(100, () => { $old.remove() this.generalBind() - $tempData.eq(0).fadeIn(200, () => { + $tempData.fadeIn(200, () => { $tempData.removeClass('new-content-global') let pageShowEndEvent = new CustomEvent('pageshowend') window.dispatchEvent(pageShowEndEvent) @@ -317,6 +304,7 @@ export default class Lazyload { this.attributeValuesPosition, this.customFormFieldsPosition, this.settingsSaveButtons, + this.nodeTypeFieldEdit, this.nodeEditSource, this.tagEdit, this.nodeTree, @@ -351,6 +339,7 @@ export default class Lazyload { this.customFormFieldsPosition = new CustomFormFieldsPosition() this.nodeTreeContextActions = new NodeTreeContextActions() this.settingsSaveButtons = new SettingsSaveButtons() + this.nodeTypeFieldEdit = new NodeTypeFieldEdit() this.nodeEditSource = new NodeEditSource() this.tagEdit = new TagEdit() this.nodeTree = new NodeTree() @@ -375,7 +364,13 @@ export default class Lazyload { // Switch checkboxes this.initBootstrapSwitches() + window.Rozier.getMessages() + + if (typeof window.Rozier.importRoutes !== 'undefined' && window.Rozier.importRoutes !== null) { + window.Rozier.import = new Import(window.Rozier.importRoutes) + window.Rozier.importRoutes = null + } } generalUnbind(objects) { diff --git a/src/Resources/app/Rozier.js b/src/Resources/app/Rozier.js index 0f6f998c..c13e2dc6 100644 --- a/src/Resources/app/Rozier.js +++ b/src/Resources/app/Rozier.js @@ -6,7 +6,6 @@ import { PointerEventsPolyfill } from './utils/plugins' import { TweenLite, Expo } from 'gsap' import NodeTreeContextActions from './components/trees/NodeTreeContextActions' import RozierMobile from './RozierMobile' -import bulkActions from './widgets/GenericBulkActions' require('gsap/ScrollToPlugin') /** @@ -21,7 +20,6 @@ export default class Rozier { this.windowHeight = null this.resizeFirst = true this.mobile = null - this.ajaxToken = null this.nodeTrees = [] this.treeTrees = [] @@ -155,14 +153,6 @@ export default class Rozier { this.refreshMainNodeTree() this.refreshMainTagTree() this.refreshMainFolderTree() - - /* - * init generic bulk actions widget - */ - bulkActions() - window.addEventListener('pageshowend', () => { - bulkActions() - }) } saveCollapsedNestableState(state = null) { @@ -233,22 +223,16 @@ export default class Rozier { bindMainTrees() { // TREES let $nodeTree = $('.nodetree-widget .root-tree') - if ($nodeTree.length) { - $nodeTree.off('change.uk.nestable') - $nodeTree.on('change.uk.nestable', this.onNestableNodeTreeChange) - } + $nodeTree.off('change.uk.nestable') + $nodeTree.on('change.uk.nestable', this.onNestableNodeTreeChange) let $tagTree = $('.tagtree-widget .root-tree') - if ($tagTree.length) { - $tagTree.off('change.uk.nestable') - $tagTree.on('change.uk.nestable', this.onNestableTagTreeChange) - } + $tagTree.off('change.uk.nestable') + $tagTree.on('change.uk.nestable', this.onNestableTagTreeChange) let $folderTree = $('.foldertree-widget .root-tree') - if ($folderTree.length) { - $folderTree.off('change.uk.nestable') - $folderTree.on('change.uk.nestable', this.onNestableFolderTreeChange) - } + $folderTree.off('change.uk.nestable') + $folderTree.on('change.uk.nestable', this.onNestableFolderTreeChange) // Tree element name this.$mainTreeElementName = this.$mainTrees.find('.tree-element-name') @@ -288,71 +272,61 @@ export default class Rozier { }) } - fetchSessionMessages() { - return new Promise(async (resolve, reject) => { - const query = new URLSearchParams({ - _action: 'messages', - _token: this.ajaxToken, - }) - const url = this.routes.ajaxSessionMessages + '?' + query.toString() - try { - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - }) - const data = await response.json() - if (!data.messages) { - reject() - } - resolve(data.messages) - } catch (e) { - reject() - } - }) - } /** * Get messages. */ - async getMessages() { - const messages = await this.fetchSessionMessages() - if (typeof messages.confirm !== 'undefined' && messages.confirm.length > 0) { - for (let i = messages.confirm.length - 1; i >= 0; i--) { - window.UIkit.notify({ - message: messages.confirm[i], - status: 'success', - timeout: 2000, - pos: 'top-center', - }) - } - } - - if (typeof messages.error !== 'undefined' && messages.error.length > 0) { - for (let j = data.messages.error.length - 1; j >= 0; j--) { - window.UIkit.notify({ - message: data.messages.error[j], - status: 'error', - timeout: 2000, - pos: 'top-center', - }) - } - } + getMessages() { + $.ajax({ + url: this.routes.ajaxSessionMessages, + type: 'GET', + dataType: 'json', + cache: false, + data: { + _action: 'messages', + _token: this.ajaxToken, + }, + }) + .done((data) => { + if (typeof data.messages !== 'undefined') { + if (typeof data.messages.confirm !== 'undefined' && data.messages.confirm.length > 0) { + for (let i = data.messages.confirm.length - 1; i >= 0; i--) { + window.UIkit.notify({ + message: data.messages.confirm[i], + status: 'success', + timeout: 2000, + pos: 'top-center', + }) + } + } + + if (typeof data.messages.error !== 'undefined' && data.messages.error.length > 0) { + for (let j = data.messages.error.length - 1; j >= 0; j--) { + window.UIkit.notify({ + message: data.messages.error[j], + status: 'error', + timeout: 2000, + pos: 'top-center', + }) + } + } + } + }) + .fail(() => { + console.log('[Rozier.getMessages] error') + }) } /** * @param translationId */ refreshAllNodeTrees(translationId) { - const promises = [] - promises.push(this.refreshMainNodeTree(translationId)) + this.refreshMainNodeTree(translationId) /* * Stack trees */ if (this.lazyload.stackNodeTrees.treeAvailable()) { - promises.push(this.lazyload.stackNodeTrees.refreshNodeTree()) + this.lazyload.stackNodeTrees.refreshNodeTree() } /* @@ -361,10 +335,9 @@ export default class Rozier { if (this.lazyload.childrenNodesFields.treeAvailable()) { for (let i = this.lazyload.childrenNodesFields.$nodeTrees.length - 1; i >= 0; i--) { let $nodeTree = this.lazyload.childrenNodesFields.$nodeTrees.eq(i) - promises.push(this.lazyload.childrenNodesFields.refreshNodeTree($nodeTree)) + this.lazyload.childrenNodesFields.refreshNodeTree($nodeTree) } } - return Promise.all(promises) } /** @@ -372,163 +345,151 @@ export default class Rozier { * * @param {Number|null|undefined} translationId */ - async refreshMainNodeTree(translationId = undefined) { - let $currentNodeTree = $('#tree-container').find('.nodetree-widget').eq(0) - if (!$currentNodeTree.length) { - console.debug('No main node-tree available.') - return - } - + refreshMainNodeTree(translationId) { + let $currentNodeTree = $('#tree-container').find('.nodetree-widget') let $currentRootTree = $currentNodeTree.find('.root-tree').eq(0) if ($currentRootTree.length && !translationId) { translationId = $currentRootTree.attr('data-translation-id') } - try { - const query = new URLSearchParams({ + if ($currentNodeTree.length) { + let postData = { _token: this.ajaxToken, _action: 'requestMainNodeTree', translationId: translationId || null, - }) - const url = this.routes.nodesTreeAjax + '?' + query.toString() - const response = await fetch(url, { - method: 'GET', - headers: { - Accept: 'application/json', - }, - }) - if (!response.ok) { - throw response - } - const data = await response.json() - if (typeof data.nodeTree !== 'undefined') { - await this.fadeOut($currentNodeTree) - $currentNodeTree.replaceWith(data.nodeTree) - $currentNodeTree = $('#tree-container').find('.nodetree-widget') - - await this.fadeIn($currentNodeTree) - this.initNestables() - this.bindMainTrees() - this.resize() - this.lazyload.bindAjaxLink() - - if (this.lazyload.nodeTreeContextActions) { - this.lazyload.nodeTreeContextActions.unbind() - } - - this.lazyload.nodeTreeContextActions = new NodeTreeContextActions() } - } catch (e) { - console.log('[Rozier.refreshMainNodeTree] Retrying in 3 seconds') - // Wait for background jobs to be done - window.setTimeout(() => { - this.refreshMainNodeTree(translationId) - }, 3000) + let url = this.routes.nodesTreeAjax + + $.ajax({ + url: url, + type: 'get', + cache: false, + dataType: 'json', + data: postData, + }) + .done((data) => { + if ($currentNodeTree.length && typeof data.nodeTree !== 'undefined') { + $currentNodeTree.fadeOut('slow', () => { + $currentNodeTree.replaceWith(data.nodeTree) + $currentNodeTree = $('#tree-container').find('.nodetree-widget') + $currentNodeTree.fadeIn() + this.initNestables() + this.bindMainTrees() + this.resize() + this.lazyload.bindAjaxLink() + + if (this.lazyload.nodeTreeContextActions) { + this.lazyload.nodeTreeContextActions.unbind() + } + + this.lazyload.nodeTreeContextActions = new NodeTreeContextActions() + }) + } + }) + .fail(() => { + console.log('[Rozier.refreshMainNodeTree] Retrying in 3 seconds') + // Wait for background jobs to be done + setTimeout(() => { + this.refreshMainNodeTree(translationId) + }, 3000) + }) + .always(() => { + this.lazyload.canvasLoader.hide() + }) + } else { + console.debug('No main node-tree available.') } - - this.lazyload.canvasLoader.hide() } /** * Refresh only main tagTree. * */ - async refreshMainTagTree() { + refreshMainTagTree() { let $currentTagTree = $('#tree-container').find('.tagtree-widget') - if (!$currentTagTree.length) { + if ($currentTagTree.length) { + let postData = { + _token: this.ajaxToken, + _action: 'requestMainTagTree', + } + + let url = this.routes.tagsTreeAjax + + $.ajax({ + url: url, + type: 'get', + cache: false, + dataType: 'json', + data: postData, + }) + .done((data) => { + if ($currentTagTree.length && typeof data.tagTree !== 'undefined') { + $currentTagTree.fadeOut('slow', () => { + $currentTagTree.replaceWith(data.tagTree) + $currentTagTree = $('#tree-container').find('.tagtree-widget') + $currentTagTree.fadeIn() + this.initNestables() + this.bindMainTrees() + this.resize() + this.lazyload.bindAjaxLink() + }) + } + }) + .always(() => { + this.lazyload.canvasLoader.hide() + }) + } else { console.debug('No main tag-tree available.') - return } - - const query = new URLSearchParams({ - _token: this.ajaxToken, - _action: 'requestMainTagTree', - }) - const url = this.routes.tagsTreeAjax + '?' + query.toString() - const response = await fetch(url, { - method: 'GET', - headers: { - Accept: 'application/json', - }, - }) - const data = await response.json() - if (typeof data.tagTree !== 'undefined') { - await this.fadeOut($currentTagTree) - $currentTagTree.replaceWith(data.tagTree) - $currentTagTree = $('#tree-container').find('.tagtree-widget') - await this.fadeIn($currentTagTree) - this.initNestables() - this.bindMainTrees() - this.resize() - this.lazyload.bindAjaxLink() - } - this.lazyload.canvasLoader.hide() } /** * Refresh only main folderTree. */ - async refreshMainFolderTree() { + refreshMainFolderTree() { let $currentFolderTree = $('#tree-container').find('.foldertree-widget') - if (!$currentFolderTree.length) { - console.debug('No main folder-tree available.') - return - } + if ($currentFolderTree.length) { + let postData = { + _token: this.ajaxToken, + _action: 'requestMainFolderTree', + } - const query = new URLSearchParams({ - _token: this.ajaxToken, - _action: 'requestMainFolderTree', - }) - const url = this.routes.foldersTreeAjax + '?' + query.toString() - const response = await fetch(url, { - method: 'GET', - headers: { - Accept: 'application/json', - }, - }) - const data = await response.json() - if (typeof data.folderTree !== 'undefined') { - await this.fadeOut($currentFolderTree) - $currentFolderTree.replaceWith(data.folderTree) - $currentFolderTree = $('#tree-container').find('.foldertree-widget') - await this.fadeIn($currentFolderTree) - this.initNestables() - this.bindMainTrees() - this.resize() - this.lazyload.bindAjaxLink() - } - - this.lazyload.canvasLoader.hide() - } + let url = this.routes.foldersTreeAjax - /** - * @param {jQuery} element - * @return {Promise} - */ - async fadeIn(element) { - return new Promise((resolve, reject) => { - element.fadeIn(() => { - resolve() + $.ajax({ + url: url, + type: 'get', + cache: false, + dataType: 'json', + data: postData, }) - }) - } - - /** - * @param {jQuery} element - * @return {Promise} - */ - async fadeOut(element) { - return new Promise((resolve, reject) => { - element.fadeOut('slow', () => { - resolve() - }) - }) + .done((data) => { + if ($currentFolderTree.length && typeof data.folderTree !== 'undefined') { + $currentFolderTree.fadeOut('slow', () => { + $currentFolderTree.replaceWith(data.folderTree) + $currentFolderTree = $('#tree-container').find('.foldertree-widget') + $currentFolderTree.fadeIn() + this.initNestables() + this.bindMainTrees() + this.resize() + this.lazyload.bindAjaxLink() + }) + } + }) + .always(() => { + this.lazyload.canvasLoader.hide() + }) + } else { + console.debug('No main folder-tree available.') + } } /** * Toggle trees panel + * @param {[type]} event [description] + * @return {[type]} [description] */ toggleTreesPanel() { $('#main-container-inner').toggleClass('trees-panel--minified') @@ -549,6 +510,8 @@ export default class Rozier { /** * Toggle user panel + * @param {[type]} event [description] + * @return {[type]} [description] */ toggleUserPanel() { $('#user-panel').toggleClass('minified') @@ -602,12 +565,12 @@ export default class Rozier { /** * @param event - * @param {HTMLElement} rootEl - * @param {HTMLElement} el - * @param {string|null|undefined} status - * @returns {false|undefined} + * @param rootEl + * @param el + * @param status + * @returns {boolean} */ - async onNestableNodeTreeChange(event, rootEl, el, status) { + onNestableNodeTreeChange(event, rootEl, el, status) { let element = $(el) /* * If node removed, do not do anything, the other change.uk.nestable nodeTree will be triggered @@ -643,7 +606,7 @@ export default class Rozier { return false } - const postData = { + let postData = { _token: this.ajaxToken, _action: 'updatePosition', nodeId: nodeId, @@ -659,43 +622,39 @@ export default class Rozier { postData.prevNodeId = parseInt(element.prev().attr('data-node-id')) } - try { - const response = await fetch(this.routes.nodeAjaxEdit.replace('%nodeId%', nodeId), { - method: 'POST', - headers: { - Accept: 'application/json', - }, - body: new URLSearchParams(postData), - }) - if (!response.ok) { - throw response - } - const data = await response.json() - window.UIkit.notify({ - message: data.responseText || data.detail, - status: data.status, - timeout: 3000, - pos: 'top-center', + $.ajax({ + url: this.routes.nodeAjaxEdit.replace('%nodeId%', nodeId), + type: 'POST', + dataType: 'json', + data: postData, + }) + .done((data) => { + window.UIkit.notify({ + message: data.responseText, + status: data.status, + timeout: 3000, + pos: 'top-center', + }) }) - } catch (response) { - const data = await response.json() - window.UIkit.notify({ - message: data.error_message || data.detail, - status: 'danger', - timeout: 3000, - pos: 'top-center', + .fail((data) => { + data = JSON.parse(data.responseText) + window.UIkit.notify({ + message: data.error_message, + status: 'danger', + timeout: 3000, + pos: 'top-center', + }) }) - } } /** * @param event - * @param {HTMLElement} rootEl - * @param {HTMLElement} el - * @param {string|null|undefined} status - * @returns {false|undefined} + * @param rootEl + * @param el + * @param status + * @returns {boolean} */ - async onNestableTagTreeChange(event, rootEl, el, status) { + onNestableTagTreeChange(event, rootEl, el, status) { let element = $(el) /* @@ -747,43 +706,29 @@ export default class Rozier { postData.prevTagId = parseInt(element.prev().attr('data-tag-id')) } - try { - const response = await fetch(this.routes.tagAjaxEdit.replace('%tagId%', tagId), { - method: 'POST', - headers: { - Accept: 'application/json', - }, - body: new URLSearchParams(postData), - }) - if (!response.ok) { - throw response - } - const data = await response.json() + $.ajax({ + url: this.routes.tagAjaxEdit.replace('%tagId%', tagId), + type: 'POST', + dataType: 'json', + data: postData, + }).done((data) => { window.UIkit.notify({ message: data.responseText, status: data.status, timeout: 3000, pos: 'top-center', }) - } catch (response) { - const data = await response.json() - window.UIkit.notify({ - message: data.error_message || data.detail, - status: 'danger', - timeout: 3000, - pos: 'top-center', - }) - } + }) } /** + * * @param event - * @param {HTMLElement} rootEl - * @param {HTMLElement} el - * @param {string|null|undefined} status - * @returns {false|undefined} + * @param element + * @param status + * @returns {boolean} */ - async onNestableFolderTreeChange(event, rootEl, el, status) { + onNestableFolderTreeChange(event, rootEl, el, status) { let element = $(el) /* * If folder removed, do not do anything, the other folderTree will be triggered @@ -836,33 +781,19 @@ export default class Rozier { postData.prevFolderId = parseInt(element.prev().attr('data-folder-id')) } - try { - const response = await fetch(this.routes.folderAjaxEdit.replace('%folderId%', folderId), { - method: 'POST', - headers: { - Accept: 'application/json', - }, - body: new URLSearchParams(postData), - }) - if (!response.ok) { - throw response - } - const data = await response.json() + $.ajax({ + url: this.routes.folderAjaxEdit.replace('%folderId%', folderId), + type: 'POST', + dataType: 'json', + data: postData, + }).done((data) => { window.UIkit.notify({ message: data.responseText, status: data.status, timeout: 3000, pos: 'top-center', }) - } catch (response) { - const data = await response.json() - window.UIkit.notify({ - message: data.error_message || data.detail, - status: 'danger', - timeout: 3000, - pos: 'top-center', - }) - } + }) } /** @@ -886,9 +817,9 @@ export default class Rozier { if (this.windowWidth >= 768 && this.windowWidth <= 1200 && this.$mainTrees.length && this.resizeFirst) { this.$mainTrees[0].style.display = 'none' this.$minifyTreePanelButton.trigger('click') - window.requestAnimationFrame(() => { + window.setTimeout(() => { this.$mainTrees[0].style.display = 'table-cell' - }) + }, 1000) } // Check if mobile @@ -935,6 +866,9 @@ export default class Rozier { this.lazyload.resize() this.entriesPanel.replaceSubNavs() + // Documents list + // if(this.lazyload !== null && !this.resizeFirst) this.lazyload.documentsList.resize(); + // Set resize first to false if (this.resizeFirst) this.resizeFirst = false } diff --git a/src/Resources/app/api/DocumentApi.js b/src/Resources/app/api/DocumentApi.js index e76828c9..9729e172 100644 --- a/src/Resources/app/api/DocumentApi.js +++ b/src/Resources/app/api/DocumentApi.js @@ -29,6 +29,7 @@ export function getDocumentsByIds({ ids = [] }) { } }) .catch((error) => { + // TODO // Log request error or display a message throw new Error(error.response.data.humanMessage) }) @@ -76,6 +77,7 @@ export function getDocuments({ searchTerms, filters, filterExplorerSelection, mo } }) .catch((error) => { + // TODO // Log request error or display a message throw new Error(error) }) diff --git a/src/Resources/app/components/Dropzone.vue b/src/Resources/app/components/Dropzone.vue index 0babde35..d6b06dc3 100644 --- a/src/Resources/app/components/Dropzone.vue +++ b/src/Resources/app/components/Dropzone.vue @@ -183,7 +183,7 @@ export default { */ if (file.previewElement) { let $preview = $(file.previewElement) - window.setTimeout(function () { + setTimeout(function () { $preview.fadeOut(500) }, 3000) } diff --git a/src/Resources/app/components/RzTextarea.vue b/src/Resources/app/components/RzTextarea.vue index 5638efae..ec8ea488 100644 --- a/src/Resources/app/components/RzTextarea.vue +++ b/src/Resources/app/components/RzTextarea.vue @@ -1,5 +1,5 @@ + - + diff --git a/src/Resources/views/partials/simple-js-inject.html.twig b/src/Resources/views/partials/simple-js-inject.html.twig index 4f775e93..c77fcff7 100644 --- a/src/Resources/views/partials/simple-js-inject.html.twig +++ b/src/Resources/views/partials/simple-js-inject.html.twig @@ -1,3 +1,3 @@ - + diff --git a/src/Resources/views/redirections/add.html.twig b/src/Resources/views/redirections/add.html.twig index 39253b55..de268d6b 100644 --- a/src/Resources/views/redirections/add.html.twig +++ b/src/Resources/views/redirections/add.html.twig @@ -24,7 +24,7 @@
{% apply spaceless %} -
- {{ form_end(form) }} - - {% endif %} -{% endblock %} diff --git a/src/Resources/views/redirections/edit.html.twig b/src/Resources/views/redirections/edit.html.twig index 891a55bd..214bd60a 100644 --- a/src/Resources/views/redirections/edit.html.twig +++ b/src/Resources/views/redirections/edit.html.twig @@ -24,7 +24,7 @@
{% apply spaceless %} diff --git a/src/Resources/views/roles/edit.html.twig b/src/Resources/views/roles/edit.html.twig index 9ce227af..d9efcc6c 100644 --- a/src/Resources/views/roles/edit.html.twig +++ b/src/Resources/views/roles/edit.html.twig @@ -20,7 +20,7 @@ {{ form_widget(form) }}
{% apply spaceless %} - diff --git a/src/Resources/views/roles/list.html.twig b/src/Resources/views/roles/list.html.twig index ff21a251..110b8321 100644 --- a/src/Resources/views/roles/list.html.twig +++ b/src/Resources/views/roles/list.html.twig @@ -69,7 +69,7 @@ href="{{ path('rolesExportPage', { id: item.getId }) }}" title="{% trans %}export{% endtrans %}" data-uk-tooltip="{animation:true}"> - diff --git a/src/Resources/views/settingGroups/add.html.twig b/src/Resources/views/settingGroups/add.html.twig index 8c2109b2..6a043446 100644 --- a/src/Resources/views/settingGroups/add.html.twig +++ b/src/Resources/views/settingGroups/add.html.twig @@ -22,7 +22,7 @@ {{ form_start(form, { attr: { id: 'add-settingGroup-form'}}) }} {{ form_widget(form) }}
- diff --git a/src/Resources/views/settingGroups/edit.html.twig b/src/Resources/views/settingGroups/edit.html.twig index a3bca2b3..f95a60fe 100644 --- a/src/Resources/views/settingGroups/edit.html.twig +++ b/src/Resources/views/settingGroups/edit.html.twig @@ -25,7 +25,7 @@ {{ form_widget(form) }}
diff --git a/src/Resources/views/settings/edit.html.twig b/src/Resources/views/settings/edit.html.twig index 0f51429e..d8e390b8 100644 --- a/src/Resources/views/settings/edit.html.twig +++ b/src/Resources/views/settings/edit.html.twig @@ -19,7 +19,7 @@ {{ form_start(form, { attr: { id: 'edit-setting-form'}}) }} {{ form_widget(form) }}
- diff --git a/src/Resources/views/settings/list.html.twig b/src/Resources/views/settings/list.html.twig index 867439b4..824ed098 100644 --- a/src/Resources/views/settings/list.html.twig +++ b/src/Resources/views/settings/list.html.twig @@ -76,9 +76,9 @@ {% apply spaceless %} - + - + {% endapply %} diff --git a/src/Resources/views/tags/add.html.twig b/src/Resources/views/tags/add.html.twig index 9dc9062f..3ddf468e 100644 --- a/src/Resources/views/tags/add.html.twig +++ b/src/Resources/views/tags/add.html.twig @@ -19,7 +19,7 @@ {{ form_widget(form) }}
{% apply spaceless %} - diff --git a/src/Resources/views/tags/edit.html.twig b/src/Resources/views/tags/edit.html.twig index 82ff225f..5fc466f9 100644 --- a/src/Resources/views/tags/edit.html.twig +++ b/src/Resources/views/tags/edit.html.twig @@ -51,7 +51,7 @@
{% if not readOnly %} {% apply spaceless %} - diff --git a/src/Resources/views/tags/list.html.twig b/src/Resources/views/tags/list.html.twig index 0079b274..5bdbe3a7 100644 --- a/src/Resources/views/tags/list.html.twig +++ b/src/Resources/views/tags/list.html.twig @@ -54,7 +54,7 @@ {% endif %} {% if is_granted('ROLE_ACCESS_TAGS_DELETE') and not tag.locked %} - + {% endif %} @@ -75,7 +75,7 @@ {% if is_granted('ROLE_ACCESS_TAGS_DELETE') and not tag.locked %} - + {% endif %} diff --git a/src/Resources/views/tags/settings.html.twig b/src/Resources/views/tags/settings.html.twig index 416465d1..d738357b 100644 --- a/src/Resources/views/tags/settings.html.twig +++ b/src/Resources/views/tags/settings.html.twig @@ -21,7 +21,7 @@ {{ form_widget(form) }}
{% apply spaceless %} - @@ -32,15 +32,15 @@