Load chat tool as a resource

pull/3124/head
Julio Montoya 6 years ago
parent 45d6994726
commit 0f570fd0b2
  1. 6
      assets/js/vendor.js
  2. 2
      composer.json
  3. 2
      package.json
  4. 26
      public/main/chat/chat.php
  5. 85
      public/main/inc/ajax/course_chat.ajax.php
  6. 256
      public/main/inc/lib/CourseChatUtils.php
  7. 0
      public/sound/notification.mp3
  8. 0
      public/sound/notification.ogg
  9. 0
      public/sound/notification.wav
  10. 35
      src/CoreBundle/Controller/AbstractResourceController.php
  11. 145
      src/CoreBundle/Controller/ChatController.php
  12. 30
      src/CoreBundle/Controller/ResourceController.php
  13. 3
      src/CoreBundle/Repository/ResourceFactory.php
  14. 63
      src/CoreBundle/Repository/ResourceNodeRepository.php
  15. 71
      src/CoreBundle/Repository/ResourceRepository.php
  16. 4
      src/CoreBundle/Resources/config/repositories.yml
  17. 6
      src/CoreBundle/Resources/config/tools.yml
  18. 81
      src/CourseBundle/Entity/CChatConversation.php
  19. 54
      src/CourseBundle/Repository/CChatConversationRepository.php
  20. 1
      src/CourseBundle/Repository/CForumPostRepository.php
  21. 4
      src/CourseBundle/Resources/config/services.yml
  22. 107
      src/ThemeBundle/Resources/views/Chat/chat.html.twig
  23. 0
      src/ThemeBundle/Resources/views/Chat/video.html.twig
  24. 29
      yarn.lock

@ -35,6 +35,12 @@ require('@fancyapps/fancybox/dist/jquery.fancybox.js');
require('@fancyapps/fancybox/src/js/media.js'); require('@fancyapps/fancybox/src/js/media.js');
require('jquery-contextmenu/dist/jquery.contextMenu.js'); require('jquery-contextmenu/dist/jquery.contextMenu.js');
var hljs = require('highlight.js');
global.hljs = hljs;
var textcomplete = require('textcomplete');
global.textcomplete = textcomplete;
require('chart.js'); require('chart.js');
require('./annotation.js'); require('./annotation.js');

@ -176,7 +176,7 @@
"symfony/browser-kit": "^4.0|^5.0", "symfony/browser-kit": "^4.0|^5.0",
"symfony/css-selector": "^4.0|^5.0", "symfony/css-selector": "^4.0|^5.0",
"symfony/debug-pack": "*", "symfony/debug-pack": "*",
"symfony/phpunit-bridge": "^4.0|^5.0", "symfony/phpunit-bridge": "^5.0",
"symfony/profiler-pack": "*", "symfony/profiler-pack": "*",
"symfony/test-pack": "*", "symfony/test-pack": "*",
"symplify/easy-coding-standard": "^7.0", "symplify/easy-coding-standard": "^7.0",

@ -36,6 +36,7 @@
"fs": "0.0.1-security", "fs": "0.0.1-security",
"fullcalendar": "^3.0", "fullcalendar": "^3.0",
"highlight.js": "^9.12.0", "highlight.js": "^9.12.0",
"hljs": "^6.2.3",
"image-map-resizer": "^1.0.10", "image-map-resizer": "^1.0.10",
"jquery-contextmenu": "^2.9.0", "jquery-contextmenu": "^2.9.0",
"jquery-ui": "^1.12.1", "jquery-ui": "^1.12.1",
@ -60,6 +61,7 @@
"readmore-js": "^2.2.1", "readmore-js": "^2.2.1",
"select2": "^4.0", "select2": "^4.0",
"sweetalert2": "^9.5.3", "sweetalert2": "^9.5.3",
"textcomplete": "^0.18.1",
"timeago": "^1.6.7", "timeago": "^1.6.7",
"timepicker": "^1.11.14", "timepicker": "^1.11.14",
"uglifyjs-webpack-plugin": "^1.3.0", "uglifyjs-webpack-plugin": "^1.3.0",

@ -19,10 +19,10 @@ Event::registerLog($logInfo);
// View // View
$externalCSS = [ $externalCSS = [
'jquery-emojiarea/jquery.emojiarea.css', //'jquery-emojiarea/jquery.emojiarea.css',
'jquery-textcomplete/jquery.textcomplete.css', //'jquery-textcomplete/jquery.textcomplete.css',
'emojione/css/emojione.min.css', //'emojione/css/emojione.min.css',
'emojione/css/autocomplete.css', //'emojione/css/autocomplete.css',
'highlight/styles/github.css', 'highlight/styles/github.css',
]; ];
@ -35,9 +35,9 @@ $htmlHeadXtra[] = api_get_css(api_get_path(WEB_CSS_PATH).'markdown.css');
$externalJS = [ $externalJS = [
'highlight/highlight.pack.js', 'highlight/highlight.pack.js',
'jquery-textcomplete/jquery.textcomplete.js', //'jquery-textcomplete/jquery.textcomplete.js',
'emojione/js/emojione.min.js', //'emojione/js/emojione.min.js',
'jquery-emojiarea/jquery.emojiarea.js', //'jquery-emojiarea/jquery.emojiarea.js',
]; ];
foreach ($externalJS as $js) { foreach ($externalJS as $js) {
@ -46,20 +46,12 @@ foreach ($externalJS as $js) {
$iconList = []; $iconList = [];
foreach (Emojione\Emojione::$shortcode_replace as $key => $icon) {
if (!in_array($key, CourseChatUtils::getEmojisToInclude())) {
continue;
}
$iconList[$key] = strtoupper($icon).'.png';
}
$view = new Template(get_lang('Chat'), false, false, false, true, false); $view = new Template(get_lang('Chat'), false, false, false, true, false);
$view->assign('icons', $iconList); $view->assign('icons', $iconList);
$view->assign('emoji_strategy', CourseChatUtils::getEmojiStrategy()); $view->assign('emoji_strategy', CourseChatUtils::getEmojiStrategy());
$view->assign('emoji_smile', \Emojione\Emojione::toImage(':smile:')); //$view->assign('emoji_smile', \Emojione\Emojione::toImage(':smile:'));
$view->assign('restrict_to_coach', api_get_configuration_value('course_chat_restrict_to_coach')); $view->assign('restrict_to_coach', api_get_configuration_value('course_chat_restrict_to_coach'));
$view->assign('user', api_get_user_info());
$template = $view->get_template('chat/chat.tpl'); $template = $view->get_template('chat/chat.tpl');
$content = $view->fetch($template); $content = $view->fetch($template);

@ -1,85 +0,0 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Responses to AJAX calls for course chat.
*/
require_once __DIR__.'/../global.inc.php';
if (!api_protect_course_script(false)) {
exit;
}
$courseId = api_get_course_int_id();
$userId = api_get_user_id();
$sessionId = api_get_session_id();
$groupId = api_get_group_id();
$json = ['status' => false];
$courseChatUtils = new CourseChatUtils($courseId, $userId, $sessionId, $groupId);
switch ($_REQUEST['action']) {
case 'chat_logout':
$logInfo = [
'tool' => TOOL_CHAT,
'tool_id' => 0,
'tool_id_detail' => 0,
'action' => 'exit',
'action_details' => 'exit-chat',
'info' => '',
];
Event::registerLog($logInfo);
break;
case 'track':
$courseChatUtils->keepUserAsConnected();
$courseChatUtils->disconnectInactiveUsers();
$friend = isset($_REQUEST['friend']) ? (int) $_REQUEST['friend'] : 0;
$filePath = $courseChatUtils->getFileName(true, $friend);
$newFileSize = file_exists($filePath) ? filesize($filePath) : 0;
$oldFileSize = isset($_GET['size']) ? (int) $_GET['size'] : -1;
$newUsersOnline = $courseChatUtils->countUsersOnline();
$oldUsersOnline = isset($_GET['users_online']) ? (int) $_GET['users_online'] : 0;
$json = [
'status' => true,
'data' => [
'oldFileSize' => file_exists($filePath) ? filesize($filePath) : 0,
'history' => $newFileSize !== $oldFileSize ? $courseChatUtils->readMessages(false, $friend) : null,
'usersOnline' => $newUsersOnline,
'userList' => $newUsersOnline != $oldUsersOnline ? $courseChatUtils->listUsersOnline() : null,
'currentFriend' => $friend,
],
];
break;
case 'preview':
$json = [
'status' => true,
'data' => [
'message' => CourseChatUtils::prepareMessage($_REQUEST['message']),
],
];
break;
case 'reset':
$friend = isset($_REQUEST['friend']) ? (int) $_REQUEST['friend'] : 0;
$json = [
'status' => true,
'data' => $courseChatUtils->readMessages(true, $friend),
];
break;
case 'write':
$friend = isset($_REQUEST['friend']) ? (int) $_REQUEST['friend'] : 0;
$writed = $courseChatUtils->saveMessage($_POST['message'], $friend);
$json = [
'status' => $writed,
'data' => [
'writed' => $writed,
],
];
break;
}
header('Content-Type: application/json');
echo json_encode($json);

@ -1,14 +1,22 @@
<?php <?php
/* For licensing terms, see /license.txt */ /* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\Course; use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\CourseRelUser; use Chamilo\CoreBundle\Entity\CourseRelUser;
use Chamilo\CoreBundle\Entity\Resource\ResourceLink;
use Chamilo\CoreBundle\Entity\Resource\ResourceNode;
use Chamilo\CoreBundle\Entity\Session; use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser; use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
use Chamilo\CoreBundle\Repository\ResourceRepository;
use Chamilo\CourseBundle\Entity\CChatConnected; use Chamilo\CourseBundle\Entity\CChatConnected;
use Chamilo\CourseBundle\Entity\CChatConversation;
use Chamilo\UserBundle\Entity\User; use Chamilo\UserBundle\Entity\User;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Criteria;
use Michelf\MarkdownExtra; use Michelf\MarkdownExtra;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/** /**
* Class CourseChat * Class CourseChat
@ -20,6 +28,7 @@ class CourseChatUtils
private $courseId; private $courseId;
private $sessionId; private $sessionId;
private $userId; private $userId;
private $resourceNode;
/** /**
* CourseChat constructor. * CourseChat constructor.
@ -29,12 +38,14 @@ class CourseChatUtils
* @param int $sessionId * @param int $sessionId
* @param int $groupId * @param int $groupId
*/ */
public function __construct($courseId, $userId, $sessionId = 0, $groupId = 0) public function __construct($courseId, $userId, $sessionId = 0, $groupId = 0, ResourceNode $resourceNode, ResourceRepository $repository)
{ {
$this->courseId = (int) $courseId; $this->courseId = (int) $courseId;
$this->userId = (int) $userId; $this->userId = (int) $userId;
$this->sessionId = (int) $sessionId; $this->sessionId = (int) $sessionId;
$this->groupId = (int) $groupId; $this->groupId = (int) $groupId;
$this->resourceNode = $resourceNode;
$this->repository = $repository;
} }
/** /**
@ -50,8 +61,8 @@ class CourseChatUtils
return ''; return '';
} }
Emojione\Emojione::$imagePathPNG = api_get_path(WEB_LIBRARY_PATH).'javascript/emojione/png/'; //Emojione\Emojione::$imagePathPNG = api_get_path(WEB_LIBRARY_PATH).'javascript/emojione/png/';
Emojione\Emojione::$ascii = true; //Emojione\Emojione::$ascii = true;
$message = trim($message); $message = trim($message);
$message = nl2br($message); $message = nl2br($message);
@ -69,8 +80,9 @@ class CourseChatUtils
'<a href="http://$1" target="_blank">', '<a href="http://$1" target="_blank">',
$message $message
); );
// Parsing emojis // Parsing emojis
$message = Emojione\Emojione::toImage($message); //$message = Emojione\Emojione::toImage($message);
// Parsing text to understand markdown (code highlight) // Parsing text to understand markdown (code highlight)
$message = MarkdownExtra::defaultTransform($message); $message = MarkdownExtra::defaultTransform($message);
@ -95,22 +107,6 @@ class CourseChatUtils
$user = api_get_user_entity($this->userId); $user = api_get_user_entity($this->userId);
$courseInfo = api_get_course_info_by_id($this->courseId); $courseInfo = api_get_course_info_by_id($this->courseId);
$isMaster = api_is_course_admin(); $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'); $date_now = date('Y-m-d');
$timeNow = date('d/m/y H:i:s'); $timeNow = date('d/m/y H:i:s');
$basename_chat = 'messages-'.$date_now; $basename_chat = 'messages-'.$date_now;
@ -129,30 +125,10 @@ class CourseChatUtils
$message = self::prepareMessage($message); $message = self::prepareMessage($message);
$fileTitle = $basename_chat.'.log.html'; $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); $userPhoto = UserManager::getUserPicture($this->userId, USER_IMAGE_SIZE_MEDIUM);
if ($isMaster) { if ($isMaster) {
@ -181,11 +157,26 @@ class CourseChatUtils
'; ';
} }
fputs($fp, $fileContent); $criteria = [
'slug' => $fileTitle,
'parent' => $this->resourceNode,
];
$resourceNode = $this->repository->getResourceNodeRepository()->findOneBy($criteria);
if ($resourceNode) {
$resource = $this->repository->getResourceFromResourceNode($resourceNode->getId());
if ($resource) {
$content = $this->repository->getResourceNodeFileContent($resourceNode);
$this->repository->updateResourceFileContent($resource, $content.$fileContent);
}
}
/*fputs($fp, $fileContent);
fclose($fp); fclose($fp);
$size = filesize($absoluteFilePath); $size = filesize($absoluteFilePath);
update_existing_document($courseInfo, $doc_id, $size); update_existing_document($courseInfo, $doc_id, $size);
item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId); item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);*/
return true; return true;
} }
@ -266,13 +257,14 @@ class CourseChatUtils
$extraCondition = null; $extraCondition = null;
if ($this->groupId) { if ($this->groupId) {
$extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId); $extraCondition = 'AND ccc.toGroupId = '.$this->groupId;
} else { } else {
$extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId); $extraCondition = 'AND ccc.sessionId = '.$this->sessionId;
} }
$currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC')); $currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
/** @var CChatConnected $connection */
$connection = $em $connection = $em
->createQuery(" ->createQuery("
SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
@ -286,7 +278,7 @@ class CourseChatUtils
if ($connection) { if ($connection) {
$connection->setLastConnection($currentTime); $connection->setLastConnection($currentTime);
$em->merge($connection); $em->persist($connection);
$em->flush(); $em->flush();
return; return;
@ -309,10 +301,10 @@ class CourseChatUtils
* *
* @return array * @return array
*/ */
public static function getEmojiStrategy() /*public static function getEmojiStrategy()
{ {
return require_once api_get_path(SYS_CODE_PATH).'chat/emoji_strategy.php'; return require_once api_get_path(SYS_CODE_PATH).'chat/emoji_strategy.php';
} }*/
/** /**
* Get the emoji list to include in chat. * Get the emoji list to include in chat.
@ -416,8 +408,9 @@ class CourseChatUtils
return $base; return $base;
} }
$courseInfo = api_get_course_info_by_id($this->courseId); //$courseInfo = api_get_course_info_by_id($this->courseId);
$document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
$document_path = '/document';
$chatPath = $document_path.'/chat_files/'; $chatPath = $document_path.'/chat_files/';
if ($this->groupId) { if ($this->groupId) {
@ -441,119 +434,72 @@ class CourseChatUtils
$courseInfo = api_get_course_info_by_id($this->courseId); $courseInfo = api_get_course_info_by_id($this->courseId);
$date_now = date('Y-m-d'); $date_now = date('Y-m-d');
$isMaster = (bool) api_is_course_admin(); $isMaster = (bool) api_is_course_admin();
$basepath_chat = '/chat_files'; //$basepath_chat = '/chat_files';
$document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document'; //$document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
$group_info = [];
if ($this->groupId) { if ($this->groupId) {
$group_info = GroupManager:: get_group_properties($this->groupId); $group_info = GroupManager:: get_group_properties($this->groupId);
$basepath_chat = $group_info['directory'].'/chat_files'; //$basepath_chat = $group_info['directory'].'/chat_files';
} }
$chat_path = $document_path.$basepath_chat.'/'; //$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'; $filename_chat = 'messages-'.$date_now.'-log.html';
if ($this->groupId && !$friendId) { if ($this->groupId && !$friendId) {
$filename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId.'.log.html'; $filename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId.'-log.html';
} elseif ($this->sessionId && !$friendId) { } elseif ($this->sessionId && !$friendId) {
$filename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId.'.log.html'; $filename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId.'-log.html';
} elseif ($friendId) { } elseif ($friendId) {
if ($this->userId < $friendId) { if ($this->userId < $friendId) {
$filename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId.'.log.html'; $filename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId.'-log.html';
} else { } else {
$filename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId.'.log.html'; $filename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId.'-log.html';
} }
} }
if (!file_exists($chat_path.$filename_chat)) { $criteria = [
@fclose(fopen($chat_path.$filename_chat, 'w')); 'slug' => $filename_chat,
if (!api_is_anonymous()) { 'parent' => $this->resourceNode,
DocumentManager::addDocument( ];
$courseInfo,
$basepath_chat.'/'.$filename_chat, $resourceNode = $this->repository->getResourceNodeRepository()->findOneBy($criteria);
'file',
0, /** @var ResourceNode $resourceNode */
$filename_chat, //$resourceNode = $this->repository->findOneBy($criteria);
null, //var_dump($filename_chat, $this->resourceNode->getId());exit;
0,
true, if (null === $resourceNode) {
0, $em = Database::getManager();
0, $resource = new CChatConversation();
0, $resource->setName($filename_chat);
false
); $handle = tmpfile();
} fwrite($handle, '');
$meta = stream_get_meta_data($handle);
$file = new UploadedFile($meta['uri'], $filename_chat, 'text/html', null, true);
$this->repository->addResourceToCourse(
$resource,
ResourceLink::VISIBILITY_PUBLISHED,
api_get_user_entity(api_get_user_id()),
api_get_course_entity(),
api_get_session_entity(),
api_get_group_entity(),
$file
);
$em->flush();
$resourceNode = $resource->getResourceNode();
} }
$basename_chat = 'messages-'.$date_now; if ($resourceNode->hasResourceFile()) {
if ($this->groupId && !$friendId) { //$resourceFile = $resourceNode->getResourceFile();
$basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId; //$fileName = $this->getFilename($resourceFile);
} elseif ($this->sessionId && !$friendId) { return $this->repository->getResourceNodeFileContent($resourceNode);
$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) { return '';
$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; $remove = 0;
$content = []; $content = [];
@ -605,9 +551,9 @@ class CourseChatUtils
$date->modify('-5 seconds'); $date->modify('-5 seconds');
if ($this->groupId) { if ($this->groupId) {
$extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId); $extraCondition = 'AND ccc.toGroupId = '.$this->groupId;
} else { } else {
$extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId); $extraCondition = 'AND ccc.sessionId = '.$this->sessionId;
} }
$number = Database::getManager() $number = Database::getManager()
@ -627,10 +573,6 @@ class CourseChatUtils
/** /**
* Get the users online data. * Get the users online data.
* *
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*
* @return array * @return array
*/ */
public function listUsersOnline() public function listUsersOnline()
@ -686,11 +628,7 @@ class CourseChatUtils
/** /**
* Get the users subscriptions (SessionRelCourseRelUser array or CourseRelUser array) for chat. * Get the users subscriptions (SessionRelCourseRelUser array or CourseRelUser array) for chat.
* *
* @throws \Doctrine\ORM\ORMException * @return ArrayCollection
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*
* @return \Doctrine\Common\Collections\ArrayCollection
*/ */
private function getUsersSubscriptions() private function getUsersSubscriptions()
{ {
@ -772,9 +710,9 @@ class CourseChatUtils
$date->modify('-5 seconds'); $date->modify('-5 seconds');
if ($this->groupId) { if ($this->groupId) {
$extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId); $extraCondition = 'AND ccc.toGroupId = '.$this->groupId;
} else { } else {
$extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId); $extraCondition = 'AND ccc.sessionId = '.$this->sessionId;
} }
$number = Database::getManager() $number = Database::getManager()

@ -6,11 +6,13 @@ namespace Chamilo\CoreBundle\Controller;
use Chamilo\CoreBundle\Component\Utils\Glide; use Chamilo\CoreBundle\Component\Utils\Glide;
use Chamilo\CoreBundle\Entity\Resource\AbstractResource; use Chamilo\CoreBundle\Entity\Resource\AbstractResource;
use Chamilo\CoreBundle\Entity\Resource\ResourceNode;
use Chamilo\CoreBundle\Repository\ResourceFactory; use Chamilo\CoreBundle\Repository\ResourceFactory;
use Chamilo\CoreBundle\Repository\ResourceRepository; use Chamilo\CoreBundle\Repository\ResourceRepository;
use Chamilo\UserBundle\Entity\User;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Vich\UploaderBundle\Storage\FlysystemStorage; use Symfony\Component\Security\Core\Exception\AccessDeniedException;
/** /**
* Class AbstractResourceController. * Class AbstractResourceController.
@ -29,6 +31,11 @@ abstract class AbstractResourceController extends BaseController
$tool = $request->get('tool'); $tool = $request->get('tool');
$type = $request->get('type'); $type = $request->get('type');
return $this->getRepository($tool, $type);
}
public function getRepository($tool, $type): ResourceRepository
{
return $this->resourceRepositoryFactory->createRepository($tool, $type); return $this->resourceRepositoryFactory->createRepository($tool, $type);
} }
@ -54,6 +61,32 @@ abstract class AbstractResourceController extends BaseController
} }
} }
protected function getParentResourceNode(Request $request): ResourceNode
{
$parentNodeId = $request->get('id');
$parentResourceNode = null;
if (empty($parentNodeId)) {
if ($this->hasCourse()) {
$parentResourceNode = $this->getCourse()->getResourceNode();
} else {
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
/** @var User $user */
$parentResourceNode = $this->getUser()->getResourceNode();
}
}
} else {
$repo = $this->getDoctrine()->getRepository('ChamiloCoreBundle:Resource\ResourceNode');
$parentResourceNode = $repo->find($parentNodeId);
}
if (null === $parentResourceNode) {
throw new AccessDeniedException();
}
return $parentResourceNode;
}
/** /**
* @return Glide * @return Glide
*/ */

@ -0,0 +1,145 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Controller;
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
use Chamilo\CourseBundle\Controller\CourseControllerInterface;
use Chamilo\CourseBundle\Controller\CourseControllerTrait;
use Chamilo\CourseBundle\Repository\CChatConversationRepository;
use Event;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class ChatController.
*/
class ChatController extends AbstractResourceController implements CourseControllerInterface
{
use CourseControllerTrait;
/**
* @Route("/resources/chat/", name="chat_home", options={"expose"=true})
*/
public function indexAction(Request $request): Response
{
Event::event_access_tool(TOOL_CHAT);
$logInfo = [
'tool' => TOOL_CHAT,
'action' => 'start',
'action_details' => 'start-chat',
];
Event::registerLog($logInfo);
return $this->render(
'@ChamiloTheme/Chat/chat.html.twig',
[
'restrict_to_coach' => api_get_configuration_value('course_chat_restrict_to_coach'),
'user' => api_get_user_info(),
]
);
}
/**
* @Route("/resources/chat/conversations/", name="chat_ajax", options={"expose"=true})
*/
public function ajaxAction(Request $request, ResourceNodeRepository $repo): Response
{
if (!api_protect_course_script(false)) {
exit;
}
/** @var CChatConversationRepository $resourceRepo */
$resourceRepo = $this->getRepository('chat', 'conversations');
$courseId = api_get_course_int_id();
$userId = api_get_user_id();
$sessionId = api_get_session_id();
$groupId = api_get_group_id();
$json = ['status' => false];
$parentResourceNode = $this->getParentResourceNode($request);
$courseChatUtils = new \CourseChatUtils(
$courseId,
$userId,
$sessionId,
$groupId,
$parentResourceNode,
$resourceRepo
);
$action = $request->get('action');
switch ($action) {
case 'chat_logout':
$logInfo = [
'tool' => TOOL_CHAT,
'action' => 'exit',
'action_details' => 'exit-chat',
];
Event::registerLog($logInfo);
break;
case 'track':
$courseChatUtils->keepUserAsConnected();
$courseChatUtils->disconnectInactiveUsers();
$friend = isset($_REQUEST['friend']) ? (int) $_REQUEST['friend'] : 0;
//$filePath = $courseChatUtils->getFileName(true, $friend);
//$newFileSize = file_exists($filePath) ? filesize($filePath) : 0;
//$oldFileSize = isset($_GET['size']) ? (int) $_GET['size'] : -1;
$newUsersOnline = $courseChatUtils->countUsersOnline();
$oldUsersOnline = isset($_GET['users_online']) ? (int) $_GET['users_online'] : 0;
$json = [
'status' => true,
'data' => [
//'oldFileSize' => file_exists($filePath) ? filesize($filePath) : 0,
'oldFileSize' => false,
'history' => $courseChatUtils->readMessages(false, $friend),
'usersOnline' => $newUsersOnline,
'userList' => $newUsersOnline != $oldUsersOnline ? $courseChatUtils->listUsersOnline() : null,
'currentFriend' => $friend,
],
];
break;
case 'preview':
$json = [
'status' => true,
'data' => [
'message' => CourseChatUtils::prepareMessage($_REQUEST['message']),
],
];
break;
case 'reset':
$friend = isset($_REQUEST['friend']) ? (int) $_REQUEST['friend'] : 0;
$json = [
'status' => true,
'data' => $courseChatUtils->readMessages(true, $friend),
];
break;
case 'write':
$friend = isset($_REQUEST['friend']) ? (int) $_REQUEST['friend'] : 0;
$status = $courseChatUtils->saveMessage($_REQUEST['message'], $friend);
$json = [
'status' => $status,
'data' => [
'writed' => $status,
],
];
break;
}
return new JsonResponse($json);
}
}

@ -23,7 +23,6 @@ use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter;
use Chamilo\CourseBundle\Controller\CourseControllerInterface; use Chamilo\CourseBundle\Controller\CourseControllerInterface;
use Chamilo\CourseBundle\Controller\CourseControllerTrait; use Chamilo\CourseBundle\Controller\CourseControllerTrait;
use Chamilo\CourseBundle\Entity\CDocument; use Chamilo\CourseBundle\Entity\CDocument;
use Chamilo\UserBundle\Entity\User;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
@ -40,7 +39,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Router; use Symfony\Component\Routing\Router;
use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use ZipStream\Option\Archive; use ZipStream\Option\Archive;
use ZipStream\ZipStream; use ZipStream\ZipStream;
@ -1074,32 +1072,6 @@ class ResourceController extends AbstractResourceController implements CourseCon
} }
} }
private function getParentResourceNode(Request $request): ResourceNode
{
$parentNodeId = $request->get('id');
$parentResourceNode = null;
if (empty($parentNodeId)) {
if ($this->hasCourse()) {
$parentResourceNode = $this->getCourse()->getResourceNode();
} else {
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
/** @var User $user */
$parentResourceNode = $this->getUser()->getResourceNode();
}
}
} else {
$repo = $this->getDoctrine()->getRepository('ChamiloCoreBundle:Resource\ResourceNode');
$parentResourceNode = $repo->find($parentNodeId);
}
if (null === $parentResourceNode) {
throw new AccessDeniedException();
}
return $parentResourceNode;
}
/** /**
* @param string $mode * @param string $mode
* @param string $filter * @param string $filter
@ -1150,7 +1122,7 @@ class ResourceController extends AbstractResourceController implements CourseCon
$params['crop'] = $crop; $params['crop'] = $crop;
} }
$fileName = $repo->getFilename($resourceFile); $fileName = $repo->getResourceNodeRepository()->getFilename($resourceFile);
return $server->getImageResponse($fileName, $params); return $server->getImageResponse($fileName, $params);
} }

@ -18,7 +18,6 @@ class ResourceFactory
{ {
protected $mountManager; protected $mountManager;
protected $toolChain; protected $toolChain;
protected $fs;
protected $slugify; protected $slugify;
protected $entityManager; protected $entityManager;
protected $authorizationChecker; protected $authorizationChecker;
@ -33,8 +32,6 @@ class ResourceFactory
Container $container Container $container
) { ) {
$this->mountManager = $mountManager; $this->mountManager = $mountManager;
// @todo create a service to remove hardcode value of "resources_fs"
$this->fs = $mountManager->getFilesystem('resources_fs');
$this->toolChain = $toolChain; $this->toolChain = $toolChain;
$this->slugify = $slugify; $this->slugify = $slugify;
$this->entityManager = $entityManager; $this->entityManager = $entityManager;

@ -5,17 +5,80 @@
namespace Chamilo\CoreBundle\Repository; namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Entity\Course; use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Resource\ResourceFile;
use Chamilo\CoreBundle\Entity\Resource\ResourceLink; use Chamilo\CoreBundle\Entity\Resource\ResourceLink;
use Chamilo\CoreBundle\Entity\Resource\ResourceNode; use Chamilo\CoreBundle\Entity\Resource\ResourceNode;
use Chamilo\CoreBundle\Entity\Resource\ResourceType; use Chamilo\CoreBundle\Entity\Resource\ResourceType;
use Chamilo\CoreBundle\Entity\Session; use Chamilo\CoreBundle\Entity\Session;
use Doctrine\ORM\EntityManagerInterface;
use Gedmo\Tree\Entity\Repository\MaterializedPathRepository; use Gedmo\Tree\Entity\Repository\MaterializedPathRepository;
use League\Flysystem\MountManager;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Vich\UploaderBundle\Storage\FlysystemStorage;
/** /**
* Class ResourceNodeRepository. * Class ResourceNodeRepository.
*/ */
class ResourceNodeRepository extends MaterializedPathRepository class ResourceNodeRepository extends MaterializedPathRepository
{ {
protected $mountManager;
protected $storage;
public function __construct(EntityManagerInterface $manager, FlysystemStorage $storage, MountManager $mountManager)
{
parent::__construct($manager, $manager->getClassMetadata(ResourceNode::class));
$this->storage = $storage;
$this->mountManager = $mountManager;
}
public function getFilename(ResourceFile $resourceFile)
{
return $this->storage->resolveUri($resourceFile);
}
/**
* @return \League\Flysystem\FilesystemInterface
*/
public function getFileSystem()
{
// Flysystem mount name is saved in config/packages/oneup_flysystem.yaml @todo add it as a service.
$this->fs = $this->mountManager->getFilesystem('resources_fs');
return $this->fs;
}
public function getResourceNodeFileContent(ResourceNode $resourceNode): string
{
try {
if ($resourceNode->hasResourceFile()) {
$resourceFile = $resourceNode->getResourceFile();
$fileName = $this->getFilename($resourceFile);
return $this->getFileSystem()->read($fileName);
}
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($resourceNode);
}
}
public function getResourceNodeFileStream(ResourceNode $resourceNode)
{
try {
if ($resourceNode->hasResourceFile()) {
$resourceFile = $resourceNode->getResourceFile();
$fileName = $this->getFilename($resourceFile);
return $this->getFileSystem()->readStream($fileName);
}
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($resourceNode);
}
}
/** /**
* @todo filter files, check status * @todo filter files, check status
*/ */

@ -29,7 +29,6 @@ use Doctrine\ORM\EntityRepository as BaseEntityRepository;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use League\Flysystem\FilesystemInterface; use League\Flysystem\FilesystemInterface;
use League\Flysystem\MountManager;
use Symfony\Component\Filesystem\Exception\FileNotFoundException; use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\Form\FormFactory; use Symfony\Component\Form\FormFactory;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
@ -37,7 +36,6 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Vich\UploaderBundle\Storage\FlysystemStorage;
/** /**
* Class ResourceRepository. * Class ResourceRepository.
@ -78,40 +76,28 @@ class ResourceRepository extends BaseEntityRepository
*/ */
protected $authorizationChecker; protected $authorizationChecker;
/** @var MountManager */
protected $mountManager;
/** @var SlugifyInterface */ /** @var SlugifyInterface */
protected $slugify; protected $slugify;
/** @var ToolChain */ /** @var ToolChain */
protected $toolChain; protected $toolChain;
/** @var FlysystemStorage */
protected $storage;
/** /**
* ResourceRepository constructor. * ResourceRepository constructor.
*/ */
public function __construct( public function __construct(
AuthorizationCheckerInterface $authorizationChecker, AuthorizationCheckerInterface $authorizationChecker,
EntityManager $entityManager, EntityManager $entityManager,
MountManager $mountManager,
RouterInterface $router, RouterInterface $router,
SlugifyInterface $slugify, SlugifyInterface $slugify,
ToolChain $toolChain, ToolChain $toolChain,
FlysystemStorage $storage, ResourceNodeRepository $resourceNodeRepository,
string $className string $className
) { ) {
$this->authorizationChecker = $authorizationChecker; $this->authorizationChecker = $authorizationChecker;
$this->repository = $entityManager->getRepository($className); $this->repository = $entityManager->getRepository($className);
// Flysystem mount name is saved in config/packages/oneup_flysystem.yaml @todo add it as a service.
$this->fs = $mountManager->getFilesystem('resources_fs');
$this->storage = $storage;
$this->mountManager = $mountManager;
$this->router = $router; $this->router = $router;
$this->resourceNodeRepository = $entityManager->getRepository('ChamiloCoreBundle:Resource\ResourceNode'); $this->resourceNodeRepository = $resourceNodeRepository;
$this->slugify = $slugify; $this->slugify = $slugify;
$this->toolChain = $toolChain; $this->toolChain = $toolChain;
} }
@ -139,14 +125,6 @@ class ResourceRepository extends BaseEntityRepository
return $this->resourceNodeRepository; return $this->resourceNodeRepository;
} }
/**
* @return FilesystemInterface
*/
public function getFileSystem()
{
return $this->fs;
}
public function getEntityManager(): EntityManager public function getEntityManager(): EntityManager
{ {
return $this->getRepository()->getEntityManager(); return $this->getRepository()->getEntityManager();
@ -737,14 +715,8 @@ class ResourceRepository extends BaseEntityRepository
{ {
try { try {
$resourceNode = $resource->getResourceNode(); $resourceNode = $resource->getResourceNode();
if ($resourceNode->hasResourceFile()) {
$resourceFile = $resourceNode->getResourceFile();
$fileName = $this->getFilename($resourceFile);
return $this->getFileSystem()->read($fileName);
}
return ''; return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
} catch (\Throwable $exception) { } catch (\Throwable $exception) {
throw new FileNotFoundException($resource); throw new FileNotFoundException($resource);
} }
@ -752,39 +724,12 @@ class ResourceRepository extends BaseEntityRepository
public function getResourceNodeFileContent(ResourceNode $resourceNode): string public function getResourceNodeFileContent(ResourceNode $resourceNode): string
{ {
try { return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
if ($resourceNode->hasResourceFile()) {
$resourceFile = $resourceNode->getResourceFile();
$fileName = $this->getFilename($resourceFile);
return $this->getFileSystem()->read($fileName);
}
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($resourceNode);
}
} }
public function getResourceNodeFileStream(ResourceNode $resourceNode) public function getResourceNodeFileStream(ResourceNode $resourceNode)
{ {
try { return $this->resourceNodeRepository->getResourceNodeFileStream($resourceNode);
if ($resourceNode->hasResourceFile()) {
$resourceFile = $resourceNode->getResourceFile();
$fileName = $this->getFilename($resourceFile);
return $this->getFileSystem()->readStream($fileName);
}
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($resourceNode);
}
}
public function getFilename(ResourceFile $resourceFile)
{
return $this->storage->resolveUri($resourceFile);
} }
public function getResourceFileUrl(AbstractResource $resource, array $extraParams = [], $referenceType = null): string public function getResourceFileUrl(AbstractResource $resource, array $extraParams = [], $referenceType = null): string
@ -837,9 +782,9 @@ class ResourceRepository extends BaseEntityRepository
if ($resourceNode->hasResourceFile()) { if ($resourceNode->hasResourceFile()) {
$resourceFile = $resourceNode->getResourceFile(); $resourceFile = $resourceNode->getResourceFile();
if ($resourceFile) { if ($resourceFile) {
$fileName = $this->getFilename($resourceFile); $fileName = $this->getResourceNodeRepository()->getFilename($resourceFile);
$this->getFileSystem()->update($fileName, $content); $this->getResourceNodeRepository()->getFileSystem()->update($fileName, $content);
$size = $this->getFileSystem()->getSize($fileName); $size = $this->getResourceNodeRepository()->getSize($fileName);
if ($resource instanceof CDocument) { if ($resource instanceof CDocument) {
$resource->setSize($size); $resource->setSize($size);

@ -4,12 +4,14 @@ services:
public: true public: true
autoconfigure: true autoconfigure: true
Vich\UploaderBundle\Storage\FlysystemStorage: ~
Chamilo\CoreBundle\Repository\ResourceFactory: ~ Chamilo\CoreBundle\Repository\ResourceFactory: ~
Chamilo\CoreBundle\Repository\ResourceNodeRepository: ~
# Classic entity repositories # Classic entity repositories
Chamilo\CoreBundle\Repository\: Chamilo\CoreBundle\Repository\:
resource: '../../Repository' resource: '../../Repository'
exclude: '../../Repository/{BranchSyncRepository.php,ResourceRepository.php,ResourceNodeRepository.php}' exclude: '../../Repository/{BranchSyncRepository.php,ResourceRepository.php}'
tags: ['doctrine.repository_service'] tags: ['doctrine.repository_service']
# Resource repositories # Resource repositories

@ -145,9 +145,11 @@ services:
arguments: arguments:
- 'chat' - 'chat'
- 'interaction' - 'interaction'
- '/main/chat/chat.php' - '/resources/chat/'
- '@chamilo_course.settings.chat' - '@chamilo_course.settings.chat'
- ~ -
conversations:
repository: Chamilo\CourseBundle\Repository\CChatConversationRepository
tags: tags:
- {name: chamilo_core.tool} - {name: chamilo_core.tool}

@ -0,0 +1,81 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\CourseBundle\Entity;
use Chamilo\CoreBundle\Entity\Resource\AbstractResource;
use Chamilo\CoreBundle\Entity\Resource\ResourceInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* CChatConversation.
*
* @ORM\Table(name="c_chat_conversation")
* @ORM\Entity
*/
class CChatConversation extends AbstractResource implements ResourceInterface
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
protected $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, nullable=true)
*/
protected $name;
public function __toString(): string
{
return $this->getName();
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
*
* @return CChatConversation
*/
public function setName(string $name): CChatConversation
{
$this->name = $name;
return $this;
}
/**
* Resource identifier.
*/
public function getResourceIdentifier(): int
{
return $this->getId();
}
public function getResourceName(): string
{
return $this->getName();
}
}

@ -0,0 +1,54 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\CourseBundle\Repository;
use APY\DataGridBundle\Grid\Column\Column;
use APY\DataGridBundle\Grid\Grid;
use Chamilo\CoreBundle\Component\Utils\ResourceSettings;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Resource\ResourceNode;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Repository\ResourceRepository;
use Chamilo\CoreBundle\Repository\ResourceRepositoryInterface;
use Chamilo\CourseBundle\Entity\CGroupInfo;
use Chamilo\UserBundle\Entity\User;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
final class CChatConversationRepository extends ResourceRepository implements ResourceRepositoryInterface
{
public function getResourceSettings(): ResourceSettings
{
$settings = new ResourceSettings();
$settings
->setAllowNodeCreation(false)
->setAllowResourceCreation(false)
->setAllowResourceUpload(false)
;
return $settings;
}
public function getResources(User $user, ResourceNode $parentNode, Course $course = null, Session $session = null, CGroupInfo $group = null): QueryBuilder
{
return $this->getResourcesByCourse($course, $session, $group, $parentNode);
}
public function saveUpload(UploadedFile $file)
{
throw new AccessDeniedException();
}
public function saveResource(FormInterface $form, $course, $session, $fileType)
{
}
public function getTitleColumn(Grid $grid): Column
{
return $grid->getColumn('name');
}
}

@ -72,6 +72,7 @@ class CForumPostRepository extends ResourceRepository
public function delete(AbstractResource $resource) public function delete(AbstractResource $resource)
{ {
/** @var CForumPost $resource */
$attachments = $resource->getAttachments(); $attachments = $resource->getAttachments();
if (!empty($attachments)) { if (!empty($attachments)) {
foreach ($attachments as $attachment) { foreach ($attachments as $attachment) {

@ -75,6 +75,10 @@ services:
arguments: arguments:
$className: 'Chamilo\CourseBundle\Entity\CCalendarEvent' $className: 'Chamilo\CourseBundle\Entity\CCalendarEvent'
Chamilo\CourseBundle\Repository\CChatConversationRepository:
arguments:
$className: 'Chamilo\CourseBundle\Entity\CChatConversation'
Chamilo\CourseBundle\Repository\CDocumentRepository: Chamilo\CourseBundle\Repository\CDocumentRepository:
arguments: arguments:
$className: 'Chamilo\CourseBundle\Entity\CDocument' $className: 'Chamilo\CourseBundle\Entity\CDocument'

@ -1,3 +1,7 @@
{% extends "@ChamiloTheme/Layout/no_layout.html.twig" %}
{% block content %}
{% autoescape false %}
<div class="page-chat"> <div class="page-chat">
<div class="row"> <div class="row">
<div class="col-sm-4 col-md-5 col-lg-4"> <div class="col-sm-4 col-md-5 col-lg-4">
@ -60,14 +64,14 @@
</div> </div>
</div> </div>
<audio id="chat-alert" class="skip"> <audio id="chat-alert" class="skip">
<source src="{{ _p.web_main }}/chat/sound/notification.wav" type="audio/wav"></source> <source src="{{ asset('sound/notification.wav') }}" type="audio/wav"></source>
<source src="{{ _p.web_main }}chat/sound/notification.ogg" type="audio/ogg"></source> <source src="{{ asset('chat/sound/notification.ogg') }}" type="audio/ogg"></source>
<source src="{{ _p.web_main }}chat/sound/notification.mp3" type="audio/mpeg"></source> <source src="{{ asset('chat/sound/notification.mp3') }}" type="audio/mpeg"></source>
</audio> </audio>
<script> <script>
$(function () { $(function () {
var ChChat = { var ChChat = {
_ajaxUrl: '{{ _p.web_ajax }}course_chat.ajax.php?{{ _p.web_cid_query }}', _ajaxUrl: '{{ url('chat_ajax') ~ '?' ~ course_url_params }}',
_historySize: -1, _historySize: -1,
usersOnline: 0, usersOnline: 0,
currentFriend: 0, currentFriend: 0,
@ -100,7 +104,7 @@
.prop('scrollTop', function () { .prop('scrollTop', function () {
return this.scrollHeight; return this.scrollHeight;
}); });
$('#chat-alert').get(0).play(); //$('#chat-alert').get(0).play();
}, },
setConnectedUsers: function (userList) { setConnectedUsers: function (userList) {
var html = ''; var html = '';
@ -115,7 +119,7 @@
' <ul class="list-unstyled">' + ' <ul class="list-unstyled">' +
' <li>' + user.complete_name; ' <li>' + user.complete_name;
if (user.id != {{ _u.user_id }}) { if (user.id != {{ user.id }}) {
html += ' <button type="button" class="btn btn-link btn-xs" title="' + buttonTitle + '" data-name="' + user.complete_name + '" data-user="' + user.id + '">' + html += ' <button type="button" class="btn btn-link btn-xs" title="' + buttonTitle + '" data-name="' + user.complete_name + '" data-user="' + user.id + '">' +
' <i class="fa fa-comments text-' + buttonStatus + '"></i><span class="sr-only">' + buttonTitle + '</span>' + ' <i class="fa fa-comments text-' + buttonStatus + '"></i><span class="sr-only">' + buttonTitle + '</span>' +
' </button>'; ' </button>';
@ -134,18 +138,17 @@
$('#chat-users').html(html); $('#chat-users').html(html);
}, },
onPreviewListener: function () { onPreviewListener: function () {
$ $.post(ChChat._ajaxUrl, {
.post(ChChat._ajaxUrl, { action: 'preview',
action: 'preview', 'message': $('textarea#chat-writer').val()
'message': $('textarea#chat-writer').val() })
}) .done(function (response) {
.done(function (response) { if (!response.status) {
if (!response.status) { return;
return; }
}
$('#html-preview').html(response.data.message); $('#html-preview').html(response.data.message);
}); });
}, },
onSendMessageListener: function (e) { onSendMessageListener: function (e) {
e.preventDefault(); e.preventDefault();
@ -157,40 +160,39 @@
var self = this; var self = this;
self.disabled = true; self.disabled = true;
$ $.get(ChChat._ajaxUrl, {
.post(ChChat._ajaxUrl, { action: 'write',
action: 'write', message: $('textarea#chat-writer').val(),
message: $('textarea#chat-writer').val(), friend: ChChat.currentFriend
friend: ChChat.currentFriend })
}) .done(function (response) {
.done(function (response) { self.disabled = false;
self.disabled = false;
if (!response.status) { if (!response.status) {
return; return;
} }
$('textarea#chat-writer').val(''); $('textarea#chat-writer').val('');
$(".emoji-wysiwyg-editor").html(''); $(".emoji-wysiwyg-editor").html('');
}); });
}, },
onResetListener: function (e) { onResetListener: function (e) {
if (!confirm("{{ 'ConfirmReset'|get_lang }}")) { if (!confirm("{{ 'ConfirmReset'|get_lang }}")) {
e.preventDefault(); e.preventDefault();
return; return;
} }
$
.get(ChChat._ajaxUrl, {
action: 'reset',
friend: ChChat.currentFriend
})
.done(function (response) {
if (!response.status) {
return;
}
ChChat.setHistory(response.data); $.get(ChChat._ajaxUrl, {
}); action: 'reset',
friend: ChChat.currentFriend
})
.done(function (response) {
if (!response.status) {
return;
}
ChChat.setHistory(response.data);
});
}, },
init: function () { init: function () {
ChChat.track().done(function () { ChChat.track().done(function () {
@ -201,27 +203,17 @@
hljs.initHighlightingOnLoad(); hljs.initHighlightingOnLoad();
emojione.ascii = true;
emojione.imagePathPNG = '{{ _p.web_lib }}javascript/emojione/png/';
emojione.imagePathSVG = '{{ _p.web_lib }}javascript/emojione/svg/';
emojione.imagePathSVGSprites = '{{ _p.web_lib }}javascript/emojione/sprites/';
var emojiStrategy = {{ emoji_strategy|json_encode }};
$.emojiarea.path = '{{ _p.web_lib }}javascript/emojione/png/';
$.emojiarea.icons = {{ icons|json_encode }};
$('body').on('click', '#chat-reset', ChChat.onResetListener); $('body').on('click', '#chat-reset', ChChat.onResetListener);
$('#preview').on('click', ChChat.onPreviewListener); $('#preview').on('click', ChChat.onPreviewListener);
$('#emojis').on('click', function () { $('#emojis').on('click', function () {
$('[data-toggle="tab"][href="#tab1"]').show().tab('show'); $('[data-toggle="tab"][href="#tab1"]').show().tab('show');
}); });
$('textarea#chat-writer').emojiarea({ /*$('textarea#chat-writer').emojiarea({
button: '#emojis' button: '#emojis'
}); });*/
$('body').delay(1500).find('.emoji-wysiwyg-editor').textcomplete([{ /*$('body').delay(1500).find('.emoji-wysiwyg-editor').textcomplete([{
match: /\B:([\-+\w]*)$/, match: /\B:([\-+\w]*)$/,
search: function (term, callback) { search: function (term, callback) {
var results = []; var results = [];
@ -263,7 +255,7 @@
index: 1, index: 1,
maxCount: 10 maxCount: 10
}], {}); }], {});
*/
$('button#chat-send-message').on('click', ChChat.onSendMessageListener); $('button#chat-send-message').on('click', ChChat.onSendMessageListener);
$('#chat-users').on('click', 'div.chat-user', function (e) { $('#chat-users').on('click', 'div.chat-user', function (e) {
e.preventDefault(); e.preventDefault();
@ -328,3 +320,6 @@
ChChat.init(); ChChat.init();
}); });
</script> </script>
{% endautoescape %}
{% endblock %}

@ -3094,6 +3094,11 @@ ev-emitter@^1.0.0:
resolved "https://registry.yarnpkg.com/ev-emitter/-/ev-emitter-1.1.1.tgz#8f18b0ce5c76a5d18017f71c0a795c65b9138f2a" resolved "https://registry.yarnpkg.com/ev-emitter/-/ev-emitter-1.1.1.tgz#8f18b0ce5c76a5d18017f71c0a795c65b9138f2a"
integrity sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q== integrity sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q==
eventemitter3@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
eventemitter3@^4.0.0: eventemitter3@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
@ -3886,6 +3891,11 @@ highlight.js@^9.12.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c"
integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg== integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==
hljs@^6.2.3:
version "6.2.3"
resolved "https://registry.yarnpkg.com/hljs/-/hljs-6.2.3.tgz#d4d6208fa2a84f294956bc50f2c812e9cbd49bcc"
integrity sha1-1NYgj6KoTylJVrxQ8sgS6cvUm8w=
hmac-drbg@^1.0.0: hmac-drbg@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -7571,6 +7581,20 @@ terser@^4.1.2:
source-map "~0.6.1" source-map "~0.6.1"
source-map-support "~0.5.12" source-map-support "~0.5.12"
textarea-caret@^3.0.1:
version "3.1.0"
resolved "https://registry.yarnpkg.com/textarea-caret/-/textarea-caret-3.1.0.tgz#5d5a35bb035fd06b2ff0e25d5359e97f2655087f"
integrity sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==
textcomplete@^0.18.1:
version "0.18.1"
resolved "https://registry.yarnpkg.com/textcomplete/-/textcomplete-0.18.1.tgz#43a3eb545275b5f5714669220a4725b69f13b490"
integrity sha512-ukWti83oL4rBAnj6HVwl76mhKRScAvjJps90EeT5qVYm6dwwa0XCytLCfSGQOYbj4CyjgN//oIfq+Gnb29KCPg==
dependencies:
eventemitter3 "^2.0.3"
textarea-caret "^3.0.1"
undate "^0.2.3"
through2@^2.0.0: through2@^2.0.0:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
@ -7749,6 +7773,11 @@ uglifyjs-webpack-plugin@^1.3.0:
webpack-sources "^1.1.0" webpack-sources "^1.1.0"
worker-farm "^1.5.2" worker-farm "^1.5.2"
undate@^0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/undate/-/undate-0.2.4.tgz#ccb2a8cf38edc035d1006fcb2909c4c6024a8400"
integrity sha512-k1WTRVhI076HYqP6e3pZPzS7K2xNAcuhCcWOPjHmR8SwU3byyKYvyNZ4XTEAd7Ofe40+wrEjNq6WmmO8WoUVNg==
unicode-canonical-property-names-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"

Loading…
Cancel
Save