diff --git a/config/routes.yaml b/config/routes.yaml index 25644c2d35..8f6054ecf7 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -20,12 +20,12 @@ sonata_user_admin_security_logout: # Resources -app_document: - resource: | - alias: app.document - path: /resource/documents - form: Chamilo\CoreBundle\Form\Type\DocumentType - type: sylius.resource +#app_document: +# resource: | +# alias: app.document +# path: /resource/documents +# form: Chamilo\CoreBundle\Form\Type\DocumentType +# type: sylius.resource app_document_index: path: /courses/{course}/resource/documents/ diff --git a/config/services.yaml b/config/services.yaml index 2dd12384a9..afccb8429c 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -46,14 +46,14 @@ services: sylius_settings: driver: doctrine/orm -sylius_resource: - resources: - app.document: - templates: ChamiloCoreBundle:Document - classes: - controller: Chamilo\CoreBundle\Controller\ResourceController - model: Chamilo\CourseBundle\Entity\CDocument - repository: Chamilo\CoreBundle\Repository\ResourceRepository +#sylius_resource: +# resources: +# app.document: +# templates: ChamiloCoreBundle:Document +# classes: +# controller: Chamilo\CoreBundle\Controller\ResourceController +# model: Chamilo\CourseBundle\Entity\CDocument +# repository: Chamilo\CoreBundle\Repository\ResourceRepository # #sonata.media.provider.private: diff --git a/main/document/document.php b/main/document/document.php index 873f2f6aeb..1eb83e1666 100755 --- a/main/document/document.php +++ b/main/document/document.php @@ -40,6 +40,7 @@ $actionsRight = ''; $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; $allowUseTool = false; + if ($allowDownloadDocumentsByApiKey) { try { if ($action != 'download') { @@ -329,6 +330,13 @@ switch ($action) { // Launch event Event::event_download($document_data['url']); + $publicPath = api_get_path(WEB_PUBLIC_PATH); + $courseCode = api_get_course_id(); + $path = $document_data['path']; + $url = $publicPath."courses/$courseCode/document$path"; + header("Location: $url"); + exit; + // Check visibility of document and paths if (!($isAllowedToEdit || $groupMemberWithUploadRights) && !DocumentManager::is_visible_by_id($document_id, $courseInfo, $sessionId, api_get_user_id()) diff --git a/main/document/show_content.php b/main/document/show_content.php index e57db36c59..8a5ac9a847 100755 --- a/main/document/show_content.php +++ b/main/document/show_content.php @@ -44,6 +44,17 @@ if (empty($document_data)) { api_not_allowed(true); } +$publicPath = api_get_path(WEB_PUBLIC_PATH); +// http://localhost/chamilo2/courses/ABC/document/aa.html +$courseCode = api_get_course_id(); +$path = $document_data['path']; +$type = 'show'; +$url = $publicPath."courses/$courseCode/document$path?type=$type"; +header("Location: $url"); +exit; + + + $header_file = $document_data['path']; $name_to_show = cut($header_file, 80); diff --git a/public/courses.php b/public/courses.php index 37c80c2c93..c5def9eb77 100644 --- a/public/courses.php +++ b/public/courses.php @@ -4,13 +4,14 @@ require_once '../main/inc/global.inc.php'; /** - * Redirects "courses/ABC/document/my_file.html" to the symfony Resourcecontroller see: + * Redirects "courses/ABC/document/my_file.html" to the symfony ResourceController see: * src/CoreBundle/Controller/ResourceController.php. */ $publicPath = api_get_path(WEB_PUBLIC_PATH); // http://localhost/chamilo2/courses/ABC/document/aa.html $courseCode = Security::remove_XSS($_GET['courseCode']); $path = Security::remove_XSS($_GET['url']); -$url = $publicPath."courses/$courseCode/document/$path"; +$type = Security::remove_XSS($_GET['type']) ?? 'show'; +$url = $publicPath."courses/$courseCode/document/$path?type=$type"; header("Location: $url"); exit; diff --git a/src/CoreBundle/Controller/ResourceController.php b/src/CoreBundle/Controller/ResourceController.php index 5e0dab438a..870001b8e6 100644 --- a/src/CoreBundle/Controller/ResourceController.php +++ b/src/CoreBundle/Controller/ResourceController.php @@ -9,6 +9,7 @@ use APY\DataGridBundle\Grid\Export\CSVExport; use APY\DataGridBundle\Grid\Export\ExcelExport; use APY\DataGridBundle\Grid\Grid; use APY\DataGridBundle\Grid\Source\Entity; +use Chamilo\CoreBundle\Entity\Resource\ResourceNode; use Chamilo\CoreBundle\Entity\Resource\ResourceRight; use Chamilo\CoreBundle\Repository\ResourceRepository; use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter; @@ -17,13 +18,16 @@ use Chamilo\CourseBundle\Controller\CourseControllerTrait; use Chamilo\CourseBundle\Entity\CDocument; use Chamilo\CourseBundle\Repository\CDocumentRepository; use FOS\RestBundle\View\View; +use Sonata\MediaBundle\Provider\MediaProviderInterface; use Sylius\Bundle\ResourceBundle\Controller\ResourceController as BaseResourceController; use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent; use Sylius\Component\Resource\Exception\UpdateHandlingException; use Sylius\Component\Resource\ResourceActions; +use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class ResourceController. @@ -32,7 +36,8 @@ use Symfony\Component\HttpKernel\Exception\HttpException; * * @package Chamilo\CoreBundle\Controller */ -class ResourceController extends BaseResourceController implements CourseControllerInterface +//class ResourceController extends BaseResourceController implements CourseControllerInterface +class ResourceController extends BaseController implements CourseControllerInterface { use CourseControllerTrait; @@ -400,6 +405,79 @@ class ResourceController extends BaseResourceController implements CourseControl */ public function showAction(Request $request): Response { + $file = $request->get('file'); + $type = $request->get('type'); + + if (empty($type)) { + $type = 'show'; + } + + $documentRepo = $this->getDoctrine()->getRepository('ChamiloCourseBundle:CDocument'); + $criteria = [ + 'path' => "/$file", + 'course' => $this->getCourse(), + ]; + + $document = $documentRepo->findOneBy($criteria); + + if (empty($document)) { + throw new NotFoundHttpException(); + } + + /** @var ResourceNode $resourceNode */ + $resourceNode = $document->getResourceNode(); + + $this->denyAccessUnlessGranted( + ResourceNodeVoter::VIEW, + $resourceNode, + 'Unauthorised access to resource' + ); + + $resourceFile = $resourceNode->getResourceFile(); + $media = $resourceFile->getMedia(); + $format = MediaProviderInterface::FORMAT_REFERENCE; + + if ($media) { + switch ($type) { + case 'show': + /** @var \Sonata\MediaBundle\Provider\ImageProvider $provider */ + $provider = $this->get('sonata.media.pool')->getProvider($media->getProviderName()); + $reference = $provider->getReferenceFile($media); + + $filename = sprintf('%s/%s', + $provider->getFilesystem()->getAdapter()->getDirectory(), + $provider->generatePrivateUrl($media, $format) + ); + + //var_dump($provider->generatePublicUrl($media, $format)); + + //var_dump($filename); + return new BinaryFileResponse($filename); + exit; + return $this->render('@SonataMedia/Media/view.html.twig', [ + 'media' => $media, + 'formats' => $this->get('sonata.media.pool')->getFormatNamesByContext($media->getContext()), + 'format' => $format, + ]); + break; + case 'download': + $provider = $this->get('sonata.media.pool')->getProvider($media->getProviderName()); + $response = $provider->getDownloadResponse($media, $format, $this->get('sonata.media.pool')->getDownloadMode($media)); + + return $response; + + + break; + } + } + + throw new NotFoundHttpException(); + + var_dump($resourceFile->getMedia()->getName()); + + + + $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::SHOW); diff --git a/src/CoreBundle/Entity/Resource/AbstractResource.php b/src/CoreBundle/Entity/Resource/AbstractResource.php index cacfa179bc..27b66f5ab4 100644 --- a/src/CoreBundle/Entity/Resource/AbstractResource.php +++ b/src/CoreBundle/Entity/Resource/AbstractResource.php @@ -48,7 +48,7 @@ abstract class AbstractResource implements ResourceInterface /** * @return ResourceNode */ - public function getResourceNode() + public function getResourceNode(): ResourceNode { return $this->resourceNode; } diff --git a/src/CoreBundle/Entity/Resource/ResourceFile.php b/src/CoreBundle/Entity/Resource/ResourceFile.php index dd73beae97..a6abe77a60 100644 --- a/src/CoreBundle/Entity/Resource/ResourceFile.php +++ b/src/CoreBundle/Entity/Resource/ResourceFile.php @@ -3,6 +3,7 @@ namespace Chamilo\CoreBundle\Entity\Resource; +use Chamilo\MediaBundle\Entity\Media; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; @@ -337,7 +338,7 @@ class ResourceFile } /** - * @return mixed + * @return Media */ public function getMedia() { @@ -345,7 +346,7 @@ class ResourceFile } /** - * @param mixed $media + * @param Media $media * * @return ResourceFile */ diff --git a/src/CoreBundle/Entity/Resource/ResourceLink.php b/src/CoreBundle/Entity/Resource/ResourceLink.php index 95a4e09822..5126c59a81 100644 --- a/src/CoreBundle/Entity/Resource/ResourceLink.php +++ b/src/CoreBundle/Entity/Resource/ResourceLink.php @@ -173,6 +173,11 @@ class ResourceLink implements ResourceInterface return $this; } + public function getResourceRight() + { + return $this->resourceRight; + } + /** * @return int */ diff --git a/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php b/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php index 44ac0baa30..50e88c41e0 100644 --- a/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php +++ b/src/CoreBundle/Security/Authorization/Voter/ResourceNodeVoter.php @@ -6,6 +6,8 @@ namespace Chamilo\CoreBundle\Security\Authorization\Voter; use Chamilo\CoreBundle\Entity\Course; use Chamilo\CoreBundle\Entity\Resource\ResourceLink; use Chamilo\CoreBundle\Entity\Resource\ResourceNode; +use Chamilo\CoreBundle\Entity\Resource\ResourceRight; +use Zend\Permissions\Acl\Resource\GenericResource as SecurityResource; use Chamilo\CoreBundle\Entity\Session; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Security\Acl\Permission\MaskBuilder; @@ -100,7 +102,11 @@ class ResourceNodeVoter extends Voter } /** - * {@inheritdoc} + * @param string $attribute + * @param ResourceNode $resourceNode + * @param TokenInterface $token + * + * @return bool */ protected function voteOnAttribute($attribute, $resourceNode, TokenInterface $token): bool { @@ -132,7 +138,7 @@ class ResourceNodeVoter extends Voter $courseCode = $request->get('course'); $sessionId = $request->get('session'); - $links = $resourceNode->getLinks(); + $links = $resourceNode->getResourceLinks(); $linkFound = false; /** @var ResourceLink $link */ @@ -171,7 +177,7 @@ class ResourceNodeVoter extends Voter // Check if resource was sent to a course if ($linkCourse instanceof Course && !empty($courseCode)) { - $course = $this->container->get('chamilo_core.manager.course')->findOneByCode($courseCode); + $course = $this->container->get('chamilo_core.entity.manager.course_manager')->findOneByCode($courseCode); if ($course instanceof Course && $linkCourse->getCode() === $course->getCode() ) { @@ -187,14 +193,34 @@ class ResourceNodeVoter extends Voter } // Getting rights from the link - $rightFromResourceLink = $link->getRights(); + $rightFromResourceLink = $link->getResourceRight(); if ($rightFromResourceLink->count()) { // Taken rights from the link $rights = $rightFromResourceLink; } else { // Taken the rights from the default tool - $rights = $link->getResourceNode()->getTool()->getToolResourceRight(); + //$rights = $link->getResourceNode()->getTool()->getToolResourceRight(); + //$rights = $link->getResourceNode()->getResourceType()->getTool()->getToolResourceRight(); + // By default the rights are: + // teacher: CRUD + // student: read + $readerMask = self::getReaderMask(); + $editorMask = self::getEditorMask(); + + $resourceRight = new ResourceRight(); + $resourceRight + ->setMask($editorMask) + ->setRole(self::ROLE_CURRENT_COURSE_TEACHER) + ; + $rights[] = $resourceRight; + + $resourceRight = new ResourceRight(); + $resourceRight + ->setMask($readerMask) + ->setRole(self::ROLE_CURRENT_COURSE_STUDENT) + ; + $rights[] = $resourceRight; } // Asked mask @@ -230,7 +256,7 @@ class ResourceNodeVoter extends Voter $acl->addRole($admin); // Adds a resource - $resource = new Resource($link); + $resource = new SecurityResource($link); $acl->addResource($resource); // Role and permissions settings diff --git a/src/CourseBundle/Resources/config/routing.yml b/src/CourseBundle/Resources/config/routing.yml index ddddff46ee..3a085a5462 100644 --- a/src/CourseBundle/Resources/config/routing.yml +++ b/src/CourseBundle/Resources/config/routing.yml @@ -10,7 +10,7 @@ core_tool: core_tool_document: path: /courses/{course}/document/{file} defaults: - _controller: ChamiloCoreBundle:Resource:getDocument + _controller: ChamiloCoreBundle:Resource:show requirements: file: .+ # allow "/" in {file} diff --git a/tests/scripts/migrate_item_property.php b/tests/scripts/migrate_item_property.php index 5e63a5a069..e5f7f4d7a5 100644 --- a/tests/scripts/migrate_item_property.php +++ b/tests/scripts/migrate_item_property.php @@ -13,7 +13,23 @@ use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter; require_once __DIR__.'/../../main/inc/global.inc.php'; -$sql = "SELECT * FROM c_item_property WHERE tool = 'document' LIMIT 10"; +$sql = "SELECT + d.id, + d.c_id, + d.session_id, + i.to_group_id, + i.to_user_id, + i.iid, + insert_user_id, + insert_date, + lastedit_date, + tool, + visibility + FROM c_item_property i + INNER JOIN c_document d + ON (d.iid = i.ref AND i.c_id = d.c_id) + WHERE i.tool = 'document' AND filetype <> 'folder' AND d.c_id = 4 + ORDER BY d.path LIMIT 10"; $result = Database::query($sql); $fs = Container::$container->get('oneup_flysystem.courses_filesystem'); @@ -22,6 +38,9 @@ $em = Database::getManager(); $resourceType = $em->getRepository('ChamiloCoreBundle:Resource\ResourceType')->findOneBy(['name' => 'document']); $coursePath = api_get_path(SYS_PATH).'app/courses/'; $mediaManager = Container::$container->get('sonata.media.manager.media'); +$documentManager = $em->getRepository('ChamiloCourseBundle:CDocument'); +$contextManager = Container::$container->get('sonata.classification.manager.context'); +$defaultContext = $contextManager->findOneBy(['id' => 'default']); while ($row = Database::fetch_array($result, 'ASSOC')) { $itemIid = $row['iid']; @@ -29,20 +48,31 @@ while ($row = Database::fetch_array($result, 'ASSOC')) { $sessionId = $row['session_id']; $groupId = $row['to_group_id']; $toUserId = $row['to_user_id']; + $documentId = $row['id']; $toUser = api_get_user_entity($toUserId); $author = api_get_user_entity($row['insert_user_id']); + + if (empty($author)) { + error_log("User does not exists in the DB ".$row['insert_user_id']); + continue; + } + $createdAt = api_get_utc_datetime($row['insert_date'], true, true); $lastUpdatedAt = api_get_utc_datetime($row['lastedit_date'], true, true); $course = api_get_course_entity($courseId); - $session = api_get_course_entity($sessionId); + if (empty($course)) { + error_log("Course does not exists in the DB $courseId"); + continue; + } + $session = api_get_session_entity($sessionId); $group = api_get_group_entity($groupId); switch ($row['tool']) { case 'document': - $documentData = DocumentManager::get_document_data_by_id($row['ref'], $course->getCode(), $sessionId); - var_dump($documentData); + $documentData = DocumentManager::get_document_data_by_id($documentId, $course->getCode(), $sessionId); + //var_dump($documentData); if (!$documentData) { //$documentData = DocumentManager::get_document_data_by_id($row['ref'], $course->getCode(), $sessionId); error_log("Skipped item property iid #$itemIid"); @@ -50,103 +80,114 @@ while ($row = Database::fetch_array($result, 'ASSOC')) { } $folderPath = $course->getDirectory().'/document/'.$documentData['path']; - $file = $coursePath.$folderPath; switch ($documentData['filetype']) { case 'folder': //$fs->createDir($folderPath); + /*$node = new ResourceNode(); + $node + ->setName($documentData['title']) + ->setDescription('') + ->setCreator($author) + ->setResourceType($resourceType) + ->setCreatedAt($createdAt) + ->setUpdatedAt($lastUpdatedAt) + ; + $em->persist($node); + $em->flush();*/ break; case 'file': - //$stream = fopen($file, 'r+'); - //$fs->writeStream($folderPath, $stream); - //fclose($stream); + $document = $documentManager->find($documentData['iid']); + var_dump('Parsing document iid #'.$document->getIid()); + /** @var Media $media */ $media = $mediaManager->create(); - //$media = new Media(); $media->setName($documentData['title']); + + $fileName = basename($documentData['path']); + $extension = pathinfo($fileName, PATHINFO_EXTENSION); + $media->setSize($documentData['size']); $media->setContext('default'); - $media->setProviderName('sonata.media.provider.image'); - var_dump($file); + + $provider = 'sonata.media.provider.image'; + if (!in_array($extension, ['jpeg', 'jpg', 'gif', 'png'])) { + $provider = 'sonata.media.provider.file'; + } + + $media->setProviderName($provider); $media->setEnabled(true); - $stdFile = new Std - $media->setBinaryContent(file_get_contents($file)); + $media->setBinaryContent($file); $mediaManager->save($media, true); + $resourceFile = new ResourceFile(); + $resourceFile->setMedia($media); + $resourceFile->setName($documentData['title']); + + $em->persist($resourceFile); + + $node = new ResourceNode(); + $node + ->setName($documentData['title']) + ->setDescription($documentData['comment'] ?? '') + ->setResourceFile($resourceFile) + ->setCreator($author) + ->setResourceType($resourceType) + ->setCreatedAt($createdAt) + ->setUpdatedAt($lastUpdatedAt) + ; + $em->persist($node); + + $document->setResourceNode($node); + $em->persist($document); + $em->flush(); + + $rights = []; + switch ($row['visibility']) { + case '0': + $newVisibility = ResourceLink::VISIBILITY_DRAFT; + + $readerMask = ResourceNodeVoter::getReaderMask(); + $editorMask = ResourceNodeVoter::getEditorMask(); + + $resourceRight = new ResourceRight(); + $resourceRight + ->setMask($editorMask) + ->setRole(ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER) + ; + $rights[] = $resourceRight; + break; + case '1': + $newVisibility = ResourceLink::VISIBILITY_PUBLISHED; + break; + case '2': + $newVisibility = ResourceLink::VISIBILITY_DELETED; + break; + } + + $link = new ResourceLink(); + $link + ->setCourse($course) + ->setSession($session) + ->setGroup($group) + ->setUser($toUser) + ->setResourceNode($node) + ->setVisibility($newVisibility) + ; + if (!empty($rights)) { + foreach ($rights as $right) { + $link->addResourceRight($right); + } + } - break; - } + $em->persist($link); + $em->flush(); - continue; - - $file = new ResourceFile(); - $file - ->setHash('') - ->setName($documentData['title']) - ->setOriginalFilename(basename($documentData['path'])) - ->setSize($documentData['size']) - ; - - $node = new ResourceNode(); - $node - ->setName($documentData['title']) - ->setDescription($documentData['comment']) - ->setResourceFile($file) - ->setCreator($author) - ->setResourceFile($file) - ->setResourceType($resourceType) - - ->setCreatedAt($createdAt) - ->setUpdatedAt($lastUpdatedAt) - ; - $em->persist($node); - $em->flush(); - - $rights = []; - switch ($row['visibility']) { - case '0': - $newVisibility = ResourceLink::VISIBILITY_DRAFT; - - $readerMask = ResourceNodeVoter::getReaderMask(); - $editorMask = ResourceNodeVoter::getEditorMask(); - - $resourceRight = new ResourceRight(); - $resourceRight - ->setMask($editorMask) - ->setRole(ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER) - ; - $rights[] = $resourceRight; - break; - case '1': - $newVisibility = ResourceLink::VISIBILITY_PUBLISHED; break; - case '2': - $newVisibility = ResourceLink::VISIBILITY_DELETED; - break; - } - - $link = new ResourceLink(); - $link - ->setCourse($course) - ->setSession($session) - ->setGroup($group) - ->setUser($toUser) - ->setResourceNode($node) - ->setVisibility($newVisibility) - ; - - if (!empty($rights)) { - foreach ($rights as $right) { - $link->addResourceRight($right); - } } - - $em->persist($link); - $em->flush(); - break; } }