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

CPS-IT / mailqueue / 16873710786

11 Aug 2025 07:31AM UTC coverage: 0.0%. Remained the same
16873710786

Pull #120

github

web-flow
Merge a3ef0687f into 2649364d2
Pull Request #120: [TASK] Move CGL handling to dedicated directory and update dependencies

0 of 6 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

0 of 710 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
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17

18
namespace CPSIT\Typo3Mailqueue\Controller;
19

20
use CPSIT\Typo3Mailqueue\Configuration;
21
use CPSIT\Typo3Mailqueue\Enums;
22
use CPSIT\Typo3Mailqueue\Exception;
23
use CPSIT\Typo3Mailqueue\Mail;
24
use CPSIT\Typo3Mailqueue\Traits;
25
use Psr\Http\Message;
26
use Symfony\Component\Mailer;
27
use TYPO3\CMS\Backend;
28
use TYPO3\CMS\Core;
29
use TYPO3\CMS\Fluid;
30

31
/**
32
 * MailqueueModuleController
33
 *
34
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
35
 * @license GPL-2.0-or-later
36
 */
37
final class MailqueueModuleController
38
{
39
    use Traits\TranslatableTrait;
40
    use Core\Http\AllowedMethodsTrait;
41

42
    private readonly Core\Information\Typo3Version $typo3Version;
43

44
    public function __construct(
×
45
        private readonly Configuration\ExtensionConfiguration $extensionConfiguration,
46
        private readonly Core\Imaging\IconFactory $iconFactory,
47
        private readonly Backend\Template\ModuleTemplateFactory $moduleTemplateFactory,
48
        private readonly Core\Mail\Mailer $mailer,
49
        private readonly Backend\Routing\UriBuilder $uriBuilder,
50
        private readonly Core\Context\Context $context,
51
    ) {
52
        $this->typo3Version = new Core\Information\Typo3Version();
×
53
    }
54

55
    public function __invoke(Message\ServerRequestInterface $request): Message\ResponseInterface
×
56
    {
57
        $page = $this->resolvePageIdFromRequest($request);
×
58

59
        // Force redirect when page selector was used
60
        if ($request->getMethod() === 'POST' && !isset($request->getQueryParams()['page'])) {
×
61
            return new Core\Http\RedirectResponse(
×
62
                $this->uriBuilder->buildUriFromRoute('system_mailqueue', ['page' => $page]),
×
63
            );
×
64
        }
65

66
        $this->assertAllowedHttpMethod($request, 'GET');
×
67

68
        $template = $this->moduleTemplateFactory->create($request);
×
69
        $transport = $this->mailer->getTransport();
×
70
        /** @var string|null $sendId */
71
        $sendId = $request->getQueryParams()['send'] ?? null;
×
72
        /** @var string|null $deleteId */
UNCOV
73
        $deleteId = $request->getQueryParams()['delete'] ?? null;
×
74

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

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

87
        if (Core\Utility\ExtensionManagementUtility::isLoaded('lowlevel')) {
×
88
            $this->addLinkToConfigurationModule($template);
×
89
        }
90

91
        // @todo Remove once support for TYPO3 v11 is dropped
92
        if ($this->typo3Version->getMajorVersion() < 12) {
×
93
            return $this->renderLegacyTemplate($template, $templateVariables);
×
94
        }
95

96
        return $template
×
97
            ->assignMultiple($templateVariables)
×
98
            ->renderResponse('List')
×
99
        ;
×
100
    }
101

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

106
        if (\is_numeric($pageId)) {
×
107
            return (int)$pageId;
×
108
        }
109

110
        if (!\is_array($request->getParsedBody())) {
×
111
            return 1;
×
112
        }
113

NEW
114
        $pageId = $request->getParsedBody()['page'] ?? 1;
×
115

NEW
116
        if (\is_numeric($pageId)) {
×
NEW
117
            return (int)$pageId;
×
118
        }
119

NEW
120
        return 1;
×
121
    }
122

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

148
        if (is_string($sendId)) {
×
149
            $sendResult = $this->sendMail($transport, $sendId);
×
150
        }
151

152
        if (is_string($deleteId)) {
×
153
            $deleteResult = $this->deleteMail($transport, $deleteId);
×
154
        }
155

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

161
            if ($mailQueueItem->state === Enums\MailState::Failed) {
×
162
                $failing = true;
×
163
            }
164
        }
165

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

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

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

190
        if ($mailQueueItem === null) {
×
191
            return Enums\MailState::AlreadySent;
×
192
        }
193

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

200
        return Enums\MailState::Sent;
×
201
    }
202

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

207
        if ($mailQueueItem === null) {
×
208
            return false;
×
209
        }
210

211
        return $transport->delete($mailQueueItem);
×
212
    }
213

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

224
        return null;
×
225
    }
226

227
    private function addLinkToConfigurationModule(Backend\Template\ModuleTemplate $template): void
×
228
    {
229
        $buttonBar = $template->getDocHeaderComponent()->getButtonBar();
×
230

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

242
        $buttonBar->addButton($button);
×
243
    }
244

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

252
        if (!\is_array($mailConfiguration)) {
×
253
            throw new Exception\MailTransportIsNotConfigured();
×
254
        }
255

256
        $spoolType = $mailConfiguration['transport_spool_type'] ?? null;
×
257

258
        if (is_string($spoolType) && trim($spoolType) !== '') {
×
259
            return $spoolType;
×
260
        }
261

262
        $transport = $mailConfiguration['transport'] ?? null;
×
263

264
        if (is_string($transport) && trim($transport) !== '') {
×
265
            return $transport;
×
266
        }
267

268
        throw new Exception\MailTransportIsNotConfigured();
×
269
    }
270

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

287
        $template->setContent($view->render());
×
288

289
        return new Core\Http\HtmlResponse($template->renderContent());
×
290
    }
291
}
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