Passed
Push — typo3_11 ( 59c1e4...0b15fc )
by Torben
04:11
created

removePossibleSpamCheckFieldsFromArguments()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 5
nop 0
dl 0
loc 19
rs 9.9666
c 0
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\Controller;
13
14
use DERHANSEN\SfEventMgt\Domain\Model\Dto\CategoryDemand;
15
use DERHANSEN\SfEventMgt\Domain\Model\Dto\EventDemand;
16
use DERHANSEN\SfEventMgt\Domain\Model\Dto\ForeignRecordDemand;
17
use DERHANSEN\SfEventMgt\Domain\Model\Dto\SearchDemand;
18
use DERHANSEN\SfEventMgt\Domain\Model\Event;
19
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
20
use DERHANSEN\SfEventMgt\Event\AfterRegistrationCancelledEvent;
21
use DERHANSEN\SfEventMgt\Event\AfterRegistrationConfirmedEvent;
22
use DERHANSEN\SfEventMgt\Event\AfterRegistrationSavedEvent;
23
use DERHANSEN\SfEventMgt\Event\EventPidCheckFailedEvent;
24
use DERHANSEN\SfEventMgt\Event\ModifyCalendarViewVariablesEvent;
25
use DERHANSEN\SfEventMgt\Event\ModifyCancelRegistrationViewVariablesEvent;
26
use DERHANSEN\SfEventMgt\Event\ModifyConfirmRegistrationViewVariablesEvent;
27
use DERHANSEN\SfEventMgt\Event\ModifyCreateDependingRegistrationsEvent;
28
use DERHANSEN\SfEventMgt\Event\ModifyDetailViewVariablesEvent;
29
use DERHANSEN\SfEventMgt\Event\ModifyListViewVariablesEvent;
30
use DERHANSEN\SfEventMgt\Event\ModifyRegistrationViewVariablesEvent;
31
use DERHANSEN\SfEventMgt\Event\ModifySearchViewVariablesEvent;
32
use DERHANSEN\SfEventMgt\Event\WaitlistMoveUpEvent;
33
use DERHANSEN\SfEventMgt\Service\EventCacheService;
34
use DERHANSEN\SfEventMgt\Utility\MessageType;
35
use DERHANSEN\SfEventMgt\Utility\PageUtility;
36
use DERHANSEN\SfEventMgt\Utility\RegistrationResult;
37
use Psr\Http\Message\ResponseInterface;
38
use TYPO3\CMS\Core\Context\Context;
39
use TYPO3\CMS\Core\Http\PropagateResponseException;
40
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
41
use TYPO3\CMS\Core\Utility\ArrayUtility;
42
use TYPO3\CMS\Core\Utility\GeneralUtility;
43
use TYPO3\CMS\Extbase\Annotation as Extbase;
44
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
45
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
46
use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter;
47
use TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter;
48
use TYPO3\CMS\Fluid\View\StandaloneView;
49
use TYPO3\CMS\Frontend\Controller\ErrorController;
50
51
/**
52
 * EventController
53
 */
54
class EventController extends AbstractController
55
{
56
    protected EventCacheService $eventCacheService;
57
58
    public function injectEventCacheService(EventCacheService $cacheService)
59
    {
60
        $this->eventCacheService = $cacheService;
61
    }
62
63
    /**
64
     * Assign contentObjectData and pageData to earch view
65
     * @todo: Remove $view parameter for TYPO3 v12 version and use $this->view instead to assign variables.
66
     *
67
     * @param ViewInterface $view @extensionScannerIgnoreLine
68
     */
69
    protected function initializeView(ViewInterface $view)
70
    {
71
        // @extensionScannerIgnoreLine
72
        $view->assign('contentObjectData', $this->configurationManager->getContentObject()->data);
73
        if ($this->getTypoScriptFrontendController()) {
74
            $view->assign('pageData', $this->getTypoScriptFrontendController()->page);
75
        }
76
    }
77
78
    /**
79
     * Initializes the current action
80
     */
81
    public function initializeAction()
82
    {
83
        $typoScriptFrontendController = $this->getTypoScriptFrontendController();
84
        if ($typoScriptFrontendController !== null) {
85
            static $cacheTagsSet = false;
86
87
            if (!$cacheTagsSet) {
88
                $typoScriptFrontendController->addCacheTags(['tx_sfeventmgt']);
89
                $cacheTagsSet = true;
90
            }
91
        }
92
    }
93
94
    /**
95
     * Initialize list action and set format
96
     */
97
    public function initializeListAction()
98
    {
99
        if (isset($this->settings['list']['format'])) {
100
            $this->request->setFormat($this->settings['list']['format']);
101
        }
102
    }
103
104
    /**
105
     * List view
106
     *
107
     * @param array $overwriteDemand OverwriteDemand
108
     */
109
    public function listAction(array $overwriteDemand = []): ResponseInterface
110
    {
111
        $eventDemand = EventDemand::createFromSettings($this->settings);
112
        $foreignRecordDemand = ForeignRecordDemand::createFromSettings($this->settings);
113
        $categoryDemand = CategoryDemand::createFromSettings($this->settings);
114
        if ($this->isOverwriteDemand($overwriteDemand)) {
115
            $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand);
116
        }
117
        $events = $this->eventRepository->findDemanded($eventDemand);
118
        $categories = $this->categoryRepository->findDemanded($categoryDemand);
119
        $locations = $this->locationRepository->findDemanded($foreignRecordDemand);
120
        $organisators = $this->organisatorRepository->findDemanded($foreignRecordDemand);
121
        $speakers = $this->speakerRepository->findDemanded($foreignRecordDemand);
122
123
        $modifyListViewVariablesEvent = new ModifyListViewVariablesEvent(
124
            [
125
                'events' => $events,
126
                'pagination' => $this->getPagination($events, $this->settings['pagination'] ?? []),
0 ignored issues
show
Bug introduced by
It seems like $events can also be of type array; however, parameter $events of DERHANSEN\SfEventMgt\Con...roller::getPagination() does only seem to accept TYPO3\CMS\Extbase\Persistence\QueryResultInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
                'pagination' => $this->getPagination(/** @scrutinizer ignore-type */ $events, $this->settings['pagination'] ?? []),
Loading history...
127
                'categories' => $categories,
128
                'locations' => $locations,
129
                'organisators' => $organisators,
130
                'speakers' => $speakers,
131
                'overwriteDemand' => $overwriteDemand,
132
                'eventDemand' => $eventDemand,
133
            ],
134
            $this
135
        );
136
        $this->eventDispatcher->dispatch($modifyListViewVariablesEvent);
137
        $variables = $modifyListViewVariablesEvent->getVariables();
138
        $this->view->assignMultiple($variables);
139
140
        $this->eventCacheService->addPageCacheTagsByEventDemandObject($eventDemand);
141
142
        return $this->htmlResponse();
143
    }
144
145
    /**
146
     * Calendar view
147
     *
148
     * @param array $overwriteDemand OverwriteDemand
149
     */
150
    public function calendarAction(array $overwriteDemand = []): ResponseInterface
151
    {
152
        $eventDemand = EventDemand::createFromSettings($this->settings);
153
        $foreignRecordDemand = ForeignRecordDemand::createFromSettings($this->settings);
154
        $categoryDemand = CategoryDemand::createFromSettings($this->settings);
155
        if ($this->isOverwriteDemand($overwriteDemand)) {
156
            $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand);
157
        }
158
159
        // Set month/year to demand if not given
160
        if (!$eventDemand->getMonth()) {
161
            $currentMonth = (int)date('n');
162
            $eventDemand->setMonth($currentMonth);
163
        } else {
164
            $currentMonth = $eventDemand->getMonth();
165
        }
166
        if (!$eventDemand->getYear()) {
167
            $currentYear = (int)date('Y');
168
            $eventDemand->setYear($currentYear);
169
        } else {
170
            $currentYear = $eventDemand->getYear();
171
        }
172
173
        // Set demand from calendar date range instead of month / year
174
        if ((bool)($this->settings['calendar']['includeEventsForEveryDayOfAllCalendarWeeks'] ?? false)) {
175
            $eventDemand = $this->changeEventDemandToFullMonthDateRange($eventDemand);
176
        }
177
178
        $events = $this->eventRepository->findDemanded($eventDemand);
179
        $weeks = $this->calendarService->getCalendarArray(
180
            $currentMonth,
181
            $currentYear,
182
            strtotime('today midnight'),
183
            (int)($this->settings['calendar']['firstDayOfWeek'] ?? 1),
184
            $events
185
        );
186
187
        $modifyCalendarViewVariablesEvent = new ModifyCalendarViewVariablesEvent(
188
            [
189
                'events' => $events,
190
                'weeks' => $weeks,
191
                'categories' => $this->categoryRepository->findDemanded($categoryDemand),
192
                'locations' => $this->locationRepository->findDemanded($foreignRecordDemand),
193
                'organisators' => $this->organisatorRepository->findDemanded($foreignRecordDemand),
194
                'eventDemand' => $eventDemand,
195
                'overwriteDemand' => $overwriteDemand,
196
                'currentPageId' => $this->getTypoScriptFrontendController()->id,
197
                'firstDayOfMonth' => \DateTime::createFromFormat(
198
                    'd.m.Y',
199
                    sprintf('1.%s.%s', $currentMonth, $currentYear)
200
                ),
201
                'previousMonthConfig' => $this->calendarService->getDateConfig($currentMonth, $currentYear, '-1 month'),
202
                'nextMonthConfig' => $this->calendarService->getDateConfig($currentMonth, $currentYear, '+1 month'),
203
            ],
204
            $this
205
        );
206
        $this->eventDispatcher->dispatch($modifyCalendarViewVariablesEvent);
207
        $variables = $modifyCalendarViewVariablesEvent->getVariables();
208
209
        $this->view->assignMultiple($variables);
210
        return $this->htmlResponse();
211
    }
212
213
    /**
214
     * Changes the given event demand object to select a date range for a calendar month including days of the previous
215
     * month for the first week and they days for the next month for the last week
216
     *
217
     * @param EventDemand $eventDemand
218
     * @return EventDemand
219
     */
220
    protected function changeEventDemandToFullMonthDateRange(EventDemand $eventDemand): EventDemand
221
    {
222
        $calendarDateRange = $this->calendarService->getCalendarDateRange(
223
            $eventDemand->getMonth(),
224
            $eventDemand->getYear(),
225
            (int)($this->settings['calendar']['firstDayOfWeek'] ?? 0)
226
        );
227
228
        $eventDemand->setMonth(0);
229
        $eventDemand->setYear(0);
230
231
        $startDate = new \DateTime();
232
        $startDate->setTimestamp($calendarDateRange['firstDayOfCalendar']);
233
        $endDate = new \DateTime();
234
        $endDate->setTimestamp($calendarDateRange['lastDayOfCalendar']);
235
        $endDate->setTime(23, 59, 59);
236
237
        $searchDemand = new SearchDemand();
238
        $searchDemand->setStartDate($startDate);
239
        $searchDemand->setEndDate($endDate);
240
        $eventDemand->setSearchDemand($searchDemand);
241
242
        return $eventDemand;
243
    }
244
245
    /**
246
     * Detail view for an event
247
     *
248
     * @param Event|null $event Event
249
     * @return mixed
250
     */
251
    public function detailAction(?Event $event = null)
252
    {
253
        $event = $this->evaluateSingleEventSetting($event);
254
        $event = $this->evaluateIsShortcutSetting($event);
255
        if (is_a($event, Event::class) && ($this->settings['detail']['checkPidOfEventRecord'] ?? false)) {
256
            $event = $this->checkPidOfEventRecord($event);
257
        }
258
259
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
260
            return $this->handleEventNotFoundError($this->settings);
261
        }
262
263
        $modifyDetailViewVariablesEvent = new ModifyDetailViewVariablesEvent(['event' => $event], $this);
264
        $this->eventDispatcher->dispatch($modifyDetailViewVariablesEvent);
265
        $variables = $modifyDetailViewVariablesEvent->getVariables();
266
267
        $this->view->assignMultiple($variables);
268
        if ($event !== null) {
269
            $this->eventCacheService->addCacheTagsByEventRecords([$event]);
270
        }
271
272
        return $this->htmlResponse();
273
    }
274
275
    /**
276
     * Error handling if event is not found
277
     *
278
     * @param array $settings
279
     * @return ResponseInterface|void|null
280
     * @throws PropagateResponseException
281
     * @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
282
     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
283
     */
284
    protected function handleEventNotFoundError(array $settings)
285
    {
286
        if (empty($settings['event']['errorHandling'])) {
287
            return null;
288
        }
289
290
        $configuration = GeneralUtility::trimExplode(',', $settings['event']['errorHandling'], true);
291
292
        switch ($configuration[0]) {
293
            case 'redirectToListView':
294
                $listPid = (int)($settings['listPid'] ?? 0) > 0 ? (int)$settings['listPid'] : 1;
295
                $this->redirect('list', null, null, null, $listPid);
296
                break;
297
            case 'pageNotFoundHandler':
298
                $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
299
                    $this->request,
300
                    'Event not found.'
301
                );
302
                throw new PropagateResponseException($response, 1631261423);
303
            case 'showStandaloneTemplate':
304
                $status = (int)($configuration[2] ?? 200);
305
                $standaloneTemplate = GeneralUtility::makeInstance(StandaloneView::class);
306
                $standaloneTemplate->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($configuration[1]));
307
308
                $response = $this->responseFactory->createResponse()
309
                    ->withStatus($status)
310
                    ->withHeader('Content-Type', 'text/html; charset=utf-8');
311
                $response->getBody()->write($standaloneTemplate->render());
312
                return $response;
313
            default:
314
        }
315
    }
316
317
    /**
318
     * Initiates the iCalendar download for the given event
319
     *
320
     * @param Event|null $event The event
321
     *
322
     * @return mixed
323
     */
324
    public function icalDownloadAction(?Event $event = null)
325
    {
326
        if (is_a($event, Event::class) && ($this->settings['detail']['checkPidOfEventRecord'] ?? false)) {
327
            $event = $this->checkPidOfEventRecord($event);
328
        }
329
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
330
            return $this->handleEventNotFoundError($this->settings);
331
        }
332
        $this->icalendarService->downloadiCalendarFile($event);
0 ignored issues
show
Bug introduced by
It seems like $event can also be of type null; however, parameter $event of DERHANSEN\SfEventMgt\Ser...downloadiCalendarFile() does only seem to accept DERHANSEN\SfEventMgt\Domain\Model\Event, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

332
        $this->icalendarService->downloadiCalendarFile(/** @scrutinizer ignore-type */ $event);
Loading history...
333
        exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
334
    }
335
336
    /**
337
     * Registration view for an event
338
     *
339
     * @param Event|null $event Event
340
     *
341
     * @return mixed
342
     */
343
    public function registrationAction(?Event $event = null)
344
    {
345
        $event = $this->evaluateSingleEventSetting($event);
346
        if (is_a($event, Event::class) && ($this->settings['registration']['checkPidOfEventRecord'] ?? false)) {
347
            $event = $this->checkPidOfEventRecord($event);
348
        }
349
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
350
            return $this->handleEventNotFoundError($this->settings);
351
        }
352
        if ($event->getRestrictPaymentMethods()) {
353
            $paymentMethods = $this->paymentService->getRestrictedPaymentMethods($event);
354
        } else {
355
            $paymentMethods = $this->paymentService->getPaymentMethods();
356
        }
357
358
        $modifyRegistrationViewVariablesEvent = new ModifyRegistrationViewVariablesEvent(
359
            [
360
                'event' => $event,
361
                'paymentMethods' => $paymentMethods,
362
            ],
363
            $this
364
        );
365
        $this->eventDispatcher->dispatch($modifyRegistrationViewVariablesEvent);
366
        $variables = $modifyRegistrationViewVariablesEvent->getVariables();
367
        $this->view->assignMultiple($variables);
368
369
        return $this->htmlResponse();
370
    }
371
372
    /**
373
     * Removes all possible spamcheck fields (which do not belong to the domain model) from arguments.
374
     */
375
    protected function removePossibleSpamCheckFieldsFromArguments(): void
376
    {
377
        $arguments = $this->request->getArguments();
378
        if (!isset($arguments['event'])) {
379
            return;
380
        }
381
382
        // Remove a possible honeypot field
383
        $honeypotField = 'hp' . (int)$arguments['event'];
384
        if (isset($arguments['registration'][$honeypotField])) {
385
            unset($arguments['registration'][$honeypotField]);
386
        }
387
388
        // Remove a possible challenge/response field
389
        if (isset($arguments['registration']['cr-response'])) {
390
            unset($arguments['registration']['cr-response']);
391
        }
392
393
        $this->request->setArguments($arguments);
394
    }
395
396
    /**
397
     * Processes incoming registrations fields and adds field values to arguments
398
     */
399
    protected function setRegistrationFieldValuesToArguments(): void
400
    {
401
        $arguments = $this->request->getArguments();
402
        if (!isset($arguments['event'])) {
403
            return;
404
        }
405
406
        /** @var Event $event */
407
        $event = $this->eventRepository->findByUid((int)$this->request->getArgument('event'));
408
        if (!is_a($event, Event::class) || $event->getRegistrationFields()->count() === 0) {
409
            return;
410
        }
411
412
        $registrationMvcArgument = $this->arguments->getArgument('registration');
413
        $propertyMapping = $registrationMvcArgument->getPropertyMappingConfiguration();
414
        $propertyMapping->allowProperties('fieldValues');
415
        $propertyMapping->allowCreationForSubProperty('fieldValues');
416
        $propertyMapping->allowModificationForSubProperty('fieldValues');
417
418
        // allow creation of new objects (for validation)
419
        $propertyMapping->setTypeConverterOptions(
420
            PersistentObjectConverter::class,
421
            [
422
                PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => true,
423
                PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED => true,
424
            ]
425
        );
426
427
        // Set event to registration (required for validation)
428
        $propertyMapping->allowProperties('event');
429
        $propertyMapping->allowCreationForSubProperty('event');
430
        $propertyMapping->allowModificationForSubProperty('event');
431
        $arguments['registration']['event'] = (int)$this->request->getArgument('event');
432
433
        $index = 0;
434
        foreach ((array)$arguments['registration']['fields'] as $fieldUid => $value) {
435
            // Only accept registration fields of the current event
436
            if (!in_array((int)$fieldUid, $event->getRegistrationFieldsUids(), true)) {
437
                continue;
438
            }
439
440
            // allow subvalues in new property mapper
441
            $propertyMapping->forProperty('fieldValues')->allowProperties($index);
442
            $propertyMapping->forProperty('fieldValues.' . $index)->allowAllProperties();
443
            $propertyMapping->allowCreationForSubProperty('fieldValues.' . $index);
444
            $propertyMapping->allowModificationForSubProperty('fieldValues.' . $index);
445
446
            if (is_array($value)) {
447
                if (empty($value)) {
448
                    $value = '';
449
                } else {
450
                    $value = json_encode($value);
451
                }
452
            }
453
454
            /** @var Registration\Field $field */
455
            $field = $this->fieldRepository->findByUid((int)$fieldUid);
456
457
            $arguments['registration']['fieldValues'][$index] = [
458
                'pid' => $field->getPid(),
459
                'value' => $value,
460
                'field' => (string)$fieldUid,
461
                'valueType' => $field->getValueType(),
462
            ];
463
464
            $index++;
465
        }
466
467
        // Remove temporary "fields" field
468
        if (isset($arguments['registration']['fields'])) {
469
            $arguments = ArrayUtility::removeByPath($arguments, 'registration/fields');
470
        }
471
        $this->request->setArguments($arguments);
472
    }
473
474
    /**
475
     * Set date format for field dateOfBirth
476
     */
477
    public function initializeSaveRegistrationAction()
478
    {
479
        $this->arguments->getArgument('registration')
480
            ->getPropertyMappingConfiguration()->forProperty('dateOfBirth')
481
            ->setTypeConverterOption(
482
                DateTimeConverter::class,
483
                DateTimeConverter::CONFIGURATION_DATE_FORMAT,
484
                $this->settings['registration']['formatDateOfBirth'] ?? 'd.m.Y'
485
            );
486
        $this->removePossibleSpamCheckFieldsFromArguments();
487
        $this->setRegistrationFieldValuesToArguments();
488
    }
489
490
    /**
491
     * Saves the registration
492
     *
493
     * @param Registration $registration Registration
494
     * @param Event $event Event
495
     * @Extbase\Validate("DERHANSEN\SfEventMgt\Validation\Validator\RegistrationFieldValidator", param="registration")
496
     * @Extbase\Validate("DERHANSEN\SfEventMgt\Validation\Validator\RegistrationValidator", param="registration")
497
     *
498
     * @return mixed|void
499
     */
500
    public function saveRegistrationAction(Registration $registration, Event $event)
501
    {
502
        if (is_a($event, Event::class) && ($this->settings['registration']['checkPidOfEventRecord'] ?? false)) {
503
            $event = $this->checkPidOfEventRecord($event);
504
        }
505
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
506
            return $this->handleEventNotFoundError($this->settings);
507
        }
508
        $autoConfirmation = (bool)($this->settings['registration']['autoConfirmation'] ?? false) ||
509
            $event->getEnableAutoconfirm();
510
        $result = RegistrationResult::REGISTRATION_SUCCESSFUL;
511
        list($success, $result) = $this->registrationService->checkRegistrationSuccess($event, $registration, $result);
512
513
        // Save registration if no errors
514
        if ($success) {
515
            $isWaitlistRegistration = $this->registrationService->isWaitlistRegistration(
516
                $event,
517
                $registration->getAmountOfRegistrations()
518
            );
519
            $linkValidity = (int)($this->settings['confirmation']['linkValidity'] ?? 3600);
520
            if ($linkValidity === 0) {
521
                // Use 3600 seconds as default value if not set or zero
522
                $linkValidity = 3600;
523
            }
524
            $confirmationUntil = new \DateTime();
525
            $confirmationUntil->add(new \DateInterval('PT' . $linkValidity . 'S'));
526
527
            $registration->setEvent($event);
528
            $registration->setPid($event->getPid());
529
            $registration->setRegistrationDate(new \DateTime());
530
            $registration->setConfirmationUntil($confirmationUntil);
531
            $registration->setLanguage($this->getCurrentLanguageTwoLetterIsoCode());
532
            $registration->setFeUser($this->registrationService->getCurrentFeUserObject());
533
            $registration->setWaitlist($isWaitlistRegistration);
534
            $this->registrationRepository->add($registration);
535
536
            // Persist registration, so we have an UID
537
            $this->persistAll();
538
539
            if ($isWaitlistRegistration) {
540
                $messageType = MessageType::REGISTRATION_WAITLIST_NEW;
541
            } else {
542
                $messageType = MessageType::REGISTRATION_NEW;
543
            }
544
545
            $this->eventDispatcher->dispatch(new AfterRegistrationSavedEvent($registration, $this));
546
547
            // Send notifications to user and admin if confirmation link should be sent
548
            if (!$autoConfirmation) {
549
                $this->notificationService->sendUserMessage(
550
                    $event,
551
                    $registration,
552
                    $this->settings ?? [],
553
                    $messageType
554
                );
555
                $this->notificationService->sendAdminMessage(
556
                    $event,
557
                    $registration,
558
                    $this->settings ?? [],
559
                    $messageType
560
                );
561
            }
562
563
            // Create given amount of registrations if necessary
564
            $modifyCreateDependingRegistrationsEvent = new ModifyCreateDependingRegistrationsEvent(
565
                $registration,
566
                ($registration->getAmountOfRegistrations() > 1),
567
                $this
568
            );
569
            $this->eventDispatcher->dispatch($modifyCreateDependingRegistrationsEvent);
570
            $createDependingRegistrations = $modifyCreateDependingRegistrationsEvent->getCreateDependingRegistrations();
571
            if ($createDependingRegistrations) {
572
                $this->registrationService->createDependingRegistrations($registration);
573
            }
574
575
            // Flush page cache for event, since new registration has been added
576
            $this->eventCacheService->flushEventCache($event->getUid(), $event->getPid());
577
        }
578
579
        if ($autoConfirmation && $success) {
580
            $this->redirect(
581
                'confirmRegistration',
582
                null,
583
                null,
584
                [
585
                    'reguid' => $registration->getUid(),
586
                    'hmac' => $this->hashService->generateHmac('reg-' . $registration->getUid()),
587
                ]
588
            );
589
        } else {
590
            $this->redirect(
591
                'saveRegistrationResult',
592
                null,
593
                null,
594
                [
595
                    'result' => $result,
596
                    'eventuid' => $event->getUid(),
597
                    'hmac' => $this->hashService->generateHmac('event-' . $event->getUid()),
598
                ]
599
            );
600
        }
601
    }
602
603
    /**
604
     * Shows the result of the saveRegistrationAction
605
     *
606
     * @param int $result Result
607
     * @param int $eventuid
608
     * @param string $hmac
609
     * @return ResponseInterface
610
     */
611
    public function saveRegistrationResultAction(int $result, int $eventuid, string $hmac): ResponseInterface
612
    {
613
        $event = null;
614
615
        switch ($result) {
616
            case RegistrationResult::REGISTRATION_SUCCESSFUL:
617
                $messageKey = 'event.message.registrationsuccessful';
618
                $titleKey = 'registrationResult.title.successful';
619
                break;
620
            case RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST:
621
                $messageKey = 'event.message.registrationwaitlistsuccessful';
622
                $titleKey = 'registrationWaitlistResult.title.successful';
623
                break;
624
            case RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED:
625
                $messageKey = 'event.message.registrationfailedeventexpired';
626
                $titleKey = 'registrationResult.title.failed';
627
                break;
628
            case RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS:
629
                $messageKey = 'event.message.registrationfailedmaxparticipants';
630
                $titleKey = 'registrationResult.title.failed';
631
                break;
632
            case RegistrationResult::REGISTRATION_NOT_ENABLED:
633
                $messageKey = 'event.message.registrationfailednotenabled';
634
                $titleKey = 'registrationResult.title.failed';
635
                break;
636
            case RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED:
637
                $messageKey = 'event.message.registrationfaileddeadlineexpired';
638
                $titleKey = 'registrationResult.title.failed';
639
                break;
640
            case RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES:
641
                $messageKey = 'event.message.registrationfailednotenoughfreeplaces';
642
                $titleKey = 'registrationResult.title.failed';
643
                break;
644
            case RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED:
645
                $messageKey = 'event.message.registrationfailedmaxamountregistrationsexceeded';
646
                $titleKey = 'registrationResult.title.failed';
647
                break;
648
            case RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE:
649
                $messageKey = 'event.message.registrationfailedemailnotunique';
650
                $titleKey = 'registrationResult.title.failed';
651
                break;
652
            default:
653
                $messageKey = '';
654
                $titleKey = '';
655
        }
656
657
        if (!$this->hashService->validateHmac('event-' . $eventuid, $hmac)) {
658
            $messageKey = 'event.message.registrationsuccessfulwrongeventhmac';
659
            $titleKey = 'registrationResult.title.failed';
660
        } else {
661
            $event = $this->eventRepository->findByUid((int)$eventuid);
662
        }
663
664
        $this->view->assignMultiple([
665
            'messageKey' => $messageKey,
666
            'titleKey' => $titleKey,
667
            'event' => $event,
668
        ]);
669
670
        return $this->htmlResponse();
671
    }
672
673
    /**
674
     * Confirms the registration if possible and sends emails to admin and user
675
     *
676
     * @param int $reguid UID of registration
677
     * @param string $hmac HMAC for parameters
678
     */
679
    public function confirmRegistrationAction(int $reguid, string $hmac)
680
    {
681
        $event = null;
682
683
        /* @var $registration Registration */
684
        list($failed, $registration, $messageKey, $titleKey) = $this->registrationService->checkConfirmRegistration(
685
            $reguid,
686
            $hmac
687
        );
688
689
        if ($failed === false) {
690
            $registration->setConfirmed(true);
691
            $event = $registration->getEvent();
692
            $this->registrationRepository->update($registration);
693
694
            $this->eventDispatcher->dispatch(new AfterRegistrationConfirmedEvent($registration, $this));
695
696
            $messageType = MessageType::REGISTRATION_CONFIRMED;
697
            if ($registration->getWaitlist()) {
698
                $messageType = MessageType::REGISTRATION_WAITLIST_CONFIRMED;
699
            }
700
701
            // Send notifications to user and admin
702
            $this->notificationService->sendUserMessage(
703
                $registration->getEvent(),
704
                $registration,
705
                $this->settings ?? [],
706
                $messageType
707
            );
708
            $this->notificationService->sendAdminMessage(
709
                $registration->getEvent(),
710
                $registration,
711
                $this->settings ?? [],
712
                $messageType
713
            );
714
715
            // Confirm registrations depending on main registration if necessary
716
            if ($registration->getAmountOfRegistrations() > 1) {
717
                $this->registrationService->confirmDependingRegistrations($registration);
718
            }
719
        }
720
721
        // Redirect to payment provider if payment/redirect is enabled
722
        $paymentPid = (int)($this->settings['paymentPid'] ?? 0);
723
        if (!$failed && $paymentPid > 0 && $this->registrationService->redirectPaymentEnabled($registration)) {
724
            $this->uriBuilder->reset()
725
                ->setTargetPageUid($paymentPid);
726
            $uri = $this->uriBuilder->uriFor(
727
                'redirect',
728
                [
729
                    'registration' => $registration,
730
                    'hmac' => $this->hashService->generateHmac('redirectAction-' . $registration->getUid()),
731
                ],
732
                'Payment',
733
                'sfeventmgt',
734
                'Pipayment'
735
            );
736
            $this->redirectToUri($uri);
737
        }
738
739
        $modifyConfirmRegistrationViewVariablesEvent = new ModifyConfirmRegistrationViewVariablesEvent(
740
            [
741
                'failed' => $failed,
742
                'messageKey' => $messageKey,
743
                'titleKey' => $titleKey,
744
                'event' => $event,
745
                'registration' => $registration,
746
            ],
747
            $this
748
        );
749
        $this->eventDispatcher->dispatch($modifyConfirmRegistrationViewVariablesEvent);
750
        $variables = $modifyConfirmRegistrationViewVariablesEvent->getVariables();
751
        $this->view->assignMultiple($variables);
752
753
        return $this->htmlResponse();
754
    }
755
756
    /**
757
     * Cancels the registration if possible and sends emails to admin and user
758
     *
759
     * @param int $reguid UID of registration
760
     * @param string $hmac HMAC for parameters
761
     */
762
    public function cancelRegistrationAction(int $reguid, string $hmac): ResponseInterface
763
    {
764
        $event = null;
765
766
        /* @var $registration Registration */
767
        list($failed, $registration, $messageKey, $titleKey) =
768
            $this->registrationService->checkCancelRegistration($reguid, $hmac);
769
770
        if ($failed === false) {
771
            $event = $registration->getEvent();
772
773
            // Send notifications (must run before cancelling the registration)
774
            $this->notificationService->sendUserMessage(
775
                $registration->getEvent(),
776
                $registration,
777
                $this->settings,
778
                MessageType::REGISTRATION_CANCELLED
779
            );
780
            $this->notificationService->sendAdminMessage(
781
                $registration->getEvent(),
782
                $registration,
783
                $this->settings,
784
                MessageType::REGISTRATION_CANCELLED
785
            );
786
787
            // First cancel depending registrations
788
            if ($registration->getAmountOfRegistrations() > 1) {
789
                $this->registrationService->cancelDependingRegistrations($registration);
790
            }
791
792
            // Finally cancel registration
793
            $this->registrationRepository->remove($registration);
794
795
            // Persist changes, so following functions can work with $event properties (e.g. amount of registrations)
796
            $this->persistAll();
797
798
            $afterRegistrationCancelledEvent = new AfterRegistrationCancelledEvent($registration, $this);
799
            $this->eventDispatcher->dispatch($afterRegistrationCancelledEvent);
800
801
            // Dispatch event, so waitlist registrations can be moved up and default move up process can be stopped
802
            $waitlistMoveUpEvent = new WaitlistMoveUpEvent($event, $this, true);
803
            $this->eventDispatcher->dispatch($waitlistMoveUpEvent);
804
805
            // Move up waitlist registrations if configured on event basis and if not disabled by $waitlistMoveUpEvent
806
            if ($waitlistMoveUpEvent->getProcessDefaultMoveUp()) {
807
                $this->registrationService->moveUpWaitlistRegistrations($event, $this->settings);
808
            }
809
810
            // Flush page cache for event, since amount of registrations has changed
811
            $this->eventCacheService->flushEventCache($event->getUid(), $event->getPid());
0 ignored issues
show
Bug introduced by
It seems like $event->getPid() can also be of type null; however, parameter $eventPid of DERHANSEN\SfEventMgt\Ser...vice::flushEventCache() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

811
            $this->eventCacheService->flushEventCache($event->getUid(), /** @scrutinizer ignore-type */ $event->getPid());
Loading history...
Bug introduced by
It seems like $event->getUid() can also be of type null; however, parameter $eventUid of DERHANSEN\SfEventMgt\Ser...vice::flushEventCache() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

811
            $this->eventCacheService->flushEventCache(/** @scrutinizer ignore-type */ $event->getUid(), $event->getPid());
Loading history...
812
        }
813
814
        $modifyCancelRegistrationViewVariablesEvent = new ModifyCancelRegistrationViewVariablesEvent(
815
            [
816
                'failed' => $failed,
817
                'messageKey' => $messageKey,
818
                'titleKey' => $titleKey,
819
                'event' => $event,
820
            ],
821
            $this
822
        );
823
        $this->eventDispatcher->dispatch($modifyCancelRegistrationViewVariablesEvent);
824
        $variables = $modifyCancelRegistrationViewVariablesEvent->getVariables();
825
        $this->view->assignMultiple($variables);
826
827
        return $this->htmlResponse();
828
    }
829
830
    /**
831
     * Set date format for field startDate and endDate
832
     */
833
    public function initializeSearchAction()
834
    {
835
        if ($this->settings !== null && ($this->settings['search']['dateFormat'] ?? false)) {
836
            $this->arguments->getArgument('searchDemand')
837
                ->getPropertyMappingConfiguration()->forProperty('startDate')
838
                ->setTypeConverterOption(
839
                    DateTimeConverter::class,
840
                    DateTimeConverter::CONFIGURATION_DATE_FORMAT,
841
                    $this->settings['search']['dateFormat']
842
                );
843
            $this->arguments->getArgument('searchDemand')
844
                ->getPropertyMappingConfiguration()->forProperty('endDate')
845
                ->setTypeConverterOption(
846
                    DateTimeConverter::class,
847
                    DateTimeConverter::CONFIGURATION_DATE_FORMAT,
848
                    $this->settings['search']['dateFormat']
849
                );
850
        }
851
        if ($this->arguments->hasArgument('searchDemand')) {
852
            $propertyMappingConfiguration = $this->arguments->getArgument('searchDemand')
853
                ->getPropertyMappingConfiguration();
854
            $propertyMappingConfiguration->allowAllProperties();
855
            $propertyMappingConfiguration->setTypeConverterOption(
856
                PersistentObjectConverter::class,
857
                PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
858
                true
859
            );
860
        }
861
    }
862
863
    /**
864
     * Search view
865
     *
866
     * @param SearchDemand|null $searchDemand
867
     * @param array $overwriteDemand OverwriteDemand
868
     */
869
    public function searchAction(SearchDemand $searchDemand = null, array $overwriteDemand = []): ResponseInterface
870
    {
871
        $eventDemand = EventDemand::createFromSettings($this->settings);
872
        $eventDemand->setSearchDemand($searchDemand);
873
        $foreignRecordDemand = ForeignRecordDemand::createFromSettings($this->settings);
874
        $categoryDemand = CategoryDemand::createFromSettings($this->settings);
875
876
        if ($searchDemand !== null) {
877
            $searchDemand->setFields($this->settings['search']['fields'] ?? '');
878
879
            $adjustTime = (bool)($this->settings['search']['adjustTime'] ?? false);
880
            if ($adjustTime && $searchDemand->getStartDate() !== null) {
881
                $searchDemand->getStartDate()->setTime(0, 0);
882
            }
883
884
            if ($adjustTime && $searchDemand->getEndDate() !== null) {
885
                $searchDemand->getEndDate()->setTime(23, 59, 59);
886
            }
887
        }
888
889
        if ($this->isOverwriteDemand($overwriteDemand)) {
890
            $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand);
891
        }
892
893
        $categories = $this->categoryRepository->findDemanded($categoryDemand);
894
        $locations = $this->locationRepository->findDemanded($foreignRecordDemand);
895
        $organisators = $this->organisatorRepository->findDemanded($foreignRecordDemand);
896
        $speakers = $this->speakerRepository->findDemanded($foreignRecordDemand);
897
        $events = $this->eventRepository->findDemanded($eventDemand);
898
899
        $modifySearchViewVariablesEvent = new ModifySearchViewVariablesEvent(
900
            [
901
                'events' => $events,
902
                'categories' => $categories,
903
                'locations' => $locations,
904
                'organisators' => $organisators,
905
                'speakers' => $speakers,
906
                'searchDemand' => $searchDemand,
907
                'overwriteDemand' => $overwriteDemand,
908
            ],
909
            $this
910
        );
911
        $this->eventDispatcher->dispatch($modifySearchViewVariablesEvent);
912
        $variables = $modifySearchViewVariablesEvent->getVariables();
913
        $this->view->assignMultiple($variables);
914
915
        return $this->htmlResponse();
916
    }
917
918
    /**
919
     * Returns if a demand object can be overwritten with the given overwriteDemand array
920
     *
921
     * @param array $overwriteDemand
922
     * @return bool
923
     */
924
    protected function isOverwriteDemand(array $overwriteDemand): bool
925
    {
926
        return $this->settings['disableOverrideDemand'] !== 1 && $overwriteDemand !== [];
927
    }
928
929
    /**
930
     * If no event is given and the singleEvent setting is set, the configured single event is returned
931
     *
932
     * @param Event|null $event
933
     * @return Event|null
934
     */
935
    protected function evaluateSingleEventSetting(?Event $event): ?Event
936
    {
937
        if ($event === null && (int)($this->settings['singleEvent'] ?? 0) > 0) {
938
            $event = $this->eventRepository->findByUid((int)$this->settings['singleEvent']);
939
        }
940
941
        return $event;
942
    }
943
944
    /**
945
     * If no event is given and the isShortcut setting is set, the event is displayed using the "Insert Record"
946
     * content element and should be loaded from contect object data
947
     *
948
     * @param Event|null $event
949
     * @return Event|null
950
     */
951
    protected function evaluateIsShortcutSetting(?Event $event): ?Event
952
    {
953
        if ($event === null && (bool)($this->settings['detail']['isShortcut'] ?? false)) {
954
            $eventRawData = $this->configurationManager->getContentObject()->data;
955
            $event = $this->eventRepository->findByUid($eventRawData['uid']);
956
        }
957
958
        return $event;
959
    }
960
961
    /**
962
     * Checks if the event pid could be found in the storagePage settings of the detail plugin and
963
     * if the pid could not be found it return null instead of the event object.
964
     *
965
     * @param Event $event
966
     * @return Event|null
967
     */
968
    protected function checkPidOfEventRecord(Event $event): ?Event
969
    {
970
        $allowedStoragePages = GeneralUtility::trimExplode(
971
            ',',
972
            PageUtility::extendPidListByChildren(
973
                $this->settings['storagePage'] ?? '',
974
                $this->settings['recursive'] ?? 0
975
            ),
976
            true
977
        );
978
        if (count($allowedStoragePages) > 0 && !in_array($event->getPid(), $allowedStoragePages)) {
979
            $this->eventDispatcher->dispatch(new EventPidCheckFailedEvent($event, $this));
980
            $event = null;
981
        }
982
983
        return $event;
984
    }
985
986
    /**
987
     * Calls persistAll() of the persistenceManager
988
     */
989
    protected function persistAll(): void
990
    {
991
        GeneralUtility::makeInstance(PersistenceManager::class)->persistAll();
992
    }
993
994
    /**
995
     * Returns the current sys_language_uid
996
     *
997
     * @return int
998
     */
999
    protected function getSysLanguageUid(): int
1000
    {
1001
        $languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
1002
1003
        return $languageAspect->getId();
1004
    }
1005
1006
    /**
1007
     * Returns the two letter ISO code for the current language
1008
     *
1009
     * @return string
1010
     */
1011
    protected function getCurrentLanguageTwoLetterIsoCode(): string
1012
    {
1013
        if ($this->request->getAttribute('language') instanceof SiteLanguage) {
1014
            /** @var SiteLanguage $siteLanguage */
1015
            $siteLanguage = $this->request->getAttribute('language');
1016
            return $siteLanguage->getTwoLetterIsoCode();
1017
        }
1018
1019
        return '';
1020
    }
1021
}
1022