Passed
Push — master ( d52980...afe0a2 )
by Yannick
10:56
created

Chat::isChatBlockedByExercises()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 0
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\UserRelUser;
5
use ChamiloSession as Session;
6
7
/**
8
 * Class Chat.
9
 *
10
 * @todo ChamiloSession instead of $_SESSION
11
 */
12
class Chat extends Model
13
{
14
    public $columns = [
15
        'id',
16
        'from_user',
17
        'to_user',
18
        'message',
19
        'sent',
20
        'recd',
21
    ];
22
    public $window_list = [];
23
24
    /**
25
     * The contructor sets the chat table name and the window_list attribute.
26
     */
27
    public function __construct()
28
    {
29
        parent::__construct();
30
        $this->table = Database::get_main_table(TABLE_MAIN_CHAT);
31
        $this->window_list = Session::read('window_list');
32
        Session::write('window_list', $this->window_list);
33
    }
34
35
    /**
36
     * Get user chat status.
37
     *
38
     * @return int 0 if disconnected, 1 if connected
39
     */
40
    public function getUserStatus()
41
    {
42
        $status = UserManager::get_extra_user_data_by_field(
43
            api_get_user_id(),
44
            'user_chat_status',
45
            false,
46
            true
47
        );
48
49
        return $status['user_chat_status'];
50
    }
51
52
    /**
53
     * Set user chat status.
54
     *
55
     * @param int $status 0 if disconnected, 1 if connected
56
     */
57
    public function setUserStatus($status)
58
    {
59
        UserManager::update_extra_field_value(
60
            api_get_user_id(),
61
            'user_chat_status',
62
            $status
63
        );
64
    }
65
66
    /**
67
     * @param int  $currentUserId
68
     * @param int  $userId
69
     * @param bool $latestMessages
70
     *
71
     * @return array
72
     */
73
    public function getLatestChat($currentUserId, $userId, $latestMessages)
74
    {
75
        $items = $this->getPreviousMessages(
76
            $currentUserId,
77
            $userId,
78
            0,
79
            $latestMessages
80
        );
81
82
        return array_reverse($items);
83
    }
84
85
    /**
86
     * @return string
87
     */
88
    public function getContacts(): string
89
    {
90
        return (string) SocialManager::listMyFriendsBlock(api_get_user_id(), '', true);
91
    }
92
93
    /**
94
     * @param array $chatHistory
95
     * @param int   $latestMessages
96
     *
97
     * @return mixed
98
     */
99
    public function getAllLatestChats($chatHistory, $latestMessages = 5)
100
    {
101
        $currentUserId = api_get_user_id();
102
103
        if (empty($chatHistory)) {
104
            return [];
105
        }
106
107
        $chats = [];
108
        foreach ($chatHistory as $userId => $time) {
109
            $total = $this->getCountMessagesExchangeBetweenUsers($userId, $currentUserId);
110
            $start = $total - $latestMessages;
111
            if ($start < 0) {
112
                $start = 0;
113
            }
114
            $items = $this->getMessages($userId, $currentUserId, $start, $latestMessages);
115
            $chats[$userId]['items'] = $items;
116
            $chats[$userId]['window_user_info'] = api_get_user_info($userId);
117
        }
118
119
        return $chats;
120
    }
121
122
    /**
123
     * Starts a chat session and returns JSON array of status and chat history.
124
     *
125
     * @return bool (prints output in JSON format)
126
     */
127
    public function startSession()
128
    {
129
        // ofaj
130
        // $chat = new Chat();
131
        // $chat->setUserStatus(1);
132
133
        $chatList = Session::read('openChatBoxes');
134
        $chats = $this->getAllLatestChats($chatList);
135
        $return = [
136
            'user_status' => $this->getUserStatus(),
137
            'me' => get_lang('Me'),
138
            'user_id' => api_get_user_id(),
139
            'items' => $chats,
140
        ];
141
        echo json_encode($return);
142
143
        return true;
144
    }
145
146
    /**
147
     * @param int $fromUserId
148
     * @param int $toUserId
149
     *
150
     * @return int
151
     */
152
    public function getCountMessagesExchangeBetweenUsers($fromUserId, $toUserId)
153
    {
154
        $row = Database::select(
155
            'count(*) as count',
156
            $this->table,
157
            [
158
                'where' => [
159
                    '(from_user = ? AND to_user = ?) OR (from_user = ? AND to_user = ?) ' => [
160
                        $fromUserId,
161
                        $toUserId,
162
                        $toUserId,
163
                        $fromUserId,
164
                    ],
165
                ],
166
            ],
167
            'first'
168
        );
169
170
        return (int) $row['count'];
171
    }
172
173
    /**
174
     * @param int $fromUserId
175
     * @param int $toUserId
176
     * @param int $visibleMessages
177
     * @param int $previousMessageCount messages to show
178
     *
179
     * @return array
180
     */
181
    public function getPreviousMessages(
182
        $fromUserId,
183
        $toUserId,
184
        $visibleMessages = 1,
185
        $previousMessageCount = 5,
186
        $orderBy = ''
187
    ) {
188
        $toUserId = (int) $toUserId;
189
        $fromUserId = (int) $fromUserId;
190
        $visibleMessages = (int) $visibleMessages;
191
        $previousMessageCount = (int) $previousMessageCount;
192
193
        $total = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $toUserId);
194
        $show = $total - $visibleMessages;
195
196
        if ($show < $previousMessageCount) {
197
            $show = $previousMessageCount;
198
        }
199
        $from = $show - $previousMessageCount;
200
201
        if ($from < 0) {
202
            return [];
203
        }
204
205
        return $this->getMessages($fromUserId, $toUserId, $from, $previousMessageCount, $orderBy);
206
    }
207
208
    /**
209
     * @param int    $fromUserId
210
     * @param int    $toUserId
211
     * @param int    $start
212
     * @param int    $end
213
     * @param string $orderBy
214
     *
215
     * @return array
216
     */
217
    public function getMessages($fromUserId, $toUserId, $start, $end, $orderBy = '')
218
    {
219
        $toUserId = (int) $toUserId;
220
        $fromUserId = (int) $fromUserId;
221
        $start = (int) $start;
222
        $end = (int) $end;
223
224
        if (empty($toUserId) || empty($fromUserId)) {
225
            return [];
226
        }
227
228
        $orderBy = Database::escape_string($orderBy);
229
        if (empty($orderBy)) {
230
            $orderBy = 'ORDER BY id ASC';
231
        }
232
233
        $sql = "SELECT * FROM ".$this->table."
234
                WHERE
235
                    (
236
                        to_user = $toUserId AND
237
                        from_user = $fromUserId
238
                    )
239
                    OR
240
                    (
241
                        from_user = $toUserId AND
242
                        to_user =  $fromUserId
243
                    )
244
                $orderBy
245
                LIMIT $start, $end
246
                ";
247
        $result = Database::query($sql);
248
        $rows = Database::store_result($result);
249
        $fromUserInfo = api_get_user_info($fromUserId, true);
250
        $toUserInfo = api_get_user_info($toUserId, true);
251
        $users = [
252
            $fromUserId => $fromUserInfo,
253
            $toUserId => $toUserInfo,
254
        ];
255
        $items = [];
256
        $rows = array_reverse($rows);
257
        foreach ($rows as $chat) {
258
            $fromUserId = $chat['from_user'];
259
            $userInfo = $users[$fromUserId];
260
            $toUserInfo = $users[$toUserId];
261
262
            $items[$chat['id']] = [
263
                'id' => $chat['id'],
264
                'message' => Security::remove_XSS($chat['message']),
265
                'date' => api_strtotime($chat['sent'], 'UTC'),
266
                'recd' => $chat['recd'],
267
                'from_user_info' => $userInfo,
268
                'to_user_info' => $toUserInfo,
269
            ];
270
            $_SESSION['openChatBoxes'][$fromUserId] = api_strtotime($chat['sent'], 'UTC');
271
        }
272
273
        return $items;
274
    }
275
276
    /**
277
     * Refreshes the chat windows (usually called every x seconds through AJAX).
278
     */
279
    public function heartbeat()
280
    {
281
        $chatHistory = Session::read('chatHistory');
282
        $currentUserId = api_get_user_id();
283
284
        // update current chats
285
        if (!empty($chatHistory) && is_array($chatHistory)) {
286
            foreach ($chatHistory as $fromUserId => &$data) {
287
                $userInfo = api_get_user_info($fromUserId, true);
288
                $count = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $currentUserId);
289
                $chatItems = $this->getLatestChat($fromUserId, $currentUserId, 5);
290
                $data['window_user_info'] = $userInfo;
291
                $data['items'] = $chatItems;
292
                $data['total_messages'] = $count;
293
            }
294
        }
295
296
        $sql = "SELECT * FROM ".$this->table."
297
                WHERE
298
                    to_user = '".$currentUserId."' AND recd = 0
299
                ORDER BY id ASC";
300
        $result = Database::query($sql);
301
302
        $chatList = [];
303
        while ($chat = Database::fetch_assoc($result)) {
304
            $chatList[$chat['from_user']][] = $chat;
305
        }
306
307
        foreach ($chatList as $fromUserId => $messages) {
308
            $userInfo = api_get_user_info($fromUserId, true);
309
            $count = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $currentUserId);
310
            $chatItems = $this->getLatestChat($fromUserId, $currentUserId, 5);
311
312
            // Cleaning tsChatBoxes
313
            unset($_SESSION['tsChatBoxes'][$fromUserId]);
314
315
            foreach ($messages as $chat) {
316
                $_SESSION['openChatBoxes'][$fromUserId] = api_strtotime($chat['sent'], 'UTC');
317
            }
318
319
            $chatHistory[$fromUserId] = [
320
                'window_user_info' => $userInfo,
321
                'total_messages' => $count,
322
                'items' => $chatItems,
323
            ];
324
        }
325
326
        Session::write('chatHistory', $chatHistory);
327
328
        $sql = "UPDATE ".$this->table."
329
                SET recd = 1
330
                WHERE to_user = $currentUserId AND recd = 0";
331
        Database::query($sql);
332
333
        echo json_encode(['items' => $chatHistory]);
334
    }
335
336
    /**
337
     * Saves into session the fact that a chat window exists with the given user.
338
     *
339
     * @param int $userId
340
     */
341
    public function saveWindow($userId)
342
    {
343
        $this->window_list[$userId] = true;
344
        Session::write('window_list', $this->window_list);
345
    }
346
347
    /**
348
     * Sends a message from one user to another user.
349
     *
350
     * @param int    $fromUserId  The ID of the user sending the message
351
     * @param int    $to_user_id  The ID of the user receiving the message
352
     * @param string $message     Message
353
     * @param bool   $printResult Optional. Whether print the result
354
     * @param bool   $sanitize    Optional. Whether sanitize the message
355
     */
356
    public function send(
357
        $fromUserId,
358
        $to_user_id,
359
        $message,
360
        $printResult = true,
361
        $sanitize = true
362
    ) {
363
        $relation = SocialManager::get_relation_between_contacts($fromUserId, $to_user_id);
364
365
        if ($relation === UserRelUser::USER_RELATION_TYPE_FRIEND
366
            || $relation === UserRelUser::USER_RELATION_TYPE_GOODFRIEND) {
367
            $now = api_get_utc_datetime();
368
            $user_info = api_get_user_info($to_user_id, true);
369
            $this->saveWindow($to_user_id);
370
            $_SESSION['openChatBoxes'][$to_user_id] = api_strtotime($now, 'UTC');
371
372
            if ($sanitize) {
373
                $messagesan = $this->sanitize($message);
374
            } else {
375
                $messagesan = $message;
376
            }
377
378
            if (!isset($_SESSION['chatHistory'][$to_user_id])) {
379
                $_SESSION['chatHistory'][$to_user_id] = [];
380
            }
381
            $item = [
382
                's' => '1',
383
                'f' => $fromUserId,
384
                'm' => $messagesan,
385
                'date' => api_strtotime($now, 'UTC'),
386
                'username' => get_lang('Me'),
387
            ];
388
            $_SESSION['chatHistory'][$to_user_id]['items'][] = $item;
389
            $_SESSION['chatHistory'][$to_user_id]['user_info']['user_name'] = $user_info['complete_name'];
390
            $_SESSION['chatHistory'][$to_user_id]['user_info']['online'] = $user_info['user_is_online'];
391
            $_SESSION['chatHistory'][$to_user_id]['user_info']['avatar'] = $user_info['avatar_small'];
392
            $_SESSION['chatHistory'][$to_user_id]['user_info']['user_id'] = $user_info['user_id'];
393
394
            unset($_SESSION['tsChatBoxes'][$to_user_id]);
395
396
            $params = [];
397
            $params['from_user'] = (int) $fromUserId;
398
            $params['to_user'] = (int) $to_user_id;
399
            $params['message'] = $messagesan;
400
            $params['sent'] = api_get_utc_datetime();
401
            $params['recd']      = 0;
402
403
            if (!empty($fromUserId) && !empty($to_user_id)) {
404
                $messageId = $this->save($params);
405
                if ($printResult) {
406
                    echo $messageId;
0 ignored issues
show
Bug introduced by
Are you sure $messageId of type false|integer can be used in echo? ( Ignorable by Annotation )

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

406
                    echo /** @scrutinizer ignore-type */ $messageId;
Loading history...
407
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
408
                }
409
            }
410
        }
411
412
        if ($printResult) {
413
            echo '0';
414
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
415
        }
416
    }
417
418
    /**
419
     * Close a specific chat box (user ID taken from $_POST['chatbox']).
420
     *
421
     * @param int $userId
422
     */
423
    public function closeWindow($userId)
424
    {
425
        if (empty($userId)) {
426
            return false;
427
        }
428
429
        $list = Session::read('openChatBoxes');
430
        if (isset($list[$userId])) {
431
            unset($list[$userId]);
432
            Session::write('openChatBoxes', $list);
433
        }
434
435
        $list = Session::read('chatHistory');
436
        if (isset($list[$userId])) {
437
            unset($list[$userId]);
438
            Session::write('chatHistory', $list);
439
        }
440
441
        return true;
442
    }
443
444
    /**
445
     * Close chat - disconnects the user.
446
     */
447
    public function close()
448
    {
449
        Session::erase('tsChatBoxes');
450
        Session::erase('openChatBoxes');
451
        Session::erase('chatHistory');
452
        Session::erase('window_list');
453
    }
454
455
    /**
456
     * Filter chat messages to avoid XSS or other JS.
457
     *
458
     * @param string $text Unfiltered message
459
     *
460
     * @return string Filtered message
461
     */
462
    public function sanitize($text)
463
    {
464
        $text = htmlspecialchars($text, ENT_QUOTES);
465
        $text = str_replace("\n\r", "\n", $text);
466
        $text = str_replace("\r\n", "\n", $text);
467
        $text = str_replace("\n", "<br>", $text);
468
469
        return $text;
470
    }
471
472
    /**
473
     * SET Disable Chat.
474
     *
475
     * @param bool $status to disable chat
476
     */
477
    public static function setDisableChat($status = true)
478
    {
479
        Session::write('disable_chat', $status);
480
    }
481
482
    /**
483
     * Disable Chat - disable the chat.
484
     *
485
     * @return bool - return true if setDisableChat status is true
486
     */
487
    public static function disableChat()
488
    {
489
        $status = Session::read('disable_chat');
490
        if (!empty($status)) {
491
            if (true == $status) {
492
                Session::write('disable_chat', null);
493
494
                return true;
495
            }
496
        }
497
498
        return false;
499
    }
500
501
    /**
502
     * @return bool
503
     */
504
    public function isChatBlockedByExercises()
505
    {
506
        $currentExercises = Session::read('current_exercises');
507
        if (!empty($currentExercises)) {
508
            foreach ($currentExercises as $attempt_status) {
509
                if (true == $attempt_status) {
510
                    return true;
511
                }
512
            }
513
        }
514
515
        return false;
516
    }
517
518
    public function ackReadUpTo(int $fromUserId, int $toUserId, int $lastSeenMessageId): int
519
    {
520
        if ($fromUserId <= 0 || $toUserId <= 0 || $lastSeenMessageId <= 0) {
521
            return 0;
522
        }
523
524
        $sql = "UPDATE {$this->table}
525
            SET recd = 2
526
            WHERE from_user = {$fromUserId}
527
              AND to_user = {$toUserId}
528
              AND id <= {$lastSeenMessageId}
529
              AND recd < 2";
530
        $res = Database::query($sql);
531
532
        return Database::affected_rows($res);
533
    }
534
}
535