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

AdministrationController::initializeView()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 5
nop 1
dl 0
loc 22
rs 9.2222
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace DERHANSEN\SfEventMgt\Controller;
13
14
use DERHANSEN\SfEventMgt\Domain\Model\Dto\CustomNotification;
15
use DERHANSEN\SfEventMgt\Domain\Model\Dto\EventDemand;
16
use DERHANSEN\SfEventMgt\Domain\Model\Dto\SearchDemand;
17
use DERHANSEN\SfEventMgt\Domain\Model\Event;
18
use DERHANSEN\SfEventMgt\Domain\Repository\CustomNotificationLogRepository;
19
use DERHANSEN\SfEventMgt\Service\BeUserSessionService;
20
use DERHANSEN\SfEventMgt\Service\ExportService;
21
use DERHANSEN\SfEventMgt\Service\MaintenanceService;
22
use DERHANSEN\SfEventMgt\Service\SettingsService;
23
use Psr\Http\Message\ResponseInterface;
24
use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
25
use TYPO3\CMS\Backend\Routing\UriBuilder;
26
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
27
use TYPO3\CMS\Backend\Template\ModuleTemplate;
28
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
29
use TYPO3\CMS\Backend\Utility\BackendUtility;
30
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
31
use TYPO3\CMS\Core\Imaging\Icon;
32
use TYPO3\CMS\Core\Imaging\IconFactory;
33
use TYPO3\CMS\Core\Localization\LanguageService;
34
use TYPO3\CMS\Core\Messaging\FlashMessage;
35
use TYPO3\CMS\Core\Page\PageRenderer;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
38
use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter;
39
40
/**
41
 * AdministrationController
42
 */
43
class AdministrationController extends AbstractController
44
{
45
    private const LANG_FILE = 'LLL:EXT:sf_event_mgt/Resources/Private/Language/locallang_be.xlf:';
46
47
    protected ModuleTemplateFactory $moduleTemplateFactory;
48
    protected CustomNotificationLogRepository $customNotificationLogRepository;
49
    protected ExportService $exportService;
50
    protected SettingsService $settingsService;
51
    protected BeUserSessionService $beUserSessionService;
52
    protected MaintenanceService $maintenanceService;
53
    protected IconFactory $iconFactory;
54
    protected PageRenderer $pageRenderer;
55
    protected int $pid = 0;
56
57
    public function injectCustomNotificationLogRepository(
58
        CustomNotificationLogRepository $customNotificationLogRepository
59
    ) {
60
        $this->customNotificationLogRepository = $customNotificationLogRepository;
61
    }
62
63
    public function injectExportService(ExportService $exportService)
64
    {
65
        $this->exportService = $exportService;
66
    }
67
68
    public function injectSettingsService(SettingsService $settingsService)
69
    {
70
        $this->settingsService = $settingsService;
71
    }
72
73
    public function injectBeUserSessionService(BeUserSessionService $beUserSessionService)
74
    {
75
        $this->beUserSessionService = $beUserSessionService;
76
    }
77
78
    public function injectIconFactory(IconFactory $iconFactory)
79
    {
80
        $this->iconFactory = $iconFactory;
81
    }
82
83
    public function injectMaintenanceService(MaintenanceService $maintenanceService)
84
    {
85
        $this->maintenanceService = $maintenanceService;
86
    }
87
88
    public function injectModuleTemplateFactory(ModuleTemplateFactory $moduleTemplateFactory)
89
    {
90
        $this->moduleTemplateFactory = $moduleTemplateFactory;
91 2
    }
92
93 2
    public function injectPageRenderer(PageRenderer $pageRenderer)
94 2
    {
95
        $this->pageRenderer = $pageRenderer;
96
    }
97
98
    /**
99
     * Register docHeaderButtons
100
     */
101 4
    protected function registerDocHeaderButtons(ModuleTemplate $moduleTemplate): void
102
    {
103 4
        $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar();
104 2
105 2
        if ($this->request->getControllerActionName() === 'list') {
106 4
            $buttons = [
107 4
                [
108 4
                    'label' => 'administration.newEvent',
109 4
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_event'),
110 4
                    'icon' => 'ext-sfeventmgt-event',
111 4
                    'group' => 1,
112 4
                ],
113 4
                [
114 4
                    'label' => 'administration.newLocation',
115 4
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_location'),
116 4
                    'icon' => 'ext-sfeventmgt-location',
117 4
                    'group' => 1,
118 4
                ],
119 4
                [
120 4
                    'label' => 'administration.newOrganisator',
121
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_organisator'),
122
                    'icon' => 'ext-sfeventmgt-organisator',
123
                    'group' => 1,
124
                ],
125
                [
126
                    'label' => 'administration.newSpeaker',
127
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_speaker'),
128
                    'icon' => 'ext-sfeventmgt-speaker',
129
                    'group' => 1,
130 8
                ],
131
                [
132
                    'label' => 'administration.handleExpiredRegistrations',
133 8
                    'link' => $this->uriBuilder->reset()->setRequest($this->request)
134
                        ->uriFor('handleExpiredRegistrations', [], 'Administration'),
135 8
                    'icon' => 'ext-sfeventmgt-action-handle-expired',
136 6
                    'group' => 2,
137 6
                ],
138 8
            ];
139
            foreach ($buttons as $tableConfiguration) {
140 8
                $title = $this->getLanguageService()->sL(self::LANG_FILE . $tableConfiguration['label']);
141 4
                $icon = $this->iconFactory->getIcon($tableConfiguration['icon'], Icon::SIZE_SMALL);
142 4
                $viewButton = $buttonBar->makeLinkButton()
143
                    ->setHref($tableConfiguration['link'])
144 8
                    ->setDataAttributes([
145 2
                        'toggle' => 'tooltip',
146 2
                        'placement' => 'bottom',
147 2
                        'title' => $title,
148 2
                        ])
149
                    ->setTitle($title)
150 8
                    ->setIcon($icon);
151 8
                $buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, $tableConfiguration['group']);
152 8
            }
153 8
        }
154
    }
155
156
    /**
157
     * Returns the create new record URL for the given table
158
     *
159
     * @param string $table
160
     * @throws RouteNotFoundException
161
     * @return string
162 2
     */
163
    private function getCreateNewRecordUri(string $table): string
164 2
    {
165 2
        $pid = $this->pid;
166
        $tsConfig = BackendUtility::getPagesTSconfig(0);
167
        if ($pid === 0 && isset($tsConfig['defaultPid.'])
168
            && is_array($tsConfig['defaultPid.'])
169
            && isset($tsConfig['defaultPid.'][$table])
170
        ) {
171
            $pid = (int)$tsConfig['defaultPid.'][$table];
172
        }
173 2
174
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
175 2
176 2
        return (string)$uriBuilder->buildUriFromRoute('record_edit', [
177 2
            'edit[' . $table . '][' . $pid . ']' => 'new',
178
            'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
179
        ]);
180
    }
181
182
    /**
183
     * Initializes module template and returns a response which must be used as response for any extbase action
184
     * that should render a view.
185
     *
186 2
     * @return ResponseInterface
187
     */
188 2
    protected function initModuleTemplateAndReturnResponse(): ResponseInterface
189 2
    {
190 2
        $moduleTemplate = $this->moduleTemplateFactory->create($this->request);
191 2
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DateTimePicker');
192 2
        $this->pageRenderer->addCssFile('EXT:sf_event_mgt/Resources/Public/Css/administration.css');
193 2
194 2
        $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ?
195 2
            ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] :
196
            ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY'];
197
        $this->pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
198
199
        $this->registerDocHeaderButtons($moduleTemplate);
200
201
        $moduleTemplate->setFlashMessageQueue($this->getFlashMessageQueue());
202
203
        $moduleTemplate->setContent($this->view->render());
204
        return $this->htmlResponse($moduleTemplate->renderContent());
205 2
    }
206
207 2
    /**
208 2
     * Initialize action
209 2
     */
210 2
    public function initializeAction()
211 2
    {
212
        $this->pid = (int)GeneralUtility::_GET('id');
213 2
    }
214 2
215 2
    /**
216
     * Set date format for fields startDate and endDate
217
     */
218
    public function initializeListAction()
219
    {
220
        if (empty($this->settings)) {
221
            $this->redirect('settingsError');
222
        }
223
        $this->arguments->getArgument('searchDemand')
224
            ->getPropertyMappingConfiguration()->forProperty('startDate')
225
            ->setTypeConverterOption(
226
                DateTimeConverter::class,
227
                DateTimeConverter::CONFIGURATION_DATE_FORMAT,
228
                $this->settings['search']['dateFormat'] ?? 'H:i d-m-Y'
229
            );
230
        $this->arguments->getArgument('searchDemand')
231
            ->getPropertyMappingConfiguration()->forProperty('endDate')
232
            ->setTypeConverterOption(
233
                DateTimeConverter::class,
234
                DateTimeConverter::CONFIGURATION_DATE_FORMAT,
235
                $this->settings['search']['dateFormat'] ?? 'H:i d-m-Y'
236
            );
237
    }
238
239
    /**
240
     * List action for backend module
241
     *
242
     * @param SearchDemand|null $searchDemand
243
     * @param array $overwriteDemand
244
     * @return ResponseInterface
245
     */
246
    public function listAction(?SearchDemand $searchDemand = null, array $overwriteDemand = []): ResponseInterface
247
    {
248
        if ($searchDemand !== null) {
249
            $searchDemand->setFields($this->settings['search']['fields'] ?? '');
250
251
            $sessionData = [];
252
            $sessionData['searchDemand'] = $searchDemand->toArray();
253
            $sessionData['overwriteDemand'] = $overwriteDemand;
254
            $this->beUserSessionService->saveSessionData($sessionData);
255
        } else {
256
            // Try to restore search demand from Session
257
            $sessionSearchDemand = $this->beUserSessionService->getSessionDataByKey('searchDemand') ?? [];
258
            $searchDemand = SearchDemand::fromArray($sessionSearchDemand);
259
            $overwriteDemand = $this->beUserSessionService->getSessionDataByKey('overwriteDemand');
260
        }
261
262
        if ($this->isResetFilter()) {
263
            $searchDemand = GeneralUtility::makeInstance(SearchDemand::class);
264
            $overwriteDemand = [];
265
266
            $sessionData = [];
267
            $sessionData['searchDemand'] = $searchDemand->toArray();
268
            $sessionData['overwriteDemand'] = $overwriteDemand;
269
            $this->beUserSessionService->saveSessionData($sessionData);
270
        }
271
272
        $eventDemand = GeneralUtility::makeInstance(EventDemand::class);
273
        $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand ?? []);
0 ignored issues
show
Bug introduced by
It seems like $overwriteDemand ?? array() can also be of type null; however, parameter $overwriteDemand of DERHANSEN\SfEventMgt\Con...riteEventDemandObject() does only seem to accept array, 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

273
        $eventDemand = $this->overwriteEventDemandObject($eventDemand, /** @scrutinizer ignore-type */ $overwriteDemand ?? []);
Loading history...
274
        $eventDemand->setOrderFieldAllowed($this->settings['orderFieldAllowed'] ?? '');
275
        $eventDemand->setSearchDemand($searchDemand);
276
        $eventDemand->setStoragePage((string)$this->pid);
277
        $eventDemand->setIgnoreEnableFields(true);
278
279
        $events = [];
280
        if ($this->getBackendUser()->isInWebMount($this->pid)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getBackendUser()->isInWebMount($this->pid) of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Deprecated Code introduced by
The function TYPO3\CMS\Core\Authentic...ication::isInWebMount() has been deprecated. ( Ignorable by Annotation )

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

280
        if (/** @scrutinizer ignore-deprecated */ $this->getBackendUser()->isInWebMount($this->pid)) {
Loading history...
281
            $events = $this->eventRepository->findDemanded($eventDemand);
282
        }
283
284
        $this->view->assignMultiple([
285
            'pid' => $this->pid,
286
            'events' => $events,
287
            'searchDemand' => $searchDemand,
288
            'orderByFields' => $this->getOrderByFields(),
289
            'orderDirections' => $this->getOrderDirections(),
290
            'overwriteDemand' => $overwriteDemand,
291
            'pagination' => $this->getPagination($events, $this->settings['pagination'] ?? []),
0 ignored issues
show
Bug introduced by
It seems like $events can also be of type array and 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

291
            'pagination' => $this->getPagination(/** @scrutinizer ignore-type */ $events, $this->settings['pagination'] ?? []),
Loading history...
292
        ]);
293
294
        return $this->initModuleTemplateAndReturnResponse();
295
    }
296
297
    /**
298
     * Returns, if reset filter operation has been used
299
     *
300
     * @return bool
301
     */
302
    private function isResetFilter(): bool
303
    {
304
        $resetFilter = false;
305
        if ($this->request->hasArgument('operation')) {
306
            $resetFilter = $this->request->getArgument('operation') === 'reset-filters' ?? false;
307
        }
308
309
        return $resetFilter;
310
    }
311
312
    /**
313
     * Export registrations for a given event
314
     *
315
     * @param int $eventUid
316
     */
317
    public function exportAction(int $eventUid): void
318
    {
319
        /** @var Event $event */
320
        $event = $this->eventRepository->findByUidIncludeHidden($eventUid);
321
        if ($event !== null) {
322
            $this->checkEventAccess($event);
323
            $this->exportService->downloadRegistrationsCsv($eventUid, $this->settings['csvExport'] ?? []);
324
        }
325
        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...
326
    }
327
328
    /**
329
     * Handles expired registrations
330
     */
331
    public function handleExpiredRegistrationsAction(): void
332
    {
333
        $delete = (bool)($this->settings['registration']['deleteExpiredRegistrations'] ?? false);
334
        $this->maintenanceService->handleExpiredRegistrations($delete);
335
336
        $this->addFlashMessage(
337
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-1.content'),
338
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-1.title'),
339
            FlashMessage::OK
340
        );
341
342
        $this->redirect('list');
343
    }
344
345
    /**
346
     * The index notify action
347
     *
348
     * @param Event $event
349
     * @return ResponseInterface
350
     * @throws StopActionException
351
     */
352
    public function indexNotifyAction(Event $event): ResponseInterface
353
    {
354
        $this->checkEventAccess($event);
355
        $customNotification = GeneralUtility::makeInstance(CustomNotification::class);
356
        $customNotifications = $this->settingsService->getCustomNotifications($this->settings ?? []);
357
        $logEntries = $this->customNotificationLogRepository->findByEvent($event);
0 ignored issues
show
Bug introduced by
The method findByEvent() does not exist on DERHANSEN\SfEventMgt\Dom...tificationLogRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

357
        /** @scrutinizer ignore-call */ 
358
        $logEntries = $this->customNotificationLogRepository->findByEvent($event);
Loading history...
358
        $this->view->assignMultiple([
359
            'event' => $event,
360
            'recipients' => $this->getNotificationRecipients(),
361
            'customNotification' => $customNotification,
362
            'customNotifications' => $customNotifications,
363
            'logEntries' => $logEntries,
364
        ]);
365
366
        return $this->initModuleTemplateAndReturnResponse();
367
    }
368
369
    /**
370
     * Returns an array of recipient option for the indexNotify action
371
     *
372
     * @return array|array[]
373
     */
374
    public function getNotificationRecipients(): array
375
    {
376
        return [
377
            [
378
                'value' => CustomNotification::RECIPIENTS_ALL,
379
                'label' => $this->getLanguageService()->sL(
380
                    self::LANG_FILE . 'administration.notify.recipients.' . CustomNotification::RECIPIENTS_ALL
381
                ),
382
            ],
383
            [
384
                'value' => CustomNotification::RECIPIENTS_CONFIRMED,
385
                'label' => $this->getLanguageService()->sL(
386
                    self::LANG_FILE . 'administration.notify.recipients.' . CustomNotification::RECIPIENTS_CONFIRMED
387
                ),
388
            ],
389
            [
390
                'value' => CustomNotification::RECIPIENTS_UNCONFIRMED,
391
                'label' => $this->getLanguageService()->sL(
392
                    self::LANG_FILE . 'administration.notify.recipients.' . CustomNotification::RECIPIENTS_UNCONFIRMED
393
                ),
394
            ],
395
        ];
396
    }
397
398
    /**
399
     * Notify action
400
     *
401
     * @param Event $event
402
     * @param CustomNotification $customNotification
403
     */
404
    public function notifyAction(Event $event, CustomNotification $customNotification)
405
    {
406
        $this->checkEventAccess($event);
407
        $customNotifications = $this->settingsService->getCustomNotifications($this->settings);
408
        $result = $this->notificationService->sendCustomNotification($event, $customNotification, $this->settings);
409
        $this->notificationService->createCustomNotificationLogentry(
410
            $event,
411
            $customNotifications[$customNotification->getTemplate()],
412
            $result
413
        );
414
        $this->addFlashMessage(
415
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-2.content'),
416
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-2.title'),
417
            FlashMessage::OK
418
        );
419
        $this->redirect('list');
420
    }
421
422
    /**
423
     * Checks if the current backend user has access to the PID of the event and if not, enqueue an
424
     * access denied flash message and redirect to list view
425
     *
426
     * @param Event $event
427
     * @throws StopActionException
428
     */
429
    public function checkEventAccess(Event $event)
430
    {
431
        if ($this->getBackendUser()->isInWebMount($event->getPid()) === null) {
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Core\Authentic...ication::isInWebMount() has been deprecated. ( Ignorable by Annotation )

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

431
        if (/** @scrutinizer ignore-deprecated */ $this->getBackendUser()->isInWebMount($event->getPid()) === null) {
Loading history...
432
            $this->addFlashMessage(
433
                $this->getLanguageService()->sL(self::LANG_FILE . 'administration.accessdenied.content'),
434
                $this->getLanguageService()->sL(self::LANG_FILE . 'administration.accessdenied.title'),
435
                FlashMessage::ERROR
436
            );
437
438
            $this->redirect('list');
439
        }
440
    }
441
442
    /**
443
     * Shows the settings error view
444
     */
445
    public function settingsErrorAction()
446
    {
447
    }
448
449
    /**
450
     * Suppress default validation messages
451
     *
452
     * @return bool
453
     */
454
    protected function getErrorFlashMessage(): bool
455
    {
456
        return false;
457
    }
458
459
    /**
460
     * Returns an array with possible order directions
461
     *
462
     * @return array
463
     */
464
    public function getOrderDirections(): array
465
    {
466
        return [
467
            'asc' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.sortOrder.asc'),
468
            'desc' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.sortOrder.desc'),
469
        ];
470
    }
471
472
    /**
473
     * Returns an array with possible orderBy fields
474
     *
475
     * @return array
476
     */
477
    public function getOrderByFields(): array
478
    {
479
        return [
480
            'title' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.orderBy.title'),
481
            'startdate' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.orderBy.startdate'),
482
            'enddate' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.orderBy.enddate'),
483
        ];
484
    }
485
486
    protected function getLanguageService(): LanguageService
487
    {
488
        return $GLOBALS['LANG'];
489
    }
490
491
    protected function getBackendUser(): BackendUserAuthentication
492
    {
493
        return $GLOBALS['BE_USER'];
494
    }
495
}
496