See https://github.com/chamilo/chamilo-lms/pull/5045pull/5064/head
parent
770d043943
commit
3ebefc6ca9
@ -0,0 +1,17 @@ |
||||
<?php |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Chamilo\CoreBundle\ApiResource; |
||||
|
||||
use Symfony\Component\Serializer\Annotation\Groups; |
||||
|
||||
abstract class AbstractResource |
||||
{ |
||||
#[Groups([ |
||||
'ctool:read', |
||||
])] |
||||
public ?string $illustrationUrl = null; |
||||
} |
||||
@ -0,0 +1,41 @@ |
||||
<?php |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Chamilo\CoreBundle\ApiResource; |
||||
|
||||
use Chamilo\CoreBundle\Entity\ResourceNode; |
||||
use Chamilo\CoreBundle\Tool\AbstractTool; |
||||
use Symfony\Component\Serializer\Annotation\Groups; |
||||
|
||||
class CourseTool extends AbstractResource |
||||
{ |
||||
#[Groups(['ctool:read'])] |
||||
public ?int $iid = null; |
||||
|
||||
#[Groups(['ctool:read'])] |
||||
public string $name; |
||||
|
||||
#[Groups(['ctool:read'])] |
||||
public ?bool $visibility = null; |
||||
|
||||
#[Groups(['ctool:read'])] |
||||
public AbstractTool $tool; |
||||
|
||||
#[Groups(['ctool:read'])] |
||||
public ?ResourceNode $resourceNode = null; |
||||
|
||||
#[Groups(['ctool:read'])] |
||||
public string $url; |
||||
|
||||
#[Groups(['ctool:read'])] |
||||
public string $category = ''; |
||||
|
||||
#[Groups(['ctool:read'])] |
||||
public function getNameToTranslate(): string |
||||
{ |
||||
return ucfirst(str_replace('_', ' ', $this->name)); |
||||
} |
||||
} |
||||
@ -0,0 +1,46 @@ |
||||
<?php |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Chamilo\CoreBundle\DataProvider\Extension; |
||||
|
||||
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface; |
||||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; |
||||
use ApiPlatform\Metadata\Operation; |
||||
use Chamilo\CoreBundle\Entity\ResourceLink; |
||||
use Chamilo\CoreBundle\Traits\ControllerTrait; |
||||
use Chamilo\CoreBundle\Traits\CourseControllerTrait; |
||||
use Chamilo\CourseBundle\Entity\CTool; |
||||
use Doctrine\ORM\QueryBuilder; |
||||
|
||||
class CToolExtension implements QueryCollectionExtensionInterface |
||||
{ |
||||
use ControllerTrait; |
||||
use CourseControllerTrait; |
||||
|
||||
public function applyToCollection( |
||||
QueryBuilder $queryBuilder, |
||||
QueryNameGeneratorInterface $queryNameGenerator, |
||||
string $resourceClass, |
||||
Operation $operation = null, |
||||
array $context = [] |
||||
): void { |
||||
if (CTool::class !== $resourceClass) { |
||||
return; |
||||
} |
||||
|
||||
$alias = $queryBuilder->getRootAliases()[0]; |
||||
|
||||
$queryBuilder |
||||
->innerJoin("$alias.resourceNode", 'resource_node') |
||||
->innerJoin('resource_node.resourceLinks', 'resource_links') |
||||
->andWhere( |
||||
$queryBuilder->expr()->notIn("$alias.name", ['course_tool', 'course_homepage']) |
||||
) |
||||
->andWhere('resource_links.visibility != :visibility_deleted') |
||||
->setParameter('visibility_deleted', ResourceLink::VISIBILITY_DELETED) |
||||
; |
||||
} |
||||
} |
||||
@ -0,0 +1,74 @@ |
||||
<?php |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Chamilo\CoreBundle\DataTransformer; |
||||
|
||||
use ApiPlatform\Core\DataTransformer\DataTransformerInterface; |
||||
use Chamilo\CoreBundle\ApiResource\CourseTool; |
||||
use Chamilo\CoreBundle\Entity\Course; |
||||
use Chamilo\CoreBundle\Tool\AbstractTool; |
||||
use Chamilo\CoreBundle\Tool\ToolChain; |
||||
use Chamilo\CoreBundle\Traits\CourseFromRequestTrait; |
||||
use Chamilo\CourseBundle\Entity\CTool; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Symfony\Component\HttpFoundation\RequestStack; |
||||
|
||||
class CourseToolDataTranformer implements DataTransformerInterface |
||||
{ |
||||
use CourseFromRequestTrait; |
||||
|
||||
public function __construct( |
||||
protected RequestStack $requestStack, |
||||
protected EntityManagerInterface $entityManager, |
||||
protected readonly ToolChain $toolChain, |
||||
) {} |
||||
|
||||
public function transform($object, string $to, array $context = []) |
||||
{ |
||||
\assert($object instanceof CTool); |
||||
|
||||
$tool = $object->getTool(); |
||||
|
||||
$toolModel = $this->toolChain->getToolFromName( |
||||
$tool->getName() |
||||
); |
||||
|
||||
$course = $this->getCourse(); |
||||
|
||||
$cTool = new CourseTool(); |
||||
$cTool->iid = $object->getIid(); |
||||
$cTool->name = $object->getName(); |
||||
$cTool->visibility = $object->getVisibility(); |
||||
$cTool->resourceNode = $object->resourceNode; |
||||
$cTool->illustrationUrl = $object->illustrationUrl; |
||||
$cTool->url = $this->generateToolUrl($toolModel, $course); |
||||
$cTool->tool = $toolModel; |
||||
|
||||
return $cTool; |
||||
} |
||||
|
||||
private function generateToolUrl(AbstractTool $tool, Course $course): string |
||||
{ |
||||
$link = $tool->getLink(); |
||||
|
||||
if (strpos($link, 'nodeId')) { |
||||
$nodeId = (string) $course->getResourceNode()->getId(); |
||||
$link = str_replace(':nodeId', $nodeId, $link); |
||||
} |
||||
|
||||
return $link.'?' |
||||
.http_build_query([ |
||||
'cid' => $this->getCourse()->getId(), |
||||
'sid' => $this->getSession()?->getId(), |
||||
'gid' => 0, |
||||
]); |
||||
} |
||||
|
||||
public function supportsTransformation($data, string $to, array $context = []): bool |
||||
{ |
||||
return $data instanceof CTool && CourseTool::class === $to; |
||||
} |
||||
} |
||||
@ -0,0 +1,69 @@ |
||||
<?php |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Chamilo\CoreBundle\Filter; |
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter; |
||||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; |
||||
use ApiPlatform\Metadata\Operation; |
||||
use Chamilo\CoreBundle\Traits\CourseFromRequestTrait; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Doctrine\ORM\QueryBuilder; |
||||
use Doctrine\Persistence\ManagerRegistry; |
||||
use Psr\Log\LoggerInterface; |
||||
use Symfony\Component\HttpFoundation\RequestStack; |
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface; |
||||
|
||||
class CidFilter extends AbstractFilter |
||||
{ |
||||
use CourseFromRequestTrait; |
||||
|
||||
public function __construct( |
||||
protected RequestStack $requestStack, |
||||
protected EntityManagerInterface $entityManager, |
||||
ManagerRegistry $managerRegistry, |
||||
LoggerInterface $logger = null, |
||||
array $properties = null, |
||||
NameConverterInterface $nameConverter = null, |
||||
) { |
||||
parent::__construct($managerRegistry, $logger, $properties, $nameConverter); |
||||
} |
||||
|
||||
public function getDescription(string $resourceClass): array |
||||
{ |
||||
return [ |
||||
'cid' => [ |
||||
'property' => null, |
||||
'type' => 'int', |
||||
'required' => true, |
||||
'description' => 'Course identifier', |
||||
], |
||||
]; |
||||
} |
||||
|
||||
protected function filterProperty( |
||||
string $property, |
||||
$value, |
||||
QueryBuilder $queryBuilder, |
||||
QueryNameGeneratorInterface $queryNameGenerator, |
||||
string $resourceClass, |
||||
Operation $operation = null, |
||||
array $context = [] |
||||
): void { |
||||
if ('cid' !== $property) { |
||||
return; |
||||
} |
||||
|
||||
$course = $this->getCourse(); |
||||
|
||||
$queryBuilder |
||||
->andWhere( |
||||
$queryBuilder->expr()->eq('resource_links.course', ':course') |
||||
) |
||||
->setParameter('course', $course->getId()) |
||||
; |
||||
} |
||||
} |
||||
@ -0,0 +1,105 @@ |
||||
<?php |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Chamilo\CoreBundle\Filter; |
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter; |
||||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; |
||||
use ApiPlatform\Metadata\Operation; |
||||
use Chamilo\CoreBundle\Entity\ResourceShowCourseResourcesInSessionInterface; |
||||
use Chamilo\CoreBundle\Traits\CourseFromRequestTrait; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Doctrine\ORM\QueryBuilder; |
||||
use Doctrine\Persistence\ManagerRegistry; |
||||
use Psr\Log\LoggerInterface; |
||||
use ReflectionClass; |
||||
use ReflectionException; |
||||
use Symfony\Component\HttpFoundation\RequestStack; |
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface; |
||||
|
||||
class SidFilter extends AbstractFilter |
||||
{ |
||||
use CourseFromRequestTrait; |
||||
|
||||
public function __construct( |
||||
protected RequestStack $requestStack, |
||||
protected EntityManagerInterface $entityManager, |
||||
ManagerRegistry $managerRegistry, |
||||
LoggerInterface $logger = null, |
||||
array $properties = null, |
||||
NameConverterInterface $nameConverter = null |
||||
) { |
||||
parent::__construct($managerRegistry, $logger, $properties, $nameConverter); |
||||
} |
||||
|
||||
public function getDescription(string $resourceClass): array |
||||
{ |
||||
return [ |
||||
'sid' => [ |
||||
'property' => null, |
||||
'type' => 'string', |
||||
'required' => false, |
||||
'description' => 'Course identifier', |
||||
], |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $value |
||||
* |
||||
* @throws ReflectionException |
||||
*/ |
||||
protected function filterProperty( |
||||
string $property, |
||||
$value, |
||||
QueryBuilder $queryBuilder, |
||||
QueryNameGeneratorInterface $queryNameGenerator, |
||||
string $resourceClass, |
||||
Operation $operation = null, |
||||
array $context = [] |
||||
): void { |
||||
if ('sid' !== $property) { |
||||
return; |
||||
} |
||||
|
||||
$reflection = new ReflectionClass($resourceClass); |
||||
|
||||
$loadBaseSessionContent = \in_array( |
||||
ResourceShowCourseResourcesInSessionInterface::class, |
||||
$reflection->getInterfaceNames(), |
||||
true |
||||
); |
||||
|
||||
// Session was set with a kernel request from CoreBundle\EventListener\CourseListener class |
||||
$session = $this->getSession(); |
||||
|
||||
if (null === $session) { |
||||
$queryBuilder->andWhere( |
||||
$queryBuilder->expr()->orX( |
||||
$queryBuilder->expr()->isNull('resource_links.session'), |
||||
$queryBuilder->expr()->eq('resource_links.session', 0) |
||||
) |
||||
); |
||||
} elseif ($loadBaseSessionContent) { |
||||
$queryBuilder |
||||
->andWhere( |
||||
$queryBuilder->expr()->orX( |
||||
$queryBuilder->expr()->eq('resource_links.session', ':session'), |
||||
$queryBuilder->expr()->isNull('resource_links.session') |
||||
) |
||||
) |
||||
->setParameter('session', $session?->getId()) |
||||
; |
||||
} else { |
||||
$queryBuilder |
||||
->andWhere( |
||||
$queryBuilder->expr()->eq('resource_links.session', ':session') |
||||
) |
||||
->setParameter('session', $session?->getId()) |
||||
; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,101 @@ |
||||
<?php |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Chamilo\CoreBundle\State; |
||||
|
||||
use ApiPlatform\Doctrine\Orm\State\CollectionProvider; |
||||
use ApiPlatform\Metadata\Operation; |
||||
use ApiPlatform\State\Pagination\PartialPaginatorInterface; |
||||
use ApiPlatform\State\ProviderInterface; |
||||
use Chamilo\CoreBundle\Entity\ResourceLink; |
||||
use Chamilo\CoreBundle\Entity\User; |
||||
use Chamilo\CoreBundle\Settings\SettingsManager; |
||||
use Chamilo\CoreBundle\Tool\ToolChain; |
||||
use Chamilo\CoreBundle\Traits\CourseFromRequestTrait; |
||||
use Chamilo\CourseBundle\Entity\CTool; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Symfony\Component\HttpFoundation\RequestStack; |
||||
use Symfony\Component\Security\Core\Security; |
||||
|
||||
/** |
||||
* @template-implements ProviderInterface<CTool> |
||||
*/ |
||||
class CToolProvider implements ProviderInterface |
||||
{ |
||||
use CourseFromRequestTrait; |
||||
|
||||
public function __construct( |
||||
private readonly CollectionProvider $provider, |
||||
protected EntityManagerInterface $entityManager, |
||||
private readonly SettingsManager $settingsManager, |
||||
private readonly Security $security, |
||||
private readonly ToolChain $toolChain, |
||||
protected RequestStack $requestStack, |
||||
) {} |
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array |
||||
{ |
||||
/** @var PartialPaginatorInterface $result */ |
||||
$result = $this->provider->provide($operation, $uriVariables, $context); |
||||
|
||||
$request = $this->requestStack->getMainRequest(); |
||||
|
||||
$studentView = $request ? $request->getSession()->get('studentview') : 'studentview'; |
||||
|
||||
/** @var User|null $user */ |
||||
$user = $this->security->getUser(); |
||||
|
||||
$isAllowToEdit = $user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')); |
||||
$isAllowToEditBack = $user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER')); |
||||
$isAllowToSessionEdit = $user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_CURRENT_COURSE_TEACHER') || $user->hasRole('ROLE_CURRENT_COURSE_SESSION_TEACHER')); |
||||
|
||||
$allowVisibilityInSession = $this->settingsManager->getSetting('course.allow_edit_tool_visibility_in_session'); |
||||
$session = $this->getSession(); |
||||
|
||||
$results = []; |
||||
|
||||
/** @var CTool $cTool */ |
||||
foreach ($result as $cTool) { |
||||
$toolModel = $this->toolChain->getToolFromName( |
||||
$cTool->getTool()->getName() |
||||
); |
||||
|
||||
if (!$isAllowToEdit && 'admin' === $toolModel->getCategory()) { |
||||
continue; |
||||
} |
||||
|
||||
$resourceLinks = $cTool->getResourceNode()->getResourceLinks(); |
||||
|
||||
if ($session && $allowVisibilityInSession) { |
||||
$sessionLink = $resourceLinks->findFirst( |
||||
fn (int $key, ResourceLink $resourceLink): bool => $resourceLink->getSession()?->getId() === $session->getId() |
||||
); |
||||
|
||||
if ($sessionLink) { |
||||
// Set the session link as unique to include in repsonse |
||||
$resourceLinks->clear(); |
||||
$resourceLinks->add($sessionLink); |
||||
|
||||
$isAllowToEdit = $isAllowToSessionEdit; |
||||
} else { |
||||
$isAllowToEdit = $isAllowToEditBack; |
||||
} |
||||
} |
||||
|
||||
if (!$isAllowToEdit || 'studentview' === $studentView) { |
||||
$notPublishedLink = ResourceLink::VISIBILITY_PUBLISHED !== $resourceLinks->first()->getVisibility(); |
||||
|
||||
if ($notPublishedLink) { |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
$results[] = $cTool; |
||||
} |
||||
|
||||
return $results; |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\CoreBundle\Traits; |
||||
|
||||
use Chamilo\CoreBundle\Entity\Course; |
||||
use Chamilo\CoreBundle\Entity\Session; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\RequestStack; |
||||
|
||||
trait CourseFromRequestTrait |
||||
{ |
||||
protected RequestStack $requestStack; |
||||
protected EntityManagerInterface $entityManager; |
||||
|
||||
public function getRequest(): ?Request |
||||
{ |
||||
return $this->requestStack->getMainRequest(); |
||||
} |
||||
|
||||
public function getCourse(): ?Course |
||||
{ |
||||
$request = $this->getRequest(); |
||||
|
||||
if ($request) { |
||||
$courseId = $request->getSession()->get('cid', 0); |
||||
} |
||||
|
||||
if (empty($courseId)) { |
||||
return null; |
||||
} |
||||
|
||||
return $this->entityManager->find(Course::class, $courseId); |
||||
} |
||||
|
||||
public function getSession(): ?Session |
||||
{ |
||||
$request = $this->getRequest(); |
||||
|
||||
if ($request) { |
||||
$sessionId = $request->getSession()->get('sid', 0); |
||||
} |
||||
|
||||
if (empty($sessionId)) { |
||||
return null; |
||||
} |
||||
|
||||
return $this->entityManager->find(Session::class, $sessionId); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue