RegistrationService::getCurrentFeUserObject()   A
last analyzed

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->getConfirmed() && $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 && !$registration->getEvent()) {
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.cancel_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): array
234
    {
235
        $result = RegistrationResult::REGISTRATION_SUCCESSFUL;
236
        $success = true;
237
        if ($event->getEnableRegistration() === false) {
238
            $success = false;
239
            $result = RegistrationResult::REGISTRATION_NOT_ENABLED;
240
        } elseif ($event->getRegistrationDeadline() != null && $event->getRegistrationDeadline() < new DateTime()) {
241
            $success = false;
242
            $result = RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED;
243
        } elseif (!$event->getAllowRegistrationUntilEnddate() && $event->getStartdate() < new DateTime()) {
244
            $success = false;
245
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED;
246
        } elseif ($event->getAllowRegistrationUntilEnddate() && $event->getEnddate() && $event->getEnddate() < new DateTime()) {
247
            $success = false;
248
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_ENDED;
249
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
250
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
251
        ) {
252
            $success = false;
253
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS;
254
        } elseif ($event->getFreePlaces() < $registration->getAmountOfRegistrations()
255
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
256
        ) {
257
            $success = false;
258
            $result = RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES;
259
        } elseif ($event->getMaxRegistrationsPerUser() < $registration->getAmountOfRegistrations()) {
260
            $success = false;
261
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED;
262
        } elseif ($event->getUniqueEmailCheck() &&
263
            $this->emailNotUnique($event, $registration->getEmail())
264
        ) {
265
            $success = false;
266
            $result = RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE;
267
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
268
            && $event->getMaxParticipants() > 0 && $event->getEnableWaitlist()
269
        ) {
270
            $result = RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST;
271
        }
272
273
        $modifyCheckRegistrationSuccessEvent = new ModifyCheckRegistrationSuccessEvent(
274
            $success,
275
            $result,
276
            $registration
277
        );
278
        $this->eventDispatcher->dispatch($modifyCheckRegistrationSuccessEvent);
279
280
        return [$modifyCheckRegistrationSuccessEvent->getSuccess(), $modifyCheckRegistrationSuccessEvent->getResult()];
281
    }
282
283
    /**
284
     * Returns if the given email is registered to the given event
285
     */
286
    protected function emailNotUnique(Event $event, string $email): bool
287
    {
288
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
289
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
290
        $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
291
        $registrations = $queryBuilder->count('email')
292
            ->from('tx_sfeventmgt_domain_model_registration')
293
            ->where(
294
                $queryBuilder->expr()->eq(
295
                    'event',
296
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
297
                ),
298
                $queryBuilder->expr()->eq(
299
                    'email',
300
                    $queryBuilder->createNamedParameter($email, Connection::PARAM_STR)
301
                )
302
            )
303
            ->executeQuery()
304
            ->fetchOne();
305
306
        return $registrations >= 1;
307
    }
308
309
    /**
310
     * Returns, if payment redirect for the payment method is enabled
311
     */
312
    public function redirectPaymentEnabled(Registration $registration): bool
313
    {
314
        if ($registration->getEvent()->getEnablePayment() === false) {
315
            return false;
316
        }
317
318
        /** @var AbstractPayment $paymentInstance */
319
        $paymentInstance = $this->paymentService->getPaymentInstance($registration->getPaymentmethod());
320
321
        return $paymentInstance !== null && $paymentInstance->isRedirectEnabled();
322
    }
323
324
    /**
325
     * Returns if the given amount of registrations for the event will be registrations for the waitlist
326
     * (depending on the total amount of registrations and free places)
327
     */
328
    public function isWaitlistRegistration(Event $event, int $amountOfRegistrations): bool
329
    {
330
        if ($event->getMaxParticipants() === 0 || !$event->getEnableWaitlist()) {
331
            return false;
332
        }
333
334
        $result = false;
335
        if (($event->getFreePlaces() > 0 && $event->getFreePlaces() < $amountOfRegistrations)
336
            || $event->getFreePlaces() <= 0) {
337
            $result = true;
338
        }
339
340
        return $result;
341
    }
342
343
    /**
344
     * Handles the process of moving registration up from the waitlist.
345
     */
346
    public function moveUpWaitlistRegistrations(RequestInterface $request, Event $event, array $settings): void
347
    {
348
        // Early return if move up not enabled, no registrations on waitlist or no free places left
349
        if (!$event->getEnableWaitlistMoveup() || $event->getRegistrationsWaitlist()->count() === 0 ||
350
            $event->getFreePlaces() <= 0
351
        ) {
352
            return;
353
        }
354
355
        $keepMainRegistrationDependency = $settings['waitlist']['moveUp']['keepMainRegistrationDependency'] ?? false;
356
        $freePlaces = $event->getFreePlaces();
357
        $moveupRegistrations = $this->registrationRepository->findWaitlistMoveUpRegistrations($event);
358
359
        /** @var Registration $registration */
360
        foreach ($moveupRegistrations as $registration) {
361
            $registration->setWaitlist(false);
362
            $registration->setIgnoreNotifications(false);
363
364
            if (!(bool)$keepMainRegistrationDependency) {
365
                $registration->_setProperty('mainRegistration', null);
366
            }
367
368
            $this->registrationRepository->update($registration);
369
370
            // Send messages to user and admin
371
            $this->notificationService->sendUserMessage(
372
                $request,
373
                $event,
374
                $registration,
375
                $settings,
376
                MessageType::REGISTRATION_WAITLIST_MOVE_UP
377
            );
378
            $this->notificationService->sendAdminMessage(
379
                $request,
380
                $registration->getEvent(),
381
                $registration,
382
                $settings,
383
                MessageType::REGISTRATION_WAITLIST_MOVE_UP
384
            );
385
386
            $this->eventDispatcher->dispatch(new AfterRegistrationMovedFromWaitlist($registration, $this, $request));
387
388
            $freePlaces--;
389
            if ($freePlaces === 0) {
390
                break;
391
            }
392
        }
393
    }
394
395
    /**
396
     * Checks, if the given registration belongs to the current logged in frontend user. If not, a
397
     * page not found response is thrown.
398
     */
399
    public function checkRegistrationAccess(ServerRequestInterface $request, Registration $registration): void
400
    {
401
        $isLoggedIn = $this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn');
402
        $userUid = $this->context->getPropertyFromAspect('frontend.user', 'id');
403
404
        if (!$isLoggedIn ||
405
            !$registration->getFeUser() ||
406
            $userUid !== (int)$registration->getFeUser()->getUid()) {
407
            $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
408
                $request,
409
                'Registration not found.'
410
            );
411
            throw new PropagateResponseException($response, 1671627320);
412
        }
413
    }
414
415
    /**
416
     * Evaluates and returns the price for the given registration to the given event.
417
     */
418
    public function evaluateRegistrationPrice(
419
        Event $event,
420
        Registration $registration,
421
        ServerRequestInterface $request
422
    ): float {
423
        $price = $event->getPrice();
424
        if ($registration->getPriceOption()) {
425
            $price = $registration->getPriceOption()->getPrice();
426
        }
427
428
        $modifyRegistrationPriceEvent = new ModifyRegistrationPriceEvent($price, $event, $registration, $request);
429
        $this->eventDispatcher->dispatch($modifyRegistrationPriceEvent);
430
431
        return $modifyRegistrationPriceEvent->getPrice();
432
    }
433
}
434