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

CPS-IT / mailqueue / 12888988476

21 Jan 2025 02:22PM UTC coverage: 0.0%. Remained the same
12888988476

Pull #81

github

web-flow
Merge f7d2d2417 into 131584b2a
Pull Request #81: [BUGFIX] enforcing http methods

0 of 2 new or added lines in 1 file covered. (0.0%)

5 existing lines in 1 file now uncovered.

0 of 707 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/Classes/Controller/MailqueueModuleController.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "mailqueue".
7
 *
8
 * Copyright (C) 2024 Elias Häußler <e.haeussler@familie-redlich.de>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace CPSIT\Typo3Mailqueue\Controller;
25

26
use CPSIT\Typo3Mailqueue\Configuration;
27
use CPSIT\Typo3Mailqueue\Enums;
28
use CPSIT\Typo3Mailqueue\Exception;
29
use CPSIT\Typo3Mailqueue\Mail;
30
use CPSIT\Typo3Mailqueue\Traits;
31
use Psr\Http\Message;
32
use Symfony\Component\Mailer;
33
use TYPO3\CMS\Backend;
34
use TYPO3\CMS\Core;
35
use TYPO3\CMS\Core\Http\AllowedMethodsTrait;
36
use TYPO3\CMS\Fluid;
37

38
/**
39
 * MailqueueModuleController
40
 *
41
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
42
 * @license GPL-2.0-or-later
43
 */
44
final class MailqueueModuleController
45
{
46
    use Traits\TranslatableTrait;
47
    use AllowedMethodsTrait;
48

49
    private readonly Core\Information\Typo3Version $typo3Version;
50

UNCOV
51
    public function __construct(
×
52
        private readonly Configuration\ExtensionConfiguration $extensionConfiguration,
53
        private readonly Core\Imaging\IconFactory $iconFactory,
54
        private readonly Backend\Template\ModuleTemplateFactory $moduleTemplateFactory,
55
        private readonly Core\Mail\Mailer $mailer,
56
        private readonly Backend\Routing\UriBuilder $uriBuilder,
57
        private readonly Core\Context\Context $context,
58
    ) {
UNCOV
59
        $this->typo3Version = new Core\Information\Typo3Version();
×
60
    }
61

UNCOV
62
    public function __invoke(Message\ServerRequestInterface $request): Message\ResponseInterface
×
63
    {
64
        $template = $this->moduleTemplateFactory->create($request);
×
65
        $transport = $this->mailer->getTransport();
×
66
        $page = $this->resolvePageIdFromRequest($request);
×
67
        $sendId = $request->getQueryParams()['send'] ?? null;
×
68
        $deleteId = $request->getQueryParams()['delete'] ?? null;
×
69

70
        // Force redirect when page selector was used
UNCOV
71
        if ($request->getMethod() === 'POST' && !isset($request->getQueryParams()['page'])) {
×
NEW
72
            $this->assertAllowedHttpMethod($request, 'POST');
×
73
            return new Core\Http\RedirectResponse(
×
74
                $this->uriBuilder->buildUriFromRoute('system_mailqueue', ['page' => $page]),
×
75
            );
×
76
        }
77

78
        if ($transport instanceof Mail\Transport\QueueableTransport) {
×
79
            $templateVariables = $this->resolveTemplateVariables($transport, $page, $sendId, $deleteId);
×
80
        } else {
81
            $templateVariables = [
×
82
                'unsupportedTransport' => $this->getTransportFromMailConfiguration(),
×
83
                'typo3Version' => $this->typo3Version->getMajorVersion(),
×
84
            ];
×
85
        }
86

87
        // @todo Remove once support for TYPO3 v11 is dropped
88
        $templateVariables['layout'] = $this->typo3Version->getMajorVersion() < 12 ? 'Default' : 'Module';
×
89

90
        if (Core\Utility\ExtensionManagementUtility::isLoaded('lowlevel')) {
×
91
            $this->addLinkToConfigurationModule($template);
×
92
        }
93

94
        // @todo Remove once support for TYPO3 v11 is dropped
95
        if ($this->typo3Version->getMajorVersion() < 12) {
×
96
            return $this->renderLegacyTemplate($template, $templateVariables);
×
97
        }
NEW
UNCOV
98
        $this->assertAllowedHttpMethod($request, 'GET');
×
99
        return $template
×
100
            ->assignMultiple($templateVariables)
×
101
            ->renderResponse('List')
×
102
        ;
×
103
    }
104

105
    private function resolvePageIdFromRequest(Message\ServerRequestInterface $request): int
×
106
    {
107
        $pageId = $request->getQueryParams()['page'] ?? null;
×
108

109
        if (\is_numeric($pageId)) {
×
110
            return (int)$pageId;
×
111
        }
112

113
        if (!\is_array($request->getParsedBody())) {
×
114
            return 1;
×
115
        }
116

117
        return (int)($request->getParsedBody()['page'] ?? 1);
×
118
    }
119

120
    /**
121
     * @return array{
122
     *     delayThreshold: positive-int,
123
     *     deleteResult: bool,
124
     *     failing: bool,
125
     *     longestPendingInterval: non-negative-int,
126
     *     pagination: Core\Pagination\SimplePagination,
127
     *     paginator: Core\Pagination\ArrayPaginator,
128
     *     queue: list<Mail\Queue\MailQueueItem>,
129
     *     sendResult: Enums\MailState|null,
130
     * }
131
     */
132
    private function resolveTemplateVariables(
×
133
        Mail\Transport\QueueableTransport $transport,
134
        int $currentPageNumber = 1,
135
        string $sendId = null,
136
        string $deleteId = null,
137
    ): array {
138
        $failing = false;
×
139
        $longestPendingInterval = 0;
×
140
        /** @var int $now */
141
        $now = $this->context->getPropertyFromAspect('date', 'timestamp');
×
142
        $sendResult = null;
×
143
        $deleteResult = false;
×
144

145
        if (is_string($sendId)) {
×
146
            $sendResult = $this->sendMail($transport, $sendId);
×
147
        }
148

149
        if (is_string($deleteId)) {
×
150
            $deleteResult = $this->deleteMail($transport, $deleteId);
×
151
        }
152

153
        foreach ($transport->getMailQueue() as $mailQueueItem) {
×
154
            if ($mailQueueItem->date !== null) {
×
155
                $longestPendingInterval = max($longestPendingInterval, $now - $mailQueueItem->date->getTimestamp());
×
156
            }
157

158
            if ($mailQueueItem->state === Enums\MailState::Failed) {
×
159
                $failing = true;
×
160
            }
161
        }
162

163
        $queue = $transport->getMailQueue()->get();
×
164
        $paginator = new Core\Pagination\ArrayPaginator(
×
165
            $queue,
×
166
            $currentPageNumber,
×
167
            $this->extensionConfiguration->getItemsPerPage(),
×
168
        );
×
169
        $pagination = new Core\Pagination\SimplePagination($paginator);
×
170

171
        return [
×
172
            'delayThreshold' => $this->extensionConfiguration->getQueueDelayThreshold(),
×
173
            'deleteResult' => $deleteResult,
×
174
            'failing' => $failing,
×
175
            'longestPendingInterval' => $longestPendingInterval,
×
176
            'pagination' => $pagination,
×
177
            'paginator' => $paginator,
×
178
            'queue' => $queue,
×
179
            'sendResult' => $sendResult,
×
180
        ];
×
181
    }
182

183
    private function sendMail(Mail\Transport\QueueableTransport $transport, string $queueItemId): Enums\MailState
×
184
    {
185
        $mailQueueItem = $this->getMailById($transport, $queueItemId);
×
186

187
        if ($mailQueueItem === null) {
×
188
            return Enums\MailState::AlreadySent;
×
189
        }
190

191
        try {
192
            $transport->dequeue($mailQueueItem, $this->mailer->getRealTransport());
×
193
        } catch (Mailer\Exception\TransportExceptionInterface) {
×
194
            return Enums\MailState::Failed;
×
195
        }
196

197
        return Enums\MailState::Sent;
×
198
    }
199

200
    private function deleteMail(Mail\Transport\QueueableTransport $transport, string $queueItemId): bool
×
201
    {
202
        $mailQueueItem = $this->getMailById($transport, $queueItemId);
×
203

204
        if ($mailQueueItem === null) {
×
205
            return false;
×
206
        }
207

208
        return $transport->delete($mailQueueItem);
×
209
    }
210

211
    private function getMailById(
×
212
        Mail\Transport\QueueableTransport $transport,
213
        string $queueItemId,
214
    ): ?Mail\Queue\MailQueueItem {
215
        foreach ($transport->getMailQueue() as $mailQueueItem) {
×
216
            if ($mailQueueItem->id === $queueItemId) {
×
217
                return $mailQueueItem;
×
218
            }
219
        }
220

221
        return null;
×
222
    }
223

224
    private function addLinkToConfigurationModule(Backend\Template\ModuleTemplate $template): void
×
225
    {
226
        $buttonBar = $template->getDocHeaderComponent()->getButtonBar();
×
227

228
        $button = $buttonBar->makeLinkButton();
×
229
        $button->setHref(
×
230
            (string)$this->uriBuilder->buildUriFromRoute(
×
231
                'system_config',
×
232
                ['node' => ['MAIL' => 1], 'tree' => 'confVars'],
×
233
            )
×
234
        );
×
235
        $button->setIcon($this->iconFactory->getIcon('actions-cog', Core\Imaging\Icon::SIZE_SMALL));
×
236
        $button->setTitle(self::translate('button.config'));
×
237
        $button->setShowLabelText(true);
×
238

239
        $buttonBar->addButton($button);
×
240
    }
241

242
    /**
243
     * @throws Exception\MailTransportIsNotConfigured
244
     */
245
    private function getTransportFromMailConfiguration(): string
×
246
    {
247
        $mailConfiguration = $GLOBALS['TYPO3_CONF_VARS']['MAIL'] ?? null;
×
248

249
        if (!\is_array($mailConfiguration)) {
×
250
            throw new Exception\MailTransportIsNotConfigured();
×
251
        }
252

253
        $spoolType = $mailConfiguration['transport_spool_type'] ?? null;
×
254

255
        if (is_string($spoolType) && trim($spoolType) !== '') {
×
256
            return $spoolType;
×
257
        }
258

259
        $transport = $mailConfiguration['transport'] ?? null;
×
260

261
        if (is_string($transport) && trim($transport) !== '') {
×
262
            return $transport;
×
263
        }
264

265
        throw new Exception\MailTransportIsNotConfigured();
×
266
    }
267

268
    /**
269
     * @todo Remove once support for TYPO3 v11 is dropped
270
     *
271
     * @param array<string, mixed> $templateVariables
272
     */
273
    private function renderLegacyTemplate(
×
274
        Backend\Template\ModuleTemplate $template,
275
        array $templateVariables,
276
    ): Core\Http\HtmlResponse {
277
        $view = Core\Utility\GeneralUtility::makeInstance(Fluid\View\StandaloneView::class);
×
278
        $view->setTemplateRootPaths(['EXT:mailqueue/Resources/Private/Templates/']);
×
279
        $view->setPartialRootPaths(['EXT:mailqueue/Resources/Private/Partials/']);
×
280
        $view->setLayoutRootPaths(['EXT:mailqueue/Resources/Private/Layouts/']);
×
281
        $view->setTemplate('List');
×
282
        $view->assignMultiple($templateVariables);
×
283

284
        $template->setContent($view->render());
×
285

286
        return new Core\Http\HtmlResponse($template->renderContent());
×
287
    }
288
}
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