chat.lib.php 14 KB

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