Notification   F
last analyzed

Complexity

Total Complexity 65

Size/Duplication

Total Lines 535
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 298
c 1
b 0
f 0
dl 0
loc 535
rs 3.2
wmc 65

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getTitlePrefix() 0 3 1
A __construct() 0 24 4
A send() 0 23 4
A getDefaultPlatformSenderEmail() 0 3 1
A getDefaultPlatformSenderName() 0 3 1
B formatTitle() 0 60 9
B sendPushNotification() 0 74 7
D formatContent() 0 96 18
F saveNotification() 0 118 20

How to fix   Complexity   

Complex Class

Complex classes like Notification 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 Notification, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Framework\Container;
5
use Chamilo\CoreBundle\Event\AbstractEvent;
6
use Chamilo\CoreBundle\Event\Events;
7
use Chamilo\CoreBundle\Event\NotificationContentFormattedEvent;
8
use Chamilo\CoreBundle\Event\NotificationTitleFormattedEvent;
9
10
/**
11
 * Notification class
12
 * This class provides methods for the Notification management.
13
 * Include/require it in your code to use its features.
14
 */
15
class Notification extends Model
16
{
17
    // mail_notify_message ("At once", "Daily", "No")
18
    const NOTIFY_MESSAGE_AT_ONCE = 1;
19
    const NOTIFY_MESSAGE_DAILY = 8;
20
    const NOTIFY_MESSAGE_WEEKLY = 12;
21
    const NOTIFY_MESSAGE_NO = 0;
22
23
    // mail_notify_invitation ("At once", "Daily", "No")
24
    const NOTIFY_INVITATION_AT_ONCE = 1;
25
    const NOTIFY_INVITATION_DAILY = 8;
26
    const NOTIFY_INVITATION_WEEKLY = 12;
27
    const NOTIFY_INVITATION_NO = 0;
28
29
    // mail_notify_group_message ("At once", "Daily", "No")
30
    const NOTIFY_GROUP_AT_ONCE = 1;
31
    const NOTIFY_GROUP_DAILY = 8;
32
    const NOTIFY_GROUP_WEEKLY = 12;
33
    const NOTIFY_GROUP_NO = 0;
34
35
    // Notification types
36
    const NOTIFICATION_TYPE_MESSAGE = 1;
37
    const NOTIFICATION_TYPE_INVITATION = 2;
38
    const NOTIFICATION_TYPE_GROUP = 3;
39
    const NOTIFICATION_TYPE_WALL_MESSAGE = 4;
40
    const NOTIFICATION_TYPE_DIRECT_MESSAGE = 5;
41
42
    public $table;
43
    public $columns = [
44
        'id',
45
        'dest_user_id',
46
        'dest_mail',
47
        'title',
48
        'content',
49
        'send_freq',
50
        'created_at',
51
        'sent_at',
52
    ];
53
54
    //Max length of the notification.content field
55
    public $max_content_length = 254;
56
    public $debug = false;
57
58
    /* message, invitation, group messages */
59
    public $type;
60
    public string $adminName = '';
61
    public string $adminEmail = '';
62
    public $titlePrefix;
63
64
    public function __construct()
65
    {
66
        $this->table = Database::get_main_table(TABLE_NOTIFICATION);
67
68
        if ($smtpFromEmail = api_get_setting('mail.mailer_from_email')) {
69
            $this->adminEmail = $smtpFromEmail;
70
71
            if ($smtpFromName = api_get_setting('mail.mailer_from_name')) {
72
                $this->adminName = $smtpFromName;
73
            }
74
        } else {
75
            // Default no-reply email
76
            $this->adminEmail = api_get_setting('mail.mailer_from_email');
77
            $this->adminName = api_get_setting('platform.site_name');
78
            $this->titlePrefix = '['.api_get_setting('platform.site_name').'] ';
0 ignored issues
show
Bug introduced by
Are you sure api_get_setting('platform.site_name') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

78
            $this->titlePrefix = '['./** @scrutinizer ignore-type */ api_get_setting('platform.site_name').'] ';
Loading history...
79
80
            // If no-reply email doesn't exist use the admin name/email
81
            if (empty($this->adminEmail)) {
82
                $this->adminEmail = api_get_setting('admin.administrator_email');
83
                $this->adminName = api_get_person_name(
84
                    api_get_setting('admin.administrator_name'),
85
                    api_get_setting('admin.administrator_surname'),
86
                    null,
87
                    PERSON_NAME_EMAIL_ADDRESS
88
                );
89
            }
90
        }
91
    }
92
93
    /**
94
     * @return string
95
     */
96
    public function getTitlePrefix()
97
    {
98
        return $this->titlePrefix;
99
    }
100
101
    /**
102
     * @return string
103
     */
104
    public function getDefaultPlatformSenderEmail()
105
    {
106
        return $this->adminEmail;
107
    }
108
109
    /**
110
     * @return string
111
     */
112
    public function getDefaultPlatformSenderName()
113
    {
114
        return $this->adminName;
115
    }
116
117
    /**
118
     *  Send the notifications.
119
     *
120
     *  @param int $frequency notification frequency
121
     */
122
    public function send($frequency = 8)
123
    {
124
        $notifications = $this->find(
125
            'all',
126
            ['where' => ['sent_at IS NULL AND send_freq = ?' => $frequency]]
127
        );
128
129
        if (!empty($notifications)) {
130
            foreach ($notifications as $item_to_send) {
131
                // Sending email
132
                api_mail_html(
133
                    $item_to_send['dest_mail'],
134
                    $item_to_send['dest_mail'],
135
                    Security::filter_terms($item_to_send['title']),
136
                    Security::filter_terms($item_to_send['content']),
137
                    $this->adminName,
138
                    $this->adminEmail
139
                );
140
                // Updating
141
                $item_to_send['sent_at'] = api_get_utc_datetime();
142
                $this->update($item_to_send);
143
                if ($this->debug) {
144
                    error_log('Updating record : '.print_r($item_to_send, 1));
0 ignored issues
show
Bug introduced by
Are you sure print_r($item_to_send, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

144
                    error_log('Updating record : './** @scrutinizer ignore-type */ print_r($item_to_send, 1));
Loading history...
145
                }
146
            }
147
        }
148
    }
149
150
    /**
151
     * Formats the title of the notification.
152
     *
153
     * @param string $title The original title of the notification.
154
     * @param array  $senderInfo Information about the sender.
155
     * @param bool   $forceTitleWhenSendingEmail Whether to force the use of the original title.
156
     * @param string|null $recipientLanguage The language of the recipient for translation.
157
     *
158
     * @return string The formatted title for the notification.
159
     */
160
    public function formatTitle(string $title, array $senderInfo, bool $forceTitleWhenSendingEmail = false, $recipientLanguage = null): string
161
    {
162
        $notificationTitleEvent = new NotificationTitleFormattedEvent(['title' => $title], AbstractEvent::TYPE_PRE);
163
164
        Container::getEventDispatcher()->dispatch($notificationTitleEvent, Events::NOTIFICATION_TITLE_FORMATTED);
165
166
        $title = $notificationTitleEvent->getTitle();
167
168
        $newTitle = $this->getTitlePrefix();
169
170
        switch ($this->type) {
171
            case self::NOTIFICATION_TYPE_MESSAGE:
172
                if (!empty($senderInfo)) {
173
                    $senderName = api_get_person_name(
174
                        $senderInfo['firstname'],
175
                        $senderInfo['lastname'],
176
                        null,
177
                        PERSON_NAME_EMAIL_ADDRESS
178
                    );
179
                    $newTitle .= sprintf(get_lang('You have a new message from %s', $recipientLanguage), $senderName);
180
                }
181
                break;
182
            case self::NOTIFICATION_TYPE_DIRECT_MESSAGE:
183
                $newTitle = $title;
184
                break;
185
            case self::NOTIFICATION_TYPE_INVITATION:
186
                if (!empty($senderInfo)) {
187
                    $senderName = api_get_person_name(
188
                        $senderInfo['firstname'],
189
                        $senderInfo['lastname'],
190
                        null,
191
                        PERSON_NAME_EMAIL_ADDRESS
192
                    );
193
                    $newTitle .= sprintf(get_lang('You have a new invitation from %s', $recipientLanguage), $senderName);
194
                }
195
                break;
196
            case self::NOTIFICATION_TYPE_GROUP:
197
                if (!empty($senderInfo)) {
198
                    $senderName = $senderInfo['group_info']['title'];
199
                    $newTitle .= sprintf(get_lang('You have received a new message in group %s', $recipientLanguage), $senderName);
200
                    $senderName = api_get_person_name(
201
                        $senderInfo['user_info']['firstname'],
202
                        $senderInfo['user_info']['lastname'],
203
                        null,
204
                        PERSON_NAME_EMAIL_ADDRESS
205
                    );
206
                    $newTitle .= $senderName;
207
                }
208
                break;
209
        }
210
211
        if ($forceTitleWhenSendingEmail) {
212
            $newTitle = $title;
213
        }
214
215
        $notificationTitleEvent = new NotificationTitleFormattedEvent(['title' => $newTitle], AbstractEvent::TYPE_POST);
216
217
        Container::getEventDispatcher()->dispatch($notificationTitleEvent, Events::NOTIFICATION_TITLE_FORMATTED);
218
219
        return $notificationTitleEvent->getTitle();
220
    }
221
222
    /**
223
     * Save message notification.
224
     *
225
     * @param int    $type                       message type
226
     *                                           NOTIFICATION_TYPE_MESSAGE,
227
     *                                           NOTIFICATION_TYPE_INVITATION,
228
     *                                           NOTIFICATION_TYPE_GROUP
229
     * @param int    $messageId
230
     * @param array  $userList                   recipients: user list of ids
231
     * @param string $title
232
     * @param string $content
233
     * @param array  $senderInfo                 result of api_get_user_info() or GroupPortalManager:get_group_data()
234
     * @param array  $attachments
235
     * @param bool   $forceTitleWhenSendingEmail force the use of $title as subject instead of "You have a new message"
236
     */
237
    public function saveNotification(
238
        $messageId,
239
        $type,
240
        $userList,
241
        $title,
242
        $content,
243
        $senderInfo = [],
244
        $attachments = [],
245
        $forceTitleWhenSendingEmail = false,
246
        $baseUrl = null
247
    ) {
248
        $this->type = (int) $type;
249
        $messageId = (int) $messageId;
250
        $settingToCheck = '';
251
        $avoid_my_self = false;
252
253
        switch ($this->type) {
254
            case self::NOTIFICATION_TYPE_DIRECT_MESSAGE:
255
            case self::NOTIFICATION_TYPE_MESSAGE:
256
                $settingToCheck = 'mail_notify_message';
257
                $defaultStatus = self::NOTIFY_MESSAGE_AT_ONCE;
258
                break;
259
            case self::NOTIFICATION_TYPE_INVITATION:
260
                $settingToCheck = 'mail_notify_invitation';
261
                $defaultStatus = self::NOTIFY_INVITATION_AT_ONCE;
262
                break;
263
            case self::NOTIFICATION_TYPE_GROUP:
264
                $settingToCheck = 'mail_notify_group_message';
265
                $defaultStatus = self::NOTIFY_GROUP_AT_ONCE;
266
                $avoid_my_self = true;
267
                break;
268
            default:
269
                $defaultStatus = self::NOTIFY_MESSAGE_AT_ONCE;
270
                break;
271
        }
272
273
        $settingInfo = UserManager::get_extra_field_information_by_name($settingToCheck);
274
275
        if (!empty($userList)) {
276
            foreach ($userList as $user_id) {
277
                if ($avoid_my_self) {
278
                    if ($user_id == api_get_user_id()) {
279
                        continue;
280
                    }
281
                }
282
                $userInfo = api_get_user_info($user_id);
283
284
                $recipientLanguage = !empty($userInfo['locale']) ? $userInfo['locale'] : null;
285
                $content = $this->formatContent($messageId, $content, $senderInfo, $recipientLanguage, $baseUrl);
286
                $titleToNotification = $this->formatTitle($title, $senderInfo, $forceTitleWhenSendingEmail, $recipientLanguage);
287
288
                // Extra field was deleted or removed? Use the default status.
289
                $userSetting = $defaultStatus;
290
291
                if (!empty($settingInfo)) {
292
                    $extra_data = UserManager::get_extra_user_data($user_id);
293
294
                    if (isset($extra_data[$settingToCheck])) {
295
                        $userSetting = $extra_data[$settingToCheck];
296
                    }
297
298
                    // Means that user extra was not set
299
                    // Then send email now.
300
                    if ('' === $userSetting) {
301
                        $userSetting = self::NOTIFY_MESSAGE_AT_ONCE;
302
                    }
303
                }
304
305
                $sendDate = null;
306
                switch ($userSetting) {
307
                    // No notifications
308
                    case self::NOTIFY_MESSAGE_NO:
309
                    case self::NOTIFY_INVITATION_NO:
310
                    case self::NOTIFY_GROUP_NO:
311
                        break;
312
                    // Send notification right now!
313
                    case self::NOTIFY_MESSAGE_AT_ONCE:
314
                    case self::NOTIFY_INVITATION_AT_ONCE:
315
                    case self::NOTIFY_GROUP_AT_ONCE:
316
                        $extraHeaders = [];
317
                        $extraHeaders = [
318
                            'reply_to' => [
319
                                'name' => $this->adminName,
320
                                'mail' => $this->adminEmail,
321
                            ],
322
                        ];
323
324
                        if (!empty($userInfo['email'])) {
325
                            api_mail_html(
326
                                $userInfo['complete_name'],
327
                                $userInfo['mail'],
328
                                Security::filter_terms($titleToNotification),
329
                                Security::filter_terms($content),
330
                                $this->adminName,
331
                                $this->adminEmail,
332
                                $extraHeaders,
333
                                $attachments,
334
                                false
335
                            );
336
                        }
337
                        $sendDate = api_get_utc_datetime();
338
                }
339
340
                // Saving the notification to be sent some day.
341
                //$content = cut($content, $this->max_content_length);
342
                $params = [
343
                    'sent_at' => $sendDate,
344
                    'dest_user_id' => $user_id,
345
                    'dest_mail' => $userInfo['email'],
346
                    'title' => $title,
347
                    'content' => $content,
348
                    'send_freq' => $userSetting,
349
                ];
350
351
                $this->save($params);
352
            }
353
354
            self::sendPushNotification($userList, $title, $content);
355
        }
356
    }
357
358
    /**
359
     * Formats the content in order to add the welcome message,
360
     * the notification preference, etc.
361
     *
362
     * @param int    $messageId
363
     * @param string $content
364
     * @param array  $senderInfo result of api_get_user_info() or
365
     *                           GroupPortalManager:get_group_data()
366
     *
367
     * @return string
368
     * */
369
    public function formatContent($messageId, $content, $senderInfo, $recipientLanguage = null, $baseUrl = null)
370
    {
371
        $notificationContentEvent = new NotificationContentFormattedEvent(['content' => $content], AbstractEvent::TYPE_PRE);
372
373
        Container::getEventDispatcher()->dispatch($notificationContentEvent, Events::NOTIFICATION_CONTENT_FORMATTED);
374
375
        $content = $notificationContentEvent->getContent();
376
377
        $newMessageText = $linkToNewMessage = '';
378
        $showEmail = ('true' === api_get_setting('mail.show_user_email_in_notification'));
379
        $senderInfoName = '';
380
        $baseUrl = $baseUrl ?: api_get_path(WEB_PATH);
381
        $baseUrl = rtrim($baseUrl, '/');
382
383
        if (!empty($senderInfo) && isset($senderInfo['complete_name'])) {
384
            $senderInfoName = $senderInfo['complete_name'];
385
            if ($showEmail && isset($senderInfo['complete_name_with_email_forced'])) {
386
                $senderInfoName = $senderInfo['complete_name_with_email_forced'];
387
            }
388
        }
389
390
        switch ($this->type) {
391
            case self::NOTIFICATION_TYPE_DIRECT_MESSAGE:
392
                $newMessageText = '';
393
                $linkToNewMessage = Display::url(
394
                    get_lang('See message', $recipientLanguage),
395
                    $baseUrl . '/resources/messages/show?id=/api/messages/' . $messageId
396
                );
397
                break;
398
            case self::NOTIFICATION_TYPE_MESSAGE:
399
                $allow = ('true' === api_get_setting('mail.messages_hide_mail_content'));
400
                if ($allow) {
401
                    $content = '';
402
                }
403
                if (!empty($senderInfo)) {
404
                    $newMessageText = sprintf(
405
                        get_lang('You have a new message from %s', $recipientLanguage),
406
                        $senderInfoName
407
                    );
408
                }
409
                $linkToNewMessage = Display::url(
410
                    get_lang('See message', $recipientLanguage),
411
                    $baseUrl . '/resources/messages/show?id=/api/messages/' . $messageId
412
                );
413
                break;
414
            case self::NOTIFICATION_TYPE_INVITATION:
415
                if (!empty($senderInfo)) {
416
                    $newMessageText = sprintf(
417
                        get_lang('You have a new invitation from %s', $recipientLanguage),
418
                        $senderInfoName
419
                    );
420
                }
421
                $linkToNewMessage = Display::url(
422
                    get_lang('See invitation', $recipientLanguage),
423
                    $baseUrl . '/main/social/invitations.php'
424
                );
425
                break;
426
            case self::NOTIFICATION_TYPE_GROUP:
427
                $topicPage = isset($_REQUEST['topics_page_nr']) ? (int) $_REQUEST['topics_page_nr'] : 0;
428
                if (!empty($senderInfo)) {
429
                    $senderName = $senderInfo['group_info']['title'];
430
                    $newMessageText = sprintf(get_lang('You have received a new message in group %s', $recipientLanguage), $senderName);
431
                    $senderName = Display::url(
432
                        $senderInfoName,
433
                        $baseUrl . '/main/social/profile.php?' . $senderInfo['user_info']['user_id']
434
                    );
435
                    $newMessageText .= '<br />' . get_lang('User', $recipientLanguage) . ': ' . $senderName;
436
                }
437
                $groupUrl = $baseUrl . '/resources/usergroups/show/' . $senderInfo['group_info']['id'];
438
                $linkToNewMessage = Display::url(get_lang('See message', $recipientLanguage), $groupUrl);
439
                break;
440
        }
441
        $preferenceUrl = $baseUrl . '/main/auth/profile.php';
442
443
        // You have received a new message text
444
        if (!empty($newMessageText)) {
445
            $content = $newMessageText.'<br /><hr><br />'.$content;
446
        }
447
448
        // See message with link text
449
        if (!empty($linkToNewMessage) && 'true' == api_get_setting('message.allow_message_tool')) {
450
            $content = $content.'<br /><br />'.$linkToNewMessage;
451
        }
452
453
        // You have received this message because you are subscribed text
454
        $content = $content.'<br /><hr><i>'.
455
            sprintf(
456
                get_lang('You have received this notification because you are subscribed or involved in it to change your notification preferences please click here: %s', $recipientLanguage),
457
                Display::url($preferenceUrl, $preferenceUrl)
458
            ).'</i>';
459
460
        $notificationContentEvent = new NotificationContentFormattedEvent(['content' => $content], AbstractEvent::TYPE_POST);
461
462
        Container::getEventDispatcher()->dispatch($notificationContentEvent, Events::NOTIFICATION_CONTENT_FORMATTED);
463
464
        return $notificationContentEvent->getContent();
465
    }
466
467
    /**
468
     * Send the push notifications to Chamilo Mobile app.
469
     *
470
     * @param array  $userIds The IDs of users who will be notified
471
     * @param string $title   The notification title
472
     * @param string $content The notification content
473
     *
474
     * @return int The number of success notifications. Otherwise returns false
475
     */
476
    public static function sendPushNotification(array $userIds, $title, $content)
477
    {
478
        if ('true' !== api_get_setting('webservice.messaging_allow_send_push_notification')) {
479
            return false;
480
        }
481
482
        $gdcApiKey = api_get_setting('webservice.messaging_gdc_api_key');
483
484
        if (false === $gdcApiKey) {
485
            return false;
486
        }
487
488
        $content = strip_tags($content);
489
        $content = explode("\n", $content);
490
        $content = array_map('trim', $content);
491
        $content = array_filter($content);
492
        $content = implode(PHP_EOL, $content);
493
494
        $gcmRegistrationIds = [];
495
        foreach ($userIds as $userId) {
496
            $extraFieldValue = new ExtraFieldValue('user');
497
            $valueInfo = $extraFieldValue->get_values_by_handler_and_field_variable(
498
                $userId,
499
                Rest::EXTRA_FIELD_GCM_REGISTRATION
500
            );
501
502
            if (empty($valueInfo)) {
503
                continue;
504
            }
505
506
            $gcmRegistrationIds[] = $valueInfo['field_value'];
507
        }
508
509
        if (!$gcmRegistrationIds) {
510
            return 0;
511
        }
512
513
        $headers = [
514
            'Authorization: key='.$gdcApiKey,
0 ignored issues
show
Bug introduced by
Are you sure $gdcApiKey of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

514
            'Authorization: key='./** @scrutinizer ignore-type */ $gdcApiKey,
Loading history...
515
            'Content-Type: application/json',
516
        ];
517
518
        $fields = json_encode([
519
            'registration_ids' => $gcmRegistrationIds,
520
            'data' => [
521
                'title' => $title,
522
                'message' => $content,
523
                'body' => $content,
524
                'sound' => 'default',
525
            ],
526
            'notification' => [
527
                'title' => $title,
528
                'body' => $content,
529
                'sound' => 'default',
530
            ],
531
            'collapse_key' => get_lang('Messages'),
532
        ]);
533
534
        $ch = curl_init();
535
        curl_setopt($ch, CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send');
536
        curl_setopt($ch, CURLOPT_POST, true);
537
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
538
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
539
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
540
        curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
541
        $result = curl_exec($ch);
542
        curl_close($ch);
543
544
        /** @var array $decodedResult */
545
        $decodedResult = json_decode($result, true);
546
547
        $return = isset($decodedResult['success']) ? (int) $decodedResult['success'] : 0;
548
549
        return $return;
550
    }
551
}
552