Passed
Push — master ( af9933...6d2375 )
by Torben
04:41 queued 01:21
created

EventController::saveRegistrationResultAction()   C

Complexity

Conditions 11
Paths 20

Size

Total Lines 60
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 11

Importance

Changes 0
Metric Value
cc 11
eloc 51
nc 20
nop 3
dl 0
loc 60
ccs 14
cts 14
cp 1
crap 11
rs 6.9224
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 2
        $variables = $modifyListViewVariablesEvent->getVariables();
138
        $this->view->assignMultiple($variables);
139
140 2
        $this->eventCacheService->addPageCacheTagsByEventDemandObject($eventDemand);
141 2
142 2
        return $this->htmlResponse();
143 2
    }
144 2
145 2
    /**
146 2
     * Calendar view
147 2
     *
148 2
     * @param array $overwriteDemand OverwriteDemand
149 2
     */
150 2
    public function calendarAction(array $overwriteDemand = []): ResponseInterface
151 2
    {
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 2
178
        $events = $this->eventRepository->findDemanded($eventDemand);
179
        $weeks = $this->calendarService->getCalendarArray(
180 2
            $currentMonth,
181 2
            $currentYear,
182 2
            strtotime('today midnight'),
183 2
            (int)($this->settings['calendar']['firstDayOfWeek'] ?? 1),
184 2
            $events
185 2
        );
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 4
                'currentPageId' => $this->getTypoScriptFrontendController()->id,
197
                'firstDayOfMonth' => \DateTime::createFromFormat(
198 4
                    'd.m.Y',
199 4
                    sprintf('1.%s.%s', $currentMonth, $currentYear)
200 4
                ),
201
                'previousMonthConfig' => $this->calendarService->getDateConfig($currentMonth, $currentYear, '-1 month'),
202 4
                'nextMonthConfig' => $this->calendarService->getDateConfig($currentMonth, $currentYear, '+1 month'),
203 4
            ],
204 2
            $this
205
        );
206 4
        $this->eventDispatcher->dispatch($modifyCalendarViewVariablesEvent);
207 4
        $variables = $modifyCalendarViewVariablesEvent->getVariables();
208 4
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 6
231
        $startDate = new \DateTime();
232 6
        $startDate->setTimestamp($calendarDateRange['firstDayOfCalendar']);
233 6
        $endDate = new \DateTime();
234 6
        $endDate->setTimestamp($calendarDateRange['lastDayOfCalendar']);
235 6
        $endDate->setTime(23, 59, 59);
236 2
237 2
        $searchDemand = GeneralUtility::makeInstance(SearchDemand::class);
238 6
        $searchDemand->setStartDate($startDate);
239 6
        $searchDemand->setEndDate($endDate);
240 6
        $eventDemand->setSearchDemand($searchDemand);
241 6
242 6
        return $eventDemand;
243 6
    }
244 6
245 6
    /**
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 2
        $event = $this->evaluateIsShortcutSetting($event);
255
        if (is_a($event, Event::class) && ($this->settings['detail']['checkPidOfEventRecord'] ?? false)) {
256 2
            $event = $this->checkPidOfEventRecord($event);
257 2
        }
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 2
267
        $this->view->assignMultiple($variables);
268 2
        if ($event !== null) {
269 2
            $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 2
     * @return ResponseInterface|void|null
280
     * @throws PropagateResponseException
281 2
     * @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
282
     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
283
     */
284 2
    protected function handleEventNotFoundError(array $settings)
285
    {
286 2
        if (empty($settings['event']['errorHandling'])) {
287 2
            return null;
288 2
        }
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 2
                $this->redirect('list', null, null, null, $listPid);
296
                break;
297 2
            case 'pageNotFoundHandler':
298 2
                $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
299 2
                    $this->request,
300 2
                    'Event not found.'
301 2
                );
302 2
                throw new PropagateResponseException($response, 1631261423);
303 2
            case 'showStandaloneTemplate':
304 2
                $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 22
    }
316
317 22
    /**
318 22
     * Initiates the iCalendar download for the given event
319 22
     *
320
     * @param Event|null $event The event
321
     *
322 22
     * @return mixed
323 8
     */
324 8
    public function icalDownloadAction(?Event $event = null)
325 8
    {
326 8
        if (is_a($event, Event::class) && ($this->settings['detail']['checkPidOfEventRecord'] ?? false)) {
327 8
            $event = $this->checkPidOfEventRecord($event);
328 8
        }
329
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
330 8
            return $this->handleEventNotFoundError($this->settings);
331 8
        }
332 8
        $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 8
        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 8
336 8
    /**
337 8
     * Registration view for an event
338 8
     *
339 8
     * @param Event|null $event Event
340 8
     *
341 8
     * @return mixed
342 8
     */
343
    public function registrationAction(?Event $event = null)
344
    {
345 8
        $event = $this->evaluateSingleEventSetting($event);
346
        if (is_a($event, Event::class) && ($this->settings['registration']['checkPidOfEventRecord'] ?? false)) {
347
            $event = $this->checkPidOfEventRecord($event);
348 8
        }
349 2
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
350 2
            return $this->handleEventNotFoundError($this->settings);
351 2
        }
352 6
        if ($event->getRestrictPaymentMethods()) {
353 6
            $paymentMethods = $this->paymentService->getRestrictedPaymentMethods($event);
354
        } else {
355 8
            $paymentMethods = $this->paymentService->getPaymentMethods();
356
        }
357
358 8
        $modifyRegistrationViewVariablesEvent = new ModifyRegistrationViewVariablesEvent(
359 6
            [
360 6
                'event' => $event,
361 6
                'paymentMethods' => $paymentMethods,
362 6
            ],
363
            $this
364 6
        );
365 6
        $this->eventDispatcher->dispatch($modifyRegistrationViewVariablesEvent);
366 6
        $variables = $modifyRegistrationViewVariablesEvent->getVariables();
367 6
        $this->view->assignMultiple($variables);
368 6
369
        return $this->htmlResponse();
370 6
    }
371 6
372
    /**
373
     * Removes all possible spamcheck fields (which do not belong to the domain model) from arguments.
374 8
     */
375 2
    protected function removePossibleSpamCheckFieldsFromArguments(): void
376 2
    {
377
        $arguments = $this->request->getArguments();
378
        if (!isset($arguments['event'])) {
379 8
            return;
380 8
        }
381
382 22
        // Remove a possible honeypot field
383 2
        $honeypotField = 'hp' . (int)$arguments['event'];
384 2
        if (isset($arguments['registration'][$honeypotField])) {
385 2
            unset($arguments['registration'][$honeypotField]);
386 2
        }
387
388 2
        // Remove a possible challenge/response field
389 2
        if (isset($arguments['registration']['cr-response'])) {
390 2
            unset($arguments['registration']['cr-response']);
391 2
        }
392 2
393 20
        $this->request->setArguments($arguments);
394 20
    }
395 20
396 20
    /**
397 20
     * Processes incoming registrations fields and adds field values to arguments
398 20
     */
399
    protected function setRegistrationFieldValuesToArguments(): void
400 22
    {
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)) {
409 18
            return;
410
        }
411
412 18
        $registrationMvcArgument = $this->arguments->getArgument('registration');
413 2
        $propertyMapping = $registrationMvcArgument->getPropertyMappingConfiguration();
414 2
        $propertyMapping->allowProperties('fieldValues');
415 2
        $propertyMapping->allowCreationForSubProperty('fieldValues');
416 16
        $propertyMapping->allowModificationForSubProperty('fieldValues');
417
418
        // Set event to registration (required for validation)
419
        $propertyMapping->allowProperties('event');
420 16
        $propertyMapping->allowCreationForSubProperty('event');
421 2
        $propertyMapping->allowModificationForSubProperty('event');
422 2
        $arguments['registration']['event'] = (int)$this->request->getArgument('event');
423 2
424 14
        if ($event->getRegistrationFields()->count() === 0) {
425 2
            // Set arguments to request, so event is set for event
426 2
            $this->request->setArguments($arguments);
427 2
            return;
428 12
        }
429 2
430 2
        // allow creation of new objects (for validation)
431 2
        $propertyMapping->setTypeConverterOptions(
432 10
            PersistentObjectConverter::class,
433 2
            [
434 2
                PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => true,
435 2
                PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED => true,
436 8
            ]
437 2
        );
438 2
439 2
        $index = 0;
440 6
        foreach ((array)$arguments['registration']['fields'] as $fieldUid => $value) {
441 2
            // Only accept registration fields of the current event
442 2
            if (!in_array((int)$fieldUid, $event->getRegistrationFieldsUids(), true)) {
443 2
                continue;
444 4
            }
445 2
446 2
            // allow subvalues in new property mapper
447 2
            $propertyMapping->forProperty('fieldValues')->allowProperties($index);
448 2
            $propertyMapping->forProperty('fieldValues.' . $index)->allowAllProperties();
449 2
            $propertyMapping->allowCreationForSubProperty('fieldValues.' . $index);
450 2
            $propertyMapping->allowModificationForSubProperty('fieldValues.' . $index);
451 2
452
            if (is_array($value)) {
453 18
                if (empty($value)) {
454 18
                    $value = '';
455 18
                } else {
456
                    $value = json_encode($value);
457
                }
458
            }
459
460
            /** @var Registration\Field $field */
461
            $field = $this->fieldRepository->findByUid((int)$fieldUid);
462
463
            $arguments['registration']['fieldValues'][$index] = [
464
                'pid' => $field->getPid(),
465 6
                'value' => $value,
466
                'field' => (string)$fieldUid,
467
                'valueType' => $field->getValueType(),
468 6
            ];
469
470 6
            $index++;
471 4
        }
472 4
473
        // Remove temporary "fields" field
474 4
        if (isset($arguments['registration']['fields'])) {
475 4
            $arguments = ArrayUtility::removeByPath($arguments, 'registration/fields');
476 2
        }
477 2
        $this->request->setArguments($arguments);
478
    }
479
480 4
    /**
481 4
     * Set date format for field dateOfBirth
482 4
     */
483 4
    public function initializeSaveRegistrationAction()
484
    {
485 4
        $this->arguments->getArgument('registration')
486 4
            ->getPropertyMappingConfiguration()->forProperty('dateOfBirth')
487 4
            ->setTypeConverterOption(
488 4
                DateTimeConverter::class,
489 4
                DateTimeConverter::CONFIGURATION_DATE_FORMAT,
490
                $this->settings['registration']['formatDateOfBirth'] ?? 'd.m.Y'
491 4
            );
492
        $this->removePossibleSpamCheckFieldsFromArguments();
493
        $this->setRegistrationFieldValuesToArguments();
494 4
    }
495 4
496 4
    /**
497 4
     * Saves the registration
498
     *
499
     * @param Registration $registration Registration
500 6
     * @param Event $event Event
501 6
     * @Extbase\Validate("DERHANSEN\SfEventMgt\Validation\Validator\RegistrationFieldValidator", param="registration")
502
     * @Extbase\Validate("DERHANSEN\SfEventMgt\Validation\Validator\RegistrationValidator", param="registration")
503
     *
504
     * @return mixed|void
505
     */
506
    public function saveRegistrationAction(Registration $registration, Event $event)
507
    {
508
        if (is_a($event, Event::class) && ($this->settings['registration']['checkPidOfEventRecord'] ?? false)) {
509
            $event = $this->checkPidOfEventRecord($event);
510
        }
511
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
512
            return $this->handleEventNotFoundError($this->settings);
513
        }
514
        $autoConfirmation = (bool)($this->settings['registration']['autoConfirmation'] ?? false) ||
515
            $event->getEnableAutoconfirm();
516
        $result = RegistrationResult::REGISTRATION_SUCCESSFUL;
517
        list($success, $result) = $this->registrationService->checkRegistrationSuccess($event, $registration, $result);
518 6
519 6
        // Save registration if no errors
520 6
        if ($success) {
521
            $isWaitlistRegistration = $this->registrationService->isWaitlistRegistration(
522
                $event,
523
                $registration->getAmountOfRegistrations()
524
            );
525
            $linkValidity = (int)($this->settings['confirmation']['linkValidity'] ?? 3600);
526
            if ($linkValidity === 0) {
527
                // Use 3600 seconds as default value if not set or zero
528
                $linkValidity = 3600;
529
            }
530 4
            $confirmationUntil = new \DateTime();
531
            $confirmationUntil->add(new \DateInterval('PT' . $linkValidity . 'S'));
532
533 4
            $registration->setEvent($event);
534
            $registration->setPid($event->getPid());
535 4
            $registration->setRegistrationDate(new \DateTime());
536
            $registration->setConfirmationUntil($confirmationUntil);
537 2
            $registration->setLanguage($this->getCurrentLanguageTwoLetterIsoCode());
538 2
            $registration->setFeUser($this->registrationService->getCurrentFeUserObject());
539 2
            $registration->setWaitlist($isWaitlistRegistration);
540 2
            $this->registrationRepository->add($registration);
541
542 2
            // Persist registration, so we have an UID
543 2
            $this->persistAll();
544 2
545 2
            if ($isWaitlistRegistration) {
546 2
                $messageType = MessageType::REGISTRATION_WAITLIST_NEW;
547
            } else {
548 2
                $messageType = MessageType::REGISTRATION_NEW;
549
            }
550
551 2
            $this->eventDispatcher->dispatch(new AfterRegistrationSavedEvent($registration, $this));
552 2
553 2
            // Send notifications to user and admin if confirmation link should be sent
554
            if (!$autoConfirmation) {
555
                $this->notificationService->sendUserMessage(
556 2
                    $event,
557
                    $registration,
558
                    $this->settings ?? [],
559 2
                    $messageType
560 2
                );
561 4
                $this->notificationService->sendAdminMessage(
562 4
                    $event,
563 4
                    $registration,
564
                    $this->settings ?? [],
565
                    $messageType
566
                );
567
            }
568
569
            // Create given amount of registrations if necessary
570 2
            $modifyCreateDependingRegistrationsEvent = new ModifyCreateDependingRegistrationsEvent(
571
                $registration,
572 2
                ($registration->getAmountOfRegistrations() > 1),
573 2
                $this
574 2
            );
575 2
            $this->eventDispatcher->dispatch($modifyCreateDependingRegistrationsEvent);
576 2
            $createDependingRegistrations = $modifyCreateDependingRegistrationsEvent->getCreateDependingRegistrations();
577 2
            if ($createDependingRegistrations) {
578 2
                $this->registrationService->createDependingRegistrations($registration);
579 2
            }
580 2
581 2
            // Flush page cache for event, since new registration has been added
582 2
            $this->eventCacheService->flushEventCache($event->getUid(), $event->getPid());
583 2
        }
584 2
585 2
        if ($autoConfirmation && $success) {
586 2
            $this->redirect(
587 2
                'confirmRegistration',
588 2
                null,
589
                null,
590
                [
591
                    'reguid' => $registration->getUid(),
592
                    'hmac' => $this->hashService->generateHmac('reg-' . $registration->getUid()),
593
                ]
594
            );
595
        } else {
596
            $this->redirect(
597
                'saveRegistrationResult',
598 12
                null,
599
                null,
600 12
                [
601 12
                    'result' => $result,
602 12
                    'eventuid' => $event->getUid(),
603 12
                    'hmac' => $this->hashService->generateHmac('event-' . $event->getUid()),
604
                ]
605 12
            );
606 10
        }
607
    }
608 10
609 2
    /**
610 2
     * Shows the result of the saveRegistrationAction
611
     *
612 10
     * @param int $result Result
613 2
     * @param int $eventuid
614 2
     * @param string $hmac
615 10
     * @return ResponseInterface
616
     */
617 12
    public function saveRegistrationResultAction(int $result, int $eventuid, string $hmac): ResponseInterface
618 2
    {
619 2
        $event = null;
620
621 12
        switch ($result) {
622 12
            case RegistrationResult::REGISTRATION_SUCCESSFUL:
623
                $messageKey = 'event.message.registrationsuccessful';
624 12
                $titleKey = 'registrationResult.title.successful';
625
                break;
626 12
            case RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST:
627 12
                $messageKey = 'event.message.registrationwaitlistsuccessful';
628 12
                $titleKey = 'registrationWaitlistResult.title.successful';
629 12
                break;
630 12
            case RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED:
631 12
                $messageKey = 'event.message.registrationfailedeventexpired';
632
                $titleKey = 'registrationResult.title.failed';
633
                break;
634
            case RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS:
635
                $messageKey = 'event.message.registrationfailedmaxparticipants';
636
                $titleKey = 'registrationResult.title.failed';
637
                break;
638
            case RegistrationResult::REGISTRATION_NOT_ENABLED:
639 18
                $messageKey = 'event.message.registrationfailednotenabled';
640
                $titleKey = 'registrationResult.title.failed';
641 18
                break;
642
            case RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED:
643
                $messageKey = 'event.message.registrationfaileddeadlineexpired';
644
                $titleKey = 'registrationResult.title.failed';
645
                break;
646
            case RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES:
647
                $messageKey = 'event.message.registrationfailednotenoughfreeplaces';
648
                $titleKey = 'registrationResult.title.failed';
649
                break;
650
            case RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED:
651
                $messageKey = 'event.message.registrationfailedmaxamountregistrationsexceeded';
652
                $titleKey = 'registrationResult.title.failed';
653
                break;
654
            case RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE:
655
                $messageKey = 'event.message.registrationfailedemailnotunique';
656
                $titleKey = 'registrationResult.title.failed';
657
                break;
658
            default:
659
                $messageKey = '';
660
                $titleKey = '';
661
        }
662
663
        if (!$this->hashService->validateHmac('event-' . $eventuid, $hmac)) {
664
            $messageKey = 'event.message.registrationsuccessfulwrongeventhmac';
665
            $titleKey = 'registrationResult.title.failed';
666
        } else {
667
            $event = $this->eventRepository->findByUid((int)$eventuid);
668
        }
669
670
        $this->view->assignMultiple([
671
            'messageKey' => $messageKey,
672
            'titleKey' => $titleKey,
673
            'event' => $event,
674
        ]);
675
676
        return $this->htmlResponse();
677
    }
678
679
    /**
680
     * Confirms the registration if possible and sends emails to admin and user
681
     *
682
     * @param int $reguid UID of registration
683
     * @param string $hmac HMAC for parameters
684
     * @return mixed
685
     */
686
    public function confirmRegistrationAction(int $reguid, string $hmac)
687
    {
688
        $event = null;
689
690
        /* @var $registration Registration */
691
        list($failed, $registration, $messageKey, $titleKey) = $this->registrationService->checkConfirmRegistration(
692
            $reguid,
693
            $hmac
694
        );
695
696
        if ($failed === false) {
697
            $registration->setConfirmed(true);
698
            $event = $registration->getEvent();
699
            $this->registrationRepository->update($registration);
700
701
            $this->eventDispatcher->dispatch(new AfterRegistrationConfirmedEvent($registration, $this));
702
703
            $messageType = MessageType::REGISTRATION_CONFIRMED;
704
            if ($registration->getWaitlist()) {
705
                $messageType = MessageType::REGISTRATION_WAITLIST_CONFIRMED;
706
            }
707
708
            // Send notifications to user and admin
709
            $this->notificationService->sendUserMessage(
710
                $registration->getEvent(),
0 ignored issues
show
Bug introduced by
It seems like $registration->getEvent() can also be of type null; however, parameter $event of DERHANSEN\SfEventMgt\Ser...vice::sendUserMessage() 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

710
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
711
                $registration,
712
                $this->settings ?? [],
713
                $messageType
714
            );
715
            $this->notificationService->sendAdminMessage(
716
                $registration->getEvent(),
0 ignored issues
show
Bug introduced by
It seems like $registration->getEvent() can also be of type null; however, parameter $event of DERHANSEN\SfEventMgt\Ser...ice::sendAdminMessage() 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

716
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
717
                $registration,
718
                $this->settings ?? [],
719
                $messageType
720
            );
721
722
            // Confirm registrations depending on main registration if necessary
723
            if ($registration->getAmountOfRegistrations() > 1) {
724
                $this->registrationService->confirmDependingRegistrations($registration);
725
            }
726
        }
727
728
        // Redirect to payment provider if payment/redirect is enabled
729
        $paymentPid = (int)($this->settings['paymentPid'] ?? 0);
730
        if (!$failed && $paymentPid > 0 && $this->registrationService->redirectPaymentEnabled($registration)) {
731
            $this->uriBuilder->reset()
732
                ->setTargetPageUid($paymentPid);
733
            $uri = $this->uriBuilder->uriFor(
734
                'redirect',
735
                [
736
                    'registration' => $registration,
737
                    'hmac' => $this->hashService->generateHmac('redirectAction-' . $registration->getUid()),
738
                ],
739
                'Payment',
740
                'sfeventmgt',
741
                'Pipayment'
742
            );
743
            $this->redirectToUri($uri);
744
        }
745
746
        $modifyConfirmRegistrationViewVariablesEvent = new ModifyConfirmRegistrationViewVariablesEvent(
747
            [
748
                'failed' => $failed,
749
                'messageKey' => $messageKey,
750
                'titleKey' => $titleKey,
751
                'event' => $event,
752
                'registration' => $registration,
753
            ],
754
            $this
755
        );
756
        $this->eventDispatcher->dispatch($modifyConfirmRegistrationViewVariablesEvent);
757
        $variables = $modifyConfirmRegistrationViewVariablesEvent->getVariables();
758
        $this->view->assignMultiple($variables);
759
760
        return $this->htmlResponse();
761
    }
762
763
    /**
764
     * Cancels the registration if possible and sends emails to admin and user
765
     *
766
     * @param int $reguid UID of registration
767
     * @param string $hmac HMAC for parameters
768
     */
769
    public function cancelRegistrationAction(int $reguid, string $hmac): ResponseInterface
770
    {
771
        $event = null;
772
773
        /* @var $registration Registration */
774
        list($failed, $registration, $messageKey, $titleKey) =
775
            $this->registrationService->checkCancelRegistration($reguid, $hmac);
776
777
        if ($failed === false) {
778
            $event = $registration->getEvent();
779
780
            // Send notifications (must run before cancelling the registration)
781
            $this->notificationService->sendUserMessage(
782
                $registration->getEvent(),
0 ignored issues
show
Bug introduced by
It seems like $registration->getEvent() can also be of type null; however, parameter $event of DERHANSEN\SfEventMgt\Ser...vice::sendUserMessage() 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

782
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
783
                $registration,
784
                $this->settings,
785
                MessageType::REGISTRATION_CANCELLED
786
            );
787
            $this->notificationService->sendAdminMessage(
788
                $registration->getEvent(),
0 ignored issues
show
Bug introduced by
It seems like $registration->getEvent() can also be of type null; however, parameter $event of DERHANSEN\SfEventMgt\Ser...ice::sendAdminMessage() 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

788
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
789
                $registration,
790
                $this->settings,
791
                MessageType::REGISTRATION_CANCELLED
792
            );
793
794
            // First cancel depending registrations
795
            if ($registration->getAmountOfRegistrations() > 1) {
796
                $this->registrationService->cancelDependingRegistrations($registration);
797
            }
798
799
            // Finally cancel registration
800
            $this->registrationRepository->remove($registration);
801
802
            // Persist changes, so following functions can work with $event properties (e.g. amount of registrations)
803
            $this->persistAll();
804
805
            $afterRegistrationCancelledEvent = new AfterRegistrationCancelledEvent($registration, $this);
806
            $this->eventDispatcher->dispatch($afterRegistrationCancelledEvent);
807
808
            // Dispatch event, so waitlist registrations can be moved up and default move up process can be stopped
809
            $waitlistMoveUpEvent = new WaitlistMoveUpEvent($event, $this, true);
0 ignored issues
show
Bug introduced by
It seems like $event can also be of type null; however, parameter $event of DERHANSEN\SfEventMgt\Eve...eUpEvent::__construct() 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

809
            $waitlistMoveUpEvent = new WaitlistMoveUpEvent(/** @scrutinizer ignore-type */ $event, $this, true);
Loading history...
810
            $this->eventDispatcher->dispatch($waitlistMoveUpEvent);
811
812
            // Move up waitlist registrations if configured on event basis and if not disabled by $waitlistMoveUpEvent
813
            if ($waitlistMoveUpEvent->getProcessDefaultMoveUp()) {
814
                $this->registrationService->moveUpWaitlistRegistrations($event, $this->settings);
0 ignored issues
show
Bug introduced by
It seems like $event can also be of type null; however, parameter $event of DERHANSEN\SfEventMgt\Ser...WaitlistRegistrations() 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

814
                $this->registrationService->moveUpWaitlistRegistrations(/** @scrutinizer ignore-type */ $event, $this->settings);
Loading history...
815
            }
816
817
            // Flush page cache for event, since amount of registrations has changed
818
            $this->eventCacheService->flushEventCache($event->getUid(), $event->getPid());
0 ignored issues
show
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

818
            $this->eventCacheService->flushEventCache(/** @scrutinizer ignore-type */ $event->getUid(), $event->getPid());
Loading history...
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

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