Passed
Push — develop ( 39554c...1633c4 )
by Torben
74:35 queued 29:26
created

EventController::getCurrentLanguageCode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 9
rs 10
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 DateInterval;
15
use DateTime;
16
use DERHANSEN\SfEventMgt\Domain\Model\Dto\CategoryDemand;
17
use DERHANSEN\SfEventMgt\Domain\Model\Dto\EventDemand;
18
use DERHANSEN\SfEventMgt\Domain\Model\Dto\ForeignRecordDemand;
19
use DERHANSEN\SfEventMgt\Domain\Model\Dto\SearchDemand;
20
use DERHANSEN\SfEventMgt\Domain\Model\Event;
21
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
22
use DERHANSEN\SfEventMgt\Event\AfterRegistrationCancelledEvent;
23
use DERHANSEN\SfEventMgt\Event\AfterRegistrationConfirmedEvent;
24
use DERHANSEN\SfEventMgt\Event\AfterRegistrationSavedEvent;
25
use DERHANSEN\SfEventMgt\Event\EventPidCheckFailedEvent;
26
use DERHANSEN\SfEventMgt\Event\ModifyCalendarViewVariablesEvent;
27
use DERHANSEN\SfEventMgt\Event\ModifyCancelRegistrationViewVariablesEvent;
28
use DERHANSEN\SfEventMgt\Event\ModifyConfirmRegistrationViewVariablesEvent;
29
use DERHANSEN\SfEventMgt\Event\ModifyCreateDependingRegistrationsEvent;
30
use DERHANSEN\SfEventMgt\Event\ModifyDetailViewVariablesEvent;
31
use DERHANSEN\SfEventMgt\Event\ModifyListViewVariablesEvent;
32
use DERHANSEN\SfEventMgt\Event\ModifyRegistrationViewVariablesEvent;
33
use DERHANSEN\SfEventMgt\Event\ModifySearchViewVariablesEvent;
34
use DERHANSEN\SfEventMgt\Event\ProcessRedirectToPaymentEvent;
35
use DERHANSEN\SfEventMgt\Event\WaitlistMoveUpEvent;
36
use DERHANSEN\SfEventMgt\Exception;
37
use DERHANSEN\SfEventMgt\Service\EventCacheService;
38
use DERHANSEN\SfEventMgt\Utility\MessageType;
39
use DERHANSEN\SfEventMgt\Utility\PageUtility;
40
use DERHANSEN\SfEventMgt\Utility\RegistrationResult;
41
use Psr\Http\Message\ResponseInterface;
42
use TYPO3\CMS\Core\Context\Context;
43
use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
44
use TYPO3\CMS\Core\Http\PropagateResponseException;
45
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
46
use TYPO3\CMS\Core\Utility\ArrayUtility;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Core\Utility\ArrayUtility was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
47
use TYPO3\CMS\Core\Utility\GeneralUtility;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Core\Utility\GeneralUtility was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
48
use TYPO3\CMS\Extbase\Annotation as Extbase;
49
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
50
use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter;
51
use TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter;
52
use TYPO3\CMS\Fluid\View\StandaloneView;
53
use TYPO3\CMS\Frontend\Controller\ErrorController;
54
55
class EventController extends AbstractController
56
{
57
    protected EventCacheService $eventCacheService;
58
59
    public function injectEventCacheService(EventCacheService $cacheService): void
60
    {
61
        $this->eventCacheService = $cacheService;
62
    }
63
64
    /**
65
     * Assign contentObjectData and pageData view
66
     */
67
    protected function initializeView(): void
68
    {
69
        $this->view->assign('contentObjectData', $this->configurationManager->getContentObject()->data);
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Config...ace::getContentObject() has been deprecated: since v12. Remove in v13. ( Ignorable by Annotation )

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

69
        $this->view->assign('contentObjectData', /** @scrutinizer ignore-deprecated */ $this->configurationManager->getContentObject()->data);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
70
        if ($this->getTypoScriptFrontendController()) {
71
            $this->view->assign('pageData', $this->getTypoScriptFrontendController()->page);
72
        }
73
    }
74
75
    /**
76
     * Initializes the current action
77
     */
78
    public function initializeAction(): void
79
    {
80
        $typoScriptFrontendController = $this->getTypoScriptFrontendController();
81
        if ($typoScriptFrontendController !== null) {
82
            static $cacheTagsSet = false;
83
84
            if (!$cacheTagsSet) {
85
                $typoScriptFrontendController->addCacheTags(['tx_sfeventmgt']);
86
                $cacheTagsSet = true;
87
            }
88
        }
89
    }
90
91
    /**
92
     * Initialize list action and set format
93
     */
94
    public function initializeListAction(): void
95
    {
96
        if (isset($this->settings['list']['format'])) {
97
            $this->request = $this->request->withFormat($this->settings['list']['format']);
98
        }
99
    }
100
101
    /**
102
     * List view
103
     */
104
    public function listAction(array $overwriteDemand = []): ResponseInterface
105
    {
106
        $eventDemand = EventDemand::createFromSettings($this->settings);
107
        $foreignRecordDemand = ForeignRecordDemand::createFromSettings($this->settings);
108
        $categoryDemand = CategoryDemand::createFromSettings($this->settings);
109
        if ($this->isOverwriteDemand($overwriteDemand)) {
110
            $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand);
111
        }
112
        $events = $this->eventRepository->findDemanded($eventDemand);
113
        $categories = $this->categoryRepository->findDemanded($categoryDemand);
114
        $locations = $this->locationRepository->findDemanded($foreignRecordDemand);
115
        $organisators = $this->organisatorRepository->findDemanded($foreignRecordDemand);
116
        $speakers = $this->speakerRepository->findDemanded($foreignRecordDemand);
117
118
        $modifyListViewVariablesEvent = new ModifyListViewVariablesEvent(
119
            [
120
                'events' => $events,
121
                'pagination' => $this->getPagination($events, $this->settings['pagination'] ?? []),
122
                'categories' => $categories,
123
                'locations' => $locations,
124
                'organisators' => $organisators,
125
                'speakers' => $speakers,
126
                'overwriteDemand' => $overwriteDemand,
127
                'eventDemand' => $eventDemand,
128
                'settings' => $this->settings,
129
            ],
130
            $this
131
        );
132
        $this->eventDispatcher->dispatch($modifyListViewVariablesEvent);
133
        $variables = $modifyListViewVariablesEvent->getVariables();
134
        $this->view->assignMultiple($variables);
135
136
        $this->eventCacheService->addPageCacheTagsByEventDemandObject($eventDemand);
137
138
        return $this->htmlResponse();
139
    }
140
141
    /**
142
     * Calendar view
143
     */
144
    public function calendarAction(array $overwriteDemand = []): ResponseInterface
145
    {
146
        $eventDemand = EventDemand::createFromSettings($this->settings);
147
        $foreignRecordDemand = ForeignRecordDemand::createFromSettings($this->settings);
148
        $categoryDemand = CategoryDemand::createFromSettings($this->settings);
149
        if ($this->isOverwriteDemand($overwriteDemand)) {
150
            $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand);
151
        }
152
153
        // Set month/year to demand if not given
154
        if (!$eventDemand->getMonth()) {
155
            $currentMonth = (int)date('n');
156
            $eventDemand->setMonth($currentMonth);
157
        } else {
158
            $currentMonth = $eventDemand->getMonth();
159
        }
160
        if (!$eventDemand->getYear()) {
161
            $currentYear = (int)date('Y');
162
            $eventDemand->setYear($currentYear);
163
        } else {
164
            $currentYear = $eventDemand->getYear();
165
        }
166
167
        // If a weeknumber is given in overwriteDemand['week'], we overwrite the current month
168
        if ($overwriteDemand['week'] ?? false) {
169
            $firstDayOfWeek = (new DateTime())->setISODate($currentYear, (int)$overwriteDemand['week']);
170
            $currentMonth = (int)$firstDayOfWeek->format('m');
171
            $eventDemand->setMonth($currentMonth);
172
        } else {
173
            $firstDayOfWeek = (new DateTime())->setISODate($currentYear, (int)date('W'));
174
        }
175
176
        // Set demand from calendar date range instead of month / year
177
        if ((bool)($this->settings['calendar']['includeEventsForEveryDayOfAllCalendarWeeks'] ?? false)) {
178
            $eventDemand = $this->changeEventDemandToFullMonthDateRange($eventDemand);
179
        }
180
181
        $events = $this->eventRepository->findDemanded($eventDemand);
182
        $weeks = $this->calendarService->getCalendarArray(
183
            $currentMonth,
184
            $currentYear,
185
            strtotime('today midnight'),
186
            (int)($this->settings['calendar']['firstDayOfWeek'] ?? 1),
187
            $events
188
        );
189
190
        $modifyCalendarViewVariablesEvent = new ModifyCalendarViewVariablesEvent(
191
            [
192
                'events' => $events,
193
                'weeks' => $weeks,
194
                'categories' => $this->categoryRepository->findDemanded($categoryDemand),
195
                'locations' => $this->locationRepository->findDemanded($foreignRecordDemand),
196
                'organisators' => $this->organisatorRepository->findDemanded($foreignRecordDemand),
197
                'eventDemand' => $eventDemand,
198
                'overwriteDemand' => $overwriteDemand,
199
                'currentPageId' => $this->getTypoScriptFrontendController()->id,
200
                'firstDayOfMonth' => DateTime::createFromFormat(
201
                    'd.m.Y',
202
                    sprintf('1.%s.%s', $currentMonth, $currentYear)
203
                ),
204
                'previousMonthConfig' => $this->calendarService->getDateConfig($currentMonth, $currentYear, '-1 month'),
205
                'nextMonthConfig' => $this->calendarService->getDateConfig($currentMonth, $currentYear, '+1 month'),
206
                'weekConfig' => $this->calendarService->getWeekConfig($firstDayOfWeek),
207
                'settings' => $this->settings,
208
            ],
209
            $this
210
        );
211
        $this->eventDispatcher->dispatch($modifyCalendarViewVariablesEvent);
212
        $variables = $modifyCalendarViewVariablesEvent->getVariables();
213
214
        $this->view->assignMultiple($variables);
215
        return $this->htmlResponse();
216
    }
217
218
    /**
219
     * Changes the given event demand object to select a date range for a calendar month including days of the previous
220
     * month for the first week and they days for the next month for the last week
221
     */
222
    protected function changeEventDemandToFullMonthDateRange(EventDemand $eventDemand): EventDemand
223
    {
224
        $calendarDateRange = $this->calendarService->getCalendarDateRange(
225
            $eventDemand->getMonth(),
226
            $eventDemand->getYear(),
227
            (int)($this->settings['calendar']['firstDayOfWeek'] ?? 0)
228
        );
229
230
        $eventDemand->setMonth(0);
231
        $eventDemand->setYear(0);
232
233
        $startDate = new DateTime();
234
        $startDate->setTimestamp($calendarDateRange['firstDayOfCalendar']);
235
        $endDate = new DateTime();
236
        $endDate->setTimestamp($calendarDateRange['lastDayOfCalendar']);
237
        $endDate->setTime(23, 59, 59);
238
239
        $searchDemand = GeneralUtility::makeInstance(SearchDemand::class);
240
        $searchDemand->setStartDate($startDate);
241
        $searchDemand->setEndDate($endDate);
242
        $eventDemand->setSearchDemand($searchDemand);
243
244
        return $eventDemand;
245
    }
246
247
    /**
248
     * Detail view for an event
249
     *
250
     * @return mixed
251
     */
252
    public function detailAction(?Event $event = null)
253
    {
254
        $event = $this->evaluateSingleEventSetting($event);
255
        $event = $this->evaluateIsShortcutSetting($event);
256
        if (is_a($event, Event::class) && ($this->settings['detail']['checkPidOfEventRecord'] ?? false)) {
257
            $event = $this->checkPidOfEventRecord($event);
258
        }
259
260
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
261
            return $this->handleEventNotFoundError($this->settings);
262
        }
263
264
        $modifyDetailViewVariablesEvent = new ModifyDetailViewVariablesEvent(['event' => $event, 'settings' => $this->settings], $this);
265
        $this->eventDispatcher->dispatch($modifyDetailViewVariablesEvent);
266
        $variables = $modifyDetailViewVariablesEvent->getVariables();
267
268
        $this->view->assignMultiple($variables);
269
        if ($event !== null) {
270
            $this->eventCacheService->addCacheTagsByEventRecords([$event]);
271
        }
272
273
        return $this->htmlResponse();
274
    }
275
276
    /**
277
     * Error handling if event is not found
278
     *
279
     * @param array $settings
280
     * @return ResponseInterface
281
     * @throws Exception
282
     * @throws PropagateResponseException
283
     * @throws PageNotFoundException
284
     */
285
    protected function handleEventNotFoundError(array $settings): ResponseInterface
286
    {
287
        if (empty($settings['event']['errorHandling'])) {
288
            throw new Exception('Event errorHandling not configured. Please check settings.event.errorHandling', 1671205677);
289
        }
290
291
        $configuration = GeneralUtility::trimExplode(',', $settings['event']['errorHandling'], true);
292
293
        switch ($configuration[0]) {
294
            case 'redirectToListView':
295
                $listPid = (int)($settings['listPid'] ?? 0) > 0 ? (int)$settings['listPid'] : 1;
296
                return $this->redirect('list', null, null, null, $listPid);
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
            default:
305
                $status = (int)($configuration[2] ?? 200);
306
                $standaloneTemplate = GeneralUtility::makeInstance(StandaloneView::class);
307
                $standaloneTemplate->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($configuration[1]));
308
309
                $response = $this->responseFactory->createResponse()
310
                    ->withStatus($status)
311
                    ->withHeader('Content-Type', 'text/html; charset=utf-8');
312
                $response->getBody()->write($standaloneTemplate->render());
313
                return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
314
        }
315
    }
316
317
    /**
318
     * Initiates the iCalendar download for the given event
319
     *
320
     * @return mixed
321
     */
322
    public function icalDownloadAction(?Event $event = null)
323
    {
324
        if (is_a($event, Event::class) && ($this->settings['detail']['checkPidOfEventRecord'] ?? false)) {
325
            $event = $this->checkPidOfEventRecord($event);
326
        }
327
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
328
            return $this->handleEventNotFoundError($this->settings);
329
        }
330
        $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

330
        $this->icalendarService->downloadiCalendarFile(/** @scrutinizer ignore-type */ $event);
Loading history...
331
        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...
332
    }
333
334
    /**
335
     * Registration view for an event
336
     */
337
    public function registrationAction(?Event $event = null): ResponseInterface
338
    {
339
        $event = $this->evaluateSingleEventSetting($event);
340
        if (is_a($event, Event::class) && ($this->settings['registration']['checkPidOfEventRecord'] ?? false)) {
341
            $event = $this->checkPidOfEventRecord($event);
342
        }
343
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
344
            return $this->handleEventNotFoundError($this->settings);
345
        }
346
        if ($event->getRestrictPaymentMethods()) {
347
            $paymentMethods = $this->paymentService->getRestrictedPaymentMethods($event);
348
        } else {
349
            $paymentMethods = $this->paymentService->getPaymentMethods();
350
        }
351
352
        $modifyRegistrationViewVariablesEvent = new ModifyRegistrationViewVariablesEvent(
353
            [
354
                'event' => $event,
355
                'paymentMethods' => $paymentMethods,
356
                'settings' => $this->settings,
357
            ],
358
            $this
359
        );
360
        $this->eventDispatcher->dispatch($modifyRegistrationViewVariablesEvent);
361
        $variables = $modifyRegistrationViewVariablesEvent->getVariables();
362
        $this->view->assignMultiple($variables);
363
364
        return $this->htmlResponse();
365
    }
366
367
    /**
368
     * Removes all possible spamcheck fields (which do not belong to the domain model) from arguments.
369
     */
370
    protected function removePossibleSpamCheckFieldsFromArguments(): void
371
    {
372
        $arguments = $this->request->getArguments();
373
        if (!isset($arguments['event'])) {
374
            return;
375
        }
376
377
        // Remove a possible honeypot field
378
        $honeypotField = 'hp' . (int)$arguments['event'];
379
        if (isset($arguments['registration'][$honeypotField])) {
380
            unset($arguments['registration'][$honeypotField]);
381
        }
382
383
        // Remove a possible challenge/response field
384
        if (isset($arguments['registration']['cr-response'])) {
385
            unset($arguments['registration']['cr-response']);
386
        }
387
388
        $this->request = $this->request->withArguments($arguments);
389
    }
390
391
    /**
392
     * Processes incoming registrations fields and adds field values to arguments
393
     */
394
    protected function setRegistrationFieldValuesToArguments(): void
395
    {
396
        $arguments = $this->request->getArguments();
397
        if (!isset($arguments['event'])) {
398
            return;
399
        }
400
401
        /** @var Event $event */
402
        $event = $this->eventRepository->findByUid((int)$this->request->getArgument('event'));
403
        if (!is_a($event, Event::class)) {
404
            return;
405
        }
406
407
        $registrationMvcArgument = $this->arguments->getArgument('registration');
408
        $propertyMapping = $registrationMvcArgument->getPropertyMappingConfiguration();
409
        $propertyMapping->allowProperties('fieldValues');
410
        $propertyMapping->allowCreationForSubProperty('fieldValues');
411
        $propertyMapping->allowModificationForSubProperty('fieldValues');
412
413
        // Set event to registration (required for validation)
414
        $propertyMapping->allowProperties('event');
415
        $propertyMapping->allowCreationForSubProperty('event');
416
        $propertyMapping->allowModificationForSubProperty('event');
417
        $arguments['registration']['event'] = (int)$this->request->getArgument('event');
418
419
        if (count($event->getRegistrationFieldsUids()) === 0) {
420
            // Set arguments to request, so event is set for event
421
            $this->request = $this->request->withArguments($arguments);
422
            return;
423
        }
424
425
        // allow creation of new objects (for validation)
426
        $propertyMapping->setTypeConverterOptions(
427
            PersistentObjectConverter::class,
428
            [
429
                PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => true,
430
                PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED => true,
431
            ]
432
        );
433
434
        $index = 0;
435
        foreach ((array)($arguments['registration']['fields'] ?? []) as $fieldUid => $value) {
436
            // Only accept registration fields of the current event
437
            if (!in_array((int)$fieldUid, $event->getRegistrationFieldsUids(), true)) {
438
                continue;
439
            }
440
441
            // allow subvalues in new property mapper
442
            $propertyMapping->forProperty('fieldValues')->allowProperties($index);
443
            $propertyMapping->forProperty('fieldValues.' . $index)->allowAllProperties();
444
            $propertyMapping->allowCreationForSubProperty('fieldValues.' . $index);
445
            $propertyMapping->allowModificationForSubProperty('fieldValues.' . $index);
446
447
            if (is_array($value)) {
448
                if (empty($value)) {
449
                    $value = '';
450
                } else {
451
                    $value = json_encode($value);
452
                }
453
            }
454
455
            /** @var Registration\Field $field */
456
            $field = $this->fieldRepository->findByUid((int)$fieldUid);
457
458
            $arguments['registration']['fieldValues'][$index] = [
459
                'pid' => $field->getPid(),
460
                'value' => $value,
461
                'field' => (string)$fieldUid,
462
                'valueType' => $field->getValueType(),
463
            ];
464
465
            $index++;
466
        }
467
468
        // Remove temporary "fields" field
469
        if (isset($arguments['registration']['fields'])) {
470
            $arguments = ArrayUtility::removeByPath($arguments, 'registration/fields');
471
        }
472
        $this->request = $this->request->withArguments($arguments);
473
    }
474
475
    /**
476
     * Set date format for field dateOfBirth
477
     */
478
    public function initializeSaveRegistrationAction(): void
479
    {
480
        $this->arguments->getArgument('registration')
481
            ->getPropertyMappingConfiguration()->forProperty('dateOfBirth')
482
            ->setTypeConverterOption(
483
                DateTimeConverter::class,
484
                DateTimeConverter::CONFIGURATION_DATE_FORMAT,
485
                $this->settings['registration']['formatDateOfBirth'] ?? 'd.m.Y'
486
            );
487
        $this->removePossibleSpamCheckFieldsFromArguments();
488
        $this->setRegistrationFieldValuesToArguments();
489
    }
490
491
    /**
492
     * Saves the registration
493
     *
494
     * @Extbase\Validate("DERHANSEN\SfEventMgt\Validation\Validator\RegistrationFieldValidator", param="registration")
495
     * @Extbase\Validate("DERHANSEN\SfEventMgt\Validation\Validator\RegistrationValidator", param="registration")
496
     */
497
    public function saveRegistrationAction(Registration $registration, Event $event): ResponseInterface
498
    {
499
        if (is_a($event, Event::class) && ($this->settings['registration']['checkPidOfEventRecord'] ?? false)) {
500
            $event = $this->checkPidOfEventRecord($event);
501
        }
502
        if (is_null($event) && isset($this->settings['event']['errorHandling'])) {
503
            return $this->handleEventNotFoundError($this->settings);
504
        }
505
        $autoConfirmation = (bool)($this->settings['registration']['autoConfirmation'] ?? false) ||
506
            $event->getEnableAutoconfirm();
507
        $result = RegistrationResult::REGISTRATION_SUCCESSFUL;
508
        list($success, $result) = $this->registrationService->checkRegistrationSuccess($event, $registration, $result);
509
510
        // Save registration if no errors
511
        if ($success) {
512
            $isWaitlistRegistration = $this->registrationService->isWaitlistRegistration(
513
                $event,
514
                $registration->getAmountOfRegistrations()
515
            );
516
            $linkValidity = (int)($this->settings['confirmation']['linkValidity'] ?? 3600);
517
            if ($linkValidity === 0) {
518
                // Use 3600 seconds as default value if not set or zero
519
                $linkValidity = 3600;
520
            }
521
            $confirmationUntil = new DateTime();
522
            $confirmationUntil->add(new DateInterval('PT' . $linkValidity . 'S'));
523
524
            $registration->setEvent($event);
525
            $registration->setPid($event->getPid());
526
            $registration->setRegistrationDate(new DateTime());
527
            $registration->setConfirmationUntil($confirmationUntil);
528
            $registration->setLanguage($this->getCurrentLanguageCode());
529
            $registration->setFeUser($this->registrationService->getCurrentFeUserObject());
530
            $registration->setWaitlist($isWaitlistRegistration);
531
            $this->registrationRepository->add($registration);
532
533
            // Persist registration, so we have an UID
534
            $this->persistAll();
535
536
            if ($isWaitlistRegistration) {
537
                $messageType = MessageType::REGISTRATION_WAITLIST_NEW;
538
            } else {
539
                $messageType = MessageType::REGISTRATION_NEW;
540
            }
541
542
            $this->eventDispatcher->dispatch(new AfterRegistrationSavedEvent($registration, $this));
543
544
            // Send notifications to user and admin if confirmation link should be sent
545
            if (!$autoConfirmation) {
546
                $this->notificationService->sendUserMessage(
547
                    $event,
548
                    $registration,
549
                    $this->settings,
550
                    $messageType
551
                );
552
                $this->notificationService->sendAdminMessage(
553
                    $event,
554
                    $registration,
555
                    $this->settings,
556
                    $messageType
557
                );
558
            }
559
560
            // Create given amount of registrations if necessary
561
            $modifyCreateDependingRegistrationsEvent = new ModifyCreateDependingRegistrationsEvent(
562
                $registration,
563
                ($registration->getAmountOfRegistrations() > 1),
564
                $this
565
            );
566
            $this->eventDispatcher->dispatch($modifyCreateDependingRegistrationsEvent);
567
            $createDependingRegistrations = $modifyCreateDependingRegistrationsEvent->getCreateDependingRegistrations();
568
            if ($createDependingRegistrations) {
569
                $this->registrationService->createDependingRegistrations($registration);
570
            }
571
572
            // Flush page cache for event, since new registration has been added
573
            $this->eventCacheService->flushEventCache($event->getUid(), $event->getPid());
574
        }
575
576
        if ($autoConfirmation && $success) {
577
            return $this->redirect(
578
                'confirmRegistration',
579
                null,
580
                null,
581
                [
582
                    'reguid' => $registration->getUid(),
583
                    'hmac' => $this->hashService->generateHmac('reg-' . $registration->getUid()),
584
                ]
585
            );
586
        }
587
588
        return $this->redirect(
589
            'saveRegistrationResult',
590
            null,
591
            null,
592
            [
593
                'result' => $result,
594
                'eventuid' => $event->getUid(),
595
                'hmac' => $this->hashService->generateHmac('event-' . $event->getUid()),
596
            ]
597
        );
598
    }
599
600
    /**
601
     * Shows the result of the saveRegistrationAction
602
     */
603
    public function saveRegistrationResultAction(int $result, int $eventuid, string $hmac): ResponseInterface
604
    {
605
        $event = null;
606
607
        switch ($result) {
608
            case RegistrationResult::REGISTRATION_SUCCESSFUL:
609
                $messageKey = 'event.message.registrationsuccessful';
610
                $titleKey = 'registrationResult.title.successful';
611
                break;
612
            case RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST:
613
                $messageKey = 'event.message.registrationwaitlistsuccessful';
614
                $titleKey = 'registrationWaitlistResult.title.successful';
615
                break;
616
            case RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED:
617
                $messageKey = 'event.message.registrationfailedeventexpired';
618
                $titleKey = 'registrationResult.title.failed';
619
                break;
620
            case RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS:
621
                $messageKey = 'event.message.registrationfailedmaxparticipants';
622
                $titleKey = 'registrationResult.title.failed';
623
                break;
624
            case RegistrationResult::REGISTRATION_NOT_ENABLED:
625
                $messageKey = 'event.message.registrationfailednotenabled';
626
                $titleKey = 'registrationResult.title.failed';
627
                break;
628
            case RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED:
629
                $messageKey = 'event.message.registrationfaileddeadlineexpired';
630
                $titleKey = 'registrationResult.title.failed';
631
                break;
632
            case RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES:
633
                $messageKey = 'event.message.registrationfailednotenoughfreeplaces';
634
                $titleKey = 'registrationResult.title.failed';
635
                break;
636
            case RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED:
637
                $messageKey = 'event.message.registrationfailedmaxamountregistrationsexceeded';
638
                $titleKey = 'registrationResult.title.failed';
639
                break;
640
            case RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE:
641
                $messageKey = 'event.message.registrationfailedemailnotunique';
642
                $titleKey = 'registrationResult.title.failed';
643
                break;
644
            default:
645
                $messageKey = '';
646
                $titleKey = '';
647
        }
648
649
        if (!$this->hashService->validateHmac('event-' . $eventuid, $hmac)) {
650
            $messageKey = 'event.message.registrationsuccessfulwrongeventhmac';
651
            $titleKey = 'registrationResult.title.failed';
652
        } else {
653
            $event = $this->eventRepository->findByUid((int)$eventuid);
654
        }
655
656
        $this->view->assignMultiple([
657
            'messageKey' => $messageKey,
658
            'titleKey' => $titleKey,
659
            'event' => $event,
660
        ]);
661
662
        return $this->htmlResponse();
663
    }
664
665
    /**
666
     * Confirms the registration if possible and sends emails to admin and user
667
     */
668
    public function confirmRegistrationAction(int $reguid, string $hmac): ResponseInterface
669
    {
670
        $event = null;
671
672
        /* @var $registration Registration */
673
        list($failed, $registration, $messageKey, $titleKey) = $this->registrationService->checkConfirmRegistration(
674
            $reguid,
675
            $hmac
676
        );
677
678
        if ($failed === false) {
679
            $registration->setConfirmed(true);
680
            $event = $registration->getEvent();
681
            $this->registrationRepository->update($registration);
682
683
            $this->eventDispatcher->dispatch(new AfterRegistrationConfirmedEvent($registration, $this));
684
685
            $messageType = MessageType::REGISTRATION_CONFIRMED;
686
            if ($registration->getWaitlist()) {
687
                $messageType = MessageType::REGISTRATION_WAITLIST_CONFIRMED;
688
            }
689
690
            // Send notifications to user and admin
691
            $this->notificationService->sendUserMessage(
692
                $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

692
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
693
                $registration,
694
                $this->settings,
695
                $messageType
696
            );
697
            $this->notificationService->sendAdminMessage(
698
                $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

698
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
699
                $registration,
700
                $this->settings,
701
                $messageType
702
            );
703
704
            // Confirm registrations depending on main registration if necessary
705
            if ($registration->getAmountOfRegistrations() > 1) {
706
                $this->registrationService->confirmDependingRegistrations($registration);
707
            }
708
        }
709
710
        // Redirect to payment provider if payment/redirect is enabled.
711
        // Skip if the registration is a waitlist registration, since it is not sure, if the user will participate.
712
        $paymentPid = (int)($this->settings['paymentPid'] ?? 0);
713
        $paymentRedirectResponse = null;
714
        $processRedirect = !$failed &&
715
            $paymentPid > 0 &&
716
            $registration &&
717
            !$registration->getWaitlist() &&
718
            $this->registrationService->redirectPaymentEnabled($registration);
719
        if ($processRedirect) {
720
            $paymentRedirectResponse = $this->getRedirectToPaymentResponse($paymentPid, $registration);
721
        }
722
723
        if ($paymentRedirectResponse instanceof ResponseInterface) {
724
            return $paymentRedirectResponse;
725
        }
726
727
        $modifyConfirmRegistrationViewVariablesEvent = new ModifyConfirmRegistrationViewVariablesEvent(
728
            [
729
                'failed' => $failed,
730
                'messageKey' => $messageKey,
731
                'titleKey' => $titleKey,
732
                'event' => $event,
733
                'registration' => $registration,
734
                'settings' => $this->settings,
735
            ],
736
            $this
737
        );
738
        $this->eventDispatcher->dispatch($modifyConfirmRegistrationViewVariablesEvent);
739
        $variables = $modifyConfirmRegistrationViewVariablesEvent->getVariables();
740
        $this->view->assignMultiple($variables);
741
742
        return $this->htmlResponse();
743
    }
744
745
    /**
746
     * Returns a response object to the given payment PID. Extension authors can use ProcessRedirectToPaymentEvent
747
     * PSR-14 event to intercept the redirect response.
748
     */
749
    private function getRedirectToPaymentResponse(int $paymentPid, Registration $registration): ?ResponseInterface
750
    {
751
        $processRedirectToPaymentEvent = new ProcessRedirectToPaymentEvent($registration, $this);
752
        if ($processRedirectToPaymentEvent->getProcessRedirect()) {
753
            $this->uriBuilder->reset()
754
                ->setTargetPageUid($paymentPid);
755
            $uri = $this->uriBuilder->uriFor(
756
                'redirect',
757
                [
758
                    'registration' => $registration,
759
                    'hmac' => $this->hashService->generateHmac('redirectAction-' . $registration->getUid()),
760
                ],
761
                'Payment',
762
                'sfeventmgt',
763
                'Pipayment'
764
            );
765
            return $this->redirectToUri($uri);
766
        }
767
768
        return null;
769
    }
770
771
    /**
772
     * Cancels the registration if possible and sends emails to admin and user
773
     */
774
    public function cancelRegistrationAction(int $reguid, string $hmac): ResponseInterface
775
    {
776
        $event = null;
777
778
        /* @var $registration Registration */
779
        list($failed, $registration, $messageKey, $titleKey) =
780
            $this->registrationService->checkCancelRegistration($reguid, $hmac);
781
782
        if ($failed === false) {
783
            $event = $registration->getEvent();
784
785
            // Send notifications (must run before cancelling the registration)
786
            $this->notificationService->sendUserMessage(
787
                $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

787
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
788
                $registration,
789
                $this->settings,
790
                MessageType::REGISTRATION_CANCELLED
791
            );
792
            $this->notificationService->sendAdminMessage(
793
                $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

793
                /** @scrutinizer ignore-type */ $registration->getEvent(),
Loading history...
794
                $registration,
795
                $this->settings,
796
                MessageType::REGISTRATION_CANCELLED
797
            );
798
799
            // First cancel depending registrations
800
            if ($registration->getAmountOfRegistrations() > 1) {
801
                $this->registrationService->cancelDependingRegistrations($registration);
802
            }
803
804
            // Finally cancel registration
805
            $this->registrationRepository->remove($registration);
806
807
            // Persist changes, so following functions can work with $event properties (e.g. amount of registrations)
808
            $this->persistAll();
809
810
            $afterRegistrationCancelledEvent = new AfterRegistrationCancelledEvent($registration, $this);
811
            $this->eventDispatcher->dispatch($afterRegistrationCancelledEvent);
812
813
            // Dispatch event, so waitlist registrations can be moved up and default move up process can be stopped
814
            $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

814
            $waitlistMoveUpEvent = new WaitlistMoveUpEvent(/** @scrutinizer ignore-type */ $event, $this, true);
Loading history...
815
            $this->eventDispatcher->dispatch($waitlistMoveUpEvent);
816
817
            // Move up waitlist registrations if configured on event basis and if not disabled by $waitlistMoveUpEvent
818
            if ($waitlistMoveUpEvent->getProcessDefaultMoveUp()) {
819
                $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

819
                $this->registrationService->moveUpWaitlistRegistrations(/** @scrutinizer ignore-type */ $event, $this->settings);
Loading history...
820
            }
821
822
            // Flush page cache for event, since amount of registrations has changed
823
            $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

823
            $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

823
            $this->eventCacheService->flushEventCache(/** @scrutinizer ignore-type */ $event->getUid(), $event->getPid());
Loading history...
824
        }
825
826
        $modifyCancelRegistrationViewVariablesEvent = new ModifyCancelRegistrationViewVariablesEvent(
827
            [
828
                'failed' => $failed,
829
                'messageKey' => $messageKey,
830
                'titleKey' => $titleKey,
831
                'event' => $event,
832
                'settings' => $this->settings,
833
            ],
834
            $this
835
        );
836
        $this->eventDispatcher->dispatch($modifyCancelRegistrationViewVariablesEvent);
837
        $variables = $modifyCancelRegistrationViewVariablesEvent->getVariables();
838
        $this->view->assignMultiple($variables);
839
840
        return $this->htmlResponse();
841
    }
842
843
    /**
844
     * Set date format for field startDate and endDate
845
     */
846
    public function initializeSearchAction(): void
847
    {
848
        if ($this->settings !== null && ($this->settings['search']['dateFormat'] ?? false)) {
849
            $this->arguments->getArgument('searchDemand')
850
                ->getPropertyMappingConfiguration()->forProperty('startDate')
851
                ->setTypeConverterOption(
852
                    DateTimeConverter::class,
853
                    DateTimeConverter::CONFIGURATION_DATE_FORMAT,
854
                    $this->settings['search']['dateFormat']
855
                );
856
            $this->arguments->getArgument('searchDemand')
857
                ->getPropertyMappingConfiguration()->forProperty('endDate')
858
                ->setTypeConverterOption(
859
                    DateTimeConverter::class,
860
                    DateTimeConverter::CONFIGURATION_DATE_FORMAT,
861
                    $this->settings['search']['dateFormat']
862
                );
863
        }
864
        if ($this->arguments->hasArgument('searchDemand')) {
865
            $propertyMappingConfiguration = $this->arguments->getArgument('searchDemand')
866
                ->getPropertyMappingConfiguration();
867
            $propertyMappingConfiguration->allowAllProperties();
868
            $propertyMappingConfiguration->setTypeConverterOption(
869
                PersistentObjectConverter::class,
870
                PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
871
                true
872
            );
873
        }
874
    }
875
876
    /**
877
     * Search view
878
     */
879
    public function searchAction(SearchDemand $searchDemand = null, array $overwriteDemand = []): ResponseInterface
880
    {
881
        $eventDemand = EventDemand::createFromSettings($this->settings);
882
        $eventDemand->setSearchDemand($searchDemand);
883
        $foreignRecordDemand = ForeignRecordDemand::createFromSettings($this->settings);
884
        $categoryDemand = CategoryDemand::createFromSettings($this->settings);
885
886
        if ($searchDemand !== null) {
887
            $searchDemand->setFields($this->settings['search']['fields'] ?? '');
888
889
            $adjustTime = (bool)($this->settings['search']['adjustTime'] ?? false);
890
            if ($adjustTime && $searchDemand->getStartDate() !== null) {
891
                $searchDemand->getStartDate()->setTime(0, 0);
892
            }
893
894
            if ($adjustTime && $searchDemand->getEndDate() !== null) {
895
                $searchDemand->getEndDate()->setTime(23, 59, 59);
896
            }
897
        }
898
899
        if ($this->isOverwriteDemand($overwriteDemand)) {
900
            $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand);
901
        }
902
903
        $categories = $this->categoryRepository->findDemanded($categoryDemand);
904
        $locations = $this->locationRepository->findDemanded($foreignRecordDemand);
905
        $organisators = $this->organisatorRepository->findDemanded($foreignRecordDemand);
906
        $speakers = $this->speakerRepository->findDemanded($foreignRecordDemand);
907
        $events = $this->eventRepository->findDemanded($eventDemand);
908
909
        $modifySearchViewVariablesEvent = new ModifySearchViewVariablesEvent(
910
            [
911
                'events' => $events,
912
                'categories' => $categories,
913
                'locations' => $locations,
914
                'organisators' => $organisators,
915
                'speakers' => $speakers,
916
                'searchDemand' => $searchDemand,
917
                'overwriteDemand' => $overwriteDemand,
918
                'settings' => $this->settings,
919
            ],
920
            $this
921
        );
922
        $this->eventDispatcher->dispatch($modifySearchViewVariablesEvent);
923
        $variables = $modifySearchViewVariablesEvent->getVariables();
924
        $this->view->assignMultiple($variables);
925
926
        return $this->htmlResponse();
927
    }
928
929
    /**
930
     * Returns if a demand object can be overwritten with the given overwriteDemand array
931
     *
932
     * @param array $overwriteDemand
933
     * @return bool
934
     */
935
    protected function isOverwriteDemand(array $overwriteDemand): bool
936
    {
937
        return ($this->settings['disableOverrideDemand'] ?? 0) !== 1 && $overwriteDemand !== [];
938
    }
939
940
    /**
941
     * If no event is given and the singleEvent setting is set, the configured single event is returned
942
     */
943
    protected function evaluateSingleEventSetting(?Event $event): ?Event
944
    {
945
        if ($event === null && (int)($this->settings['singleEvent'] ?? 0) > 0) {
946
            $event = $this->eventRepository->findByUid((int)$this->settings['singleEvent']);
947
        }
948
949
        return $event;
950
    }
951
952
    /**
953
     * If no event is given and the isShortcut setting is set, the event is displayed using the "Insert Record"
954
     * content element and should be loaded from contect object data
955
     */
956
    protected function evaluateIsShortcutSetting(?Event $event): ?Event
957
    {
958
        if ($event === null && (bool)($this->settings['detail']['isShortcut'] ?? false)) {
959
            $eventRawData = $this->configurationManager->getContentObject()->data;
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Config...ace::getContentObject() has been deprecated: since v12. Remove in v13. ( Ignorable by Annotation )

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

959
            $eventRawData = /** @scrutinizer ignore-deprecated */ $this->configurationManager->getContentObject()->data;

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
960
            $event = $this->eventRepository->findByUid($eventRawData['uid']);
961
        }
962
963
        return $event;
964
    }
965
966
    /**
967
     * Checks if the event pid could be found in the storagePage settings of the detail plugin and
968
     * if the pid could not be found it return null instead of the event object.
969
     */
970
    protected function checkPidOfEventRecord(Event $event): ?Event
971
    {
972
        $allowedStoragePages = GeneralUtility::trimExplode(
973
            ',',
974
            PageUtility::extendPidListByChildren(
975
                $this->settings['storagePage'] ?? '',
976
                (int)($this->settings['recursive'] ?? 0)
977
            ),
978
            true
979
        );
980
        if (count($allowedStoragePages) > 0 && !in_array($event->getPid(), $allowedStoragePages)) {
981
            $this->eventDispatcher->dispatch(new EventPidCheckFailedEvent($event, $this));
982
            $event = null;
983
        }
984
985
        return $event;
986
    }
987
988
    /**
989
     * Calls persistAll() of the persistenceManager
990
     */
991
    protected function persistAll(): void
992
    {
993
        GeneralUtility::makeInstance(PersistenceManager::class)->persistAll();
994
    }
995
996
    /**
997
     * Returns the current sys_language_uid
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 language code of the current language
1008
     */
1009
    protected function getCurrentLanguageCode(): string
1010
    {
1011
        if ($this->request->getAttribute('language') instanceof SiteLanguage) {
1012
            /** @var SiteLanguage $siteLanguage */
1013
            $siteLanguage = $this->request->getAttribute('language');
1014
            return $siteLanguage->getLocale()->getLanguageCode();
1015
        }
1016
1017
        return '';
1018
    }
1019
}
1020