chat.lib.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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. Session::write('chatHistory', $chatHistory);
  297. $sql = "UPDATE ".$this->table."
  298. SET recd = 1
  299. WHERE to_user = $currentUserId AND recd = 0";
  300. Database::query($sql);
  301. echo json_encode(['items' => $chatHistory]);
  302. }
  303. /**
  304. * Saves into session the fact that a chat window exists with the given user.
  305. *
  306. * @param int $userId
  307. */
  308. public function saveWindow($userId)
  309. {
  310. $this->window_list[$userId] = true;
  311. Session::write('window_list', $this->window_list);
  312. }
  313. /**
  314. * Sends a message from one user to another user.
  315. *
  316. * @param int $fromUserId The ID of the user sending the message
  317. * @param int $to_user_id The ID of the user receiving the message
  318. * @param string $message Message
  319. * @param bool $printResult Optional. Whether print the result
  320. * @param bool $sanitize Optional. Whether sanitize the message
  321. */
  322. public function send(
  323. $fromUserId,
  324. $to_user_id,
  325. $message,
  326. $printResult = true,
  327. $sanitize = true
  328. ) {
  329. $relation = SocialManager::get_relation_between_contacts($fromUserId, $to_user_id);
  330. if ($relation == USER_RELATION_TYPE_FRIEND) {
  331. $now = api_get_utc_datetime();
  332. $user_info = api_get_user_info($to_user_id, true);
  333. $this->saveWindow($to_user_id);
  334. $_SESSION['openChatBoxes'][$to_user_id] = api_strtotime($now, 'UTC');
  335. if ($sanitize) {
  336. $messagesan = $this->sanitize($message);
  337. } else {
  338. $messagesan = $message;
  339. }
  340. if (!isset($_SESSION['chatHistory'][$to_user_id])) {
  341. $_SESSION['chatHistory'][$to_user_id] = [];
  342. }
  343. $item = [
  344. 's' => '1',
  345. 'f' => $fromUserId,
  346. 'm' => $messagesan,
  347. 'date' => api_strtotime($now, 'UTC'),
  348. 'username' => get_lang('Me'),
  349. ];
  350. $_SESSION['chatHistory'][$to_user_id]['items'][] = $item;
  351. $_SESSION['chatHistory'][$to_user_id]['user_info']['user_name'] = $user_info['complete_name'];
  352. $_SESSION['chatHistory'][$to_user_id]['user_info']['online'] = $user_info['user_is_online'];
  353. $_SESSION['chatHistory'][$to_user_id]['user_info']['avatar'] = $user_info['avatar_small'];
  354. $_SESSION['chatHistory'][$to_user_id]['user_info']['user_id'] = $user_info['user_id'];
  355. unset($_SESSION['tsChatBoxes'][$to_user_id]);
  356. $params = [];
  357. $params['from_user'] = (int) $fromUserId;
  358. $params['to_user'] = (int) $to_user_id;
  359. $params['message'] = $message;
  360. $params['sent'] = api_get_utc_datetime();
  361. if (!empty($fromUserId) && !empty($to_user_id)) {
  362. $messageId = $this->save($params);
  363. if ($printResult) {
  364. echo $messageId;
  365. exit;
  366. }
  367. }
  368. }
  369. if ($printResult) {
  370. echo '0';
  371. exit;
  372. }
  373. }
  374. /**
  375. * Close a specific chat box (user ID taken from $_POST['chatbox']).
  376. *
  377. * @param int $userId
  378. */
  379. public function closeWindow($userId)
  380. {
  381. if (empty($userId)) {
  382. return false;
  383. }
  384. $list = Session::read('openChatBoxes');
  385. if (isset($list[$userId])) {
  386. unset($list[$userId]);
  387. Session::write('openChatBoxes', $list);
  388. }
  389. $list = Session::read('chatHistory');
  390. if (isset($list[$userId])) {
  391. unset($list[$userId]);
  392. Session::write('chatHistory', $list);
  393. }
  394. return true;
  395. }
  396. /**
  397. * Close chat - disconnects the user.
  398. */
  399. public function close()
  400. {
  401. Session::erase('tsChatBoxes');
  402. Session::erase('openChatBoxes');
  403. Session::erase('chatHistory');
  404. Session::erase('window_list');
  405. }
  406. /**
  407. * Filter chat messages to avoid XSS or other JS.
  408. *
  409. * @param string $text Unfiltered message
  410. *
  411. * @return string Filtered message
  412. */
  413. public function sanitize($text)
  414. {
  415. $text = htmlspecialchars($text, ENT_QUOTES);
  416. $text = str_replace("\n\r", "\n", $text);
  417. $text = str_replace("\r\n", "\n", $text);
  418. $text = str_replace("\n", "<br>", $text);
  419. return $text;
  420. }
  421. /**
  422. * SET Disable Chat.
  423. *
  424. * @param bool $status to disable chat
  425. */
  426. public static function setDisableChat($status = true)
  427. {
  428. Session::write('disable_chat', $status);
  429. }
  430. /**
  431. * Disable Chat - disable the chat.
  432. *
  433. * @return bool - return true if setDisableChat status is true
  434. */
  435. public static function disableChat()
  436. {
  437. $status = Session::read('disable_chat');
  438. if (!empty($status)) {
  439. if ($status == true) {
  440. Session::write('disable_chat', null);
  441. return true;
  442. }
  443. }
  444. return false;
  445. }
  446. /**
  447. * @return bool
  448. */
  449. public function isChatBlockedByExercises()
  450. {
  451. $currentExercises = Session::read('current_exercises');
  452. if (!empty($currentExercises)) {
  453. foreach ($currentExercises as $attempt_status) {
  454. if ($attempt_status == true) {
  455. return true;
  456. }
  457. }
  458. }
  459. return false;
  460. }
  461. }