Passed
Push — main ( 8c9e9f...58ee80 )
by Torben
02:32
created

NotificationService   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 452
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 263
dl 0
loc 452
rs 5.04
c 8
b 0
f 0
wmc 57

16 Methods

Rating   Name   Duplication   Size   Complexity  
B getAdminMessageTemplateSubject() 0 28 6
B getUserMessageTemplateSubject() 0 51 11
A createCustomNotificationLogentry() 0 23 1
B sendAdminMessage() 0 71 11
A injectEventDispatcher() 0 3 1
A injectAttachmentService() 0 3 1
A sendCustomNotification() 0 33 4
A injectHashService() 0 3 1
A injectCustomNotificationLogRepository() 0 4 1
A cantSendCustomNotification() 0 5 2
A injectRegistrationRepository() 0 3 1
A injectEmailService() 0 3 1
A getNotificationBody() 0 26 4
A getTargetLinkAction() 0 16 5
B sendUserMessage() 0 111 6
A injectFluidStandaloneService() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like NotificationService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use NotificationService, and based on these observations, apply Extract Interface, too.

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\Service;
13
14
use DERHANSEN\SfEventMgt\Domain\Model\CustomNotificationLog;
15
use DERHANSEN\SfEventMgt\Domain\Model\Dto\CustomNotification;
16
use DERHANSEN\SfEventMgt\Domain\Model\Event;
17
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
18
use DERHANSEN\SfEventMgt\Domain\Repository\CustomNotificationLogRepository;
19
use DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository;
20
use DERHANSEN\SfEventMgt\Event\AfterAdminMessageSentEvent;
21
use DERHANSEN\SfEventMgt\Event\AfterUserMessageSentEvent;
22
use DERHANSEN\SfEventMgt\Event\ModifyCustomNotificationLogEvent;
23
use DERHANSEN\SfEventMgt\Event\ModifyUserMessageAttachmentsEvent;
24
use DERHANSEN\SfEventMgt\Event\ModifyUserMessageSenderEvent;
25
use DERHANSEN\SfEventMgt\Service\Notification\AttachmentService;
26
use DERHANSEN\SfEventMgt\Utility\MessageRecipient;
27
use DERHANSEN\SfEventMgt\Utility\MessageType;
28
use Psr\EventDispatcher\EventDispatcherInterface;
29
use Psr\Http\Message\ServerRequestInterface;
30
use RuntimeException;
31
use TYPO3\CMS\Core\Http\ApplicationType;
32
use TYPO3\CMS\Core\Utility\GeneralUtility;
33
use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Securi...ryptography\HashService 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...
34
35
class NotificationService
36
{
37
    protected RegistrationRepository $registrationRepository;
38
    protected EmailService $emailService;
39
    protected HashService $hashService;
40
    protected FluidStandaloneService $fluidStandaloneService;
41
    protected CustomNotificationLogRepository $customNotificationLogRepository;
42
    protected AttachmentService $attachmentService;
43
    protected EventDispatcherInterface $eventDispatcher;
44
45
    public function injectAttachmentService(AttachmentService $attachmentService): void
46
    {
47
        $this->attachmentService = $attachmentService;
48
    }
49
50
    public function injectCustomNotificationLogRepository(
51
        CustomNotificationLogRepository $customNotificationLogRepository
52
    ): void {
53
        $this->customNotificationLogRepository = $customNotificationLogRepository;
54
    }
55
56
    public function injectEmailService(EmailService $emailService): void
57
    {
58
        $this->emailService = $emailService;
59
    }
60
61
    public function injectFluidStandaloneService(FluidStandaloneService $fluidStandaloneService): void
62
    {
63
        $this->fluidStandaloneService = $fluidStandaloneService;
64
    }
65
66
    public function injectHashService(HashService $hashService): void
67
    {
68
        $this->hashService = $hashService;
69
    }
70
71
    public function injectRegistrationRepository(RegistrationRepository $registrationRepository): void
72
    {
73
        $this->registrationRepository = $registrationRepository;
74
    }
75
76
    public function injectEventDispatcher(EventDispatcherInterface $eventDispatcher): void
77
    {
78
        $this->eventDispatcher = $eventDispatcher;
79
    }
80
81
    /**
82
     * Sends a custom notification defined by the given customNotification key
83
     * to users of the event. Returns the number of notifications sent.
84
     */
85
    public function sendCustomNotification(
86
        Event $event,
87
        CustomNotification $customNotification,
88
        array $settings = []
89
    ): int {
90
        if ($this->cantSendCustomNotification($settings, $customNotification)) {
91
            return 0;
92
        }
93
        $count = 0;
94
95
        $customNotificationSettings = $settings['notification']['customNotifications'] ?? [];
96
        $constraints = $customNotificationSettings[$customNotification->getTemplate()]['constraints'] ?? [];
97
        $registrations = $this->registrationRepository->findNotificationRegistrations(
98
            $event,
99
            $customNotification,
100
            $constraints
101
        );
102
103
        foreach ($registrations as $registration) {
104
            /** @var Registration $registration */
105
            $result = $this->sendUserMessage(
106
                $event,
107
                $registration,
108
                $settings,
109
                MessageType::CUSTOM_NOTIFICATION,
110
                $customNotification
111
            );
112
            if ($result) {
113
                $count += 1;
114
            }
115
        }
116
117
        return $count;
118
    }
119
120
    /**
121
     * Returns true if conditions are not met to send a custom notification
122
     */
123
    protected function cantSendCustomNotification(
124
        array $settings,
125
        CustomNotification $customNotification
126
    ): bool {
127
        return $customNotification->getTemplate() === '' || empty($settings);
128
    }
129
130
    /**
131
     * Adds a logentry to the custom notification log
132
     */
133
    public function createCustomNotificationLogentry(
134
        Event $event,
135
        string $details,
136
        int $emailsSent,
137
        CustomNotification $customNotification
138
    ): void {
139
        $notificationlogEntry = new CustomNotificationLog();
140
        $notificationlogEntry->setPid($event->getPid());
141
        $notificationlogEntry->setEvent($event);
142
        $notificationlogEntry->setDetails($details);
143
        $notificationlogEntry->setEmailsSent($emailsSent);
144
        $notificationlogEntry->setCruserId($GLOBALS['BE_USER']->user['uid'] ?? 0);
145
146
        $modifyCustomNotificationLogEntry = new ModifyCustomNotificationLogEvent(
147
            $notificationlogEntry,
148
            $event,
149
            $details,
150
            $customNotification
151
        );
152
        $this->eventDispatcher->dispatch($modifyCustomNotificationLogEntry);
153
        $notificationlogEntry = $modifyCustomNotificationLogEntry->getCustomNotificationLog();
154
155
        $this->customNotificationLogRepository->add($notificationlogEntry);
156
    }
157
158
    /**
159
     * Sends a message to the user based on the given type
160
     */
161
    public function sendUserMessage(
162
        Event $event,
163
        Registration $registration,
164
        array $settings,
165
        int $type,
166
        ?CustomNotification $customNotification = null
167
    ): bool {
168
        [$template, $subject] = $this->getUserMessageTemplateSubject(
169
            $settings,
170
            $type,
171
            $customNotification
172
        );
173
174
        if ((bool)($settings['notification']['disabled'] ?? false) || !str_ends_with($template, '.html')) {
175
            return false;
176
        }
177
178
        $additionalBodyVariables = [
179
            'customNotification' => $customNotification,
180
        ];
181
182
        if (!$registration->isIgnoreNotifications()) {
183
            $body = $this->getNotificationBody($event, $registration, $template, $settings, $additionalBodyVariables);
184
            $subject = $this->fluidStandaloneService->parseStringFluid(
185
                $subject,
186
                [
187
                    'event' => $event,
188
                    'registration' => $registration,
189
                ]
190
            );
191
            $attachments = $this->attachmentService->getAttachments(
192
                $settings,
193
                $registration,
194
                $type,
195
                MessageRecipient::USER,
196
                $customNotification
197
            );
198
199
            // Get iCal attachment if configured
200
            $iCalAttachment = $this->attachmentService->getICalAttachment(
201
                $settings,
202
                $registration,
203
                $type,
204
                MessageRecipient::USER,
205
                $customNotification
206
            );
207
208
            if ($iCalAttachment !== '') {
209
                $attachments[] = $iCalAttachment;
210
            }
211
212
            $modifyUserMessageSenderEvent = new ModifyUserMessageSenderEvent(
213
                $settings['notification']['senderName'] ?? '',
214
                $settings['notification']['senderEmail'] ?? '',
215
                $settings['notification']['replyToEmail'] ?? '',
216
                $subject,
217
                $body,
218
                $registration,
219
                $type,
220
                $this
221
            );
222
            $this->eventDispatcher->dispatch($modifyUserMessageSenderEvent);
223
            $subject = $modifyUserMessageSenderEvent->getSubject();
224
            $body = $modifyUserMessageSenderEvent->getBody();
225
226
            $senderName = $modifyUserMessageSenderEvent->getSenderName();
227
            $senderEmail = $modifyUserMessageSenderEvent->getSenderEmail();
228
            $replyToEmail = $modifyUserMessageSenderEvent->getReplyToEmail();
229
230
            $modifyUserAttachmentsEvent = new ModifyUserMessageAttachmentsEvent(
231
                $attachments,
232
                $registration,
233
                $type,
234
                $settings,
235
                $customNotification,
236
                $this
237
            );
238
            $this->eventDispatcher->dispatch($modifyUserAttachmentsEvent);
239
            $attachments = $modifyUserAttachmentsEvent->getAttachments();
240
241
            $result = $this->emailService->sendEmailMessage(
242
                $senderEmail,
243
                $registration->getEmail(),
244
                $subject,
245
                $body,
246
                $senderName,
247
                $attachments,
248
                $replyToEmail
249
            );
250
251
            $afterUserMessageSentEvent = new AfterUserMessageSentEvent(
252
                $registration,
253
                $body,
254
                $subject,
255
                $attachments,
256
                $senderName,
257
                $senderEmail,
258
                $replyToEmail,
259
                $this
260
            );
261
            $this->eventDispatcher->dispatch($afterUserMessageSentEvent);
262
263
            // Cleanup iCal attachment if available
264
            if ($iCalAttachment !== '') {
265
                GeneralUtility::unlink_tempfile($iCalAttachment);
266
            }
267
268
            return $result;
269
        }
270
271
        return false;
272
    }
273
274
    /**
275
     * Returns an array with template and subject for the user message
276
     */
277
    protected function getUserMessageTemplateSubject(
278
        array $settings,
279
        int $type,
280
        ?CustomNotification $customNotification = null
281
    ): array {
282
        if ($type === MessageType::CUSTOM_NOTIFICATION && $customNotification === null) {
283
            return ['', ''];
284
        }
285
286
        switch ($type) {
287
            case MessageType::REGISTRATION_NEW:
288
                $template = 'Notification/User/RegistrationNew.html';
289
                $subject = $settings['notification']['registrationNew']['userSubject'] ?? '';
290
                break;
291
            case MessageType::REGISTRATION_WAITLIST_NEW:
292
                $template = 'Notification/User/RegistrationWaitlistNew.html';
293
                $subject = $settings['notification']['registrationWaitlistNew']['userSubject'] ?? '';
294
                break;
295
            case MessageType::REGISTRATION_CONFIRMED:
296
                $template = 'Notification/User/RegistrationConfirmed.html';
297
                $subject = $settings['notification']['registrationConfirmed']['userSubject'] ?? '';
298
                break;
299
            case MessageType::REGISTRATION_WAITLIST_CONFIRMED:
300
                $template = 'Notification/User/RegistrationWaitlistConfirmed.html';
301
                $subject = $settings['notification']['registrationWaitlistConfirmed']['userSubject'] ?? '';
302
                break;
303
            case MessageType::REGISTRATION_CANCELLED:
304
                $template = 'Notification/User/RegistrationCancelled.html';
305
                $subject = $settings['notification']['registrationCancelled']['userSubject'] ?? '';
306
                break;
307
            case MessageType::REGISTRATION_WAITLIST_MOVE_UP:
308
                $template = 'Notification/User/RegistrationWaitlistMoveUp.html';
309
                $subject = $settings['notification']['registrationWaitlistMoveUp']['userSubject'] ?? '';
310
                break;
311
            case MessageType::CUSTOM_NOTIFICATION:
312
                $customNotificationSettings = $settings['notification']['customNotifications'] ?? [];
313
                $templateKey = $customNotification->getTemplate();
0 ignored issues
show
Bug introduced by
The method getTemplate() does not exist on null. ( Ignorable by Annotation )

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

313
                /** @scrutinizer ignore-call */ 
314
                $templateKey = $customNotification->getTemplate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
314
                $template = 'Notification/User/Custom/' . $customNotificationSettings[$templateKey]['template'] ?? '';
315
                $subject = $customNotificationSettings[$templateKey]['subject'] ?? '';
316
                if ($customNotification->getOverwriteSubject() !== '') {
317
                    $subject = $customNotification->getOverwriteSubject();
318
                }
319
                break;
320
            default:
321
                $template = '';
322
                $subject = '';
323
        }
324
325
        return [
326
            $template,
327
            $subject,
328
        ];
329
    }
330
331
    /**
332
     * Sends a message to the admin based on the given type. Returns true, if the message was sent, otherwise false
333
     */
334
    public function sendAdminMessage(Event $event, Registration $registration, array $settings, int $type): bool
335
    {
336
        [$template, $subject] = $this->getAdminMessageTemplateSubject($settings, $type);
337
338
        if ((bool)($settings['notification']['disabled'] ?? false) ||
339
            ($event->getNotifyAdmin() === false && $event->getNotifyOrganisator() === false)
340
        ) {
341
            return false;
342
        }
343
344
        $allEmailsSent = true;
345
        $body = $this->getNotificationBody($event, $registration, $template, $settings);
346
        $subject = $this->fluidStandaloneService->parseStringFluid(
347
            $subject,
348
            [
349
                'event' => $event,
350
                'registration' => $registration,
351
            ]
352
        );
353
        $attachments = $this->attachmentService->getAttachments(
354
            $settings,
355
            $registration,
356
            $type,
357
            MessageRecipient::ADMIN
358
        );
359
360
        $senderName = $settings['notification']['senderName'] ?? '';
361
        $senderEmail = $settings['notification']['senderEmail'] ?? '';
362
        if ((bool)($settings['notification']['registrationDataAsSenderForAdminEmails'] ?? false)) {
363
            $senderName = $registration->getFullname();
364
            $senderEmail = $registration->getEmail();
365
        }
366
367
        if ($event->getNotifyAdmin()) {
368
            $adminEmailArr = GeneralUtility::trimExplode(',', $settings['notification']['adminEmail'] ?? '', true);
369
            foreach ($adminEmailArr as $adminEmail) {
370
                $allEmailsSent = $allEmailsSent && $this->emailService->sendEmailMessage(
371
                    $senderEmail,
372
                    $adminEmail,
373
                    $subject,
374
                    $body,
375
                    $senderName,
376
                    $attachments
377
                );
378
            }
379
        }
380
381
        if ($event->getNotifyOrganisator() && $event->getOrganisator()) {
382
            $allEmailsSent = $allEmailsSent && $this->emailService->sendEmailMessage(
383
                $senderEmail,
384
                $event->getOrganisator()->getEmail(),
385
                $subject,
386
                $body,
387
                $senderName,
388
                $attachments
389
            );
390
        }
391
392
        $afterAdminMessageSentEvent = new AfterAdminMessageSentEvent(
393
            $registration,
394
            $body,
395
            $subject,
396
            $attachments,
397
            $senderName,
398
            $senderEmail,
399
            $type,
400
            $this
401
        );
402
        $this->eventDispatcher->dispatch($afterAdminMessageSentEvent);
403
404
        return $allEmailsSent;
405
    }
406
407
    /**
408
     * Returns an array with template and subject for the admin message
409
     */
410
    protected function getAdminMessageTemplateSubject(array $settings, int $type): array
411
    {
412
        $template = 'Notification/Admin/RegistrationNew.html';
413
        $subject = $settings['notification']['registrationNew']['adminSubject'] ?? '';
414
        switch ($type) {
415
            case MessageType::REGISTRATION_WAITLIST_NEW:
416
                $template = 'Notification/Admin/RegistrationWaitlistNew.html';
417
                $subject = $settings['notification']['registrationWaitlistNew']['adminSubject'] ?? '';
418
                break;
419
            case MessageType::REGISTRATION_CONFIRMED:
420
                $template = 'Notification/Admin/RegistrationConfirmed.html';
421
                $subject = $settings['notification']['registrationConfirmed']['adminSubject'] ?? '';
422
                break;
423
            case MessageType::REGISTRATION_WAITLIST_CONFIRMED:
424
                $template = 'Notification/Admin/RegistrationWaitlistConfirmed.html';
425
                $subject = $settings['notification']['registrationWaitlistConfirmed']['adminSubject'] ?? '';
426
                break;
427
            case MessageType::REGISTRATION_CANCELLED:
428
                $template = 'Notification/Admin/RegistrationCancelled.html';
429
                $subject = $settings['notification']['registrationCancelled']['adminSubject'] ?? '';
430
                break;
431
            case MessageType::REGISTRATION_WAITLIST_MOVE_UP:
432
                $template = 'Notification/Admin/RegistrationWaitlistMoveUp.html';
433
                $subject = $settings['notification']['registrationWaitlistMoveUp']['adminSubject'] ?? '';
434
                break;
435
        }
436
437
        return [$template, $subject];
438
    }
439
440
    /**
441
     * Returns the rendered HTML for the given template
442
     */
443
    protected function getNotificationBody(
444
        Event $event,
445
        Registration $registration,
446
        string $template,
447
        array $settings,
448
        array $additionalBodyVariables = []
449
    ): string {
450
        $isBackendRequest = ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
451
            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend();
452
453
        if ($isBackendRequest && $registration->getLanguage() !== '') {
454
            // Temporary set Language of current BE user to given language
455
            $GLOBALS['BE_USER']->uc['lang'] = $registration->getLanguage();
456
        }
457
        $defaultVariables = [
458
            'event' => $event,
459
            'registration' => $registration,
460
            'settings' => $settings,
461
            'hmac' => $this->hashService->generateHmac('reg-' . $registration->getUid()),
462
            'reghmac' => $this->hashService->appendHmac((string)$registration->getUid()),
463
            'confirmAction' => $this->getTargetLinkAction('confirmAction', $settings),
464
            'cancelAction' => $this->getTargetLinkAction('cancelAction', $settings),
465
        ];
466
        $variables = array_merge($additionalBodyVariables, $defaultVariables);
467
468
        return $this->fluidStandaloneService->renderTemplate($template, $variables);
469
    }
470
471
    private function getTargetLinkAction(string $action, array $settings): string
472
    {
473
        switch ($action) {
474
            case 'confirmAction':
475
                $additionalStep = (bool)($settings['confirmation']['additionalVerificationStep'] ?? false);
476
                $action = $additionalStep ? 'verifyConfirmRegistration' : 'confirmRegistration';
477
                break;
478
            case 'cancelAction':
479
                $additionalStep = (bool)($settings['cancellation']['additionalVerificationStep'] ?? false);
480
                $action = $additionalStep ? 'verifyCancelRegistration' : 'cancelRegistration';
481
                break;
482
            default:
483
                throw new RuntimeException('Unknown action for getTargetLinkAction()', 1718170550);
484
        }
485
486
        return $action;
487
    }
488
}
489