Notification::saveNotification()   F
last analyzed

Complexity

Conditions 20
Paths 1015

Size

Total Lines 118
Code Lines 74

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 74
c 0
b 0
f 0
nc 1015
nop 9
dl 0
loc 118
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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