Completed
Push — master ( 9b8b24...6e1754 )
by Julito
58:58
created

CourseChatUtils::countUsersOnline()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 15
nc 2
nop 0
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Michelf\MarkdownExtra;
5
use Doctrine\Common\Collections\Criteria;
6
use Chamilo\CourseBundle\Entity\CChatConnected;
7
8
/**
9
 * Class CourseChat
10
 * Manage the chat for a course
11
 */
12
class CourseChatUtils
13
{
14
    private $groupId;
15
    private $courseId;
16
    private $sessionId;
17
    private $userId;
18
19
    /**
20
     * CourseChat constructor.
21
     * @param int $courseId
22
     * @param int $userId
23
     * @param int $sessionId
24
     * @param int $groupId
25
     */
26
    public function __construct($courseId, $userId, $sessionId = 0, $groupId = 0)
27
    {
28
        $this->courseId = (int) $courseId;
29
        $this->userId = (int) $userId;
30
        $this->sessionId = (int) $sessionId;
31
        $this->groupId = (int) $groupId;
32
    }
33
34
    /**
35
     * Get the users subscriptions (SessionRelCourseRelUser array or CourseRelUser array) for chat
36
     * @return \Doctrine\Common\Collections\ArrayCollection
37
     * @throws \Doctrine\ORM\ORMException
38
     * @throws \Doctrine\ORM\OptimisticLockException
39
     * @throws \Doctrine\ORM\TransactionRequiredException
40
     */
41
    private function getUsersSubscriptions()
42
    {
43
        $em = Database::getManager();
44
        $course = $em->find('ChamiloCoreBundle:Course', $this->courseId);
45
46
        if ($this->sessionId) {
47
            $criteria = Criteria::create()->where(Criteria::expr()->eq('course', $course));
48
49
            return $em
50
                ->find('ChamiloCoreBundle:Session', $this->sessionId)
51
                ->getUserCourseSubscriptions()
52
                ->matching($criteria);
53
        }
54
55
        return $em
56
            ->find('ChamiloCoreBundle:Course', $course)
57
            ->getUsers();
58
    }
59
60
    /**
61
     * Prepare a message. Clean and insert emojis
62
     * @param string $message The message to prepare
63
     * @return string
64
     */
65
    public static function prepareMessage($message)
66
    {
67
        if (empty($message)) {
68
            return '';
69
        }
70
71
        Emojione\Emojione::$imagePathPNG = api_get_path(WEB_LIBRARY_PATH).'javascript/emojione/png/';
72
        Emojione\Emojione::$ascii = true;
73
74
        $message = trim($message);
75
        $message = nl2br($message);
76
        // Security XSS
77
        $message = Security::remove_XSS($message);
78
        //search urls
79
        $message = preg_replace(
80
            '@((https?://)?([-\w]+\.[-\w\.]+)+\w(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)*)@',
81
            '<a href="$1" target="_blank">$1</a>',
82
            $message
83
        );
84
        // add "http://" if not set
85
        $message = preg_replace(
86
            '/<a\s[^>]*href\s*=\s*"((?!https?:\/\/)[^"]*)"[^>]*>/i',
87
            '<a href="http://$1" target="_blank">',
88
            $message
89
        );
90
        // Parsing emojis
91
        $message = Emojione\Emojione::toImage($message);
92
        // Parsing text to understand markdown (code highlight)
93
        $message = MarkdownExtra::defaultTransform($message);
94
95
        return $message;
96
    }
97
98
    /**
99
     * Save a chat message in a HTML file
100
     * @param string$message
101
     * @param int $friendId
102
     * @return bool
103
     * @throws \Doctrine\ORM\ORMException
104
     * @throws \Doctrine\ORM\OptimisticLockException
105
     * @throws \Doctrine\ORM\TransactionRequiredException
106
     */
107
    public function saveMessage($message, $friendId = 0)
108
    {
109
        if (empty($message)) {
110
            return false;
111
        }
112
        $user = api_get_user_entity($this->userId);
113
        $courseInfo = api_get_course_info_by_id($this->courseId);
114
        $isMaster = (bool) api_is_course_admin();
115
        $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
116
        $basepath_chat = '/chat_files';
117
        $group_info = [];
118
        if (!$this->groupId) {
119
            $group_info = GroupManager::get_group_properties($this->groupId);
120
            $basepath_chat = $group_info['directory'].'/chat_files';
121
        }
122
123
        $chat_path = $document_path.$basepath_chat.'/';
124
125
        if (!is_dir($chat_path)) {
126
            if (is_file($chat_path)) {
127
                @unlink($chat_path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

127
                /** @scrutinizer ignore-unhandled */ @unlink($chat_path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
128
            }
129
        }
130
131
        $date_now = date('Y-m-d');
132
        $timeNow = date('d/m/y H:i:s');
133
        $basename_chat = 'messages-'.$date_now;
134
135
        if ($this->groupId && !$friendId) {
136
            $basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
137
        } elseif ($this->sessionId && !$friendId) {
138
            $basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
139
        } elseif ($friendId) {
140
            if ($this->userId < $friendId) {
141
                $basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
142
            } else {
143
                $basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
144
            }
145
        }
146
147
        $message = self::prepareMessage($message);
148
149
        $fileTitle = $basename_chat.'.log.html';
150
        $filePath = $basepath_chat.'/'.$fileTitle;
151
        $absoluteFilePath = $chat_path.$fileTitle;
152
153
        if (!file_exists($absoluteFilePath)) {
154
            $doc_id = add_document($courseInfo, $filePath, 'file', 0, $fileTitle);
155
            $documentLogTypes = ['DocumentAdded', 'invisible'];
156
157
            foreach ($documentLogTypes as $logType) {
158
                api_item_property_update(
159
                    $courseInfo,
160
                    TOOL_DOCUMENT,
161
                    $doc_id,
162
                    $logType,
163
                    $this->userId,
164
                    $group_info,
165
                    null,
166
                    null,
167
                    null,
168
                    $this->sessionId
169
                );
170
            }
171
172
            item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
173
        } else {
174
            $doc_id = DocumentManager::get_document_id($courseInfo, $filePath);
175
        }
176
177
        $fp = fopen($absoluteFilePath, 'a');
178
        $userPhoto = UserManager::getUserPicture($this->userId, USER_IMAGE_SIZE_MEDIUM);
179
180
        if ($isMaster) {
181
            $fileContent = '
182
                <div class="message-teacher">
183
                    <div class="content-message">
184
                        <div class="chat-message-block-name">' . $user->getCompleteName().'</div>
185
                        <div class="chat-message-block-content">' . $message.'</div>
186
                        <div class="message-date">' . $timeNow.'</div>
187
                    </div>
188
                    <div class="icon-message"></div>
189
                    <img class="chat-image" src="' . $userPhoto.'">
190
                </div>
191
            ';
192
        } else {
193
            $fileContent = '
194
                <div class="message-student">
195
                    <img class="chat-image" src="' . $userPhoto.'">
196
                    <div class="icon-message"></div>
197
                    <div class="content-message">
198
                        <div class="chat-message-block-name">' . $user->getCompleteName().'</div>
199
                        <div class="chat-message-block-content">' . $message.'</div>
200
                        <div class="message-date">' . $timeNow.'</div>
201
                    </div>
202
                </div>
203
            ';
204
        }
205
206
        fputs($fp, $fileContent);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fputs() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

206
        fputs(/** @scrutinizer ignore-type */ $fp, $fileContent);
Loading history...
Bug introduced by
The call to fputs() has too few arguments starting with length. ( Ignorable by Annotation )

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

206
        /** @scrutinizer ignore-call */ 
207
        fputs($fp, $fileContent);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
207
        fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

207
        fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
208
209
        $chat_size = filesize($absoluteFilePath);
210
211
        update_existing_document($courseInfo, $doc_id, $chat_size);
212
        item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
213
214
        return true;
215
    }
216
217
    /**
218
     * Disconnect a user from course chats
219
     * @param $userId
220
     */
221
    public static function exitChat($userId)
222
    {
223
        $listCourse = CourseManager::get_courses_list_by_user_id($userId);
224
225
        foreach ($listCourse as $course) {
226
            Database::getManager()
227
                ->createQuery('
228
                    DELETE FROM ChamiloCourseBundle:CChatConnected ccc
229
                    WHERE ccc.cId = :course AND ccc.userId = :user
230
                ')
231
                ->execute([
232
                    'course' => intval($course['real_id']),
233
                    'user' => intval($userId)
234
                ]);
235
        }
236
    }
237
238
    /**
239
     * Disconnect users who are more than 5 seconds inactive
240
     */
241
    public function disconnectInactiveUsers()
242
    {
243
        $em = Database::getManager();
244
        $extraCondition = "AND ccc.toGroupId = {$this->groupId}";
245
246
        if (empty($this->groupId)) {
247
            $extraCondition = "AND ccc.sessionId = {$this->sessionId}";
248
        }
249
250
        $connectedUsers = $em
251
            ->createQuery("
252
                SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
253
                WHERE ccc.cId = :course $extraCondition
254
            ")
255
            ->setParameter('course', $this->courseId)
256
            ->getResult();
257
258
        $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
259
        $cd_count_time_seconds = $now->getTimestamp();
260
261
        foreach ($connectedUsers as $connection) {
262
            $date_count_time_seconds = $connection->getLastConnection()->getTimestamp();
263
264
            if (strcmp($now->format('Y-m-d'), $connection->getLastConnection()->format('Y-m-d')) !== 0) {
265
                continue;
266
            }
267
268
            if (($cd_count_time_seconds - $date_count_time_seconds) <= 5) {
269
                continue;
270
            }
271
272
            $em
273
                ->createQuery('
274
                    DELETE FROM ChamiloCourseBundle:CChatConnected ccc
275
                    WHERE ccc.cId = :course AND ccc.userId = :user AND ccc.toGroupId = :group
276
                ')
277
                ->execute([
278
                    'course' => $this->courseId,
279
                    'user' => $connection->getUserId(),
280
                    'group' => $this->groupId
281
                ]);
282
        }
283
    }
284
285
    /**
286
     * Keep registered to a user as connected
287
     * @throws \Doctrine\ORM\NonUniqueResultException
288
     */
289
    public function keepUserAsConnected()
290
    {
291
        $em = Database::getManager();
292
        $extraCondition = null;
293
294
        if ($this->groupId) {
295
            $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
296
        } else {
297
            $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
298
        }
299
300
        $currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
301
302
        $connection = $em
303
            ->createQuery("
304
                SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
305
                WHERE ccc.userId = :user AND ccc.cId = :course $extraCondition 
306
            ")
307
            ->setParameters([
308
                'user' => $this->userId,
309
                'course' => $this->courseId
310
            ])
311
            ->getOneOrNullResult();
312
313
        if ($connection) {
314
            $connection->setLastConnection($currentTime);
315
            $em->merge($connection);
316
            $em->flush();
317
318
            return;
319
        }
320
321
        $connection = new CChatConnected();
322
        $connection
323
            ->setCId($this->courseId)
324
            ->setUserId($this->userId)
325
            ->setLastConnection($currentTime)
326
            ->setSessionId($this->sessionId)
327
            ->setToGroupId($this->groupId);
328
329
        $em->persist($connection);
330
        $em->flush();
331
    }
332
333
    /**
334
     * Get the emoji allowed on course chat
335
     * @return array
336
     */
337
    public static function getEmojiStrategy()
338
    {
339
        return require_once api_get_path(SYS_CODE_PATH).'chat/emoji_strategy.php';
340
    }
341
342
    /**
343
     * Get the emoji list to include in chat
344
     * @return array
345
     */
346
    public static function getEmojisToInclude()
347
    {
348
        return [
349
            ':bowtie:',
350
            ':smile:' |
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise | or did you mean ||?
Loading history...
351
            ':laughing:',
352
            ':blush:',
353
            ':smiley:',
354
            ':relaxed:',
355
            ':smirk:',
356
            ':heart_eyes:',
357
            ':kissing_heart:',
358
            ':kissing_closed_eyes:',
359
            ':flushed:',
360
            ':relieved:',
361
            ':satisfied:',
362
            ':grin:',
363
            ':wink:',
364
            ':stuck_out_tongue_winking_eye:',
365
            ':stuck_out_tongue_closed_eyes:',
366
            ':grinning:',
367
            ':kissing:',
368
            ':kissing_smiling_eyes:',
369
            ':stuck_out_tongue:',
370
            ':sleeping:',
371
            ':worried:',
372
            ':frowning:',
373
            ':anguished:',
374
            ':open_mouth:',
375
            ':grimacing:',
376
            ':confused:',
377
            ':hushed:',
378
            ':expressionless:',
379
            ':unamused:',
380
            ':sweat_smile:',
381
            ':sweat:',
382
            ':disappointed_relieved:',
383
            ':weary:',
384
            ':pensive:',
385
            ':disappointed:',
386
            ':confounded:',
387
            ':fearful:',
388
            ':cold_sweat:',
389
            ':persevere:',
390
            ':cry:',
391
            ':sob:',
392
            ':joy:',
393
            ':astonished:',
394
            ':scream:',
395
            ':neckbeard:',
396
            ':tired_face:',
397
            ':angry:',
398
            ':rage:',
399
            ':triumph:',
400
            ':sleepy:',
401
            ':yum:',
402
            ':mask:',
403
            ':sunglasses:',
404
            ':dizzy_face:',
405
            ':imp:',
406
            ':smiling_imp:',
407
            ':neutral_face:',
408
            ':no_mouth:',
409
            ':innocent:',
410
            ':alien:'
411
        ];
412
    }
413
414
    /**
415
     * Get the chat history file name
416
     * @param bool $absolute Optional. Whether get the base or the absolute file path
417
     * @param int $friendId Optional.
418
     * @return string
419
     */
420
    public function getFileName($absolute = false, $friendId = 0)
421
    {
422
        $date = date('Y-m-d');
423
        $base = 'messages-'.$date.'.log.html';
424
425
        if ($this->groupId && !$friendId) {
426
            $base = 'messages-'.$date.'_gid-'.$this->groupId.'.log.html';
427
        } elseif ($this->sessionId && !$friendId) {
428
            $base = 'messages-'.$date.'_sid-'.$this->sessionId.'.log.html';
429
        } elseif ($friendId) {
430
            if ($this->userId < $friendId) {
431
                $base = 'messages-'.$date.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
432
            } else {
433
                $base = 'messages-'.$date.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
434
            }
435
        }
436
437
        if (!$absolute) {
438
            return $base;
439
        }
440
441
        $courseInfo = api_get_course_info_by_id($this->courseId);
442
        $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
443
444
        $chatPath = $document_path.'/chat_files/';
445
446
        if ($this->groupId) {
447
            $group_info = GroupManager::get_group_properties($this->groupId);
448
            $chatPath = $document_path.$group_info['directory'].'/chat_files/';
449
        }
450
451
        return $chatPath.$base;
452
    }
453
454
    /**
455
     * Get the chat history
456
     * @param bool $reset
457
     * @param int $friendId Optional.
458
     * @return string
459
     */
460
    public function readMessages($reset = false, $friendId = 0)
461
    {
462
        $courseInfo = api_get_course_info_by_id($this->courseId);
463
        $date_now = date('Y-m-d');
464
        $isMaster = (bool) api_is_course_admin();
465
        $basepath_chat = '/chat_files';
466
        $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
467
        $group_info = [];
468
        if ($this->groupId) {
469
            $group_info = GroupManager:: get_group_properties($this->groupId);
470
            $basepath_chat = $group_info['directory'].'/chat_files';
471
        }
472
473
        $chat_path = $document_path.$basepath_chat.'/';
474
475
        if (!is_dir($chat_path)) {
476
            if (is_file($chat_path)) {
477
                @unlink($chat_path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

477
                /** @scrutinizer ignore-unhandled */ @unlink($chat_path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
478
            }
479
480
            if (!api_is_anonymous()) {
481
                @mkdir($chat_path, api_get_permissions_for_new_directories());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

481
                /** @scrutinizer ignore-unhandled */ @mkdir($chat_path, api_get_permissions_for_new_directories());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
482
                // Save chat files document for group into item property
483
                if ($this->groupId) {
484
                    $doc_id = add_document($courseInfo, $basepath_chat, 'folder', 0, 'chat_files');
485
                    api_item_property_update(
486
                        $courseInfo,
487
                        TOOL_DOCUMENT,
488
                        $doc_id,
489
                        'FolderCreated',
490
                        null,
491
                        $group_info,
492
                        null,
493
                        null,
494
                        null
495
                    );
496
                }
497
            }
498
        }
499
500
        $filename_chat = 'messages-'.$date_now.'.log.html';
501
502
        if ($this->groupId && !$friendId) {
503
            $filename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId.'.log.html';
504
        } elseif ($this->sessionId && !$friendId) {
505
            $filename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId.'.log.html';
506
        } elseif ($friendId) {
507
            if ($this->userId < $friendId) {
508
                $filename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
509
            } else {
510
                $filename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
511
            }
512
        }
513
514
        if (!file_exists($chat_path.$filename_chat)) {
515
            @fclose(fopen($chat_path.$filename_chat, 'w'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

515
            /** @scrutinizer ignore-unhandled */ @fclose(fopen($chat_path.$filename_chat, 'w'));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
It seems like fopen($chat_path . $filename_chat, 'w') can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

515
            @fclose(/** @scrutinizer ignore-type */ fopen($chat_path.$filename_chat, 'w'));
Loading history...
516
            if (!api_is_anonymous()) {
517
                $doc_id = add_document($courseInfo, $basepath_chat.'/'.$filename_chat, 'file', 0, $filename_chat);
518
                api_item_property_update(
519
                    $courseInfo,
520
                    TOOL_DOCUMENT,
521
                    $doc_id,
522
                    'DocumentAdded',
523
                    $this->userId,
524
                    $group_info,
525
                    null,
526
                    null,
527
                    null,
528
                    $this->sessionId
529
                );
530
                api_item_property_update(
531
                    $courseInfo,
532
                    TOOL_DOCUMENT,
533
                    $doc_id,
534
                    'invisible',
535
                    $this->userId,
536
                    $group_info,
537
                    null,
538
                    null,
539
                    null,
540
                    $this->sessionId
541
                );
542
                item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
543
            }
544
        }
545
546
        $basename_chat = 'messages-'.$date_now;
547
        if ($this->groupId && !$friendId) {
548
            $basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
549
        } elseif ($this->sessionId && !$friendId) {
550
            $basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
551
        } elseif ($friendId) {
552
            if ($this->userId < $friendId) {
553
                $basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
554
            } else {
555
                $basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
556
            }
557
        }
558
559
        if ($reset && $isMaster) {
560
            $i = 1;
561
            while (file_exists($chat_path.$basename_chat.'-'.$i.'.log.html')) {
562
                $i++;
563
            }
564
565
            @rename($chat_path.$basename_chat.'.log.html', $chat_path.$basename_chat.'-'.$i.'.log.html');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

565
            /** @scrutinizer ignore-unhandled */ @rename($chat_path.$basename_chat.'.log.html', $chat_path.$basename_chat.'-'.$i.'.log.html');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
566
            @fclose(fopen($chat_path.$basename_chat.'.log.html', 'w'));
567
568
            $doc_id = add_document(
569
                $courseInfo,
570
                $basepath_chat.'/'.$basename_chat.'-'.$i.'.log.html',
571
                'file',
572
                filesize($chat_path.$basename_chat.'-'.$i.'.log.html'),
573
                $basename_chat.'-'.$i.'.log.html'
574
            );
575
576
            api_item_property_update(
577
                $courseInfo,
578
                TOOL_DOCUMENT,
579
                $doc_id,
580
                'DocumentAdded',
581
                $this->userId,
582
                $group_info,
583
                null,
584
                null,
585
                null,
586
                $this->sessionId
587
            );
588
            api_item_property_update(
589
                $courseInfo,
590
                TOOL_DOCUMENT,
591
                $doc_id,
592
                'invisible',
593
                $this->userId,
594
                $group_info,
595
                null,
596
                null,
597
                null,
598
                $this->sessionId
599
            );
600
            item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
601
            $doc_id = DocumentManager::get_document_id(
602
                $courseInfo,
603
                $basepath_chat.'/'.$basename_chat.'.log.html'
604
            );
605
            update_existing_document($courseInfo, $doc_id, 0);
606
        }
607
608
        $remove = 0;
609
        $content = array();
610
611
        if (file_exists($chat_path.$basename_chat.'.log.html')) {
612
            $content = file($chat_path.$basename_chat.'.log.html');
613
            $nbr_lines = sizeof($content);
0 ignored issues
show
Bug introduced by
The call to sizeof() has too few arguments starting with mode. ( Ignorable by Annotation )

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

613
            $nbr_lines = /** @scrutinizer ignore-call */ sizeof($content);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
614
            $remove = $nbr_lines - 100;
615
        }
616
617
        if ($remove < 0) {
618
            $remove = 0;
619
        }
620
621
        array_splice($content, 0, $remove);
0 ignored issues
show
Bug introduced by
It seems like $content can also be of type false; however, parameter $input of array_splice() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

621
        array_splice(/** @scrutinizer ignore-type */ $content, 0, $remove);
Loading history...
622
623
        if (isset($_GET['origin']) && $_GET['origin'] == 'whoisonline') {
624
            //the caller
625
            $content[0] = get_lang('CallSent').'<br />'.$content[0];
626
        }
627
628
        $history = '<div id="content-chat">';
629
        foreach ($content as $this_line) {
630
            $history .= $this_line;
631
        }
632
        $history .= '</div>';
633
634
        if ($isMaster || $GLOBALS['is_session_general_coach']) {
635
            $history .= '
636
                <div id="clear-chat">
637
                    <button type="button" id="chat-reset" class="btn btn-danger btn-sm">
638
                        ' . get_lang('ClearList').'
639
                    </button>
640
                </div>
641
            ';
642
        }
643
644
        return $history;
645
    }
646
647
    /**
648
     * Get the number of users connected in chat
649
     * @return mixed
650
     */
651
    public function countUsersOnline()
652
    {
653
        $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
654
        $date->modify('-5 seconds');
655
656
        $extraCondition = null;
657
658
        if ($this->groupId) {
659
            $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
660
        } else {
661
            $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
662
        }
663
664
        $number = Database::getManager()
665
            ->createQuery("
666
                SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
667
                WHERE ccc.lastConnection > :date AND ccc.cId = :course $extraCondition
668
            ")
669
            ->setParameters([
670
                'date' => $date,
671
                'course' => $this->courseId
672
            ])
673
            ->getSingleScalarResult();
674
675
        return intval($number);
676
    }
677
678
    /**
679
     * Check if a user is connected in course chat
680
     * @param int $userId
681
     * @return int
682
     */
683
    private function userIsConnected($userId)
684
    {
685
        $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
686
        $date->modify('-5 seconds');
687
688
        $extraCondition = null;
689
690
        if ($this->groupId) {
691
            $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
692
        } else {
693
            $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
694
        }
695
696
        $number = Database::getManager()
697
            ->createQuery("
698
                SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
699
                WHERE ccc.lastConnection > :date AND ccc.cId = :course AND ccc.userId = :user $extraCondition
700
            ")
701
            ->setParameters([
702
                'date' => $date,
703
                'course' => $this->courseId,
704
                'user' => $userId
705
            ])
706
            ->getSingleScalarResult();
707
708
        return intval($number);
709
    }
710
711
    /**
712
     * Get the users online data
713
     * @return string
714
     */
715
    public function listUsersOnline()
716
    {
717
        $subscriptions = $this->getUsersSubscriptions();
718
        $usersInfo = [];
719
720
        foreach ($subscriptions as $subscription) {
721
            $user = $subscription->getUser();
722
723
            $usersInfo[] = [
724
                'id' => $user->getId(),
725
                'firstname' => $user->getFirstname(),
726
                'lastname' => $user->getLastname(),
727
                'status' => !$this->sessionId ? $subscription->getStatus() : $user->getStatus(),
728
                'image_url' => UserManager::getUserPicture($user->getId(), USER_IMAGE_SIZE_MEDIUM),
729
                'profile_url' => api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user->getId(),
730
                'complete_name' => $user->getCompleteName(),
731
                'username' => $user->getUsername(),
732
                'email' => $user->getEmail(),
733
                'isConnected' => $this->userIsConnected($user->getId())
734
            ];
735
        }
736
737
        return $usersInfo;
738
    }
739
}
740