• 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

76.36
/Classes/Controller/AbstractController.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 FelixNagel\T3extblog\Domain\Repository\AbstractRepository;
13
use TYPO3\CMS\Core\Http\PropagateResponseException;
14
use TYPO3\CMS\Core\Pagination\SlidingWindowPagination;
15
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity as Message;
16
use TYPO3\CMS\Core\View\ViewInterface;
17
use TYPO3\CMS\Fluid\View\FluidViewAdapter;
18
use FelixNagel\T3extblog\Utility\FrontendUtility;
19
use FelixNagel\T3extblog\Service\FlushCacheService;
20
use FelixNagel\T3extblog\Service\RateLimiterServiceInterface;
21
use FelixNagel\T3extblog\Traits\LoggingTrait;
22
use FelixNagel\T3extblog\Utility\TypoScript;
23
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
26
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
27
use Psr\Http\Message\ResponseInterface;
28
use TYPO3\CMS\Extbase\Pagination\QueryResultPaginator;
29
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
30
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
31
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
32
use FelixNagel\T3extblog\Domain\Model\AbstractEntity;
33
use FelixNagel\T3extblog\Domain\Model\Category;
34
use FelixNagel\T3extblog\Domain\Model\Comment;
35
use FelixNagel\T3extblog\Domain\Model\Post;
36
use FelixNagel\T3extblog\Utility\TypoScriptValidator;
37
use TYPO3\CMS\Frontend\Controller\ErrorController;
38
use TYPO3\CMS\Core\Cache\CacheTag;
39

40
/**
41
 * Abstract base controller.
42
 *
43
 * @SuppressWarnings("PHPMD.CouplingBetweenObjects")
44
 */
45
abstract class AbstractController extends ActionController
46
{
47
    use LoggingTrait;
48

49
    /**
50
     * @var FluidViewAdapter
51
     */
52
    protected ViewInterface $view;
53

54
    protected ?RateLimiterServiceInterface $rateLimiter = null;
55

56
    /**
57
     * Injects the Configuration Manager and is initializing the framework settings
58
     * Function is used to override the merge of settings via TS & flexforms
59
     */
60
    public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager): void
42✔
61
    {
62
        parent::injectConfigurationManager($configurationManager);
42✔
63

64
        $settings = $this->configurationManager->getConfiguration(
42✔
65
            ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
42✔
66
            't3extblog',
42✔
67
            't3extblog_blogsystem'
42✔
68
        );
42✔
69

70
        if (isset($settings['settings']['overrideFlexformSettingsIfEmpty'])) {
42✔
71
            /** @var TypoScript $typoScriptUtility */
72
            $typoScriptUtility = GeneralUtility::makeInstance(TypoScript::class);
42✔
73
            $this->settings = $typoScriptUtility->override($this->settings, $settings);
42✔
74
        }
75
    }
76

77
    public function processRequest(RequestInterface $request): ResponseInterface
42✔
78
    {
79
        $response = null;
42✔
80

81
        try {
82
            $response = parent::processRequest($request);
42✔
83
        } catch (\Exception $exception) {
×
84
            $this->handleKnownExceptionsElseThrowAgain($exception);
×
85
        }
86

87
        return $response;
42✔
88
    }
89

90
    protected function handleKnownExceptionsElseThrowAgain(\Throwable $exception): never
×
91
    {
92
        throw $exception;
×
93
    }
94

95
    protected function initializeAction(): void
42✔
96
    {
97
        try {
98
            $this->validateTypoScriptConfiguration();
42✔
99
        } catch (\Exception $exception) {
×
100
            $this->getLog()->exception($exception, [
×
101
                'pid' => FrontendUtility::getPageUid(),
×
102
                'context' => 'frontend',
×
103
            ]);
×
104
            throw $exception;
×
105
        }
106

107
        $this->addDefaultCacheTags();
42✔
108
    }
109

110
    /**
111
     * Validate TypoScript settings.
112
     */
113
    protected function validateTypoScriptConfiguration()
42✔
114
    {
115
        TypoScriptValidator::validateSettings($this->settings);
42✔
116

117
        $frameworkConfiguration = $this->configurationManager->getConfiguration(
42✔
118
            ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
42✔
119
            $this->request->getControllerExtensionName(),
42✔
120
            $this->request->getPluginName()
42✔
121
        );
42✔
122
        TypoScriptValidator::validateFrameworkConfiguration($frameworkConfiguration);
42✔
123
    }
124

125
    /**
126
     * Override getErrorFlashMessage to present nice flash error messages.
127
     */
128
    protected function getErrorFlashMessage(): bool|string
×
129
    {
130
        $defaultFlashMessage = parent::getErrorFlashMessage();
×
131
        $locallangKey = sprintf('error.%s.%s', lcfirst($this->request->getControllerName()), $this->actionMethodName);
×
132

133
        return $this->translate($locallangKey, $defaultFlashMessage);
×
134
    }
135

136
    /**
137
     * Helper function to render localized flashmessages.
138
     */
139
    protected function addFlashMessageByKey(string $key, Message $severity = Message::OK)
7✔
140
    {
141
        $messageLocallangKey = sprintf('flashMessage.%s.%s', lcfirst($this->request->getControllerName()), $key);
7✔
142
        $localizedMessage = $this->translate($messageLocallangKey, '['.$messageLocallangKey.']');
7✔
143

144
        $titleLocallangKey = sprintf('%s.title', $messageLocallangKey);
7✔
145
        $localizedTitle = $this->translate($titleLocallangKey, '['.$titleLocallangKey.']');
7✔
146

147
        $this->addFlashMessage($localizedMessage, $localizedTitle, $severity);
7✔
148
    }
149

150
    /**
151
     * Helper function to check if flashmessages have been saved until now.
152
     */
153
    protected function hasFlashMessages(): bool
6✔
154
    {
155
        $messages = $this->getFlashMessageQueue()->getAllMessages();
6✔
156

157
        return count($messages) > 0;
6✔
158
    }
159

160
    /**
161
     * Clear cache of current page.
162
     */
163
    protected function clearPageCache()
7✔
164
    {
165
        FlushCacheService::clearPageCache();
7✔
166
    }
167

168
    protected function pageNotFoundAndExit(string $message = 'Entity not found.'): never
×
169
    {
170
        $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
×
171
            $this->request,
×
172
            $message
×
173
        );
×
174

175
        throw new PropagateResponseException($response, 1576748646);
×
176
    }
177

178
    /**
179
     * Persist all records to database.
180
     */
181
    protected function persistAllEntities(): void
×
182
    {
183
        /* @var $persistenceManager PersistenceManager */
184
        $persistenceManager = GeneralUtility::makeInstance(PersistenceManager::class);
×
185
        $persistenceManager->persistAll();
×
186
    }
187

188
    /**
189
     * Helper function to use localized strings in BlogExample controllers.
190
     *
191
     * @param string $key            locallang key
192
     * @param string $defaultMessage the default message to show if key was not found
193
     */
194
    protected function translate(string $key, string $defaultMessage = ''): string
7✔
195
    {
196
        $message = LocalizationUtility::translate($key, 'T3extblog');
7✔
197

198
        if ($message === null) {
7✔
199
            $message = $defaultMessage;
×
200
        }
201

202
        return $message;
7✔
203
    }
204

205
    /**
206
     * Add page cache tag by string or object.
207
     *
208
     * @param mixed $object A cache tag string, a blog model object or repository
209
     */
210
    protected function addCacheTags(mixed $object = null): void
25✔
211
    {
212
        $tags = is_array($object) ? $object : [];
25✔
213

214
        if (is_string($object)) {
25✔
215
            $tags[] = $object;
5✔
216
        }
217

218
        // Add base PID based tag
219
        if ($object instanceof AbstractEntity) {
25✔
220
            $tags[] = 'tx_t3extblog_'.$object->getPid();
22✔
221

222
        } elseif ($object instanceof AbstractRepository) {
8✔
223
            foreach ($object->getStoragePids() as $storagePid) {
×
224
                $tags[] = 'tx_t3extblog_'.$storagePid;
×
225
            }
226
        }
227

228
        // Add model based tag
229
        if ($object instanceof Post) {
25✔
230
            $tags[] = 'tx_t3blog_post_pid_'.$object->getPid();
16✔
231
        }
232

233
        if ($object instanceof Comment) {
25✔
234
            $tags[] = 'tx_t3blog_com_pid_'.$object->getPid();
3✔
235
        }
236

237
        if ($object instanceof Category) {
25✔
238
            $tags[] = 'tx_t3blog_cat_pid_'.$object->getPid();
3✔
239
        }
240

241
        // @extensionScannerIgnoreLine
242
        $this->request->getAttribute('frontend.cache.collector')->addCacheTags(
25✔
243
            ...array_map(fn (string $tag) => new CacheTag($tag), $tags)
25✔
244
        );
25✔
245
    }
246

247
    /**
248
     * Add basic cache tag for all related pages.
249
     */
250
    protected function addDefaultCacheTags()
42✔
251
    {
252
        static $cacheTagsSet = false;
42✔
253

254
        if ($cacheTagsSet === false) {
42✔
255
            // @extensionScannerIgnoreLine
256
            $this->addCacheTags(['tx_t3extblog']);
1✔
257

258
            // We only want to set the tag once in one request
259
            $cacheTagsSet = true;
1✔
260
        }
261
    }
262

263
    protected function getPaginationVariables(QueryResultInterface $result, array $config, int $page = 1): array
21✔
264
    {
265
        $paginator = new QueryResultPaginator($result, $page, $config['itemsPerPage'] ?? 10);
21✔
266

267
        return [
21✔
268
            'paginator' => $paginator,
21✔
269
            'pagination' => new SlidingWindowPagination($paginator, $config['maximumNumberOfLinks'] ?? 10),
21✔
270
        ];
21✔
271
    }
272

273
    protected function paginationHtmlResponse(
18✔
274
        QueryResultInterface $result,
275
        array $config,
276
        int $page = 1
277
    ): ResponseInterface {
278
        $this->view->assignMultiple($this->getPaginationVariables($result, $config, $page));
18✔
279

280
        return $this->htmlResponse();
18✔
281
    }
282

283
    protected function paginationXmlResponse(
3✔
284
        QueryResultInterface $result,
285
        array $config,
286
        int $page = 1
287
    ): ResponseInterface {
288
        $this->view->assignMultiple($this->getPaginationVariables($result, $config, $page));
3✔
289

290
        return $this->xmlResponse();
3✔
291
    }
292

293
    protected function xmlResponse(?string $xml = null): ResponseInterface
3✔
294
    {
295
        $this->view->getRenderingContext()->getTemplatePaths()->setFormat('xml');
3✔
296

297
        return $this->responseFactory->createResponse()
3✔
298
            ->withHeader('Content-Type', 'text/xml; charset=utf-8')
3✔
299
            ->withBody($this->streamFactory->createStream($xml ?? $this->view->render()));
3✔
300
    }
301

302
    protected function initRateLimiter(string $key, array $settings): RateLimiterServiceInterface
3✔
303
    {
304
        return $this->getRateLimiter()->create($this->request, $key, $settings)->consume($key);
3✔
305
    }
306

307
    protected function getRateLimiter(): RateLimiterServiceInterface
3✔
308
    {
309
        if ($this->rateLimiter === null) {
3✔
310
            $this->rateLimiter = GeneralUtility::makeInstance(RateLimiterServiceInterface::class);
3✔
311
        }
312

313
        return $this->rateLimiter;
3✔
314
    }
315
}
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