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

eliashaeussler / typo3-form-consent / 10885759019

16 Sep 2024 02:19PM UTC coverage: 94.639% (-0.9%) from 95.545%
10885759019

Pull #308

github

web-flow
Merge f8b59cd8e into 6bd6b6892
Pull Request #308: [!!!][FEATURE] Add support for TYPO3 v13.3 and drop support for v11.5

8 of 18 new or added lines in 3 files covered. (44.44%)

12 existing lines in 1 file now uncovered.

812 of 858 relevant lines covered (94.64%)

15.12 hits per line

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

71.95
/Classes/Event/Listener/InvokeFinishersListener.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "form_consent".
7
 *
8
 * Copyright (C) 2021-2024 Elias Häußler <elias@haeussler.dev>
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 EliasHaeussler\Typo3FormConsent\Event\Listener;
25

26
use EliasHaeussler\Typo3FormConsent\Compatibility;
27
use EliasHaeussler\Typo3FormConsent\Domain;
28
use EliasHaeussler\Typo3FormConsent\Event;
29
use EliasHaeussler\Typo3FormConsent\Type;
30
use Psr\Http\Message;
31
use TYPO3\CMS\Core;
32
use TYPO3\CMS\Extbase;
33
use TYPO3\CMS\Form;
34
use TYPO3\CMS\Frontend;
35

36
/**
37
 * InvokeFinishersListener
38
 *
39
 * @author Elias Häußler <elias@haeussler.dev>
40
 * @license GPL-2.0-or-later
41
 */
42
final class InvokeFinishersListener
43
{
44
    private readonly Core\Information\Typo3Version $typo3Version;
45

46
    public function __construct(
16✔
47
        private readonly Extbase\Configuration\ConfigurationManagerInterface $extbaseConfigurationManager,
48
        private readonly Form\Mvc\Configuration\ConfigurationManagerInterface $formConfigurationManager,
49
        private readonly Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager,
50
        private readonly Core\Domain\Repository\PageRepository $pageRepository,
51
    ) {
52
        $this->typo3Version = new Core\Information\Typo3Version();
16✔
53
    }
54

55
    public function onConsentApprove(Event\ApproveConsentEvent $event): void
10✔
56
    {
57
        $response = $this->invokeFinishers($event->getConsent(), 'isConsentApproved()');
10✔
58
        $event->setResponse($response);
9✔
59
    }
60

61
    public function onConsentDismiss(Event\DismissConsentEvent $event): void
8✔
62
    {
63
        $response = $this->invokeFinishers($event->getConsent(), 'isConsentDismissed()');
8✔
64
        $event->setResponse($response);
7✔
65
    }
66

67
    private function invokeFinishers(Domain\Model\Consent $consent, string $condition): ?Message\ResponseInterface
16✔
68
    {
69
        // Early return if original request is missing
70
        // or no finisher variants are configured
71
        if (
72
            $consent->getOriginalRequestParameters() === null
16✔
73
            || $consent->getOriginalContentElementUid() === 0
16✔
74
            || !$this->areFinisherVariantsConfigured($consent->getFormPersistenceIdentifier(), $condition)
16✔
75
        ) {
76
            return null;
7✔
77
        }
78

79
        // Migrate legacy HMAC hashes after upgrade to TYPO3 v13
80
        $consent->setOriginalRequestParameters(
9✔
81
            $this->migrateOriginalRequestParameters($consent->getOriginalRequestParameters()),
9✔
82
        );
9✔
83

84
        // Re-render form to invoke finishers
85
        $request = $this->createRequestFromOriginalRequestParameters($consent->getOriginalRequestParameters());
9✔
86

87
        return $this->dispatchFormReRendering($consent, $request);
9✔
88
    }
89

90
    private function dispatchFormReRendering(
9✔
91
        Domain\Model\Consent $consent,
92
        Message\ServerRequestInterface $serverRequest,
93
    ): ?Message\ResponseInterface {
94
        // Fetch record of original content element
95
        $contentElementRecord = $this->fetchOriginalContentElementRecord($consent->getOriginalContentElementUid());
9✔
96

97
        // Early return if content element record cannot be resolved
98
        if (!\is_array($contentElementRecord)) {
9✔
UNCOV
99
            return null;
×
100
        }
101

102
        // Build extbase bootstrap object
103
        $contentObjectRenderer = Core\Utility\GeneralUtility::makeInstance(Frontend\ContentObject\ContentObjectRenderer::class);
9✔
104
        $contentObjectRenderer->setRequest($serverRequest);
9✔
105
        $contentObjectRenderer->start($contentElementRecord, 'tt_content');
9✔
106
        $contentObjectRenderer->setUserObjectType(Frontend\ContentObject\ContentObjectRenderer::OBJECTTYPE_USER_INT);
9✔
107
        $bootstrap = Core\Utility\GeneralUtility::makeInstance(Extbase\Core\Bootstrap::class);
9✔
108
        $bootstrap->setContentObjectRenderer($contentObjectRenderer);
9✔
109

110
        // Inject content object renderer
111
        $serverRequest = $serverRequest->withAttribute('currentContentObject', $contentObjectRenderer);
9✔
112

113
        $configuration = [
9✔
114
            'extensionName' => 'Form',
9✔
115
            'pluginName' => 'Formframework',
9✔
116
        ];
9✔
117

118
        try {
119
            // Dispatch extbase request
120
            $content = $bootstrap->run('', $configuration, $serverRequest);
9✔
121
            $response = new Core\Http\Response();
4✔
122
            $response->getBody()->write($content);
4✔
123

124
            return $response;
4✔
125
        } catch (Core\Http\ImmediateResponseException|Core\Http\PropagateResponseException $exception) {
5✔
126
            // If any immediate response is thrown, use this for further processing
127
            return $exception->getResponse();
3✔
128
        }
129
    }
130

131
    /**
132
     * @param Type\JsonType<string, array<string, array<string, mixed>>> $originalRequestParameters
133
     * @return Type\JsonType<string, array<string, array<string, mixed>>>
134
     */
135
    private function migrateOriginalRequestParameters(Type\JsonType $originalRequestParameters): Type\JsonType
9✔
136
    {
137
        // Migration is only needed when upgrading from TYPO3 < v13
138
        if ($this->typo3Version->getMajorVersion() < 13) {
9✔
139
            return $originalRequestParameters;
9✔
140
        }
141

UNCOV
142
        $migration = new Compatibility\Migration\HmacHashMigration();
×
UNCOV
143
        $parameters = $originalRequestParameters->toArray();
×
144

UNCOV
145
        array_walk_recursive($parameters, static function (mixed &$value, string|int $key) use ($migration): void {
×
146
            if (!is_string($value) || !is_string($key)) {
×
147
                return;
×
148
            }
149

150
            // Migrate EXT:extbase and EXT:form hash scopes
151
            $hashScope = Form\Security\HashScope::tryFrom($key) ?? Extbase\Security\HashScope::tryFrom($key);
×
152

UNCOV
153
            if ($hashScope !== null) {
×
UNCOV
154
                $value = $migration->migrate($value, $hashScope->prefix());
×
155
            }
UNCOV
156
        });
×
157

158
        return Type\JsonType::fromArray($parameters);
×
159
    }
160

161
    /**
162
     * @return array<string, mixed>|null
163
     */
164
    private function fetchOriginalContentElementRecord(int $contentElementUid): ?array
9✔
165
    {
166
        // Early return if content element UID cannot be  determined
167
        if ($contentElementUid === 0) {
9✔
UNCOV
168
            return null;
×
169
        }
170

171
        // Fetch content element record
172
        $record = $this->pageRepository->checkRecord('tt_content', $contentElementUid);
9✔
173

174
        // Early return if content element record cannot be resolved
175
        if (!\is_array($record)) {
9✔
UNCOV
176
            return null;
×
177
        }
178

179
        return $this->pageRepository->getLanguageOverlay('tt_content', $record);
9✔
180
    }
181

182
    /**
183
     * @param Type\JsonType<string, array<string, array<string, mixed>>> $originalRequestParameters
184
     */
185
    private function createRequestFromOriginalRequestParameters(Type\JsonType $originalRequestParameters): Message\ServerRequestInterface
9✔
186
    {
187
        return $this->getServerRequest()
9✔
188
            ->withMethod('POST')
9✔
189
            ->withParsedBody($originalRequestParameters->toArray());
9✔
190
    }
191

192
    private function areFinisherVariantsConfigured(string $formPersistenceIdentifier, string $condition): bool
16✔
193
    {
194
        if ($this->typo3Version->getMajorVersion() >= 13) {
16✔
NEW
UNCOV
195
            $typoScriptSettings = $this->extbaseConfigurationManager->getConfiguration(
×
NEW
UNCOV
196
                Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
×
NEW
UNCOV
197
                'form',
×
NEW
198
            );
×
NEW
199
            $formSettings = $this->formConfigurationManager->getYamlConfiguration($typoScriptSettings, true);
×
NEW
200
            $formConfiguration = $this->formPersistenceManager->load(
×
NEW
201
                $formPersistenceIdentifier,
×
NEW
202
                $formSettings,
×
NEW
203
                $typoScriptSettings,
×
NEW
204
            );
×
205
        } else {
206
            $formConfiguration = $this->formPersistenceManager->load($formPersistenceIdentifier);
16✔
207
        }
208

209
        foreach ($formConfiguration['variants'] ?? [] as $variant) {
16✔
210
            if (str_contains($variant['condition'] ?? '', $condition) && isset($variant['finishers'])) {
9✔
211
                return true;
9✔
212
            }
213
        }
214

215
        return false;
7✔
216
    }
217

218
    private function getServerRequest(): Message\ServerRequestInterface
9✔
219
    {
220
        return $GLOBALS['TYPO3_REQUEST'] ?? Core\Http\ServerRequestFactory::fromGlobals();
9✔
221
    }
222
}
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

© 2025 Coveralls, Inc