chat.lib.php 14 KB

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