Completed
Push — 4.x ( 70dfe1 )
by Torben
43:43 queued 43:36
created

fixRegistationFieldValueLanguage()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 0
cp 0
rs 9.9666
c 0
b 0
f 0
cc 3
nc 2
nop 2
crap 12
1
<?php
2
namespace DERHANSEN\SfEventMgt\Service;
3
4
/*
5
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.txt file that was distributed with this source code.
9
 */
10
11
use DERHANSEN\SfEventMgt\Domain\Model\Event;
12
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
13
use DERHANSEN\SfEventMgt\Payment\AbstractPayment;
14
use DERHANSEN\SfEventMgt\Utility\RegistrationResult;
15
use TYPO3\CMS\Core\Database\Connection;
16
use TYPO3\CMS\Core\Database\ConnectionPool;
17
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
20
21
/**
22
 * RegistrationService
23
 *
24
 * @author Torben Hansen <[email protected]>
25
 */
26
class RegistrationService
27
{
28
    /**
29
     * The object manager
30
     *
31
     * @var \TYPO3\CMS\Extbase\Object\ObjectManager
32
     * */
33
    protected $objectManager;
34
35
    /**
36
     * RegistrationRepository
37
     *
38
     * @var \DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository
39
     * */
40
    protected $registrationRepository;
41
42
    /**
43
     * FrontendUserRepository
44
     *
45
     * @var \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository
46
     * */
47
    protected $frontendUserRepository;
48
49
    /**
50
     * Hash Service
51
     *
52
     * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
53
     * */
54
    protected $hashService;
55
56
    /**
57
     * Payment Service
58
     *
59
     * @var \DERHANSEN\SfEventMgt\Service\PaymentService
60
     * */
61
    protected $paymentService;
62
63
    /**
64
     * DI for $frontendUserRepository
65
     *
66
     * @param \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository $frontendUserRepository
67
     */
68
    public function injectFrontendUserRepository(
69
        \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository $frontendUserRepository
70
    ) {
71
        $this->frontendUserRepository = $frontendUserRepository;
72
    }
73
74
    /**
75
     * DI for $hashService
76
     *
77
     * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
78 4
     */
79
    public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
80 4
    {
81 4
        $this->hashService = $hashService;
82 4
    }
83
84 4
    /**
85 2
     * DI for $objectManager
86 2
     *
87 2
     * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
88 2
     */
89
    public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
90 4
    {
91 4
        $this->objectManager = $objectManager;
92 4
    }
93
94
    /**
95
     * DI for $paymentService
96
     *
97
     * @param \DERHANSEN\SfEventMgt\Service\PaymentService $paymentService
98
     */
99
    public function injectPaymentService(\DERHANSEN\SfEventMgt\Service\PaymentService $paymentService)
100
    {
101
        $this->paymentService = $paymentService;
102 2
    }
103
104 2
    /**
105 2
     * DI for $registrationRepository
106
     *
107 2
     * @param \DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository $registrationRepository
108 2
     */
109 2
    public function injectRegistrationRepository(
110 2
        \DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository $registrationRepository
111 2
    ) {
112 2
        $this->registrationRepository = $registrationRepository;
113 2
    }
114 2
115 2
    /**
116 2
     * Duplicates (all public accessable properties) the given registration the
117 2
     * amount of times configured in amountOfRegistrations
118
     *
119
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
120
     *
121
     * @return void
122
     */
123
    public function createDependingRegistrations($registration)
124
    {
125
        $registrations = $registration->getAmountOfRegistrations();
126 2
        for ($i = 1; $i <= $registrations - 1; $i++) {
127
            /** @var \DERHANSEN\SfEventMgt\Domain\Model\Registration $newReg */
128 2
            $newReg = $this->objectManager->get(Registration::class);
129 2
            $properties = ObjectAccess::getGettableProperties($registration);
130
            foreach ($properties as $propertyName => $propertyValue) {
131 2
                ObjectAccess::setProperty($newReg, $propertyName, $propertyValue);
132 2
            }
133 2
            $newReg->setMainRegistration($registration);
134 2
            $newReg->setAmountOfRegistrations(1);
135
            $newReg->setIgnoreNotifications(true);
136
            $newReg->_setProperty('_languageUid', $registration->_getProperty('_languageUid'));
137
            $this->registrationRepository->add($newReg);
138
        }
139
    }
140
141
    /**
142
     * Confirms all depending registrations based on the given main registration
143
     *
144 8
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
145
     *
146
     * @return void
147 8
     */
148 8
    public function confirmDependingRegistrations($registration)
149 8
    {
150 8
        $registrations = $this->registrationRepository->findByMainRegistration($registration);
0 ignored issues
show
Documentation Bug introduced by
The method findByMainRegistration does not exist on object<DERHANSEN\SfEvent...RegistrationRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
151
        foreach ($registrations as $foundRegistration) {
152 8
            /** @var \DERHANSEN\SfEventMgt\Domain\Model\Registration $foundRegistration */
153 2
            $foundRegistration->setConfirmed(true);
154 2
            $this->registrationRepository->update($foundRegistration);
155 2
        }
156 2
    }
157 6
158
    /**
159
     * Checks if the registration can be confirmed and returns an array of variables
160 8
     *
161 2
     * @param int $reguid UID of registration
162 2
     * @param string $hmac HMAC for parameters
163 2
     *
164 2
     * @return array
165
     */
166 8
    public function checkConfirmRegistration($reguid, $hmac)
167 2
    {
168 2
        /* @var $registration Registration */
169 2
        $registration = null;
170 2
        $failed = false;
171
        $messageKey = 'event.message.confirmation_successful';
172 8
        $titleKey = 'confirmRegistration.title.successful';
173 2
174 2
        if (!$this->hashService->validateHmac('reg-' . $reguid, $hmac)) {
175 2
            $failed = true;
176 2
            $messageKey = 'event.message.confirmation_failed_wrong_hmac';
177
            $titleKey = 'confirmRegistration.title.failed';
178 8
        } else {
179
            $registration = $this->registrationRepository->findByUid($reguid);
180
        }
181
182
        if (!$failed && is_null($registration)) {
183
            $failed = true;
184 8
            $messageKey = 'event.message.confirmation_failed_registration_not_found';
185 8
            $titleKey = 'confirmRegistration.title.failed';
186 8
        }
187
188 8
        if (!$failed && $registration->getConfirmationUntil() < new \DateTime()) {
189
            $failed = true;
190
            $messageKey = 'event.message.confirmation_failed_confirmation_until_expired';
191
            $titleKey = 'confirmRegistration.title.failed';
192
        }
193
194
        if (!$failed && $registration->getConfirmed() === true) {
195
            $failed = true;
196
            $messageKey = 'event.message.confirmation_failed_already_confirmed';
197
            $titleKey = 'confirmRegistration.title.failed';
198 2
        }
199
200 2
        if (!$failed && $registration->getWaitlist()) {
201 2
            $messageKey = 'event.message.confirmation_waitlist_successful';
202 2
            $titleKey = 'confirmRegistrationWaitlist.title.successful';
203 2
        }
204 2
205
        return [
206
            $failed,
207
            $registration,
208
            $messageKey,
209
            $titleKey
210
        ];
211
    }
212
213
    /**
214 8
     * Cancels all depending registrations based on the given main registration
215
     *
216
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
217 8
     *
218 8
     * @return void
219 8
     */
220 8
    public function cancelDependingRegistrations($registration)
221
    {
222 8
        $registrations = $this->registrationRepository->findByMainRegistration($registration);
0 ignored issues
show
Documentation Bug introduced by
The method findByMainRegistration does not exist on object<DERHANSEN\SfEvent...RegistrationRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
223 2
        foreach ($registrations as $foundRegistration) {
224 2
            $this->registrationRepository->remove($foundRegistration);
225 2
        }
226 2
    }
227 6
228
    /**
229
     * Checks if the registration can be cancelled and returns an array of variables
230 8
     *
231 2
     * @param int $reguid UID of registration
232 2
     * @param string $hmac HMAC for parameters
233 2
     *
234 2
     * @return array
235
     */
236 8
    public function checkCancelRegistration($reguid, $hmac)
237 2
    {
238 2
        /* @var $registration Registration */
239 2
        $registration = null;
240 2
        $failed = false;
241
        $messageKey = 'event.message.cancel_successful';
242 8
        $titleKey = 'cancelRegistration.title.successful';
243 8
244 8
        if (!$this->hashService->validateHmac('reg-' . $reguid, $hmac)) {
245 2
            $failed = true;
246 2
            $messageKey = 'event.message.cancel_failed_wrong_hmac';
247 2
            $titleKey = 'cancelRegistration.title.failed';
248 2
        } else {
249
            $registration = $this->registrationRepository->findByUid($reguid);
250
        }
251 8
252 8
        if (!$failed && is_null($registration)) {
253 8
            $failed = true;
254
            $messageKey = 'event.message.cancel_failed_registration_not_found_or_cancelled';
255 8
            $titleKey = 'cancelRegistration.title.failed';
256
        }
257
258
        if (!$failed && $registration->getEvent()->getEnableCancel() === false) {
259
            $failed = true;
260
            $messageKey = 'event.message.confirmation_failed_cancel_disabled';
261
            $titleKey = 'cancelRegistration.title.failed';
262
        }
263 10
264
        if (!$failed && $registration->getEvent()->getCancelDeadline() > 0
265 10
            && $registration->getEvent()->getCancelDeadline() < new \DateTime()
266 2
        ) {
267
            $failed = true;
268 8
            $messageKey = 'event.message.cancel_failed_deadline_expired';
269
            $titleKey = 'cancelRegistration.title.failed';
270
        }
271
272
        if (!$failed && $registration->getEvent()->getStartdate() < new \DateTime()) {
273
            $failed = true;
274
            $messageKey = 'event.message.cancel_failed_event_started';
275
            $titleKey = 'cancelRegistration.title.failed';
276
        }
277
278
        return [
279
            $failed,
280
            $registration,
281
            $messageKey,
282 38
            $titleKey
283
        ];
284 38
    }
285 38
286 4
    /**
287 4
     * Returns the current frontend user object if available
288 38
     *
289 4
     * @return mixed \TYPO3\CMS\Extbase\Domain\Model\FrontendUser|null
290 4
     */
291 34
    public function getCurrentFeUserObject()
292 4
    {
293 4
        if (isset($GLOBALS['TSFE']->fe_user->user['uid'])) {
294 30
            return $this->frontendUserRepository->findByUid($GLOBALS['TSFE']->fe_user->user['uid']);
295 26
        }
296 26
297 4
        return null;
298 4
    }
299 26
300 22
    /**
301 22
     * Checks, if the registration can successfully be created. Note, that
302 4
     * $result is passed by reference!
303 4
     *
304 22
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Event $event Event
305 4
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
306 4
     * @param int $result Result
307 18
     *
308 4
     * @return array
309 14
     */
310 4
    public function checkRegistrationSuccess($event, $registration, $result)
311 4
    {
312 14
        $success = true;
313 10
        if ($event->getEnableRegistration() === false) {
314 10
            $success = false;
315 2
            $result = RegistrationResult::REGISTRATION_NOT_ENABLED;
316 2
        } elseif ($event->getRegistrationDeadline() != null && $event->getRegistrationDeadline() < new \DateTime()) {
317 38
            $success = false;
318
            $result = RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED;
319
        } elseif ($event->getStartdate() < new \DateTime()) {
320
            $success = false;
321
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED;
322
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
323
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
324
        ) {
325
            $success = false;
326
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS;
327 4
        } elseif ($event->getFreePlaces() < $registration->getAmountOfRegistrations()
328
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
329 4
        ) {
330 4
            $success = false;
331
            $result = RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES;
332
        } elseif ($event->getMaxRegistrationsPerUser() < $registration->getAmountOfRegistrations()) {
333
            $success = false;
334
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED;
335
        } elseif ($event->getUniqueEmailCheck() &&
336
            $this->emailNotUnique($event, $registration->getEmail())
337
        ) {
338
            $success = false;
339 4
            $result = RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE;
340
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
341 4
            && $event->getMaxParticipants() > 0 && $event->getEnableWaitlist()
342 2
        ) {
343
            $result = RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST;
344
        }
345
346 2
        return [$success, $result];
347 2
    }
348 2
349
    /**
350
     * Returns if the given e-mail is registered to the given event
351
     *
352
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Event $event
353
     * @param string $email
354
     * @return bool
355
     */
356
    protected function emailNotUnique($event, $email)
357
    {
358
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
359
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
360
        $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
361
        $registrations = $queryBuilder->count('email')
362 14
            ->from('tx_sfeventmgt_domain_model_registration')
363
            ->where(
364 14
                $queryBuilder->expr()->eq(
365 6
                    'event',
366
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
367
                ),
368 8
                $queryBuilder->expr()->eq(
369 8
                    'email',
370 2
                    $queryBuilder->createNamedParameter($email, Connection::PARAM_STR)
371 8
                )
372 4
            )
373 4
            ->execute()
374 8
            ->fetchColumn();
375
376
        return $registrations >= 1;
377
    }
378
379
    /**
380
     * Returns, if payment redirect for the payment method is enabled
381
     *
382
     * @param Registration $registration
383
     * @return bool
384
     */
385
    public function redirectPaymentEnabled($registration)
386
    {
387
        if ($registration->getEvent()->getEnablePayment() === false) {
388
            return false;
389
        }
390
391
        /** @var AbstractPayment $paymentInstance */
392
        $paymentInstance = $this->paymentService->getPaymentInstance($registration->getPaymentmethod());
393
        if ($paymentInstance !== null && $paymentInstance->isRedirectEnabled()) {
394
            return true;
395
        }
396
397
        return false;
398
    }
399
400
    /**
401
     * Returns if the given amount of registrations for the event will be registrations for the waitlist
402
     * (depending on the total amount of registrations and free places)
403
     *
404
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Event $event
405
     * @param int $amountOfRegistrations
406
     * @return bool
407
     */
408
    public function isWaitlistRegistration($event, $amountOfRegistrations)
409
    {
410
        if ($event->getMaxParticipants() === 0 || !$event->getEnableWaitlist()) {
411
            return false;
412
        }
413
414
        $result = false;
415
        if (($event->getFreePlaces() > 0 && $event->getFreePlaces() < $amountOfRegistrations)
416
            || $event->getFreePlaces() <= 0) {
417
            $result = true;
418
        }
419
420
        return $result;
421
    }
422
423
    /**
424
     * Fixes the event uid of a registration if the event has been saved as a child of a translated event.
425
     *
426
     * Since TYPO3 9.5 (#82363), registrations for events are saved to the translated event record
427
     *
428
     * Example:
429
     *
430
     * When a registration is saved for a translated event, the registration $registration->setEvent($event) will
431
     * now save the UID of the translated event instead of the uid of the event in default language.
432
     *
433
     * This behavior breaks limitations on events (e.g. max participants). Therefore, the registration must always
434
     * be related to the default event language (Extbase behavior before TYPO3 9.5)
435
     *
436
     * @param Registration $registration
437
     * @param Event $event
438
     * @return void
439
     */
440
    public function fixRegistrationEvent(Registration $registration, Event $event)
441
    {
442
        // Early return when event is in default language
443
        if ((int)$event->_getProperty('_languageUid') === 0) {
444
            return;
445
        }
446
        $this->updateRegistrationEventUid($registration, $event);
447
        $this->updateEventRegistrationCounters($event);
448
    }
449
450
    /**
451
     * Ensures, that the field "sys_language_uid" for registration fields values has the same value as the
452
     * language of the registration and event. This is required, so emails include registration field values
453
     * and correct registration field labels in their translated state.
454
     *
455
     * @param Registration $registration
456
     * @param Event $event
457
     * @return void
458
     */
459
    public function fixRegistationFieldValueLanguage(Registration $registration, Event $event)
460
    {
461
        // Early return when event is in default language or no registration fields
462
        if ((int)$event->_getProperty('_languageUid') === 0 || $event->getRegistrationFields()->count() === 0) {
463
            return;
464
        }
465
466
        $this->updateRegistrationFieldValueLanguage($registration, $event->_getProperty('_languageUid'));
467
    }
468
469
    /**
470
     * Updates the field "sys_language_uid" for all registration field values of the given registration
471
     *
472
     * @param Registration $registration
473
     * @param int $sysLanguageUid
474
     * @return void
475
     */
476
    protected function updateRegistrationFieldValueLanguage(Registration $registration, int $sysLanguageUid)
477
    {
478
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
479
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
480
        $queryBuilder->update('tx_sfeventmgt_domain_model_registration_fieldvalue')
481
            ->set('sys_language_uid', $sysLanguageUid)
482
            ->where(
483
                $queryBuilder->expr()->eq(
484
                    'registration',
485
                    $queryBuilder->createNamedParameter($registration->getUid(), Connection::PARAM_INT)
486
                )
487
            )
488
            ->execute();
489
    }
490
491
    /**
492
     * Sets the "event" field of the given registration to the uid of the given event
493
     *
494
     * @param Registration $registration
495
     * @param Event $event
496
     * @return void
497
     */
498
    protected function updateRegistrationEventUid(Registration $registration, Event $event)
499
    {
500
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
501
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
502
        $queryBuilder->update('tx_sfeventmgt_domain_model_registration')
503
            ->set('event', $event->getUid())
504
            ->where(
505
                $queryBuilder->expr()->eq(
506
                    'uid',
507
                    $queryBuilder->createNamedParameter($registration->getUid(), Connection::PARAM_INT)
508
                )
509
            )
510
            ->execute();
511
    }
512
513
    /**
514
     * Updates registration/waitlist registration counters for the given event
515
     *
516
     * @param Event $event
517
     * @return void
518
     */
519
    protected function updateEventRegistrationCounters(Event $event)
520
    {
521
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
522
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_event');
523
524
        $countRegistrations = $this->getEventRegistrationCount($event, 0);
525
        $countRegistrationsWaitlist = $this->getEventRegistrationCount($event, 1);
526
527
        $queryBuilder->update('tx_sfeventmgt_domain_model_event')
528
            ->set('registration', $countRegistrations)
529
            ->set('registration_waitlist', $countRegistrationsWaitlist)
530
            ->where(
531
                $queryBuilder->expr()->eq(
532
                    'uid',
533
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
534
                )
535
            )
536
            ->orWhere(
537
                $queryBuilder->expr()->eq(
538
                    'l10n_parent',
539
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
540
                )
541
            )
542
            ->execute();
543
    }
544
545
    /**
546
     * Returns the total amount of registrations/waitlist registrations for an event
547
     *
548
     * @param Event $event
549
     * @param int $waitlist
550
     * @return mixed
551
     */
552
    protected function getEventRegistrationCount(Event $event, int $waitlist = 0)
553
    {
554
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
555
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
556
        $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
557
558
        return $queryBuilder->count('uid')
559
            ->from('tx_sfeventmgt_domain_model_registration')
560
            ->where(
561
                $queryBuilder->expr()->eq(
562
                    'event',
563
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
564
                ),
565
                $queryBuilder->expr()->eq(
566
                    'waitlist',
567
                    $queryBuilder->createNamedParameter($waitlist, Connection::PARAM_INT)
568
                )
569
            )
570
            ->execute()
571
            ->fetchColumn();
572
    }
573
}
574