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

eliashaeussler / typo3-form-consent / 10870239851

15 Sep 2024 10:26AM UTC coverage: 94.444% (-1.1%) from 95.545%
10870239851

Pull #308

github

web-flow
Merge 72446869e into 6bd6b6892
Pull Request #308: [FEATURE] Add support for TYPO3 v13.3

3 of 13 new or added lines in 2 files covered. (23.08%)

17 existing lines in 2 files now uncovered.

816 of 864 relevant lines covered (94.44%)

15.03 hits per line

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

71.6
/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->start($contentElementRecord, 'tt_content', $serverRequest);
9✔
105
        $contentObjectRenderer->setUserObjectType(Frontend\ContentObject\ContentObjectRenderer::OBJECTTYPE_USER_INT);
9✔
106
        $bootstrap = Core\Utility\GeneralUtility::makeInstance(Extbase\Core\Bootstrap::class);
9✔
107
        $bootstrap->setContentObjectRenderer($contentObjectRenderer);
9✔
108

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

214
        return false;
7✔
215
    }
216

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