chat.lib.php 16 KB

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