NotificationService   B
last analyzed

Complexity

Total Complexity 50

Size/Duplication

Total Lines 435
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 256
dl 0
loc 435
rs 8.4
c 7
b 0
f 0
wmc 50

10 Methods

Rating   Name   Duplication   Size   Complexity  
B getAdminMessageTemplateSubject() 0 28 6
A createCustomNotificationLogentry() 0 23 1
A __construct() 0 10 1
A sendCustomNotification() 0 35 4
A cantSendCustomNotification() 0 5 2
A getNotificationBody() 0 26 3
A getTargetLinkAction() 0 16 5
B sendUserMessage() 0 117 6
B getUserMessageTemplateSubject() 0 51 11
C sendAdminMessage() 0 78 11

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

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