CourseChatUtils.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Michelf\MarkdownExtra;
  4. use Doctrine\Common\Collections\Criteria;
  5. use Chamilo\CourseBundle\Entity\CChatConnected;
  6. /**
  7. * Class CourseChat
  8. * Manage the chat for a course
  9. */
  10. class CourseChatUtils
  11. {
  12. private $groupId;
  13. private $courseId;
  14. private $sessionId;
  15. private $userId;
  16. /**
  17. * CourseChat constructor.
  18. * @param int $courseId
  19. * @param int $userId
  20. * @param int $sessionId
  21. * @param int $groupId
  22. */
  23. public function __construct($courseId, $userId, $sessionId = 0, $groupId = 0)
  24. {
  25. $this->courseId = (int) $courseId;
  26. $this->userId = (int) $userId;
  27. $this->sessionId = (int) $sessionId;
  28. $this->groupId = (int) $groupId;
  29. }
  30. /**
  31. * Get the users subscriptions (SessionRelCourseRelUser array or CourseRelUser array) for chat
  32. * @return \Doctrine\Common\Collections\ArrayCollection
  33. * @throws \Doctrine\ORM\ORMException
  34. * @throws \Doctrine\ORM\OptimisticLockException
  35. * @throws \Doctrine\ORM\TransactionRequiredException
  36. */
  37. private function getUsersSubscriptions()
  38. {
  39. $em = Database::getManager();
  40. $course = $em->find('ChamiloCoreBundle:Course', $this->courseId);
  41. if ($this->sessionId) {
  42. $criteria = Criteria::create()->where(Criteria::expr()->eq('course', $course));
  43. return $em
  44. ->find('ChamiloCoreBundle:Session', $this->sessionId)
  45. ->getUserCourseSubscriptions()
  46. ->matching($criteria);
  47. }
  48. return $em
  49. ->find('ChamiloCoreBundle:Course', $course)
  50. ->getUsers();
  51. }
  52. /**
  53. * Prepare a message. Clean and insert emojis
  54. * @param string $message The message to prepare
  55. * @return string
  56. */
  57. public static function prepareMessage($message)
  58. {
  59. if (empty($message)) {
  60. return '';
  61. }
  62. Emojione\Emojione::$imagePathPNG = api_get_path(WEB_PUBLIC_PATH) . 'bundles/chamilocore/js/emojione/png/';
  63. Emojione\Emojione::$ascii = true;
  64. $message = trim($message);
  65. $message = nl2br($message);
  66. // Security XSS
  67. $message = Security::remove_XSS($message);
  68. //search urls
  69. $message = preg_replace(
  70. '@((https?://)?([-\w]+\.[-\w\.]+)+\w(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)*)@',
  71. '<a href="$1" target="_blank">$1</a>',
  72. $message
  73. );
  74. // add "http://" if not set
  75. $message = preg_replace(
  76. '/<a\s[^>]*href\s*=\s*"((?!https?:\/\/)[^"]*)"[^>]*>/i',
  77. '<a href="http://$1" target="_blank">',
  78. $message
  79. );
  80. // Parsing emojis
  81. $message = Emojione\Emojione::toImage($message);
  82. // Parsing text to understand markdown (code highlight)
  83. $message = MarkdownExtra::defaultTransform($message);
  84. return $message;
  85. }
  86. /**
  87. * Save a chat message in a HTML file
  88. * @param string$message
  89. * @param int $friendId
  90. * @return bool
  91. * @throws \Doctrine\ORM\ORMException
  92. * @throws \Doctrine\ORM\OptimisticLockException
  93. * @throws \Doctrine\ORM\TransactionRequiredException
  94. */
  95. public function saveMessage($message, $friendId = 0)
  96. {
  97. if (empty($message)) {
  98. return false;
  99. }
  100. $em = Database::getManager();
  101. $user = $em->find('ChamiloUserBundle:User', $this->userId);
  102. $courseInfo = api_get_course_info_by_id($this->courseId);
  103. $isMaster = (bool) api_is_course_admin();
  104. $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
  105. $basepath_chat = '/chat_files';
  106. $group_info = [];
  107. if (!$this->groupId) {
  108. $group_info = GroupManager::get_group_properties($this->groupId);
  109. $basepath_chat = $group_info['directory'].'/chat_files';
  110. }
  111. $chat_path = $document_path.$basepath_chat.'/';
  112. if (!is_dir($chat_path)) {
  113. if (is_file($chat_path)) {
  114. @unlink($chat_path);
  115. }
  116. }
  117. $date_now = date('Y-m-d');
  118. $timeNow = date('d/m/y H:i:s');
  119. $basename_chat = 'messages-'.$date_now;
  120. if ($this->groupId && !$friendId) {
  121. $basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
  122. } elseif ($this->sessionId && !$friendId) {
  123. $basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
  124. } elseif ($friendId) {
  125. if ($this->userId < $friendId) {
  126. $basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
  127. } else {
  128. $basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
  129. }
  130. }
  131. $message = self::prepareMessage($message);
  132. $fileTitle = $basename_chat.'.log.html';
  133. $filePath = $basepath_chat.'/'.$fileTitle;
  134. $absoluteFilePath = $chat_path.$fileTitle;
  135. if (!file_exists($absoluteFilePath)) {
  136. $doc_id = add_document($courseInfo, $filePath, 'file', 0, $fileTitle);
  137. $documentLogTypes = ['DocumentAdded', 'invisible'];
  138. foreach ($documentLogTypes as $logType) {
  139. api_item_property_update(
  140. $courseInfo,
  141. TOOL_DOCUMENT,
  142. $doc_id,
  143. $logType,
  144. $this->userId,
  145. $group_info,
  146. null,
  147. null,
  148. null,
  149. $this->sessionId
  150. );
  151. }
  152. item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
  153. } else {
  154. $doc_id = DocumentManager::get_document_id($courseInfo, $filePath);
  155. }
  156. $fp = fopen($absoluteFilePath, 'a');
  157. $userPhoto = UserManager::getUserPicture($this->userId, USER_IMAGE_SIZE_MEDIUM);
  158. if ($isMaster) {
  159. $fileContent = '
  160. <div class="message-teacher">
  161. <div class="content-message">
  162. <div class="chat-message-block-name">' . $user->getCompleteName().'</div>
  163. <div class="chat-message-block-content">' . $message.'</div>
  164. <div class="message-date">' . $timeNow.'</div>
  165. </div>
  166. <div class="icon-message"></div>
  167. <img class="chat-image" src="' . $userPhoto.'">
  168. </div>
  169. ';
  170. } else {
  171. $fileContent = '
  172. <div class="message-student">
  173. <img class="chat-image" src="' . $userPhoto.'">
  174. <div class="icon-message"></div>
  175. <div class="content-message">
  176. <div class="chat-message-block-name">' . $user->getCompleteName().'</div>
  177. <div class="chat-message-block-content">' . $message.'</div>
  178. <div class="message-date">' . $timeNow.'</div>
  179. </div>
  180. </div>
  181. ';
  182. }
  183. fputs($fp, $fileContent);
  184. fclose($fp);
  185. $chat_size = filesize($absoluteFilePath);
  186. update_existing_document($courseInfo, $doc_id, $chat_size);
  187. item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
  188. return true;
  189. }
  190. /**
  191. * Disconnect a user from course chats
  192. * @param $userId
  193. */
  194. public static function exitChat($userId)
  195. {
  196. $listCourse = CourseManager::get_courses_list_by_user_id($userId);
  197. foreach ($listCourse as $course) {
  198. Database::getManager()
  199. ->createQuery('
  200. DELETE FROM ChamiloCourseBundle:CChatConnected ccc
  201. WHERE ccc.cId = :course AND ccc.userId = :user
  202. ')
  203. ->execute([
  204. 'course' => intval($course['real_id']),
  205. 'user' => intval($userId)
  206. ]);
  207. }
  208. }
  209. /**
  210. * Disconnect users who are more than 5 seconds inactive
  211. */
  212. public function disconnectInactiveUsers()
  213. {
  214. $em = Database::getManager();
  215. $extraCondition = "AND ccc.toGroupId = {$this->groupId}";
  216. if (empty($this->groupId)) {
  217. $extraCondition = "AND ccc.sessionId = {$this->sessionId}";
  218. }
  219. $connectedUsers = $em
  220. ->createQuery("
  221. SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
  222. WHERE ccc.cId = :course $extraCondition
  223. ")
  224. ->setParameter('course', $this->courseId)
  225. ->getResult();
  226. $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
  227. $cd_count_time_seconds = $now->getTimestamp();
  228. foreach ($connectedUsers as $connection) {
  229. $date_count_time_seconds = $connection->getLastConnection()->getTimestamp();
  230. if (strcmp($now->format('Y-m-d'), $connection->getLastConnection()->format('Y-m-d')) !== 0) {
  231. continue;
  232. }
  233. if (($cd_count_time_seconds - $date_count_time_seconds) <= 5) {
  234. continue;
  235. }
  236. $em
  237. ->createQuery('
  238. DELETE FROM ChamiloCourseBundle:CChatConnected ccc
  239. WHERE ccc.cId = :course AND ccc.userId = :user AND ccc.toGroupId = :group
  240. ')
  241. ->execute([
  242. 'course' => $this->courseId,
  243. 'user' => $connection->getUserId(),
  244. 'group' => $this->groupId
  245. ]);
  246. }
  247. }
  248. /**
  249. * Keep registered to a user as connected
  250. * @throws \Doctrine\ORM\NonUniqueResultException
  251. */
  252. public function keepUserAsConnected()
  253. {
  254. $em = Database::getManager();
  255. $extraCondition = null;
  256. if ($this->groupId) {
  257. $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
  258. } else {
  259. $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
  260. }
  261. $currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
  262. $connection = $em
  263. ->createQuery("
  264. SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
  265. WHERE ccc.userId = :user AND ccc.cId = :course $extraCondition
  266. ")
  267. ->setParameters([
  268. 'user' => $this->userId,
  269. 'course' => $this->courseId
  270. ])
  271. ->getOneOrNullResult();
  272. if ($connection) {
  273. $connection->setLastConnection($currentTime);
  274. $em->merge($connection);
  275. $em->flush();
  276. return;
  277. }
  278. $connection = new CChatConnected();
  279. $connection
  280. ->setCId($this->courseId)
  281. ->setUserId($this->userId)
  282. ->setLastConnection($currentTime)
  283. ->setSessionId($this->sessionId)
  284. ->setToGroupId($this->groupId);
  285. $em->persist($connection);
  286. $em->flush();
  287. }
  288. /**
  289. * Get the emoji allowed on course chat
  290. * @return array
  291. */
  292. public static function getEmojiStrategy()
  293. {
  294. return require_once api_get_path(SYS_CODE_PATH).'chat/emoji_strategy.php';
  295. }
  296. /**
  297. * Get the emoji list to include in chat
  298. * @return array
  299. */
  300. public static function getEmojisToInclude()
  301. {
  302. return [
  303. ':bowtie:',
  304. ':smile:'|
  305. ':laughing:',
  306. ':blush:',
  307. ':smiley:',
  308. ':relaxed:',
  309. ':smirk:',
  310. ':heart_eyes:',
  311. ':kissing_heart:',
  312. ':kissing_closed_eyes:',
  313. ':flushed:',
  314. ':relieved:',
  315. ':satisfied:',
  316. ':grin:',
  317. ':wink:',
  318. ':stuck_out_tongue_winking_eye:',
  319. ':stuck_out_tongue_closed_eyes:',
  320. ':grinning:',
  321. ':kissing:',
  322. ':kissing_smiling_eyes:',
  323. ':stuck_out_tongue:',
  324. ':sleeping:',
  325. ':worried:',
  326. ':frowning:',
  327. ':anguished:',
  328. ':open_mouth:',
  329. ':grimacing:',
  330. ':confused:',
  331. ':hushed:',
  332. ':expressionless:',
  333. ':unamused:',
  334. ':sweat_smile:',
  335. ':sweat:',
  336. ':disappointed_relieved:',
  337. ':weary:',
  338. ':pensive:',
  339. ':disappointed:',
  340. ':confounded:',
  341. ':fearful:',
  342. ':cold_sweat:',
  343. ':persevere:',
  344. ':cry:',
  345. ':sob:',
  346. ':joy:',
  347. ':astonished:',
  348. ':scream:',
  349. ':neckbeard:',
  350. ':tired_face:',
  351. ':angry:',
  352. ':rage:',
  353. ':triumph:',
  354. ':sleepy:',
  355. ':yum:',
  356. ':mask:',
  357. ':sunglasses:',
  358. ':dizzy_face:',
  359. ':imp:',
  360. ':smiling_imp:',
  361. ':neutral_face:',
  362. ':no_mouth:',
  363. ':innocent:',
  364. ':alien:'
  365. ];
  366. }
  367. /**
  368. * Get the chat history file name
  369. * @param bool $absolute Optional. Whether get the base or the absolute file path
  370. * @param int $friendId Optional.
  371. * @return string
  372. */
  373. public function getFileName($absolute = false, $friendId = 0)
  374. {
  375. $date = date('Y-m-d');
  376. $base = 'messages-'.$date.'.log.html';
  377. if ($this->groupId && !$friendId) {
  378. $base = 'messages-'.$date.'_gid-'.$this->groupId.'.log.html';
  379. } elseif ($this->sessionId && !$friendId) {
  380. $base = 'messages-'.$date.'_sid-'.$this->sessionId.'.log.html';
  381. } elseif ($friendId) {
  382. if ($this->userId < $friendId) {
  383. $base = 'messages-'.$date.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
  384. } else {
  385. $base = 'messages-'.$date.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
  386. }
  387. }
  388. if (!$absolute) {
  389. return $base;
  390. }
  391. $courseInfo = api_get_course_info_by_id($this->courseId);
  392. $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
  393. $chatPath = $document_path.'/chat_files/';
  394. if ($this->groupId) {
  395. $group_info = GroupManager::get_group_properties($this->groupId);
  396. $chatPath = $document_path.$group_info['directory'].'/chat_files/';
  397. }
  398. return $chatPath.$base;
  399. }
  400. /**
  401. * Get the chat history
  402. * @param bool $reset
  403. * @param int $friendId Optional.
  404. * @return string
  405. */
  406. public function readMessages($reset = false, $friendId = 0)
  407. {
  408. $courseInfo = api_get_course_info_by_id($this->courseId);
  409. $date_now = date('Y-m-d');
  410. $isMaster = (bool) api_is_course_admin();
  411. $basepath_chat = '/chat_files';
  412. $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
  413. $group_info = [];
  414. if ($this->groupId) {
  415. $group_info = GroupManager:: get_group_properties($this->groupId);
  416. $basepath_chat = $group_info['directory'].'/chat_files';
  417. }
  418. $chat_path = $document_path.$basepath_chat.'/';
  419. if (!is_dir($chat_path)) {
  420. if (is_file($chat_path)) {
  421. @unlink($chat_path);
  422. }
  423. if (!api_is_anonymous()) {
  424. @mkdir($chat_path, api_get_permissions_for_new_directories());
  425. // Save chat files document for group into item property
  426. if ($this->groupId) {
  427. $doc_id = add_document($courseInfo, $basepath_chat, 'folder', 0, 'chat_files');
  428. api_item_property_update(
  429. $courseInfo,
  430. TOOL_DOCUMENT,
  431. $doc_id,
  432. 'FolderCreated',
  433. null,
  434. $group_info,
  435. null,
  436. null,
  437. null
  438. );
  439. }
  440. }
  441. }
  442. $filename_chat = 'messages-'.$date_now.'.log.html';
  443. if ($this->groupId && !$friendId) {
  444. $filename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId.'.log.html';
  445. } elseif ($this->sessionId && !$friendId) {
  446. $filename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId.'.log.html';
  447. } elseif ($friendId) {
  448. if ($this->userId < $friendId) {
  449. $filename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
  450. } else {
  451. $filename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
  452. }
  453. }
  454. if (!file_exists($chat_path.$filename_chat)) {
  455. @fclose(fopen($chat_path.$filename_chat, 'w'));
  456. if (!api_is_anonymous()) {
  457. $doc_id = add_document($courseInfo, $basepath_chat.'/'.$filename_chat, 'file', 0, $filename_chat);
  458. api_item_property_update(
  459. $courseInfo,
  460. TOOL_DOCUMENT,
  461. $doc_id,
  462. 'DocumentAdded',
  463. $this->userId,
  464. $group_info,
  465. null,
  466. null,
  467. null,
  468. $this->sessionId
  469. );
  470. api_item_property_update(
  471. $courseInfo,
  472. TOOL_DOCUMENT,
  473. $doc_id,
  474. 'invisible',
  475. $this->userId,
  476. $group_info,
  477. null,
  478. null,
  479. null,
  480. $this->sessionId
  481. );
  482. item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
  483. }
  484. }
  485. $basename_chat = 'messages-'.$date_now;
  486. if ($this->groupId && !$friendId) {
  487. $basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
  488. } elseif ($this->sessionId && !$friendId) {
  489. $basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
  490. } elseif ($friendId) {
  491. if ($this->userId < $friendId) {
  492. $basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
  493. } else {
  494. $basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
  495. }
  496. }
  497. if ($reset && $isMaster) {
  498. $i = 1;
  499. while (file_exists($chat_path.$basename_chat.'-'.$i.'.log.html')) {
  500. $i++;
  501. }
  502. @rename($chat_path.$basename_chat.'.log.html', $chat_path.$basename_chat.'-'.$i.'.log.html');
  503. @fclose(fopen($chat_path.$basename_chat.'.log.html', 'w'));
  504. $doc_id = add_document(
  505. $courseInfo,
  506. $basepath_chat.'/'.$basename_chat.'-'.$i.'.log.html',
  507. 'file',
  508. filesize($chat_path.$basename_chat.'-'.$i.'.log.html'),
  509. $basename_chat.'-'.$i.'.log.html'
  510. );
  511. api_item_property_update(
  512. $courseInfo,
  513. TOOL_DOCUMENT,
  514. $doc_id,
  515. 'DocumentAdded',
  516. $this->userId,
  517. $group_info,
  518. null,
  519. null,
  520. null,
  521. $this->sessionId
  522. );
  523. api_item_property_update(
  524. $courseInfo,
  525. TOOL_DOCUMENT,
  526. $doc_id,
  527. 'invisible',
  528. $this->userId,
  529. $group_info,
  530. null,
  531. null,
  532. null,
  533. $this->sessionId
  534. );
  535. item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
  536. $doc_id = DocumentManager::get_document_id(
  537. $courseInfo,
  538. $basepath_chat.'/'.$basename_chat.'.log.html'
  539. );
  540. update_existing_document($courseInfo, $doc_id, 0);
  541. }
  542. $remove = 0;
  543. $content = array();
  544. if (file_exists($chat_path.$basename_chat.'.log.html')) {
  545. $content = file($chat_path.$basename_chat.'.log.html');
  546. $nbr_lines = sizeof($content);
  547. $remove = $nbr_lines - 100;
  548. }
  549. if ($remove < 0) {
  550. $remove = 0;
  551. }
  552. array_splice($content, 0, $remove);
  553. if (isset($_GET['origin']) && $_GET['origin'] == 'whoisonline') {
  554. //the caller
  555. $content[0] = get_lang('CallSent').'<br />'.$content[0];
  556. }
  557. $history = '<div id="content-chat">';
  558. foreach ($content as $this_line) {
  559. $history .= $this_line;
  560. }
  561. $history .= '</div>';
  562. if ($isMaster || $GLOBALS['is_session_general_coach']) {
  563. $history .= '
  564. <div id="clear-chat">
  565. <button type="button" id="chat-reset" class="btn btn-danger btn-sm">
  566. ' . get_lang('ClearList').'
  567. </button>
  568. </div>
  569. ';
  570. }
  571. return $history;
  572. }
  573. /**
  574. * Get the number of users connected in chat
  575. * @return mixed
  576. */
  577. public function countUsersOnline()
  578. {
  579. $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
  580. $date->modify('-5 seconds');
  581. $extraCondition = null;
  582. if ($this->groupId) {
  583. $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
  584. } else {
  585. $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
  586. }
  587. $number = Database::getManager()
  588. ->createQuery("
  589. SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
  590. WHERE ccc.lastConnection > :date AND ccc.cId = :course $extraCondition
  591. ")
  592. ->setParameters([
  593. 'date' => $date,
  594. 'course' => $this->courseId
  595. ])
  596. ->getSingleScalarResult();
  597. return intval($number);
  598. }
  599. /**
  600. * Check if a user is connected in course chat
  601. * @param int $userId
  602. * @return int
  603. */
  604. private function userIsConnected($userId)
  605. {
  606. $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
  607. $date->modify('-5 seconds');
  608. $extraCondition = null;
  609. if ($this->groupId) {
  610. $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
  611. } else {
  612. $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
  613. }
  614. $number = Database::getManager()
  615. ->createQuery("
  616. SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
  617. WHERE ccc.lastConnection > :date AND ccc.cId = :course AND ccc.userId = :user $extraCondition
  618. ")
  619. ->setParameters([
  620. 'date' => $date,
  621. 'course' => $this->courseId,
  622. 'user' => $userId
  623. ])
  624. ->getSingleScalarResult();
  625. return intval($number);
  626. }
  627. /**
  628. * Get the users online data
  629. * @return string
  630. */
  631. public function listUsersOnline()
  632. {
  633. $subscriptions = $this->getUsersSubscriptions();
  634. $usersInfo = [];
  635. foreach ($subscriptions as $subscription) {
  636. $user = $subscription->getUser();
  637. $usersInfo[] = [
  638. 'id' => $user->getId(),
  639. 'firstname' => $user->getFirstname(),
  640. 'lastname' => $user->getLastname(),
  641. 'status' => !$this->sessionId ? $subscription->getStatus() : $user->getStatus(),
  642. 'image_url' => UserManager::getUserPicture($user->getId(), USER_IMAGE_SIZE_MEDIUM),
  643. 'profile_url' => api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user->getId(),
  644. 'complete_name' => $user->getCompleteName(),
  645. 'username' => $user->getUsername(),
  646. 'email' => $user->getEmail(),
  647. 'isConnected' => $this->userIsConnected($user->getId())
  648. ];
  649. }
  650. return $usersInfo;
  651. }
  652. }