Passed
Push — webservicelpcreate ( d8cb35 )
by
unknown
13:48
created

Chat::closeWindow()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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

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