Passed
Push — typo3-v13 ( ba73f3...83a476 )
by Torben
02:10
created

RegistrationService::getCurrentFeUserObject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 11
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace DERHANSEN\SfEventMgt\Service;
13
14
use DateTime;
15
use DERHANSEN\SfEventMgt\Domain\Model\Event;
16
use DERHANSEN\SfEventMgt\Domain\Model\FrontendUser;
17
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
18
use DERHANSEN\SfEventMgt\Domain\Repository\FrontendUserRepository;
19
use DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository;
20
use DERHANSEN\SfEventMgt\Event\AfterRegistrationMovedFromWaitlist;
0 ignored issues
show
Bug introduced by
The type DERHANSEN\SfEventMgt\Eve...rationMovedFromWaitlist was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use DERHANSEN\SfEventMgt\Event\ModifyCheckRegistrationSuccessEvent;
22
use DERHANSEN\SfEventMgt\Event\ModifyRegistrationPriceEvent;
23
use DERHANSEN\SfEventMgt\Payment\AbstractPayment;
24
use DERHANSEN\SfEventMgt\Security\HashScope;
25
use DERHANSEN\SfEventMgt\Utility\MessageType;
26
use DERHANSEN\SfEventMgt\Utility\RegistrationResult;
27
use Psr\EventDispatcher\EventDispatcherInterface;
28
use Psr\Http\Message\ServerRequestInterface;
29
use TYPO3\CMS\Core\Context\Context;
30
use TYPO3\CMS\Core\Crypto\HashService;
31
use TYPO3\CMS\Core\Database\Connection;
32
use TYPO3\CMS\Core\Database\ConnectionPool;
33
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
34
use TYPO3\CMS\Core\Http\PropagateResponseException;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Mvc\RequestInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Reflection\ObjectAccess was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
38
use TYPO3\CMS\Frontend\Controller\ErrorController;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Frontend\Controller\ErrorController was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
40
class RegistrationService
41
{
42
    public function __construct(
43
        protected readonly Context $context,
44
        protected readonly EventDispatcherInterface $eventDispatcher,
45
        protected readonly RegistrationRepository $registrationRepository,
46
        protected readonly FrontendUserRepository $frontendUserRepository,
47
        protected readonly HashService $hashService,
48
        protected readonly PaymentService $paymentService,
49
        protected readonly NotificationService $notificationService,
50
    ) {
51
    }
52
53
    /**
54
     * Duplicates the given registration (all public accessible properties) the
55
     * amount of times configured in amountOfRegistrations
56
     */
57
    public function createDependingRegistrations(Registration $registration): void
58
    {
59
        $registrations = $registration->getAmountOfRegistrations();
60
        for ($i = 1; $i <= $registrations - 1; $i++) {
61
            $newReg = GeneralUtility::makeInstance(Registration::class);
62
            $properties = ObjectAccess::getGettableProperties($registration);
63
            foreach ($properties as $propertyName => $propertyValue) {
64
                ObjectAccess::setProperty($newReg, $propertyName, $propertyValue);
65
            }
66
            $newReg->setMainRegistration($registration);
67
            $newReg->setAmountOfRegistrations(1);
68
            $newReg->setIgnoreNotifications(true);
69
            $this->registrationRepository->add($newReg);
70
        }
71
    }
72
73
    /**
74
     * Confirms all depending registrations based on the given main registration
75
     */
76
    public function confirmDependingRegistrations(Registration $registration): void
77
    {
78
        $registrations = $this->registrationRepository->findBy(['mainRegistration' => $registration]);
79
        foreach ($registrations as $foundRegistration) {
80
            /** @var Registration $foundRegistration */
81
            $foundRegistration->setConfirmed(true);
82
            $this->registrationRepository->update($foundRegistration);
83
        }
84
    }
85
86
    /**
87
     * Checks if the registration can be confirmed and returns an array of variables
88
     */
89
    public function checkConfirmRegistration(int $regUid, string $hmac): array
90
    {
91
        /* @var $registration Registration */
92
        $registration = null;
93
        $failed = false;
94
        $messageKey = 'event.message.confirmation_successful';
95
        $titleKey = 'confirmRegistration.title.successful';
96
97
        $isValidHmac = $this->hashService->validateHmac('reg-' . $regUid, HashScope::RegistrationUid->value, $hmac);
98
        if (!$isValidHmac) {
99
            $failed = true;
100
            $messageKey = 'event.message.confirmation_failed_wrong_hmac';
101
            $titleKey = 'confirmRegistration.title.failed';
102
        } else {
103
            $registration = $this->registrationRepository->findByUid($regUid);
104
        }
105
106
        if (!$failed && is_null($registration)) {
107
            $failed = true;
108
            $messageKey = 'event.message.confirmation_failed_registration_not_found';
109
            $titleKey = 'confirmRegistration.title.failed';
110
        }
111
112
        if (!$failed && !$registration->getEvent()) {
113
            $failed = true;
114
            $messageKey = 'event.message.confirmation_failed_registration_event_not_found';
115
            $titleKey = 'confirmRegistration.title.failed';
116
        }
117
118
        if (!$failed && $registration->getConfirmationUntil() < new DateTime()) {
119
            $failed = true;
120
            $messageKey = 'event.message.confirmation_failed_confirmation_until_expired';
121
            $titleKey = 'confirmRegistration.title.failed';
122
        }
123
124
        if (!$failed && $registration->getConfirmed() === true) {
125
            $failed = true;
126
            $messageKey = 'event.message.confirmation_failed_already_confirmed';
127
            $titleKey = 'confirmRegistration.title.failed';
128
        }
129
130
        if (!$failed && $registration->getWaitlist()) {
131
            $messageKey = 'event.message.confirmation_waitlist_successful';
132
            $titleKey = 'confirmRegistrationWaitlist.title.successful';
133
        }
134
135
        return [
136
            $failed,
137
            $registration,
138
            $messageKey,
139
            $titleKey,
140
        ];
141
    }
142
143
    /**
144
     * Cancels all depending registrations based on the given main registration
145
     */
146
    public function cancelDependingRegistrations(Registration $registration): void
147
    {
148
        $registrations = $this->registrationRepository->findBy(['mainRegistration' => $registration]);
149
        foreach ($registrations as $foundRegistration) {
150
            $this->registrationRepository->remove($foundRegistration);
151
        }
152
    }
153
154
    /**
155
     * Checks if the registration can be cancelled and returns an array of variables
156
     */
157
    public function checkCancelRegistration(int $regUid, string $hmac): array
158
    {
159
        /* @var $registration Registration */
160
        $registration = null;
161
        $failed = false;
162
        $messageKey = 'event.message.cancel_successful';
163
        $titleKey = 'cancelRegistration.title.successful';
164
165
        $isValidHmac = $this->hashService->validateHmac('reg-' . $regUid, HashScope::RegistrationUid->value, $hmac);
166
        if (!$isValidHmac) {
167
            $failed = true;
168
            $messageKey = 'event.message.cancel_failed_wrong_hmac';
169
            $titleKey = 'cancelRegistration.title.failed';
170
        } else {
171
            $registration = $this->registrationRepository->findByUid($regUid);
172
        }
173
174
        if (!$failed && is_null($registration)) {
175
            $failed = true;
176
            $messageKey = 'event.message.cancel_failed_registration_not_found_or_cancelled';
177
            $titleKey = 'cancelRegistration.title.failed';
178
        }
179
180
        if (!$failed && !is_a($registration->getEvent(), Event::class)) {
181
            $failed = true;
182
            $messageKey = 'event.message.cancel_failed_event_not_found';
183
            $titleKey = 'cancelRegistration.title.failed';
184
        }
185
186
        if (!$failed && $registration->getEvent()->getEnableCancel() === false) {
187
            $failed = true;
188
            $messageKey = 'event.message.confirmation_failed_cancel_disabled';
189
            $titleKey = 'cancelRegistration.title.failed';
190
        }
191
192
        if (!$failed && $registration->getEvent()->getCancelDeadline() !== null
193
            && $registration->getEvent()->getCancelDeadline() < new DateTime()
194
        ) {
195
            $failed = true;
196
            $messageKey = 'event.message.cancel_failed_deadline_expired';
197
            $titleKey = 'cancelRegistration.title.failed';
198
        }
199
200
        if (!$failed && $registration->getEvent()->getStartdate() < new DateTime()) {
201
            $failed = true;
202
            $messageKey = 'event.message.cancel_failed_event_started';
203
            $titleKey = 'cancelRegistration.title.failed';
204
        }
205
206
        return [
207
            $failed,
208
            $registration,
209
            $messageKey,
210
            $titleKey,
211
        ];
212
    }
213
214
    /**
215
     * Returns the current frontend user object if available
216
     */
217
    public function getCurrentFeUserObject(): ?FrontendUser
218
    {
219
        $user = null;
220
221
        $userUid = $this->context->getPropertyFromAspect('frontend.user', 'id');
222
        if ($userUid > 0) {
223
            /** @var FrontendUser $user */
224
            $user = $this->frontendUserRepository->findByUid($userUid);
225
        }
226
227
        return $user;
228
    }
229
230
    /**
231
     * Checks, if the registration can successfully be created.
232
     */
233
    public function checkRegistrationSuccess(Event $event, Registration $registration, int $result): array
234
    {
235
        $success = true;
236
        if ($event->getEnableRegistration() === false) {
237
            $success = false;
238
            $result = RegistrationResult::REGISTRATION_NOT_ENABLED;
239
        } elseif ($event->getRegistrationDeadline() != null && $event->getRegistrationDeadline() < new DateTime()) {
240
            $success = false;
241
            $result = RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED;
242
        } elseif (!$event->getAllowRegistrationUntilEnddate() && $event->getStartdate() < new DateTime()) {
243
            $success = false;
244
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED;
245
        } elseif ($event->getAllowRegistrationUntilEnddate() && $event->getEnddate() && $event->getEnddate() < new DateTime()) {
246
            $success = false;
247
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_ENDED;
248
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
249
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
250
        ) {
251
            $success = false;
252
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS;
253
        } elseif ($event->getFreePlaces() < $registration->getAmountOfRegistrations()
254
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
255
        ) {
256
            $success = false;
257
            $result = RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES;
258
        } elseif ($event->getMaxRegistrationsPerUser() < $registration->getAmountOfRegistrations()) {
259
            $success = false;
260
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED;
261
        } elseif ($event->getUniqueEmailCheck() &&
262
            $this->emailNotUnique($event, $registration->getEmail())
263
        ) {
264
            $success = false;
265
            $result = RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE;
266
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
267
            && $event->getMaxParticipants() > 0 && $event->getEnableWaitlist()
268
        ) {
269
            $result = RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST;
270
        }
271
272
        $modifyCheckRegistrationSuccessEvent = new ModifyCheckRegistrationSuccessEvent(
273
            $success,
274
            $result,
275
            $registration
276
        );
277
        $this->eventDispatcher->dispatch($modifyCheckRegistrationSuccessEvent);
278
279
        return [$modifyCheckRegistrationSuccessEvent->getSuccess(), $modifyCheckRegistrationSuccessEvent->getResult()];
280
    }
281
282
    /**
283
     * Returns if the given email is registered to the given event
284
     */
285
    protected function emailNotUnique(Event $event, string $email): bool
286
    {
287
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
288
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
289
        $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
290
        $registrations = $queryBuilder->count('email')
291
            ->from('tx_sfeventmgt_domain_model_registration')
292
            ->where(
293
                $queryBuilder->expr()->eq(
294
                    'event',
295
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
296
                ),
297
                $queryBuilder->expr()->eq(
298
                    'email',
299
                    $queryBuilder->createNamedParameter($email, Connection::PARAM_STR)
300
                )
301
            )
302
            ->executeQuery()
303
            ->fetchOne();
304
305
        return $registrations >= 1;
306
    }
307
308
    /**
309
     * Returns, if payment redirect for the payment method is enabled
310
     */
311
    public function redirectPaymentEnabled(Registration $registration): bool
312
    {
313
        if ($registration->getEvent()->getEnablePayment() === false) {
314
            return false;
315
        }
316
317
        /** @var AbstractPayment $paymentInstance */
318
        $paymentInstance = $this->paymentService->getPaymentInstance($registration->getPaymentmethod());
319
320
        return $paymentInstance !== null && $paymentInstance->isRedirectEnabled();
321
    }
322
323
    /**
324
     * Returns if the given amount of registrations for the event will be registrations for the waitlist
325
     * (depending on the total amount of registrations and free places)
326
     */
327
    public function isWaitlistRegistration(Event $event, int $amountOfRegistrations): bool
328
    {
329
        if ($event->getMaxParticipants() === 0 || !$event->getEnableWaitlist()) {
330
            return false;
331
        }
332
333
        $result = false;
334
        if (($event->getFreePlaces() > 0 && $event->getFreePlaces() < $amountOfRegistrations)
335
            || $event->getFreePlaces() <= 0) {
336
            $result = true;
337
        }
338
339
        return $result;
340
    }
341
342
    /**
343
     * Handles the process of moving registration up from the waitlist.
344
     */
345
    public function moveUpWaitlistRegistrations(RequestInterface $request, Event $event, array $settings): void
346
    {
347
        // Early return if move up not enabled, no registrations on waitlist or no free places left
348
        if (!$event->getEnableWaitlistMoveup() || $event->getRegistrationsWaitlist()->count() === 0 ||
349
            $event->getFreePlaces() <= 0
350
        ) {
351
            return;
352
        }
353
354
        $keepMainRegistrationDependency = $settings['waitlist']['moveUp']['keepMainRegistrationDependency'] ?? false;
355
        $freePlaces = $event->getFreePlaces();
356
        $moveupRegistrations = $this->registrationRepository->findWaitlistMoveUpRegistrations($event);
357
358
        /** @var Registration $registration */
359
        foreach ($moveupRegistrations as $registration) {
360
            $registration->setWaitlist(false);
361
            $registration->setIgnoreNotifications(false);
362
363
            if (!(bool)$keepMainRegistrationDependency) {
364
                $registration->_setProperty('mainRegistration', null);
365
            }
366
367
            $this->registrationRepository->update($registration);
368
369
            // Send messages to user and admin
370
            $this->notificationService->sendUserMessage(
371
                $request,
372
                $event,
373
                $registration,
374
                $settings,
375
                MessageType::REGISTRATION_WAITLIST_MOVE_UP
376
            );
377
            $this->notificationService->sendAdminMessage(
378
                $request,
379
                $registration->getEvent(),
380
                $registration,
381
                $settings,
382
                MessageType::REGISTRATION_WAITLIST_MOVE_UP
383
            );
384
385
            $this->eventDispatcher->dispatch(new AfterRegistrationMovedFromWaitlist($registration, $this, $request));
386
387
            $freePlaces--;
388
            if ($freePlaces === 0) {
389
                break;
390
            }
391
        }
392
    }
393
394
    /**
395
     * Checks, if the given registration belongs to the current logged in frontend user. If not, a
396
     * page not found response is thrown.
397
     */
398
    public function checkRegistrationAccess(ServerRequestInterface $request, Registration $registration): void
399
    {
400
        $isLoggedIn = $this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn');
401
        $userUid = $this->context->getPropertyFromAspect('frontend.user', 'id');
402
403
        if (!$isLoggedIn ||
404
            !$registration->getFeUser() ||
405
            $userUid !== (int)$registration->getFeUser()->getUid()) {
406
            $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
407
                $request,
408
                'Registration not found.'
409
            );
410
            throw new PropagateResponseException($response, 1671627320);
411
        }
412
    }
413
414
    /**
415
     * Evaluates and returns the price for the given registration to the given event.
416
     */
417
    public function evaluateRegistrationPrice(
418
        Event $event,
419
        Registration $registration,
420
        ServerRequestInterface $request
421
    ): float {
422
        $price = $event->getPrice();
423
        if ($registration->getPriceOption()) {
424
            $price = $registration->getPriceOption()->getPrice();
425
        }
426
427
        $modifyRegistrationPriceEvent = new ModifyRegistrationPriceEvent($price, $event, $registration, $request);
428
        $this->eventDispatcher->dispatch($modifyRegistrationPriceEvent);
429
430
        return $modifyRegistrationPriceEvent->getPrice();
431
    }
432
}
433