Chat   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 518
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 226
dl 0
loc 518
rs 7.92
c 0
b 0
f 0
wmc 51

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getContacts() 0 9 1
A getLatestChat() 0 10 1
A getAllLatestChats() 0 21 4
A getUserStatus() 0 10 1
A setUserStatus() 0 6 1
A disableChat() 0 12 3
A sanitize() 0 8 1
A saveWindow() 0 4 1
A isChatBlockedByExercises() 0 12 4
A closeWindow() 0 19 4
B send() 0 66 10
A setDisableChat() 0 3 1
A getMessages() 0 57 5
B heartbeat() 0 55 7
A close() 0 6 1
A getPreviousMessages() 0 25 3
A startSession() 0 18 1
A getCountMessagesExchangeBetweenUsers() 0 19 1

How to fix   Complexity   

Complex Class

Complex classes like Chat often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Chat, and based on these observations, apply Extract Interface, too.

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
            'sec_token' => Security::get_token('chat'),
146
        ];
147
        echo json_encode($return);
148
149
        return true;
150
    }
151
152
    /**
153
     * @param int $fromUserId
154
     * @param int $toUserId
155
     *
156
     * @return int
157
     */
158
    public function getCountMessagesExchangeBetweenUsers($fromUserId, $toUserId)
159
    {
160
        $row = Database::select(
161
            'count(*) as count',
162
            $this->table,
163
            [
164
                'where' => [
165
                    '(from_user = ? AND to_user = ?) OR (from_user = ? AND to_user = ?) ' => [
166
                        $fromUserId,
167
                        $toUserId,
168
                        $toUserId,
169
                        $fromUserId,
170
                    ],
171
                ],
172
            ],
173
            'first'
174
        );
175
176
        return (int) $row['count'];
177
    }
178
179
    /**
180
     * @param int $fromUserId
181
     * @param int $toUserId
182
     * @param int $visibleMessages
183
     * @param int $previousMessageCount messages to show
184
     *
185
     * @return array
186
     */
187
    public function getPreviousMessages(
188
        $fromUserId,
189
        $toUserId,
190
        $visibleMessages = 1,
191
        $previousMessageCount = 5,
192
        $orderBy = ''
193
    ) {
194
        $toUserId = (int) $toUserId;
195
        $fromUserId = (int) $fromUserId;
196
        $visibleMessages = (int) $visibleMessages;
197
        $previousMessageCount = (int) $previousMessageCount;
198
199
        $total = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $toUserId);
200
        $show = $total - $visibleMessages;
201
202
        if ($show < $previousMessageCount) {
203
            $show = $previousMessageCount;
204
        }
205
        $from = $show - $previousMessageCount;
206
207
        if ($from < 0) {
208
            return [];
209
        }
210
211
        return $this->getMessages($fromUserId, $toUserId, $from, $previousMessageCount, $orderBy);
212
    }
213
214
    /**
215
     * @param int    $fromUserId
216
     * @param int    $toUserId
217
     * @param int    $start
218
     * @param int    $end
219
     * @param string $orderBy
220
     *
221
     * @return array
222
     */
223
    public function getMessages($fromUserId, $toUserId, $start, $end, $orderBy = '')
224
    {
225
        $toUserId = (int) $toUserId;
226
        $fromUserId = (int) $fromUserId;
227
        $start = (int) $start;
228
        $end = (int) $end;
229
230
        if (empty($toUserId) || empty($fromUserId)) {
231
            return [];
232
        }
233
234
        $orderBy = Database::escape_string($orderBy);
235
        if (empty($orderBy)) {
236
            $orderBy = 'ORDER BY id ASC';
237
        }
238
239
        $sql = "SELECT * FROM ".$this->table."
240
                WHERE
241
                    (
242
                        to_user = $toUserId AND
243
                        from_user = $fromUserId
244
                    )
245
                    OR
246
                    (
247
                        from_user = $toUserId AND
248
                        to_user =  $fromUserId
249
                    )
250
                $orderBy
251
                LIMIT $start, $end
252
                ";
253
        $result = Database::query($sql);
254
        $rows = Database::store_result($result);
255
        $fromUserInfo = api_get_user_info($fromUserId, true);
256
        $toUserInfo = api_get_user_info($toUserId, true);
257
        $users = [
258
            $fromUserId => $fromUserInfo,
259
            $toUserId => $toUserInfo,
260
        ];
261
        $items = [];
262
        $rows = array_reverse($rows);
263
        foreach ($rows as $chat) {
264
            $fromUserId = $chat['from_user'];
265
            $userInfo = $users[$fromUserId];
266
            $toUserInfo = $users[$toUserId];
267
268
            $items[$chat['id']] = [
269
                'id' => $chat['id'],
270
                'message' => Security::remove_XSS($chat['message']),
271
                'date' => api_strtotime($chat['sent'], 'UTC'),
272
                'recd' => $chat['recd'],
273
                'from_user_info' => $userInfo,
274
                'to_user_info' => $toUserInfo,
275
            ];
276
            $_SESSION['openChatBoxes'][$fromUserId] = api_strtotime($chat['sent'], 'UTC');
277
        }
278
279
        return $items;
280
    }
281
282
    /**
283
     * Refreshes the chat windows (usually called every x seconds through AJAX).
284
     */
285
    public function heartbeat()
286
    {
287
        $chatHistory = Session::read('chatHistory');
288
        $currentUserId = api_get_user_id();
289
290
        // update current chats
291
        if (!empty($chatHistory) && is_array($chatHistory)) {
292
            foreach ($chatHistory as $fromUserId => &$data) {
293
                $userInfo = api_get_user_info($fromUserId, true);
294
                $count = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $currentUserId);
295
                $chatItems = $this->getLatestChat($fromUserId, $currentUserId, 5);
296
                $data['window_user_info'] = $userInfo;
297
                $data['items'] = $chatItems;
298
                $data['total_messages'] = $count;
299
            }
300
        }
301
302
        $sql = "SELECT * FROM ".$this->table."
303
                WHERE
304
                    to_user = '".$currentUserId."' AND recd = 0
305
                ORDER BY id ASC";
306
        $result = Database::query($sql);
307
308
        $chatList = [];
309
        while ($chat = Database::fetch_array($result, 'ASSOC')) {
310
            $chatList[$chat['from_user']][] = $chat;
311
        }
312
313
        foreach ($chatList as $fromUserId => $messages) {
314
            $userInfo = api_get_user_info($fromUserId, true);
315
            $count = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $currentUserId);
316
            $chatItems = $this->getLatestChat($fromUserId, $currentUserId, 5);
317
318
            // Cleaning tsChatBoxes
319
            unset($_SESSION['tsChatBoxes'][$fromUserId]);
320
321
            foreach ($messages as $chat) {
322
                $_SESSION['openChatBoxes'][$fromUserId] = api_strtotime($chat['sent'], 'UTC');
323
            }
324
325
            $chatHistory[$fromUserId] = [
326
                'window_user_info' => $userInfo,
327
                'total_messages' => $count,
328
                'items' => $chatItems,
329
            ];
330
        }
331
332
        Session::write('chatHistory', $chatHistory);
333
334
        $sql = "UPDATE ".$this->table."
335
                SET recd = 1
336
                WHERE to_user = $currentUserId AND recd = 0";
337
        Database::query($sql);
338
339
        echo json_encode(['items' => $chatHistory]);
340
    }
341
342
    /**
343
     * Saves into session the fact that a chat window exists with the given user.
344
     *
345
     * @param int $userId
346
     */
347
    public function saveWindow($userId)
348
    {
349
        $this->window_list[$userId] = true;
350
        Session::write('window_list', $this->window_list);
351
    }
352
353
    /**
354
     * Sends a message from one user to another user.
355
     *
356
     * @param int    $fromUserId  The ID of the user sending the message
357
     * @param int    $to_user_id  The ID of the user receiving the message
358
     * @param string $message     Message
359
     * @param bool   $printResult Optional. Whether print the result
360
     * @param bool   $sanitize    Optional. Whether sanitize the message
361
     */
362
    public function send(
363
        $fromUserId,
364
        $to_user_id,
365
        $message,
366
        $printResult = true,
367
        $sanitize = true
368
    ) {
369
        $relation = SocialManager::get_relation_between_contacts($fromUserId, $to_user_id);
370
371
        if (!Security::check_token('post', null, 'chat')) {
372
            if ($printResult) {
373
                echo '0';
374
                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...
375
            }
376
        }
377
378
        if (USER_RELATION_TYPE_FRIEND == $relation) {
379
            $now = api_get_utc_datetime();
380
            $user_info = api_get_user_info($to_user_id, true);
381
            $this->saveWindow($to_user_id);
382
            $_SESSION['openChatBoxes'][$to_user_id] = api_strtotime($now, 'UTC');
383
384
            if ($sanitize) {
385
                $messagesan = $this->sanitize($message);
386
            } else {
387
                $messagesan = $message;
388
            }
389
390
            if (!isset($_SESSION['chatHistory'][$to_user_id])) {
391
                $_SESSION['chatHistory'][$to_user_id] = [];
392
            }
393
            $item = [
394
                's' => '1',
395
                'f' => $fromUserId,
396
                'm' => $messagesan,
397
                'date' => api_strtotime($now, 'UTC'),
398
                'username' => get_lang('Me'),
399
            ];
400
            $_SESSION['chatHistory'][$to_user_id]['items'][] = $item;
401
            $_SESSION['chatHistory'][$to_user_id]['user_info']['user_name'] = $user_info['complete_name'];
402
            $_SESSION['chatHistory'][$to_user_id]['user_info']['online'] = $user_info['user_is_online'];
403
            $_SESSION['chatHistory'][$to_user_id]['user_info']['avatar'] = $user_info['avatar_small'];
404
            $_SESSION['chatHistory'][$to_user_id]['user_info']['user_id'] = $user_info['user_id'];
405
406
            unset($_SESSION['tsChatBoxes'][$to_user_id]);
407
408
            $params = [];
409
            $params['from_user'] = (int) $fromUserId;
410
            $params['to_user'] = (int) $to_user_id;
411
            $params['message'] = $messagesan;
412
            $params['sent'] = api_get_utc_datetime();
413
414
            if (!empty($fromUserId) && !empty($to_user_id)) {
415
                $messageId = $this->save($params);
416
417
                if ($printResult) {
418
                    header('Content-Type: application/json');
419
                    echo json_encode(['id' => $messageId, 'sec_token' => Security::get_token('chat')]);
420
                    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...
421
                }
422
            }
423
        }
424
425
        if ($printResult) {
426
            echo '0';
427
            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...
428
        }
429
    }
430
431
    /**
432
     * Close a specific chat box (user ID taken from $_POST['chatbox']).
433
     *
434
     * @param int $userId
435
     */
436
    public function closeWindow($userId)
437
    {
438
        if (empty($userId)) {
439
            return false;
440
        }
441
442
        $list = Session::read('openChatBoxes');
443
        if (isset($list[$userId])) {
444
            unset($list[$userId]);
445
            Session::write('openChatBoxes', $list);
446
        }
447
448
        $list = Session::read('chatHistory');
449
        if (isset($list[$userId])) {
450
            unset($list[$userId]);
451
            Session::write('chatHistory', $list);
452
        }
453
454
        return true;
455
    }
456
457
    /**
458
     * Close chat - disconnects the user.
459
     */
460
    public function close()
461
    {
462
        Session::erase('tsChatBoxes');
463
        Session::erase('openChatBoxes');
464
        Session::erase('chatHistory');
465
        Session::erase('window_list');
466
    }
467
468
    /**
469
     * Filter chat messages to avoid XSS or other JS.
470
     *
471
     * @param string $text Unfiltered message
472
     *
473
     * @return string Filtered message
474
     */
475
    public function sanitize($text)
476
    {
477
        $text = htmlspecialchars($text, ENT_QUOTES);
478
        $text = str_replace("\n\r", "\n", $text);
479
        $text = str_replace("\r\n", "\n", $text);
480
        $text = str_replace("\n", "<br>", $text);
481
482
        return $text;
483
    }
484
485
    /**
486
     * SET Disable Chat.
487
     *
488
     * @param bool $status to disable chat
489
     */
490
    public static function setDisableChat($status = true)
491
    {
492
        Session::write('disable_chat', $status);
493
    }
494
495
    /**
496
     * Disable Chat - disable the chat.
497
     *
498
     * @return bool - return true if setDisableChat status is true
499
     */
500
    public static function disableChat()
501
    {
502
        $status = Session::read('disable_chat');
503
        if (!empty($status)) {
504
            if ($status == true) {
505
                Session::write('disable_chat', null);
506
507
                return true;
508
            }
509
        }
510
511
        return false;
512
    }
513
514
    /**
515
     * @return bool
516
     */
517
    public function isChatBlockedByExercises()
518
    {
519
        $currentExercises = Session::read('current_exercises');
520
        if (!empty($currentExercises)) {
521
            foreach ($currentExercises as $attempt_status) {
522
                if ($attempt_status == true) {
523
                    return true;
524
                }
525
            }
526
        }
527
528
        return false;
529
    }
530
}
531