diff --git a/Classes/Controller/CommentController.php b/Classes/Controller/CommentController.php index 3caa9d13..0b9a0574 100644 --- a/Classes/Controller/CommentController.php +++ b/Classes/Controller/CommentController.php @@ -13,35 +13,12 @@ use T3G\AgencyPack\Blog\Domain\Model\Comment; use T3G\AgencyPack\Blog\Domain\Model\Post; use T3G\AgencyPack\Blog\Domain\Repository\PostRepository; -use T3G\AgencyPack\Blog\Notification\CommentAddedNotification; -use T3G\AgencyPack\Blog\Notification\NotificationManager; use T3G\AgencyPack\Blog\Service\CacheService; use T3G\AgencyPack\Blog\Service\CommentService; -use TYPO3\CMS\Core\Messaging\FlashMessage; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -use TYPO3\CMS\Extbase\Utility\LocalizationUtility; class CommentController extends ActionController { - protected static $messages = [ - CommentService::STATE_ERROR => [ - 'title' => 'message.addComment.error.title', - 'text' => 'message.addComment.error.text', - 'severity' => FlashMessage::ERROR, - ], - CommentService::STATE_MODERATION => [ - 'title' => 'message.addComment.moderation.title', - 'text' => 'message.addComment.moderation.text', - 'severity' => FlashMessage::INFO, - ], - CommentService::STATE_SUCCESS => [ - 'title' => 'message.addComment.success.title', - 'text' => 'message.addComment.success.text', - 'severity' => FlashMessage::OK, - ], - ]; - /** * @var PostRepository */ @@ -81,81 +58,15 @@ public function injectBlogCacheService(CacheService $cacheService): void $this->blogCacheService = $cacheService; } - /** - * Pre-process request and ensure a valid protocol for submitted URL - */ - protected function initializeAddCommentAction(): void - { - $arguments = $this->request->getArguments(); - if (!empty($arguments['comment']['url'])) { - $re = '/^(http([s]*):\/\/)(.*)/'; - if (preg_match($re, $arguments['comment']['url'], $matches) === 0) { - $arguments['comment']['url'] = 'http://' . $arguments['comment']['url']; - } - $this->request->setArguments($arguments); - } - } - - protected function getErrorFlashMessage(): bool - { - return false; - } - /** * Show comment form. * - * @param Comment|null $comment * @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException */ - public function formAction(Comment $comment = null): void + public function formAction(): void { $this->view->assign('post', $this->postRepository->findCurrentPost()); - $this->view->assign('comment', $comment); - } - - /** - * Add comment to blog post. - * - * @param Comment $comment - * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException - * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException - * @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException - * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException - */ - public function addCommentAction(Comment $comment): void - { - $this->commentService->injectSettings($this->settings['comments']); - $post = $this->postRepository->findCurrentPost(); - $state = $this->commentService->addComment($post, $comment); - $this->addFlashMessage( - LocalizationUtility::translate(self::$messages[$state]['text'], 'blog'), - LocalizationUtility::translate(self::$messages[$state]['title'], 'blog'), - self::$messages[$state]['severity'] - ); - if ($state !== CommentService::STATE_ERROR) { - $comment->setCrdate(new \DateTime()); - GeneralUtility::makeInstance(NotificationManager::class) - ->notify(GeneralUtility::makeInstance(CommentAddedNotification::class, '', '', [ - 'comment' => $comment, - 'post' => $post, - ])); - $this->blogCacheService->flushCacheByTag('tx_blog_post_' . $post->getUid()); - } - $this->redirectToUri( - $this->controllerContext - ->getUriBuilder() - ->reset() - ->setTargetPageUid($post->getUid()) - ->setUseCacheHash(false) - ->setAddQueryString(true) - ->setArgumentsToBeExcludedFromQueryString(['tx_blog_commentform', 'cHash']) - ->buildFrontendUri() - ); } /** diff --git a/Classes/Domain/Factory/CommentFormFactory.php b/Classes/Domain/Factory/CommentFormFactory.php new file mode 100644 index 00000000..49c3a9d9 --- /dev/null +++ b/Classes/Domain/Factory/CommentFormFactory.php @@ -0,0 +1,113 @@ +get(ConfigurationService::class); + $prototypeConfiguration = $formConfigurationService->getPrototypeConfiguration($prototypeName); + $prototypeConfiguration['formElementsDefinition']['GoogleCaptcha'] = [ + 'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement' + ]; + + $configurationManager = $objectManager->get(ConfigurationManagerInterface::class); + $blogSettings = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'blog'); + $captcha = []; + $captcha['enable'] = (bool) ($blogSettings['comments']['google_recaptcha']['_typoScriptNodeValue'] ?? false); + $captcha['sitekey'] = (string) trim($blogSettings['comments']['google_recaptcha']['website_key'] ?? ''); + $captcha['secret'] = (string) trim($blogSettings['comments']['google_recaptcha']['secret_key'] ?? ''); + + $form = $objectManager->get(FormDefinition::class, 'postcomment', $prototypeConfiguration); + $form->setRenderingOption('controllerAction', 'form'); + $form->setRenderingOption('submitButtonLabel', LocalizationUtility::translate('form.comment.submit', 'blog')); + $renderingOptions = $form->getRenderingOptions(); + $renderingOptions['partialRootPaths'][] = 'EXT:blog/Resources/Private/Partials/Form/'; + $form->setRenderingOption('partialRootPaths', $renderingOptions['partialRootPaths']); + + $page = $form->createPage('commentform'); + + // Form + $nameField = $page->createElement('name', 'Text'); + $nameField->setLabel(LocalizationUtility::translate('form.comment.name', 'blog')); + $nameField->addValidator($objectManager->get(NotEmptyValidator::class)); + + $emailField = $page->createElement('email', 'Text'); + $emailField->setLabel(LocalizationUtility::translate('form.comment.email', 'blog')); + $emailField->addValidator($objectManager->get(NotEmptyValidator::class)); + $emailField->addValidator($objectManager->get(EmailAddressValidator::class)); + + $urlField = $page->createElement('url', 'Text'); + $urlField->setLabel(LocalizationUtility::translate('form.comment.url', 'blog')); + $urlField->addValidator($objectManager->get(UrlValidator::class)); + + $commentField = $page->createElement('comment', 'Textarea'); + $commentField->setLabel(LocalizationUtility::translate('form.comment.comment', 'blog')); + $commentField->addValidator($objectManager->get(NotEmptyValidator::class)); + $commentField->addValidator($objectManager->get(StringLengthValidator::class, ['minimum' => 5])); + + $explanationText = $page->createElement('headline', 'StaticText'); + $explanationText->setProperty('text', LocalizationUtility::translate('label.required.field', 'blog') . ' ' . LocalizationUtility::translate('label.required.field.explanation', 'blog')); + + if ($captcha['enable'] && $captcha['sitekey'] && $captcha['secret']) { + $captchaField = $page->createElement('captcha', 'GoogleCaptcha'); + $captchaField->setProperty('sitekey', $captcha['sitekey']); + $captchaField->addValidator($objectManager->get(GoogleCaptchaValidator::class)); + } + + // Finisher + $commentFinisher = $objectManager->get(CommentFormFinisher::class); + $form->addFinisher($commentFinisher); + + $redirectFinisher = $objectManager->get(RedirectFinisher::class); + $redirectFinisher->setOption('pageUid', $this->getTypoScriptFrontendController()->id); + $form->addFinisher($redirectFinisher); + + $this->triggerFormBuildingFinished($form); + return $form; + } + + /** + * @return TypoScriptFrontendController + */ + protected function getTypoScriptFrontendController(): ?TypoScriptFrontendController + { + return $GLOBALS['TSFE']; + } +} diff --git a/Classes/Domain/Finisher/CommentFormFinisher.php b/Classes/Domain/Finisher/CommentFormFinisher.php new file mode 100644 index 00000000..179d0a52 --- /dev/null +++ b/Classes/Domain/Finisher/CommentFormFinisher.php @@ -0,0 +1,89 @@ + [ + 'title' => 'message.addComment.error.title', + 'text' => 'message.addComment.error.text', + 'severity' => FlashMessage::ERROR, + ], + CommentService::STATE_MODERATION => [ + 'title' => 'message.addComment.moderation.title', + 'text' => 'message.addComment.moderation.text', + 'severity' => FlashMessage::INFO, + ], + CommentService::STATE_SUCCESS => [ + 'title' => 'message.addComment.success.title', + 'text' => 'message.addComment.success.text', + 'severity' => FlashMessage::OK, + ], + ]; + + protected function executeInternal() + { + $configurationManager = $this->objectManager->get(ConfigurationManagerInterface::class); + $settings = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'blog'); + $postRepository = $this->objectManager->get(PostRepository::class); + $cacheService = $this->objectManager->get(CacheService::class); + $commentService = $this->objectManager->get(CommentService::class); + $commentService->injectSettings($settings['comments']); + + // Create Comment + $values = $this->finisherContext->getFormValues(); + $comment = new Comment(); + $comment->setName($values['name'] ?? ''); + $comment->setEmail($values['email'] ?? ''); + $comment->setUrl($values['url'] ?? ''); + $comment->setComment($values['comment'] ?? ''); + $post = $postRepository->findCurrentPost(); + $state = $commentService->addComment($post, $comment); + + // Add FlashMessage + $flashMessage = $this->objectManager->get( + FlashMessage::class, + LocalizationUtility::translate(self::$messages[$state]['text'], 'blog'), + LocalizationUtility::translate(self::$messages[$state]['title'], 'blog'), + self::$messages[$state]['severity'], + true + ); + $this->finisherContext->getControllerContext()->getFlashMessageQueue()->addMessage($flashMessage); + + if ($state !== CommentService::STATE_ERROR) { + $comment->setCrdate(new \DateTime()); + GeneralUtility::makeInstance(NotificationManager::class) + ->notify(GeneralUtility::makeInstance(CommentAddedNotification::class, '', '', [ + 'comment' => $comment, + 'post' => $post, + ])); + $cacheService->flushCacheByTag('tx_blog_post_' . $post->getUid()); + } + } +} diff --git a/Classes/Domain/Validator/CommentValidator.php b/Classes/Domain/Validator/CommentValidator.php deleted file mode 100644 index 8405f872..00000000 --- a/Classes/Domain/Validator/CommentValidator.php +++ /dev/null @@ -1,88 +0,0 @@ -getHp()) !== '') { - $this->addError('It looks like you are a bot!', 1484142303); - } - if (trim($value->getName()) === '') { - $this->addError('The name is required', 1467650564); - } - if (trim($value->getEmail()) === '') { - $this->addError('The email is required', 1467650565); - } - if (!GeneralUtility::validEmail($value->getEmail())) { - $this->addError('The email address has an invalid format', 1467650566); - } - if (trim($value->getComment()) === '') { - $this->addError('The comment is required', 1467650567); - } - if (trim($value->getUrl()) !== '' && !GeneralUtility::isValidUrl(trim($value->getUrl()))) { - $this->addError('The url has an invalid format', 1467650568); - } - - $settings = GeneralUtility::makeInstance(ObjectManager::class) - ->get(ConfigurationManagerInterface::class) - ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'blog'); - $requestData = GeneralUtility::_GPmerged('tx_blog_commentform'); - if ( - // this validator is called multiple times, if the first success, - // the global variable is set, else validate the re-captcha - empty($GLOBALS['google_recaptcha']) - // check if we create a new comment, else we don't need a validation - && (!empty($requestData['action']) && $requestData['action'] === 'addComment') - && (!empty($requestData['controller']) && $requestData['controller'] === 'Comment') - // check if google re-captcha is active, else we don't need a validation - && (int)$settings['comments']['google_recaptcha']['_typoScriptNodeValue'] === 1 - ) { - $additionalOptions = [ - 'headers' => ['Content-type' => 'application/x-www-form-urlencoded'], - 'query' => [ - 'secret' => $settings['comments']['google_recaptcha']['secret_key'], - 'response' => GeneralUtility::_GP('g-recaptcha-response'), - 'remoteip' => GeneralUtility::getIndpEnv('REMOTE_ADDR') - ] - ]; - $response = GeneralUtility::makeInstance(RequestFactory::class) - ->request('https://www.google.com/recaptcha/api/siteverify', 'POST', $additionalOptions); - if ($response->getStatusCode() === 200) { - $result = json_decode($response->getBody()->getContents()); - if (!$result->success) { - $this->addError('The re-captcha failed', 1501341100); - } else { - $GLOBALS['google_recaptcha'] = true; - } - } - } - } - } -} diff --git a/Classes/Domain/Validator/GoogleCaptchaValidator.php b/Classes/Domain/Validator/GoogleCaptchaValidator.php new file mode 100644 index 00000000..06bbc0f5 --- /dev/null +++ b/Classes/Domain/Validator/GoogleCaptchaValidator.php @@ -0,0 +1,62 @@ +get(ConfigurationManagerInterface::class) + // ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'blog'); + // $requestData = GeneralUtility::_GPmerged('tx_blog_commentform'); + // if ( + // // this validator is called multiple times, if the first success, + // // the global variable is set, else validate the re-captcha + // empty($GLOBALS['google_recaptcha']) + // // check if we create a new comment, else we don't need a validation + // && (!empty($requestData['action']) && $requestData['action'] === 'addComment') + // && (!empty($requestData['controller']) && $requestData['controller'] === 'Comment') + // // check if google re-captcha is active, else we don't need a validation + // && (int)$settings['comments']['google_recaptcha']['_typoScriptNodeValue'] === 1 + // ) { + // $additionalOptions = [ + // 'headers' => ['Content-type' => 'application/x-www-form-urlencoded'], + // 'query' => [ + // 'secret' => $settings['comments']['google_recaptcha']['secret_key'], + // 'response' => GeneralUtility::_GP('g-recaptcha-response'), + // 'remoteip' => GeneralUtility::getIndpEnv('REMOTE_ADDR') + // ] + // ]; + // $response = GeneralUtility::makeInstance(RequestFactory::class) + // ->request('https://www.google.com/recaptcha/api/siteverify', 'POST', $additionalOptions); + // if ($response->getStatusCode() === 200) { + // $result = json_decode($response->getBody()->getContents()); + // if (!$result->success) { + // $this->addError('The re-captcha failed', 1501341100); + // } else { + // $GLOBALS['google_recaptcha'] = true; + // } + // } + // } + + \TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($value); + + $this->addError('IMPLEMENT RECAPTCHA', 1501341100); + } +} diff --git a/Configuration/TypoScript/Static/setup.typoscript b/Configuration/TypoScript/Static/setup.typoscript index c2e31b75..14f4eea9 100644 --- a/Configuration/TypoScript/Static/setup.typoscript +++ b/Configuration/TypoScript/Static/setup.typoscript @@ -1,3 +1,9 @@ +###################### +#### DEPENDENCIES #### +###################### + + + ############## ### PLUGIN ### ############## diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 52c9a92e..c5d96f25 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -39,9 +39,6 @@ RSS-Feeds - - Please let this field empty - Name diff --git a/Resources/Private/Partials/Comments/Form/Closed.html b/Resources/Private/Partials/Comments/Form/Closed.html new file mode 100644 index 00000000..8a1a80a8 --- /dev/null +++ b/Resources/Private/Partials/Comments/Form/Closed.html @@ -0,0 +1,5 @@ +
+ +
diff --git a/Resources/Private/Partials/Comments/Form/Disqus.html b/Resources/Private/Partials/Comments/Form/Disqus.html new file mode 100644 index 00000000..32c5b698 --- /dev/null +++ b/Resources/Private/Partials/Comments/Form/Disqus.html @@ -0,0 +1,16 @@ +
+
+ + +
diff --git a/Resources/Private/Partials/Comments/Form/Local.html b/Resources/Private/Partials/Comments/Form/Local.html new file mode 100644 index 00000000..06872031 --- /dev/null +++ b/Resources/Private/Partials/Comments/Form/Local.html @@ -0,0 +1,5 @@ +

+
+ + +
diff --git a/Resources/Private/Partials/Form/GoogleCaptcha.html b/Resources/Private/Partials/Form/GoogleCaptcha.html new file mode 100644 index 00000000..96771a44 --- /dev/null +++ b/Resources/Private/Partials/Form/GoogleCaptcha.html @@ -0,0 +1,11 @@ + + + + +
+
+
diff --git a/Resources/Private/Templates/Comment/Form.html b/Resources/Private/Templates/Comment/Form.html index 6022f6f5..9842799a 100644 --- a/Resources/Private/Templates/Comment/Form.html +++ b/Resources/Private/Templates/Comment/Form.html @@ -1,93 +1,8 @@ - - - - - - -
- - -
- -

- -

- - - - - This is the HoneyPot field, it will be set to hidden with JavaScript and must not be - filled out. - -
- - - -
-
- - -
-
- - -
-
- - -
-
- - -
-

- - -
-
- -
-
-
-
- -

-

-
-
-
+ +
+ + + + +
diff --git a/composer.json b/composer.json index 55bf7fa5..0d2bc320 100644 --- a/composer.json +++ b/composer.json @@ -75,6 +75,7 @@ "typo3/cms-extbase": "^9.5.5 || 10.0.*@dev", "typo3/cms-extensionmanager": "^9.5.5 || 10.0.*@dev", "typo3/cms-fluid": "^9.5.5 || 10.0.*@dev", + "typo3/cms-form": "^9.5.5 || 10.0.*@dev", "typo3/cms-frontend": "^9.5.5 || 10.0.*@dev", "typo3/cms-install": "^9.5.5 || 10.0.*@dev", "typo3fluid/fluid": "^2.6" diff --git a/ext_emconf.php b/ext_emconf.php index 60e5d1dd..bfa0d1b7 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -21,6 +21,7 @@ 'constraints' => [ 'depends' => [ 'typo3' => '9.5.5-10.0.99', + 'form' => '9.5.5-10.0.99', ], 'conflicts' => [], 'suggests' => [], diff --git a/ext_localconf.php b/ext_localconf.php index 0241568b..e863c8a1 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -66,10 +66,10 @@ 'T3G.AgencyPack.Blog', 'CommentForm', [ - 'Comment' => 'form, addComment', + 'Comment' => 'form', ], [ - 'Comment' => 'form, addComment', + 'Comment' => 'form', ] );