• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fnagel / t3extblog / 23823603081

31 Mar 2026 11:05PM UTC coverage: 37.437% (-12.6%) from 50.045%
23823603081

push

github

fnagel
[FEATURE] Add YAML lint to composer scripts and CI

Using the new TYPO3 14.2 built in YAML linter.

https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/13.3/Feature-104973-ActivateLintYamlExecutableForTYPO3.html

1256 of 3355 relevant lines covered (37.44%)

3.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.82
/Classes/Controller/CommentController.php
1
<?php
2

3
namespace FelixNagel\T3extblog\Controller;
4

5
/**
6
 * This file is part of the "t3extblog" Extension for TYPO3 CMS.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11

12
use TYPO3\CMS\Extbase\Annotation\IgnoreValidation;
13
use FelixNagel\T3extblog\Validation\Validator\CommentEmailValidator;
14
use FelixNagel\T3extblog\Validation\Validator\PrivacyPolicyValidator;
15
use FelixNagel\T3extblog\Event;
16
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity as Message;
17
use FelixNagel\T3extblog\Domain\Repository\CommentRepository;
18
use FelixNagel\T3extblog\Service\CommentNotificationService;
19
use FelixNagel\T3extblog\Service\SpamCheckServiceInterface;
20
use FelixNagel\T3extblog\Utility\FrontendUtility;
21
use FelixNagel\T3extblog\Domain\Model\Comment;
22
use FelixNagel\T3extblog\Domain\Model\Post;
23
use TYPO3\CMS\Extbase\Annotation as Extbase;
24
use Psr\Http\Message\ResponseInterface;
25

26
/**
27
 * CommentController.
28
 */
29
class CommentController extends AbstractCommentController
30
{
31
    /**
32
     * CommentController constructor.
33
     */
34
    public function __construct(
8✔
35
        protected CommentRepository $commentRepository,
36
        protected CommentNotificationService $notificationService,
37
        protected SpamCheckServiceInterface $spamCheckService
38
    ) {
39
    }
8✔
40

41
    /**
42
     * action list.
43
     *
44
     * @param Post|null $post Show only comments related to this post
45
     */
46
    public function listAction(int $page = 1, ?Post $post = null): ResponseInterface
3✔
47
    {
48
        if ($post === null) {
3✔
49
            $comments = $this->commentRepository->findValid();
3✔
50
        } else {
51
            $comments = $this->commentRepository->findValidByPost($post);
×
52
            $this->view->assign('post', $post);
×
53
        }
54

55
        // Add basic PID based cache tag
56
        // @extensionScannerIgnoreLine
57
        $this->addCacheTags($comments->getFirst());
3✔
58

59
        $this->view->assign('comments', $comments);
3✔
60

61
        return $this->paginationHtmlResponse(
3✔
62
            $comments,
3✔
63
            $this->settings['latestComments']['paginate'],
3✔
64
            $page
3✔
65
        );
3✔
66
    }
67

68
    /**
69
     * action latest.
70
     *
71
     * @param Post|null $post Show only comments related to this post
72
     */
73
    public function latestAction(int $page = 1, ?Post $post = null): ResponseInterface
3✔
74
    {
75
        return $this->listAction($page, $post);
3✔
76
    }
77

78
    /**
79
     * Show action.
80
     *
81
     * Redirect to post show if empty comment create is called
82
     *
83
     * @param Post $post The post the comment is related to
84
     */
85
    public function showAction(Post $post): ResponseInterface
1✔
86
    {
87
        return $this->redirect('show', 'Post', null, $post->getLinkParameter());
1✔
88
    }
89

90
    /**
91
     * action new.
92
     *
93
     * @param Post $post The post the comment is related to
94
     */
95
    #[IgnoreValidation(['value' => 'newComment'])]
×
96
    public function newAction(Post $post, ?Comment $newComment = null): ResponseInterface
97
    {
98
        if ($newComment === null) {
×
99
            $newComment = $this->getNewComment();
×
100
        }
101

102
        $this->view->assign('newComment', $newComment);
×
103
        $this->view->assign('post', $post);
×
104

105
        return $this->htmlResponse();
×
106
    }
107

108
    public function initializeCreateAction()
4✔
109
    {
110
        $settings = $this->settings['blogsystem']['comments']['rateLimit'];
4✔
111

112
        if ($settings['enable']) {
4✔
113
            $this->initRateLimiter('comment-create', $settings);
3✔
114
        }
115
    }
116

117
    /**
118
     * Adds a comment to a blog post and redirects to single view.
119
     *
120
     * @param Post    $post       The post the comment is related to
121
     * @param Comment $newComment The comment to create
122
     */
123
    #[Extbase\Validate(['validator' => CommentEmailValidator::class, 'param' => 'newComment'])]
4✔
124
    #[Extbase\Validate(['validator' => PrivacyPolicyValidator::class, 'param' => 'newComment', 'options' => ['key' => 'comment', 'property' => 'privacyPolicyAccepted']])]
125
    public function createAction(Post $post, ?Comment $newComment = null): ResponseInterface
126
    {
127
        // @todo Fix flash messages caching issue, see: https://github.com/fnagel/t3extblog/issues/112
128
        $this->clearPageCache();
4✔
129

130
        if (($rateLimitResult = $this->checkRateLimit()) instanceof ResponseInterface) {
4✔
131
            return $rateLimitResult;
1✔
132
        }
133

134
        if ($newComment === null) {
4✔
135
            $this->addFlashMessageByKey('noComment', Message::WARNING);
×
136
            return $this->redirect('show', 'Post', null, $post->getLinkParameter());
×
137
        }
138

139
        if (($commentAllowedResult = $this->checkIfCommentIsAllowed($post)) instanceof ResponseInterface) {
4✔
140
            return $commentAllowedResult;
×
141
        }
142

143
        if (($spamPointResult = $this->checkSpamPoints($newComment)) instanceof ResponseInterface) {
4✔
144
            return $spamPointResult;
4✔
145
        }
146

147
        $this->sanitizeComment($newComment);
×
148

149
        if ($this->settings['blogsystem']['comments']['approvedByDefault']) {
×
150
            $newComment->setApproved(true);
×
151
        }
152

153
        /** @var Event\Comment\CreatePrePersistEvent $event */
154
        $event = $this->eventDispatcher->dispatch(
×
155
            new Event\Comment\CreatePrePersistEvent($post, $newComment)
×
156
        );
×
157
        $post = $event->getPost();
×
158
        $newComment = $event->getComment();
×
159

160
        $post->addComment($newComment);
×
161
        $this->persistAllEntities();
×
162

163
        // Process comment (send mails, clear cache, etc.)
164
        $this->notificationService->processNewEntity($newComment);
×
165
        $this->notificationService->notifyAdmin($newComment);
×
166
        $this->notificationService->flushFrontendCache($newComment);
×
167

168
        if (!$this->hasFlashMessages()) {
×
169
            if ($newComment->isApproved()) {
×
170
                $this->addFlashMessageByKey('created');
×
171
            } else {
172
                $this->addFlashMessageByKey('createdDisapproved', Message::INFO);
×
173
            }
174
        }
175

176
        $this->getRateLimiter()->reset('comment-create');
×
177

178
        return $this->redirect('show', 'Post', null, $post->getLinkParameter());
×
179
    }
180

181
    /**
182
     * Checks rate limit for request.
183
     */
184
    protected function checkRateLimit(): ?ResponseInterface
4✔
185
    {
186
        $settings = $this->settings['blogsystem']['comments']['rateLimit'];
4✔
187

188
        if ($settings['enable'] && !$this->getRateLimiter()->isAccepted('comment-create')) {
4✔
189
            $this->addFlashMessageByKey('rateLimit', Message::ERROR);
1✔
190
            return $this->errorAction();
1✔
191
        }
192

193
        return null;
4✔
194
    }
195

196
    /**
197
     * Checks if a new comment could be created.
198
     *
199
     * @param Post $post The post the comment is related to
200
     */
201
    protected function checkIfCommentIsAllowed(Post $post): ?ResponseInterface
4✔
202
    {
203
        $settings = $this->settings['blogsystem']['comments'];
4✔
204

205
        if (!$settings['allowed'] || $post->getAllowComments() === Post::ALLOW_COMMENTS_NOBODY) {
4✔
206
            $this->addFlashMessageByKey('notAllowed', Message::ERROR);
×
207
            return $this->errorAction();
×
208
        }
209

210
        if ($post->getAllowComments() === Post::ALLOW_COMMENTS_LOGIN && !FrontendUtility::isUserLoggedIn()) {
4✔
211
            $this->addFlashMessageByKey('notLoggedIn', Message::ERROR);
×
212
            return $this->errorAction();
×
213
        }
214

215
        if ($settings['allowedUntil'] && $post->isExpired(trim($settings['allowedUntil']))) {
4✔
216
            $this->addFlashMessageByKey('commentsClosed', Message::ERROR);
×
217
            return $this->errorAction();
×
218
        }
219

220
        return null;
4✔
221
    }
222

223
    protected function checkSpamPoints(Comment $comment): ?ResponseInterface
4✔
224
    {
225
        $settings = $this->settings['blogsystem']['comments']['spamCheck'];
4✔
226
        $comment->setSpamPoints($this->spamCheckService->process($settings));
4✔
227

228
        $threshold = $settings['threshold'];
4✔
229
        $logData = [
4✔
230
            'commentUid' => $comment->getUid(),
4✔
231
            'spamPoints' => $comment->getSpamPoints(),
4✔
232
        ];
4✔
233

234
        // block comment and redirect user
235
        if ($threshold['redirect'] > 0 && $comment->getSpamPoints() >= (int) $threshold['redirect']) {
4✔
236
            $this->getLog()->notice('New comment blocked and user redirected because of SPAM.', $logData);
4✔
237
            return $this->redirect(
4✔
238
                '',
4✔
239
                null,
4✔
240
                null,
4✔
241
                $settings['redirect']['arguments'] ?? null,
4✔
242
                (int)$settings['redirect']['pid']
4✔
243
            );
4✔
244
        }
245

246
        // block comment and show message
247
        if ($threshold['block'] > 0 && $comment->getSpamPoints() >= (int) $threshold['block']) {
×
248
            $this->getLog()->notice('New comment blocked because of SPAM.', $logData);
×
249
            $this->addFlashMessageByKey('blockedAsSpam', Message::ERROR);
×
250
            return $this->errorAction();
×
251
        }
252

253
        // mark as spam
254
        if ($comment->getSpamPoints() >= (int) $threshold['markAsSpam']) {
×
255
            $this->getLog()->notice('New comment marked as SPAM.', $logData);
×
256
            $comment->markAsSpam();
×
257
            $this->addFlashMessageByKey('markedAsSpam', Message::INFO);
×
258
        }
259

260
        return null;
×
261
    }
262

263
    /**
264
     * Sanitize comment content.
265
     */
266
    protected function sanitizeComment(Comment $comment)
×
267
    {
268
        // Remove non tag chars
269
        $allowedTags = preg_replace('#[^\w<>]#i', '', $this->settings['blogsystem']['comments']['allowTags']);
×
270
        // Remove bad tags
271
        $allowedTags = preg_replace('#<(script|link|i?frame)>#i', '', $allowedTags);
×
272

273
        // Remove unwanted tags from text
274
        $text = strip_tags($comment->getText(), $allowedTags);
×
275

276
        // Remove all attributes
277
        $text = preg_replace('#<([a-z][a-z0-9]*)[^>]*?(\/?)>#i', '<$1$2>', $text);
×
278

279
        $comment->setText($text);
×
280
    }
281

282
    /**
283
     * Disable error flash message.
284
     */
285
    protected function getErrorFlashMessage(): bool
×
286
    {
287
        return false;
×
288
    }
289
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc