chat.lib.php 15 KB

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