diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..0f698955a --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,65 @@ +name: CI + +on: ['push', 'pull_request'] + +jobs: + cs: + runs-on: ubuntu-latest + name: Coding style + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: json, mbstring + tools: composer, php-cs-fixer:2.19 + + - name: Display tools versions + run: | + composer --version + php-cs-fixer --version + - name: Check PHP code + run: php-cs-fixer fix --dry-run --diff --ansi + + phpunit: + name: PHPUnit (PHP ${{ matrix.php }} + ${{ matrix.symfony-version}}) + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + SYMFONY_REQUIRE: "${{ matrix.symfony_require }}" + strategy: + matrix: + include: + - php: '7.2' + dependencies: 'lowest' + symfony_require: '3.4.*' + - php: '7.4' + dependencies: 'highest' + symfony_require: '4.4.*' + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + tools: pecl, composer + extensions: curl, json, mbstring, openssl + + - name: Globally install symfony/flex + run: composer global require --no-progress --no-scripts --no-plugins symfony/flex + + - name: Install Composer dependencies (${{ matrix.dependencies }}) + uses: ramsey/composer-install@v1 + with: + dependency-versions: "${{ matrix.dependencies }}" + composer-options: "--prefer-dist --prefer-stable" + + - name: Run unit tests + run: | + vendor/bin/phpunit diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2a2cb78d1..000000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: php - -env: - global: - - SYMFONY_DEPRECATIONS_HELPER=weak - -php: - - 7.2 - - 7.3 - - 7.4 - -cache: - directories: - - $HOME/.composer/cache/files - -matrix: - include: - - php: 7.2 - env: COMPOSER_FLAGS="--prefer-lowest" - - php: 7.4 - env: DEPENDENCIES="symfony/flex" SYMFONY_VERSION="^3.4" - - php: 7.4 - env: DEPENDENCIES="symfony/flex" SYMFONY_VERSION="^4.4" - fast_finish: true - -before_install: - - echo "memory_limit=4G" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; - - if ! [ -z "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi; - - if ! [ -z "$SYMFONY_VERSION" ]; then composer config extra.symfony.require ${SYMFONY_VERSION}; fi - -install: - - composer update --prefer-dist --no-interaction $COMPOSER_FLAGS - -script: - - composer validate --no-check-lock --strict - - ./vendor/bin/phpunit --coverage-text --colors diff --git a/Controller/ThreadController.php b/Controller/ThreadController.php index 4e4620415..028917b6d 100644 --- a/Controller/ThreadController.php +++ b/Controller/ThreadController.php @@ -14,12 +14,12 @@ use FOS\CommentBundle\Model\CommentInterface; use FOS\CommentBundle\Model\ThreadInterface; use FOS\RestBundle\View\View; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; /** * Restful controller for the Threads. @@ -41,8 +41,13 @@ public function newThreadsAction() $form = $this->container->get('fos_comment.form_factory.thread')->createForm(); $view = View::create() - ->setData(['form' => $form->createView()]) - ->setTemplate('@FOSComment/Thread/new.html.twig'); + ->setData([ + 'data' => [ + 'form' => $form->createView(), + ], + 'template' => '@FOSComment/Thread/new.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -144,8 +149,15 @@ public function editThreadCommentableAction(Request $request, $id) $form->setData($thread); $view = View::create() - ->setData(['form' => $form, 'id' => $id, 'isCommentable' => $thread->isCommentable()]) - ->setTemplate('@FOSComment/Thread/commentable.html.twig'); + ->setData([ + 'data' => [ + 'form' => $form, + 'id' => $id, + 'isCommentable' => $thread->isCommentable(), + ], + 'template' => '@FOSComment/Thread/commentable.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -204,13 +216,16 @@ public function newThreadCommentsAction(Request $request, $id) $view = View::create() ->setData([ - 'form' => $form->createView(), - 'first' => 0 === $thread->getNumComments(), - 'thread' => $thread, - 'parent' => $parent, - 'id' => $id, - ]) - ->setTemplate('@FOSComment/Thread/comment_new.html.twig'); + 'data' => [ + 'form' => $form->createView(), + 'first' => 0 === $thread->getNumComments(), + 'thread' => $thread, + 'parent' => $parent, + 'id' => $id, + ], + 'template' => '@FOSComment/Thread/comment_new.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -239,8 +254,16 @@ public function getThreadCommentAction($id, $commentId) } $view = View::create() - ->setData(['comment' => $comment, 'thread' => $thread, 'parent' => $parent, 'depth' => $comment->getDepth()]) - ->setTemplate('@FOSComment/Thread/comment.html.twig'); + ->setData([ + 'data' => [ + 'comment' => $comment, + 'thread' => $thread, + 'parent' => $parent, + 'depth' => $comment->getDepth(), + ], + 'template' => '@FOSComment/Thread/comment.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -269,8 +292,15 @@ public function removeThreadCommentAction(Request $request, $id, $commentId) $form->setData($comment); $view = View::create() - ->setData(['form' => $form, 'id' => $id, 'commentId' => $commentId]) - ->setTemplate('@FOSComment/Thread/comment_remove.html.twig'); + ->setData([ + 'data' => [ + 'form' => $form, + 'id' => $id, + 'commentId' => $commentId, + ], + 'template' => '@FOSComment/Thread/comment_remove.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -329,10 +359,13 @@ public function editThreadCommentAction($id, $commentId) $view = View::create() ->setData([ - 'form' => $form->createView(), - 'comment' => $comment, - ]) - ->setTemplate('@FOSComment/Thread/comment_edit.html.twig'); + 'data' => [ + 'form' => $form->createView(), + 'comment' => $comment, + ], + 'template' => '@FOSComment/Thread/comment_edit.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -400,8 +433,13 @@ public function getThreadCommentsAction(Request $request, $id) if (count($errors) > 0) { $view = View::create() ->setStatusCode(Response::HTTP_BAD_REQUEST) - ->setData(['errors' => $errors]) - ->setTemplate('@FOSComment/Thread/errors.html.twig'); + ->setData([ + 'data' => [ + 'errors' => $errors, + ], + 'template' => '@FOSComment/Thread/errors.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -433,18 +471,24 @@ public function getThreadCommentsAction(Request $request, $id) $view = View::create() ->setData([ - 'comments' => $comments, - 'displayDepth' => $displayDepth, - 'sorter' => 'date', - 'thread' => $thread, - 'view' => $viewMode, - ]) - ->setTemplate('@FOSComment/Thread/comments.html.twig'); + 'data' => [ + 'comments' => $comments, + 'displayDepth' => $displayDepth, + 'sorter' => 'date', + 'thread' => $thread, + 'view' => $viewMode, + ], + 'template' => '@FOSComment/Thread/comments.html.twig', + ] + ); // Register a special handler for RSS. Only available on this route. if ('rss' === $request->getRequestFormat()) { $templatingHandler = function ($handler, $view, $request) { - $view->setTemplate('@FOSComment/Thread/thread_xml_feed.html.twig'); + $data = $view->getData(); + $data['template'] = '@FOSComment/Thread/thread_xml_feed.html.twig'; + + $view->setData($data); return new Response($handler->renderTemplate($view, 'rss'), Response::HTTP_OK, $view->getHeaders()); }; @@ -512,9 +556,12 @@ public function getThreadCommentVotesAction($id, $commentId) $view = View::create() ->setData([ - 'commentScore' => $comment->getScore(), - ]) - ->setTemplate('@FOSComment/Thread/comment_votes.html.twig'); + 'data' => [ + 'commentScore' => $comment->getScore(), + ], + 'template' => '@FOSComment/Thread/comment_votes.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -545,11 +592,14 @@ public function newThreadCommentVotesAction(Request $request, $id, $commentId) $view = View::create() ->setData([ - 'id' => $id, - 'commentId' => $commentId, - 'form' => $form->createView(), - ]) - ->setTemplate('@FOSComment/Thread/vote_new.html.twig'); + 'data' => [ + 'id' => $id, + 'commentId' => $commentId, + 'form' => $form->createView(), + ], + 'template' => '@FOSComment/Thread/vote_new.html.twig', + ] + ); return $this->getViewHandler()->handle($view); } @@ -616,11 +666,14 @@ protected function onCreateCommentError(FormInterface $form, $id, CommentInterfa $view = View::create() ->setStatusCode(Response::HTTP_BAD_REQUEST) ->setData([ - 'form' => $form, - 'id' => $id, - 'parent' => $parent, - ]) - ->setTemplate('@FOSComment/Thread/comment_new.html.twig'); + 'data' => [ + 'form' => $form, + 'id' => $id, + 'parent' => $parent, + ], + 'template' => '@FOSComment/Thread/comment_new.html.twig', + ] + ); return $view; } @@ -649,9 +702,12 @@ protected function onCreateThreadError(FormInterface $form) $view = View::create() ->setStatusCode(Response::HTTP_BAD_REQUEST) ->setData([ - 'form' => $form, - ]) - ->setTemplate('@FOSComment/Thread/new.html.twig'); + 'data' => [ + 'form' => $form, + ], + 'template' => '@FOSComment/Thread/new.html.twig', + ] + ); return $view; } @@ -698,11 +754,14 @@ protected function onCreateVoteError(FormInterface $form, $id, $commentId) $view = View::create() ->setStatusCode(Response::HTTP_BAD_REQUEST) ->setData([ - 'id' => $id, - 'commentId' => $commentId, - 'form' => $form, - ]) - ->setTemplate('@FOSComment/Thread/vote_new.html.twig'); + 'data' => [ + 'id' => $id, + 'commentId' => $commentId, + 'form' => $form, + ], + 'template' => '@FOSComment/Thread/vote_new.html.twig', + ] + ); return $view; } @@ -733,10 +792,13 @@ protected function onEditCommentError(FormInterface $form, $id) $view = View::create() ->setStatusCode(Response::HTTP_BAD_REQUEST) ->setData([ - 'form' => $form, - 'comment' => $form->getData(), - ]) - ->setTemplate('@FOSComment/Thread/comment_edit.html.twig'); + 'data' => [ + 'form' => $form, + 'comment' => $form->getData(), + ], + 'template' => '@FOSComment/Thread/comment_edit.html.twig', + ] + ); return $view; } @@ -765,11 +827,14 @@ protected function onOpenThreadError(FormInterface $form) $view = View::create() ->setStatusCode(Response::HTTP_BAD_REQUEST) ->setData([ - 'form' => $form, - 'id' => $form->getData()->getId(), - 'isCommentable' => $form->getData()->isCommentable(), - ]) - ->setTemplate('@FOSComment/Thread/commentable.html.twig'); + 'data' => [ + 'form' => $form, + 'id' => $form->getData()->getId(), + 'isCommentable' => $form->getData()->isCommentable(), + ], + 'template' => '@FOSComment/Thread/commentable.html.twig', + ] + ); return $view; } @@ -800,12 +865,15 @@ protected function onRemoveThreadCommentError(FormInterface $form, $id) $view = View::create() ->setStatusCode(Response::HTTP_BAD_REQUEST) ->setData([ - 'form' => $form, - 'id' => $id, - 'commentId' => $form->getData()->getId(), - 'value' => $form->getData()->getState(), - ]) - ->setTemplate('@FOSComment/Thread/comment_remove.html.twig'); + 'data' => [ + 'form' => $form, + 'id' => $id, + 'commentId' => $form->getData()->getId(), + 'value' => $form->getData()->getState(), + ], + 'template' => '@FOSComment/Thread/comment_remove.html.twig', + ] + ); return $view; } diff --git a/DependencyInjection/FOSCommentExtension.php b/DependencyInjection/FOSCommentExtension.php index 633a774ba..9c7a92290 100644 --- a/DependencyInjection/FOSCommentExtension.php +++ b/DependencyInjection/FOSCommentExtension.php @@ -52,7 +52,7 @@ public function load(array $configs, ContainerBuilder $container) $container->setDefinition('fos_comment.entity_manager', $def); } - foreach (['controller', 'events', 'form', 'twig', 'sorting', 'model'] as $basename) { + foreach (['controller', 'events', 'form', 'twig', 'sorting', 'model', 'view_handler'] as $basename) { $loader->load(sprintf('%s.xml', $basename)); } diff --git a/Resources/config/view_handler.xml b/Resources/config/view_handler.xml new file mode 100644 index 000000000..ec1674214 --- /dev/null +++ b/Resources/config/view_handler.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/Tests/Functional/ApiTest.php b/Tests/Functional/ApiTest.php index 2c72fbe78..089348bc1 100644 --- a/Tests/Functional/ApiTest.php +++ b/Tests/Functional/ApiTest.php @@ -142,7 +142,7 @@ public function testAddCommentToThread($id) $this->assertRedirect($this->client->getResponse(), "/comment_api/threads/{$id}/comments/1"); $crawler = $this->client->followRedirect(); - $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_body')->text()); + $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_body')->text(null, false)); return $id; } @@ -178,7 +178,7 @@ public function testReplyToComment($id) $this->assertRedirect($this->client->getResponse(), "/comment_api/threads/{$id}/comments/2"); $crawler = $this->client->followRedirect(); - $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_body')->text()); + $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_body')->text(null, false)); return $id; } @@ -196,7 +196,7 @@ public function testGetCommentTree($id) $crawler = $this->client->request('GET', "/comment_api/threads/{$id}/comments.html"); $this->assertCount(2, $crawler->filter('.fos_comment_comment_body')); - $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_show .fos_comment_comment_depth_1 .fos_comment_comment_body')->first()->text()); + $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_show .fos_comment_comment_depth_1 .fos_comment_comment_body')->first()->text(null, false)); } /** @@ -212,8 +212,8 @@ public function testGetCommentTreeDepth($id) $crawler = $this->client->request('GET', "/comment_api/threads/{$id}/comments.html?displayDepth=0"); $this->assertCount(1, $crawler->filter('.fos_comment_comment_body')); - $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_body')->first()->text()); - $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_body')->last()->text()); + $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_body')->first()->text(null, false)); + $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_body')->last()->text(null, false)); } /** @@ -229,8 +229,8 @@ public function testGetCommentFlat($id) $crawler = $this->client->request('GET', "/comment_api/threads/{$id}/comments.html?view=flat"); $this->assertCount(2, $crawler->filter('.fos_comment_comment_body')); - $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text()); - $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text()); + $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text(null, false)); + $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text(null, false)); } /** @@ -248,17 +248,17 @@ public function testGetCommentFlatSorted($id) $this->assertCount(2, $crawler->filter('.fos_comment_comment_body')); $this->assertCount(2, $crawler2->filter('.fos_comment_comment_body')); - $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text()); - $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text()); + $this->assertContains('Test Reply Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text(null, false)); + $this->assertContains('Test Comment', $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text(null, false)); $this->assertSame( - $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text(), - $crawler2->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text() + $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text(null, false), + $crawler2->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text(null, false) ); $this->assertSame( - $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text(), - $crawler2->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text() + $crawler->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->last()->text(null, false), + $crawler2->filter('.fos_comment_comment_show.fos_comment_comment_depth_0 .fos_comment_comment_body')->first()->text(null, false) ); } } diff --git a/ViewHandler/FOSRestViewHandlerAdapter.php b/ViewHandler/FOSRestViewHandlerAdapter.php new file mode 100644 index 000000000..a0f0f5c75 --- /dev/null +++ b/ViewHandler/FOSRestViewHandlerAdapter.php @@ -0,0 +1,112 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace FOS\CommentBundle\ViewHandler; + +use FOS\RestBundle\View\View; +use FOS\RestBundle\View\ViewHandlerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Twig\Environment; + +class FOSRestViewHandlerAdapter implements ViewHandlerInterface +{ + /** + * @var ViewHandlerInterface + */ + private $decorated; + + /** + * @var Environment + */ + private $twig; + + /** + * @var RequestStack + */ + private $requestStack; + + public function __construct(ViewHandlerInterface $decorated, Environment $twig, RequestStack $requestStack) + { + $this->decorated = $decorated; + $this->twig = $twig; + $this->requestStack = $requestStack; + } + + public function supports($format): bool + { + return $this->decorated->supports($format); + } + + public function registerHandler($format, $callable): void + { + $this->decorated->registerHandler($format, $callable); + } + + public function handle(View $view, Request $request = null): Response + { + $data = $view->getData(); + + if (null === $request) { + $request = $this->requestStack->getCurrentRequest(); + } + + if ('html' === ($view->getFormat() ?: $request->getRequestFormat()) && is_array($data)) { + $template = $data['template']; + $templateData = $data['data']; + + $response = $this->twig->render($template, $templateData); + + return new Response($response); + } + + if (is_array($data)) { + $view->setData($data['data'] ?? $data); + } + + return $this->decorated->handle($view, $request); + } + + public function createRedirectResponse(View $view, $location, $format): Response + { + return $this->decorated->createRedirectResponse($view, $location, $format); + } + + public function createResponse(View $view, Request $request, $format): Response + { + return $this->decorated->createResponse($view, $request, $format); + } + + /** + * @deprecated + */ + public function isFormatTemplating($format) + { + return $this->decorated->isFormatTemplating($format); + } + + /** + * @deprecated + */ + public function renderTemplate(View $view, $format) + { + return $this->decorated->renderTemplate($view, $format); + } + + /** + * @deprecated + */ + public function prepareTemplateParameters(View $view) + { + return $this->decorated->prepareTemplateParameters($view); + } +} diff --git a/composer.json b/composer.json index ffcb90f58..9dee420c9 100644 --- a/composer.json +++ b/composer.json @@ -20,12 +20,11 @@ } ], "support": { - "irc": "irc://irc.freenode.org/symfony-dev", "issues": "https://github.com/FriendsOfSymfony/FOSCommentBundle/issues" }, "require": { "php": "^7.2", - "friendsofsymfony/rest-bundle": "~2.6", + "friendsofsymfony/rest-bundle": "~2.8", "jms/serializer-bundle": "~2.0|~3.0", "symfony/framework-bundle": "~3.4|^4.3", "symfony/asset": "~3.4|^4.3", @@ -52,7 +51,7 @@ "symfony/dom-crawler": "~3.4|^4.3", "symfony/css-selector": "~3.4|^4.3", "doctrine/orm": "~2.3", - "doctrine/doctrine-bundle": "~1.6", + "doctrine/doctrine-bundle": "^1.6|^2.0", "friendsofsymfony/user-bundle": "~2.0", "ornicar/akismet-bundle": "dev-master", "symfony/expression-language": "~3.4|^4.3",