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

eliashaeussler / typo3-form-consent / 8314210214

17 Mar 2024 08:44AM UTC coverage: 95.358% (-1.4%) from 96.782%
8314210214

push

github

eliashaeussler
[RELEASE] Release of EXT:form_consent 2.1.0

760 of 797 relevant lines covered (95.36%)

14.05 hits per line

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

81.43
/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(
14✔
47
        private readonly Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager,
48
        private readonly Core\Domain\Repository\PageRepository $pageRepository,
49
    ) {
50
        $this->typo3Version = new Core\Information\Typo3Version();
14✔
51
    }
52

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

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

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

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

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

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

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

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

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

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

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

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

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

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

139
        $migration = new Compatibility\Migration\HmacHashMigration();
×
140
        $parameters = $originalRequestParameters->toArray();
×
141

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

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

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

155
        return Type\JsonType::fromArray($parameters);
×
156
    }
157

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

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

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

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

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

189
    private function areFinisherVariantsConfigured(string $formPersistenceIdentifier, string $condition): bool
14✔
190
    {
191
        $formConfiguration = $this->formPersistenceManager->load($formPersistenceIdentifier);
14✔
192

193
        foreach ($formConfiguration['variants'] ?? [] as $variant) {
14✔
194
            if (str_contains($variant['condition'] ?? '', $condition) && isset($variant['finishers'])) {
9✔
195
                return true;
9✔
196
            }
197
        }
198

199
        return false;
5✔
200
    }
201

202
    private function getServerRequest(): Message\ServerRequestInterface
9✔
203
    {
204
        return $GLOBALS['TYPO3_REQUEST'] ?? Core\Http\ServerRequestFactory::fromGlobals();
9✔
205
    }
206
}
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