Passed
Push — develop ( 85fa82...6d2c90 )
by Torben
02:38
created

AdministrationController::getBackendUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
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\Event\InitAdministrationModuleTemplateEvent;
20
use DERHANSEN\SfEventMgt\Event\ModifyAdministrationIndexNotifyViewVariablesEvent;
21
use DERHANSEN\SfEventMgt\Event\ModifyAdministrationListViewVariablesEvent;
22
use DERHANSEN\SfEventMgt\Service\BeUserSessionService;
23
use DERHANSEN\SfEventMgt\Service\ExportService;
24
use DERHANSEN\SfEventMgt\Service\MaintenanceService;
25
use DERHANSEN\SfEventMgt\Service\SettingsService;
26
use Psr\Http\Message\ResponseInterface;
27
use TYPO3\CMS\Backend\Routing\UriBuilder;
28
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
29
use TYPO3\CMS\Backend\Template\ModuleTemplate;
30
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
31
use TYPO3\CMS\Backend\Utility\BackendUtility;
32
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
33
use TYPO3\CMS\Core\Imaging\Icon;
34
use TYPO3\CMS\Core\Imaging\IconFactory;
35
use TYPO3\CMS\Core\Localization\LanguageService;
36
use TYPO3\CMS\Core\Page\PageRenderer;
37
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
40
class AdministrationController extends AbstractController
41
{
42
    private const LANG_FILE = 'LLL:EXT:sf_event_mgt/Resources/Private/Language/locallang_be.xlf:';
43
44
    protected ModuleTemplateFactory $moduleTemplateFactory;
45
    protected CustomNotificationLogRepository $customNotificationLogRepository;
46
    protected ExportService $exportService;
47
    protected SettingsService $settingsService;
48
    protected BeUserSessionService $beUserSessionService;
49
    protected MaintenanceService $maintenanceService;
50
    protected IconFactory $iconFactory;
51
    protected PageRenderer $pageRenderer;
52
    protected int $pid = 0;
53
54
    public function injectCustomNotificationLogRepository(
55
        CustomNotificationLogRepository $customNotificationLogRepository
56
    ): void {
57
        $this->customNotificationLogRepository = $customNotificationLogRepository;
58
    }
59
60
    public function injectExportService(ExportService $exportService): void
61
    {
62
        $this->exportService = $exportService;
63
    }
64
65
    public function injectSettingsService(SettingsService $settingsService): void
66
    {
67
        $this->settingsService = $settingsService;
68
    }
69
70
    public function injectBeUserSessionService(BeUserSessionService $beUserSessionService): void
71
    {
72
        $this->beUserSessionService = $beUserSessionService;
73
    }
74
75
    public function injectIconFactory(IconFactory $iconFactory): void
76
    {
77
        $this->iconFactory = $iconFactory;
78
    }
79
80
    public function injectMaintenanceService(MaintenanceService $maintenanceService): void
81
    {
82
        $this->maintenanceService = $maintenanceService;
83
    }
84
85
    public function injectModuleTemplateFactory(ModuleTemplateFactory $moduleTemplateFactory): void
86
    {
87
        $this->moduleTemplateFactory = $moduleTemplateFactory;
88
    }
89
90
    public function injectPageRenderer(PageRenderer $pageRenderer): void
91
    {
92
        $this->pageRenderer = $pageRenderer;
93
    }
94
95
    /**
96
     * Register docHeaderButtons
97
     */
98
    protected function registerDocHeaderButtons(ModuleTemplate $moduleTemplate): void
99
    {
100
        $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar();
101
102
        if ($this->request->getControllerActionName() === 'list') {
103
            $buttons = [
104
                [
105
                    'label' => 'administration.newEvent',
106
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_event'),
107
                    'icon' => 'ext-sfeventmgt-event',
108
                    'group' => 1,
109
                ],
110
                [
111
                    'label' => 'administration.newLocation',
112
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_location'),
113
                    'icon' => 'ext-sfeventmgt-location',
114
                    'group' => 1,
115
                ],
116
                [
117
                    'label' => 'administration.newOrganisator',
118
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_organisator'),
119
                    'icon' => 'ext-sfeventmgt-organisator',
120
                    'group' => 1,
121
                ],
122
                [
123
                    'label' => 'administration.newSpeaker',
124
                    'link' => $this->getCreateNewRecordUri('tx_sfeventmgt_domain_model_speaker'),
125
                    'icon' => 'ext-sfeventmgt-speaker',
126
                    'group' => 1,
127
                ],
128
                [
129
                    'label' => 'administration.handleExpiredRegistrations',
130
                    'link' => $this->uriBuilder->reset()->setRequest($this->request)
131
                        ->uriFor('handleExpiredRegistrations', [], 'Administration'),
132
                    'icon' => 'ext-sfeventmgt-action-handle-expired',
133
                    'group' => 2,
134
                ],
135
            ];
136
            foreach ($buttons as $tableConfiguration) {
137
                $title = $this->getLanguageService()->sL(self::LANG_FILE . $tableConfiguration['label']);
138
                $icon = $this->iconFactory->getIcon($tableConfiguration['icon'], Icon::SIZE_SMALL);
139
                $viewButton = $buttonBar->makeLinkButton()
140
                    ->setHref($tableConfiguration['link'])
141
                    ->setDataAttributes([
142
                        'toggle' => 'tooltip',
143
                        'placement' => 'bottom',
144
                        'title' => $title,
145
                        ])
146
                    ->setTitle($title)
147
                    ->setIcon($icon);
148
                $buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, $tableConfiguration['group']);
149
            }
150
        }
151
    }
152
153
    /**
154
     * Returns the create new record URL for the given table
155
     */
156
    private function getCreateNewRecordUri(string $table): string
157
    {
158
        $pid = $this->pid;
159
        $tsConfig = BackendUtility::getPagesTSconfig(0);
160
        if ($pid === 0 && isset($tsConfig['defaultPid.'])
161
            && is_array($tsConfig['defaultPid.'])
162
            && isset($tsConfig['defaultPid.'][$table])
163
        ) {
164
            $pid = (int)$tsConfig['defaultPid.'][$table];
165
        }
166
167
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
168
169
        return (string)$uriBuilder->buildUriFromRoute('record_edit', [
170
            'edit[' . $table . '][' . $pid . ']' => 'new',
171
            'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
172
        ]);
173
    }
174
175
    /**
176
     * Initializes module template and returns a response which must be used as response for any extbase action
177
     * that should render a view.
178
     */
179
    protected function initModuleTemplateAndReturnResponse(string $templateFileName, array $variables = []): ResponseInterface
180
    {
181
        $moduleTemplate = $this->moduleTemplateFactory->create($this->request);
182
        $this->pageRenderer->addCssFile('EXT:sf_event_mgt/Resources/Public/Css/administration.css');
183
184
        $this->pageRenderer->loadJavaScriptModule('@derhansen/sf_event_mgt/administration-module.js');
185
186
        $this->registerDocHeaderButtons($moduleTemplate);
187
188
        $moduleTemplate->setFlashMessageQueue($this->getFlashMessageQueue());
189
190
        $initAdministrationModuleTemplateEvent = new InitAdministrationModuleTemplateEvent(
191
            $moduleTemplate,
192
            $this->uriBuilder,
193
            $this
194
        );
195
        $this->eventDispatcher->dispatch($initAdministrationModuleTemplateEvent);
196
197
        $variables['settings'] = $this->settings;
198
        $moduleTemplate->assignMultiple($variables);
199
200
        return $moduleTemplate->renderResponse($templateFileName);
201
    }
202
203
    public function initializeAction(): void
204
    {
205
        $this->pid = (int)($this->request->getQueryParams()['id'] ?? 0);
206
    }
207
208
    /**
209
     * Set date format for fields startDate and endDate
210
     */
211
    public function initializeListAction(): void
212
    {
213
        // Static format needed for date picker (flatpickr), see BackendController::generateJavascript() and #91606
214
        if (!empty($this->settings)) {
215
            $this->settings['search']['dateFormat'] = 'H:i d-m-Y';
216
        }
217
    }
218
219
    /**
220
     * List action for backend module
221
     */
222
    public function listAction(?SearchDemand $searchDemand = null, array $overwriteDemand = []): ResponseInterface
223
    {
224
        if (empty($this->settings)) {
225
            return $this->redirect('settingsError');
226
        }
227
228
        if ($searchDemand !== null) {
229
            $searchDemand->setFields($this->settings['search']['fields'] ?? 'title');
230
231
            $sessionData = [];
232
            $sessionData['searchDemand'] = $searchDemand->toArray();
233
            $sessionData['overwriteDemand'] = $overwriteDemand;
234
            $this->beUserSessionService->saveSessionData($sessionData);
235
        } else {
236
            // Try to restore search demand from Session
237
            $sessionSearchDemand = $this->beUserSessionService->getSessionDataByKey('searchDemand') ?? [];
238
            $searchDemand = SearchDemand::fromArray($sessionSearchDemand);
239
            $overwriteDemand = $this->beUserSessionService->getSessionDataByKey('overwriteDemand');
240
        }
241
242
        if ($this->isResetFilter()) {
243
            $searchDemand = GeneralUtility::makeInstance(SearchDemand::class);
244
            $overwriteDemand = [];
245
246
            $sessionData = [];
247
            $sessionData['searchDemand'] = $searchDemand->toArray();
248
            $sessionData['overwriteDemand'] = $overwriteDemand;
249
            $this->beUserSessionService->saveSessionData($sessionData);
250
        }
251
252
        // Initialize default ordering when no overwriteDemand is available
253
        if (empty($overwriteDemand)) {
254
            $overwriteDemand = [
255
                'orderField' => $this->settings['defaultSorting']['orderField'] ?? 'title',
256
                'orderDirection' => $this->settings['defaultSorting']['orderDirection'] ?? 'asc',
257
            ];
258
        }
259
260
        $eventDemand = GeneralUtility::makeInstance(EventDemand::class);
261
        $eventDemand = $this->overwriteEventDemandObject($eventDemand, $overwriteDemand);
0 ignored issues
show
Bug introduced by
It seems like $overwriteDemand 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

261
        $eventDemand = $this->overwriteEventDemandObject($eventDemand, /** @scrutinizer ignore-type */ $overwriteDemand);
Loading history...
262
        $eventDemand->setOrderFieldAllowed($this->settings['orderFieldAllowed'] ?? '');
263
        $eventDemand->setSearchDemand($searchDemand);
264
        $eventDemand->setStoragePage((string)$this->pid);
265
        $eventDemand->setIgnoreEnableFields(true);
266
267
        $events = [];
268
        $pagination = null;
269
        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...
270
            $this->getBackendUser()->check('tables_select', 'tx_sfeventmgt_domain_model_event')
271
        ) {
272
            $events = $this->eventRepository->findDemanded($eventDemand);
273
            $pagination = $this->getPagination($events, $this->settings['pagination'] ?? []);
274
        }
275
276
        $modifyAdministrationListViewVariablesEvent = new ModifyAdministrationListViewVariablesEvent(
277
            [
278
                'pid' => $this->pid,
279
                'events' => $events,
280
                'searchDemand' => $searchDemand,
281
                'orderByFields' => $this->getOrderByFields(),
282
                'orderDirections' => $this->getOrderDirections(),
283
                'overwriteDemand' => $overwriteDemand,
284
                'pagination' => $pagination,
285
            ],
286
            $this
287
        );
288
        $this->eventDispatcher->dispatch($modifyAdministrationListViewVariablesEvent);
289
        $variables = $modifyAdministrationListViewVariablesEvent->getVariables();
290
291
        return $this->initModuleTemplateAndReturnResponse('Administration/List', $variables);
292
    }
293
294
    /**
295
     * Returns, if reset filter operation has been used
296
     */
297
    private function isResetFilter(): bool
298
    {
299
        $resetFilter = false;
300
        if ($this->request->hasArgument('operation')) {
301
            $resetFilter = $this->request->getArgument('operation') === 'reset-filters';
302
        }
303
304
        return $resetFilter;
305
    }
306
307
    /**
308
     * Export registrations for a given event
309
     */
310
    public function exportAction(int $eventUid): void
311
    {
312
        /** @var Event $event */
313
        $event = $this->eventRepository->findByUidIncludeHidden($eventUid);
314
        if ($event !== null) {
315
            $this->checkEventAccess($event);
316
            $this->exportService->downloadRegistrationsCsv($eventUid, $this->settings['csvExport'] ?? []);
317
        }
318
        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...
319
    }
320
321
    /**
322
     * Handles expired registrations
323
     */
324
    public function handleExpiredRegistrationsAction(): ResponseInterface
325
    {
326
        $delete = (bool)($this->settings['registration']['deleteExpiredRegistrations'] ?? false);
327
        $this->maintenanceService->handleExpiredRegistrations($delete);
328
329
        $this->addFlashMessage(
330
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-1.content'),
331
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-1.title')
332
        );
333
334
        return $this->redirect('list');
335
    }
336
337
    /**
338
     * The index notify action
339
     */
340
    public function indexNotifyAction(Event $event): ResponseInterface
341
    {
342
        $this->checkEventAccess($event);
343
        $customNotification = GeneralUtility::makeInstance(CustomNotification::class);
344
        $customNotifications = $this->settingsService->getCustomNotifications($this->settings);
345
        $logEntries = $this->customNotificationLogRepository->findByEvent($event);
0 ignored issues
show
Deprecated Code introduced by
The function DERHANSEN\SfEventMgt\Dom...LogRepository::__call() has been deprecated: since v12, will be removed in v14, use {@see findBy}, {@see findOneBy} and {@see count} instead ( Ignorable by Annotation )

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

345
        $logEntries = /** @scrutinizer ignore-deprecated */ $this->customNotificationLogRepository->findByEvent($event);

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...
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

345
        /** @scrutinizer ignore-call */ 
346
        $logEntries = $this->customNotificationLogRepository->findByEvent($event);
Loading history...
346
347
        $modifyAdministrationIndexNotifyViewVariablesEvent = new ModifyAdministrationIndexNotifyViewVariablesEvent(
348
            [
349
                'event' => $event,
350
                'recipients' => $this->getNotificationRecipients(),
351
                'customNotification' => $customNotification,
352
                'customNotifications' => $customNotifications,
353
                'logEntries' => $logEntries,
354
            ],
355
            $this
356
        );
357
        $this->eventDispatcher->dispatch($modifyAdministrationIndexNotifyViewVariablesEvent);
358
        $variables = $modifyAdministrationIndexNotifyViewVariablesEvent->getVariables();
359
360
        return $this->initModuleTemplateAndReturnResponse('Administration/IndexNotify', $variables);
361
    }
362
363
    /**
364
     * Returns an array of recipient option for the indexNotify action
365
     */
366
    public function getNotificationRecipients(): array
367
    {
368
        return [
369
            [
370
                'value' => CustomNotification::RECIPIENTS_ALL,
371
                'label' => $this->getLanguageService()->sL(
372
                    self::LANG_FILE . 'administration.notify.recipients.' . CustomNotification::RECIPIENTS_ALL
373
                ),
374
            ],
375
            [
376
                'value' => CustomNotification::RECIPIENTS_CONFIRMED,
377
                'label' => $this->getLanguageService()->sL(
378
                    self::LANG_FILE . 'administration.notify.recipients.' . CustomNotification::RECIPIENTS_CONFIRMED
379
                ),
380
            ],
381
            [
382
                'value' => CustomNotification::RECIPIENTS_UNCONFIRMED,
383
                'label' => $this->getLanguageService()->sL(
384
                    self::LANG_FILE . 'administration.notify.recipients.' . CustomNotification::RECIPIENTS_UNCONFIRMED
385
                ),
386
            ],
387
        ];
388
    }
389
390
    /**
391
     * Notify action
392
     */
393
    public function notifyAction(Event $event, CustomNotification $customNotification): ResponseInterface
394
    {
395
        $this->checkEventAccess($event);
396
        $customNotifications = $this->settingsService->getCustomNotifications($this->settings);
397
        $result = $this->notificationService->sendCustomNotification($event, $customNotification, $this->settings);
398
        $this->notificationService->createCustomNotificationLogentry(
399
            $event,
400
            $customNotifications[$customNotification->getTemplate()],
401
            $result,
402
            $customNotification
403
        );
404
        $this->addFlashMessage(
405
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-2.content'),
406
            $this->getLanguageService()->sL(self::LANG_FILE . 'administration.message-2.title')
407
        );
408
        return $this->redirect('list');
409
    }
410
411
    /**
412
     * Checks if the current backend user has access to the PID of the event and if not, enqueue an
413
     * access denied flash message and redirect to list view
414
     */
415
    public function checkEventAccess(Event $event): void
416
    {
417
        if ($this->getBackendUser()->isInWebMount($event->getPid()) === null) {
418
            $this->addFlashMessage(
419
                $this->getLanguageService()->sL(self::LANG_FILE . 'administration.accessdenied.content'),
420
                $this->getLanguageService()->sL(self::LANG_FILE . 'administration.accessdenied.title'),
421
                ContextualFeedbackSeverity::ERROR
422
            );
423
424
            $this->redirect('list');
425
        }
426
    }
427
428
    /**
429
     * Shows the settings error view
430
     */
431
    public function settingsErrorAction(): ResponseInterface
432
    {
433
        return $this->initModuleTemplateAndReturnResponse('Administration/SettingsError');
434
    }
435
436
    /**
437
     * Suppress default validation messages
438
     */
439
    protected function getErrorFlashMessage(): bool
440
    {
441
        return false;
442
    }
443
444
    /**
445
     * Returns an array with possible order directions
446
     */
447
    public function getOrderDirections(): array
448
    {
449
        return [
450
            'asc' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.sortOrder.asc'),
451
            'desc' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.sortOrder.desc'),
452
        ];
453
    }
454
455
    /**
456
     * Returns an array with possible orderBy fields
457
     */
458
    public function getOrderByFields(): array
459
    {
460
        return [
461
            'title' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.orderBy.title'),
462
            'startdate' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.orderBy.startdate'),
463
            'enddate' => $this->getLanguageService()->sL(self::LANG_FILE . 'administration.orderBy.enddate'),
464
        ];
465
    }
466
467
    protected function getLanguageService(): LanguageService
468
    {
469
        return $GLOBALS['LANG'];
470
    }
471
472
    protected function getBackendUser(): BackendUserAuthentication
473
    {
474
        return $GLOBALS['BE_USER'];
475
    }
476
}
477