123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795 |
- <?php
- /* For licensing terms, see /license.txt */
- use Chamilo\CoreBundle\Entity\Course;
- use Chamilo\CoreBundle\Entity\CourseRelUser;
- use Chamilo\CoreBundle\Entity\Session;
- use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
- use Chamilo\CourseBundle\Entity\CChatConnected;
- use Chamilo\UserBundle\Entity\User;
- use Doctrine\Common\Collections\Criteria;
- use Michelf\MarkdownExtra;
- /**
- * Class CourseChat
- * Manage the chat for a course.
- */
- class CourseChatUtils
- {
- private $groupId;
- private $courseId;
- private $sessionId;
- private $userId;
- /**
- * CourseChat constructor.
- *
- * @param int $courseId
- * @param int $userId
- * @param int $sessionId
- * @param int $groupId
- */
- public function __construct($courseId, $userId, $sessionId = 0, $groupId = 0)
- {
- $this->courseId = (int) $courseId;
- $this->userId = (int) $userId;
- $this->sessionId = (int) $sessionId;
- $this->groupId = (int) $groupId;
- }
- /**
- * Prepare a message. Clean and insert emojis.
- *
- * @param string $message The message to prepare
- *
- * @return string
- */
- public static function prepareMessage($message)
- {
- if (empty($message)) {
- return '';
- }
- Emojione\Emojione::$imagePathPNG = api_get_path(WEB_LIBRARY_PATH).'javascript/emojione/png/';
- Emojione\Emojione::$ascii = true;
- $message = trim($message);
- $message = nl2br($message);
- // Security XSS
- $message = Security::remove_XSS($message);
- //search urls
- $message = preg_replace(
- '@((https?://)?([-\w]+\.[-\w\.]+)+\w(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)*)@',
- '<a href="$1" target="_blank">$1</a>',
- $message
- );
- // add "http://" if not set
- $message = preg_replace(
- '/<a\s[^>]*href\s*=\s*"((?!https?:\/\/)[^"]*)"[^>]*>/i',
- '<a href="http://$1" target="_blank">',
- $message
- );
- // Parsing emojis
- $message = Emojione\Emojione::toImage($message);
- // Parsing text to understand markdown (code highlight)
- $message = MarkdownExtra::defaultTransform($message);
- return $message;
- }
- /**
- * Save a chat message in a HTML file.
- *
- * @param string $message
- * @param int $friendId
- *
- * @return bool
- */
- public function saveMessage($message, $friendId = 0)
- {
- if (empty($message)) {
- return false;
- }
- $friendId = (int) $friendId;
- $user = api_get_user_entity($this->userId);
- $courseInfo = api_get_course_info_by_id($this->courseId);
- $isMaster = api_is_course_admin();
- $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
- $basepath_chat = '/chat_files';
- $group_info = [];
- if ($this->groupId) {
- $group_info = GroupManager::get_group_properties($this->groupId);
- $basepath_chat = $group_info['directory'].'/chat_files';
- }
- $chat_path = $document_path.$basepath_chat.'/';
- if (!is_dir($chat_path)) {
- if (is_file($chat_path)) {
- @unlink($chat_path);
- }
- }
- $date_now = date('Y-m-d');
- $timeNow = date('d/m/y H:i:s');
- $basename_chat = 'messages-'.$date_now;
- if ($this->groupId && !$friendId) {
- $basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
- } elseif ($this->sessionId && !$friendId) {
- $basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
- } elseif ($friendId) {
- if ($this->userId < $friendId) {
- $basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
- } else {
- $basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
- }
- }
- $message = self::prepareMessage($message);
- $fileTitle = $basename_chat.'.log.html';
- $filePath = $basepath_chat.'/'.$fileTitle;
- $absoluteFilePath = $chat_path.$fileTitle;
- if (!file_exists($absoluteFilePath)) {
- $doc_id = DocumentManager::addDocument(
- $courseInfo,
- $filePath,
- 'file',
- 0,
- $fileTitle,
- null,
- 0,
- true,
- 0,
- 0,
- 0,
- false
- );
- } else {
- $doc_id = DocumentManager::get_document_id($courseInfo, $filePath);
- }
- $fp = fopen($absoluteFilePath, 'a');
- $userPhoto = UserManager::getUserPicture($this->userId, USER_IMAGE_SIZE_MEDIUM);
- if ($isMaster) {
- $fileContent = '
- <div class="message-teacher">
- <div class="content-message">
- <div class="chat-message-block-name">'.UserManager::formatUserFullName($user).'</div>
- <div class="chat-message-block-content">'.$message.'</div>
- <div class="message-date">'.$timeNow.'</div>
- </div>
- <div class="icon-message"></div>
- <img class="chat-image" src="'.$userPhoto.'">
- </div>
- ';
- } else {
- $fileContent = '
- <div class="message-student">
- <img class="chat-image" src="'.$userPhoto.'">
- <div class="icon-message"></div>
- <div class="content-message">
- <div class="chat-message-block-name">'.UserManager::formatUserFullName($user).'</div>
- <div class="chat-message-block-content">'.$message.'</div>
- <div class="message-date">'.$timeNow.'</div>
- </div>
- </div>
- ';
- }
- fputs($fp, $fileContent);
- fclose($fp);
- $size = filesize($absoluteFilePath);
- update_existing_document($courseInfo, $doc_id, $size);
- item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
- return true;
- }
- /**
- * Disconnect a user from course chats.
- *
- * @param int $userId
- */
- public static function exitChat($userId)
- {
- $listCourse = CourseManager::get_courses_list_by_user_id($userId);
- foreach ($listCourse as $course) {
- Database::getManager()
- ->createQuery('
- DELETE FROM ChamiloCourseBundle:CChatConnected ccc
- WHERE ccc.cId = :course AND ccc.userId = :user
- ')
- ->execute([
- 'course' => intval($course['real_id']),
- 'user' => intval($userId),
- ]);
- }
- }
- /**
- * Disconnect users who are more than 5 seconds inactive.
- */
- public function disconnectInactiveUsers()
- {
- $em = Database::getManager();
- $extraCondition = "AND ccc.toGroupId = {$this->groupId}";
- if (empty($this->groupId)) {
- $extraCondition = "AND ccc.sessionId = {$this->sessionId}";
- }
- $connectedUsers = $em
- ->createQuery("
- SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
- WHERE ccc.cId = :course $extraCondition
- ")
- ->setParameter('course', $this->courseId)
- ->getResult();
- $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
- $cd_count_time_seconds = $now->getTimestamp();
- /** @var CChatConnected $connection */
- foreach ($connectedUsers as $connection) {
- $date_count_time_seconds = $connection->getLastConnection()->getTimestamp();
- if (strcmp($now->format('Y-m-d'), $connection->getLastConnection()->format('Y-m-d')) !== 0) {
- continue;
- }
- if (($cd_count_time_seconds - $date_count_time_seconds) <= 5) {
- continue;
- }
- $em
- ->createQuery('
- DELETE FROM ChamiloCourseBundle:CChatConnected ccc
- WHERE ccc.cId = :course AND ccc.userId = :user AND ccc.toGroupId = :group
- ')
- ->execute([
- 'course' => $this->courseId,
- 'user' => $connection->getUserId(),
- 'group' => $this->groupId,
- ]);
- }
- }
- /**
- * Keep registered to a user as connected.
- */
- public function keepUserAsConnected()
- {
- $em = Database::getManager();
- $extraCondition = null;
- if ($this->groupId) {
- $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
- } else {
- $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
- }
- $currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
- $connection = $em
- ->createQuery("
- SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
- WHERE ccc.userId = :user AND ccc.cId = :course $extraCondition
- ")
- ->setParameters([
- 'user' => $this->userId,
- 'course' => $this->courseId,
- ])
- ->getOneOrNullResult();
- if ($connection) {
- $connection->setLastConnection($currentTime);
- $em->merge($connection);
- $em->flush();
- return;
- }
- $connection = new CChatConnected();
- $connection
- ->setCId($this->courseId)
- ->setUserId($this->userId)
- ->setLastConnection($currentTime)
- ->setSessionId($this->sessionId)
- ->setToGroupId($this->groupId);
- $em->persist($connection);
- $em->flush();
- }
- /**
- * Get the emoji allowed on course chat.
- *
- * @return array
- */
- public static function getEmojiStrategy()
- {
- return require_once api_get_path(SYS_CODE_PATH).'chat/emoji_strategy.php';
- }
- /**
- * Get the emoji list to include in chat.
- *
- * @return array
- */
- public static function getEmojisToInclude()
- {
- return [
- ':bowtie:',
- ':smile:' |
- ':laughing:',
- ':blush:',
- ':smiley:',
- ':relaxed:',
- ':smirk:',
- ':heart_eyes:',
- ':kissing_heart:',
- ':kissing_closed_eyes:',
- ':flushed:',
- ':relieved:',
- ':satisfied:',
- ':grin:',
- ':wink:',
- ':stuck_out_tongue_winking_eye:',
- ':stuck_out_tongue_closed_eyes:',
- ':grinning:',
- ':kissing:',
- ':kissing_smiling_eyes:',
- ':stuck_out_tongue:',
- ':sleeping:',
- ':worried:',
- ':frowning:',
- ':anguished:',
- ':open_mouth:',
- ':grimacing:',
- ':confused:',
- ':hushed:',
- ':expressionless:',
- ':unamused:',
- ':sweat_smile:',
- ':sweat:',
- ':disappointed_relieved:',
- ':weary:',
- ':pensive:',
- ':disappointed:',
- ':confounded:',
- ':fearful:',
- ':cold_sweat:',
- ':persevere:',
- ':cry:',
- ':sob:',
- ':joy:',
- ':astonished:',
- ':scream:',
- ':neckbeard:',
- ':tired_face:',
- ':angry:',
- ':rage:',
- ':triumph:',
- ':sleepy:',
- ':yum:',
- ':mask:',
- ':sunglasses:',
- ':dizzy_face:',
- ':imp:',
- ':smiling_imp:',
- ':neutral_face:',
- ':no_mouth:',
- ':innocent:',
- ':alien:',
- ];
- }
- /**
- * Get the chat history file name.
- *
- * @param bool $absolute Optional. Whether get the base or the absolute file path
- * @param int $friendId optional
- *
- * @return string
- */
- public function getFileName($absolute = false, $friendId = 0)
- {
- $date = date('Y-m-d');
- $base = 'messages-'.$date.'.log.html';
- if ($this->groupId && !$friendId) {
- $base = 'messages-'.$date.'_gid-'.$this->groupId.'.log.html';
- } elseif ($this->sessionId && !$friendId) {
- $base = 'messages-'.$date.'_sid-'.$this->sessionId.'.log.html';
- } elseif ($friendId) {
- if ($this->userId < $friendId) {
- $base = 'messages-'.$date.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
- } else {
- $base = 'messages-'.$date.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
- }
- }
- if (!$absolute) {
- return $base;
- }
- $courseInfo = api_get_course_info_by_id($this->courseId);
- $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
- $chatPath = $document_path.'/chat_files/';
- if ($this->groupId) {
- $group_info = GroupManager::get_group_properties($this->groupId);
- $chatPath = $document_path.$group_info['directory'].'/chat_files/';
- }
- return $chatPath.$base;
- }
- /**
- * Get the chat history.
- *
- * @param bool $reset
- * @param int $friendId optional
- *
- * @return string
- */
- public function readMessages($reset = false, $friendId = 0)
- {
- $courseInfo = api_get_course_info_by_id($this->courseId);
- $date_now = date('Y-m-d');
- $isMaster = (bool) api_is_course_admin();
- $basepath_chat = '/chat_files';
- $document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
- $group_info = [];
- if ($this->groupId) {
- $group_info = GroupManager:: get_group_properties($this->groupId);
- $basepath_chat = $group_info['directory'].'/chat_files';
- }
- $chat_path = $document_path.$basepath_chat.'/';
- if (!is_dir($chat_path)) {
- if (is_file($chat_path)) {
- @unlink($chat_path);
- }
- if (!api_is_anonymous()) {
- @mkdir($chat_path, api_get_permissions_for_new_directories());
- // Save chat files document for group into item property
- if ($this->groupId) {
- DocumentManager::addDocument(
- $courseInfo,
- $basepath_chat,
- 'folder',
- 0,
- 'chat_files',
- null,
- 0,
- true,
- 0,
- 0,
- 0,
- false
- );
- }
- }
- }
- $filename_chat = 'messages-'.$date_now.'.log.html';
- if ($this->groupId && !$friendId) {
- $filename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId.'.log.html';
- } elseif ($this->sessionId && !$friendId) {
- $filename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId.'.log.html';
- } elseif ($friendId) {
- if ($this->userId < $friendId) {
- $filename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
- } else {
- $filename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
- }
- }
- if (!file_exists($chat_path.$filename_chat)) {
- @fclose(fopen($chat_path.$filename_chat, 'w'));
- if (!api_is_anonymous()) {
- DocumentManager::addDocument(
- $courseInfo,
- $basepath_chat.'/'.$filename_chat,
- 'file',
- 0,
- $filename_chat,
- null,
- 0,
- true,
- 0,
- 0,
- 0,
- false
- );
- }
- }
- $basename_chat = 'messages-'.$date_now;
- if ($this->groupId && !$friendId) {
- $basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
- } elseif ($this->sessionId && !$friendId) {
- $basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
- } elseif ($friendId) {
- if ($this->userId < $friendId) {
- $basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
- } else {
- $basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
- }
- }
- if ($reset && $isMaster) {
- $i = 1;
- while (file_exists($chat_path.$basename_chat.'-'.$i.'.log.html')) {
- $i++;
- }
- @rename($chat_path.$basename_chat.'.log.html', $chat_path.$basename_chat.'-'.$i.'.log.html');
- @fclose(fopen($chat_path.$basename_chat.'.log.html', 'w'));
- $doc_id = DocumentManager::addDocument(
- $courseInfo,
- $basepath_chat.'/'.$basename_chat.'-'.$i.'.log.html',
- 'file',
- filesize($chat_path.$basename_chat.'-'.$i.'.log.html'),
- $basename_chat.'-'.$i.'.log.html',
- null,
- 0,
- true,
- 0,
- 0,
- 0,
- false
- );
- $doc_id = DocumentManager::get_document_id(
- $courseInfo,
- $basepath_chat.'/'.$basename_chat.'.log.html'
- );
- update_existing_document($courseInfo, $doc_id, 0);
- }
- $remove = 0;
- $content = [];
- if (file_exists($chat_path.$basename_chat.'.log.html')) {
- $content = file($chat_path.$basename_chat.'.log.html');
- $nbr_lines = sizeof($content);
- $remove = $nbr_lines - 100;
- }
- if ($remove < 0) {
- $remove = 0;
- }
- array_splice($content, 0, $remove);
- if (isset($_GET['origin']) && $_GET['origin'] == 'whoisonline') {
- //the caller
- $content[0] = get_lang('Chat call has been sent. Waiting for the approval of your partner.').'<br />'.$content[0];
- }
- $history = '<div id="content-chat">';
- foreach ($content as $this_line) {
- $history .= $this_line;
- }
- $history .= '</div>';
- if ($isMaster || $GLOBALS['is_session_general_coach']) {
- $history .= '
- <div id="clear-chat">
- <button type="button" id="chat-reset" class="btn btn-danger btn-sm">
- '.get_lang('Clear the chat').'
- </button>
- </div>
- ';
- }
- return $history;
- }
- /**
- * Get the number of users connected in chat.
- *
- * @return int
- */
- public function countUsersOnline()
- {
- $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
- $date->modify('-5 seconds');
- if ($this->groupId) {
- $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
- } else {
- $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
- }
- $number = Database::getManager()
- ->createQuery("
- SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
- WHERE ccc.lastConnection > :date AND ccc.cId = :course $extraCondition
- ")
- ->setParameters([
- 'date' => $date,
- 'course' => $this->courseId,
- ])
- ->getSingleScalarResult();
- return (int) $number;
- }
- /**
- * Get the users online data.
- *
- * @throws \Doctrine\ORM\ORMException
- * @throws \Doctrine\ORM\OptimisticLockException
- * @throws \Doctrine\ORM\TransactionRequiredException
- *
- * @return array
- */
- public function listUsersOnline()
- {
- $subscriptions = $this->getUsersSubscriptions();
- $usersInfo = [];
- if ($this->groupId) {
- /** @var User $groupUser */
- foreach ($subscriptions as $groupUser) {
- $usersInfo[] = $this->formatUser(
- $groupUser,
- $groupUser->getStatus()
- );
- }
- } else {
- /** @var CourseRelUser|SessionRelCourseRelUser $subscription */
- foreach ($subscriptions as $subscription) {
- $user = $subscription->getUser();
- $usersInfo[] = $this->formatUser(
- $user,
- $this->sessionId ? $user->getStatus() : $subscription->getStatus()
- );
- }
- }
- return $usersInfo;
- }
- /**
- * Format the user data to return it in the user list.
- *
- * @param User $user
- * @param int $status
- *
- * @return array
- */
- private function formatUser(User $user, $status)
- {
- return [
- 'id' => $user->getId(),
- 'firstname' => $user->getFirstname(),
- 'lastname' => $user->getLastname(),
- 'status' => $status,
- 'image_url' => UserManager::getUserPicture($user->getId(), USER_IMAGE_SIZE_MEDIUM),
- 'profile_url' => api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user->getId(),
- 'complete_name' => UserManager::formatUserFullName($user),
- 'username' => $user->getUsername(),
- 'email' => $user->getEmail(),
- 'isConnected' => $this->userIsConnected($user->getId()),
- ];
- }
- /**
- * Get the users subscriptions (SessionRelCourseRelUser array or CourseRelUser array) for chat.
- *
- * @throws \Doctrine\ORM\ORMException
- * @throws \Doctrine\ORM\OptimisticLockException
- * @throws \Doctrine\ORM\TransactionRequiredException
- *
- * @return \Doctrine\Common\Collections\ArrayCollection
- */
- private function getUsersSubscriptions()
- {
- $em = Database::getManager();
- if ($this->groupId) {
- $students = $em
- ->createQuery(
- 'SELECT u FROM ChamiloUserBundle:User u
- INNER JOIN ChamiloCourseBundle:CGroupRelUser gru
- WITH u.id = gru.userId AND gru.cId = :course
- WHERE u.id != :user AND gru.groupId = :group
- AND u.active = true'
- )
- ->setParameters(['course' => $this->courseId, 'user' => $this->userId, 'group' => $this->groupId])
- ->getResult();
- $tutors = $em
- ->createQuery(
- 'SELECT u FROM ChamiloUserBundle:User u
- INNER JOIN ChamiloCourseBundle:CGroupRelTutor grt
- WITH u.id = grt.userId AND grt.cId = :course
- WHERE u.id != :user AND grt.groupId = :group
- AND u.active = true'
- )
- ->setParameters(['course' => $this->courseId, 'user' => $this->userId, 'group' => $this->groupId])
- ->getResult();
- return array_merge($tutors, $students);
- }
- /** @var Course $course */
- $course = $em->find('ChamiloCoreBundle:Course', $this->courseId);
- if ($this->sessionId) {
- /** @var Session $session */
- $session = $em->find('ChamiloCoreBundle:Session', $this->sessionId);
- $criteria = Criteria::create()->where(Criteria::expr()->eq('course', $course));
- $userIsCoach = api_is_course_session_coach($this->userId, $course->getId(), $session->getId());
- if (api_get_configuration_value('course_chat_restrict_to_coach')) {
- if ($userIsCoach) {
- $criteria->andWhere(
- Criteria::expr()->eq('status', Session::STUDENT)
- );
- } else {
- $criteria->andWhere(
- Criteria::expr()->eq('status', Session::COACH)
- );
- }
- }
- $criteria->orderBy(['status' => Criteria::DESC]);
- return $session
- ->getUserCourseSubscriptions()
- ->matching($criteria)
- ->filter(function (SessionRelCourseRelUser $sessionRelCourseRelUser) {
- return $sessionRelCourseRelUser->getUser()->isActive();
- });
- }
- return $course
- ->getUsers()
- ->filter(function (CourseRelUser $courseRelUser) {
- return $courseRelUser->getUser()->isActive();
- });
- }
- /**
- * Check if a user is connected in course chat.
- *
- * @param int $userId
- *
- * @return int
- */
- private function userIsConnected($userId)
- {
- $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
- $date->modify('-5 seconds');
- if ($this->groupId) {
- $extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
- } else {
- $extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
- }
- $number = Database::getManager()
- ->createQuery("
- SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
- WHERE ccc.lastConnection > :date AND ccc.cId = :course AND ccc.userId = :user $extraCondition
- ")
- ->setParameters([
- 'date' => $date,
- 'course' => $this->courseId,
- 'user' => $userId,
- ])
- ->getSingleScalarResult();
- return (int) $number;
- }
- }
|