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

eliashaeussler / typo3-form-consent / 12070962545

28 Nov 2024 02:37PM UTC coverage: 94.146% (-0.1%) from 94.261%
12070962545

Pull #330

github

web-flow
Merge 9eb71f712 into 2b51a8566
Pull Request #330: [TASK] Update all dependencies

4 of 6 new or added lines in 2 files covered. (66.67%)

772 of 820 relevant lines covered (94.15%)

14.45 hits per line

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

71.08
/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
    // @todo Enable attribute once support for TYPO3 v12 is dropped
56
    // #[Core\Attribute\AsEventListener('formConsentInvokeFinishersOnConsentApproveListener')]
57
    public function onConsentApprove(Event\ApproveConsentEvent $event): void
10✔
58
    {
59
        $response = $this->invokeFinishers($event->getConsent(), 'isConsentApproved()');
10✔
60
        $event->setResponse($response);
9✔
61
    }
62

63
    // @todo Enable attribute once support for TYPO3 v12 is dropped
64
    // #[Core\Attribute\AsEventListener('formConsentInvokeFinishersOnConsentDismissListener')]
65
    public function onConsentDismiss(Event\DismissConsentEvent $event): void
8✔
66
    {
67
        $response = $this->invokeFinishers($event->getConsent(), 'isConsentDismissed()');
8✔
68
        $event->setResponse($response);
7✔
69
    }
70

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

83
        // Migrate legacy HMAC hashes after upgrade to TYPO3 v13
84
        $consent->setOriginalRequestParameters(
9✔
85
            $this->migrateOriginalRequestParameters($consent->getOriginalRequestParameters()),
9✔
86
        );
9✔
87

88
        // Re-render form to invoke finishers
89
        $request = $this->createRequestFromOriginalRequestParameters($consent->getOriginalRequestParameters());
9✔
90

91
        return $this->dispatchFormReRendering($consent, $request);
9✔
92
    }
93

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

101
        // Early return if content element record cannot be resolved
102
        if (!\is_array($contentElementRecord)) {
9✔
103
            return null;
×
104
        }
105

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

114
        // Inject content object renderer
115
        $serverRequest = $serverRequest->withAttribute('currentContentObject', $contentObjectRenderer);
9✔
116

117
        $configuration = [
9✔
118
            'extensionName' => 'Form',
9✔
119
            'pluginName' => 'Formframework',
9✔
120
        ];
9✔
121

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

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

135
    /**
136
     * @param Type\JsonType<string, array<string, array<string, mixed>>> $originalRequestParameters
137
     * @return Type\JsonType<string, array<string, array<string, mixed>>>
138
     */
139
    private function migrateOriginalRequestParameters(Type\JsonType $originalRequestParameters): Type\JsonType
9✔
140
    {
141
        // @todo Remove once support for TYPO3 v12 is dropped
142
        if ($this->typo3Version->getMajorVersion() < 13) {
9✔
143
            return $originalRequestParameters;
9✔
144
        }
145

146
        $migration = new Compatibility\Migration\HmacHashMigration();
×
147
        $parameters = $originalRequestParameters->toArray();
×
148

149
        array_walk_recursive($parameters, static function (mixed &$value, string|int $key) use ($migration): void {
×
150
            if (!is_string($value) || !is_string($key)) {
×
151
                return;
×
152
            }
153

154
            // Migrate EXT:extbase and EXT:form hash scopes
NEW
155
            $hashScope = Form\Security\HashScope::tryFrom($key);
×
NEW
156
            $hashScope ??= Extbase\Security\HashScope::tryFrom($key);
×
157

158
            if ($hashScope !== null) {
×
159
                $value = $migration->migrate($value, $hashScope->prefix());
×
160
            }
161
        });
×
162

163
        return Type\JsonType::fromArray($parameters);
×
164
    }
165

166
    /**
167
     * @return array<string, mixed>|null
168
     */
169
    private function fetchOriginalContentElementRecord(int $contentElementUid): ?array
9✔
170
    {
171
        // Early return if content element UID cannot be  determined
172
        if ($contentElementUid === 0) {
9✔
173
            return null;
×
174
        }
175

176
        // Fetch content element record
177
        $record = $this->pageRepository->checkRecord('tt_content', $contentElementUid);
9✔
178

179
        // Early return if content element record cannot be resolved
180
        if (!\is_array($record)) {
9✔
181
            return null;
×
182
        }
183

184
        return $this->pageRepository->getLanguageOverlay('tt_content', $record);
9✔
185
    }
186

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

197
    private function areFinisherVariantsConfigured(string $formPersistenceIdentifier, string $condition): bool
16✔
198
    {
199
        if ($this->typo3Version->getMajorVersion() >= 13) {
16✔
200
            $typoScriptSettings = $this->extbaseConfigurationManager->getConfiguration(
×
201
                Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
×
202
                'form',
×
203
            );
×
204
            $formSettings = $this->formConfigurationManager->getYamlConfiguration($typoScriptSettings, true);
×
205
            $formConfiguration = $this->formPersistenceManager->load(
×
206
                $formPersistenceIdentifier,
×
207
                $formSettings,
×
208
                $typoScriptSettings,
×
209
            );
×
210
        } else {
211
            // @todo Remove once support for TYPO3 v12 is dropped
212
            $formConfiguration = $this->formPersistenceManager->load($formPersistenceIdentifier);
16✔
213
        }
214

215
        foreach ($formConfiguration['variants'] ?? [] as $variant) {
16✔
216
            if (str_contains($variant['condition'] ?? '', $condition) && isset($variant['finishers'])) {
9✔
217
                return true;
9✔
218
            }
219
        }
220

221
        return false;
7✔
222
    }
223

224
    private function getServerRequest(): Message\ServerRequestInterface
9✔
225
    {
226
        return $GLOBALS['TYPO3_REQUEST'] ?? Core\Http\ServerRequestFactory::fromGlobals();
9✔
227
    }
228
}
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