You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
954 lines
30 KiB
954 lines
30 KiB
<?php
|
|
|
|
/* For licensing terms, see /license.txt */
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Chamilo\CoreBundle\Repository;
|
|
|
|
use Chamilo\CoreBundle\Component\Utils\CreateUploadedFile;
|
|
use Chamilo\CoreBundle\Entity\AbstractResource;
|
|
use Chamilo\CoreBundle\Entity\Course;
|
|
use Chamilo\CoreBundle\Entity\ResourceFile;
|
|
use Chamilo\CoreBundle\Entity\ResourceInterface;
|
|
use Chamilo\CoreBundle\Entity\ResourceLink;
|
|
use Chamilo\CoreBundle\Entity\ResourceNode;
|
|
use Chamilo\CoreBundle\Entity\ResourceRight;
|
|
use Chamilo\CoreBundle\Entity\ResourceShowCourseResourcesInSessionInterface;
|
|
use Chamilo\CoreBundle\Entity\ResourceType;
|
|
use Chamilo\CoreBundle\Entity\Session;
|
|
use Chamilo\CoreBundle\Entity\User;
|
|
use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter;
|
|
use Chamilo\CoreBundle\Traits\NonResourceRepository;
|
|
use Chamilo\CoreBundle\Traits\Repository\RepositoryQueryBuilderTrait;
|
|
use Chamilo\CourseBundle\Entity\CDocument;
|
|
use Chamilo\CourseBundle\Entity\CGroup;
|
|
use DateTime;
|
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
|
use Doctrine\DBAL\Types\Types;
|
|
use Doctrine\ORM\EntityRepository;
|
|
use Doctrine\ORM\QueryBuilder;
|
|
use Exception;
|
|
use LogicException;
|
|
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
|
use Throwable;
|
|
|
|
use const PATHINFO_EXTENSION;
|
|
|
|
/**
|
|
* Extends Resource EntityRepository.
|
|
*/
|
|
abstract class ResourceRepository extends ServiceEntityRepository
|
|
{
|
|
use NonResourceRepository;
|
|
use RepositoryQueryBuilderTrait;
|
|
|
|
protected ?ResourceType $resourceType = null;
|
|
|
|
public function getCount(QueryBuilder $qb): int
|
|
{
|
|
$qb
|
|
->select('count(resource)')
|
|
->setMaxResults(1)
|
|
->setFirstResult(null)
|
|
;
|
|
|
|
return (int) $qb->getQuery()->getSingleScalarResult();
|
|
}
|
|
|
|
public function getResourceByResourceNode(ResourceNode $resourceNode): ?ResourceInterface
|
|
{
|
|
return $this->findOneBy([
|
|
'resourceNode' => $resourceNode,
|
|
]);
|
|
}
|
|
|
|
public function create(AbstractResource $resource): void
|
|
{
|
|
$this->getEntityManager()->persist($resource);
|
|
$this->getEntityManager()->flush();
|
|
}
|
|
|
|
public function update(AbstractResource|User $resource, bool $andFlush = true): void
|
|
{
|
|
if (!$resource->hasResourceNode()) {
|
|
throw new Exception('Resource needs a resource node');
|
|
}
|
|
|
|
$em = $this->getEntityManager();
|
|
|
|
$resource->getResourceNode()->setUpdatedAt(new DateTime());
|
|
$resource->getResourceNode()->setTitle($resource->getResourceName());
|
|
$em->persist($resource);
|
|
|
|
if ($andFlush) {
|
|
$em->flush();
|
|
}
|
|
}
|
|
|
|
public function updateNodeForResource(ResourceInterface $resource): ResourceNode
|
|
{
|
|
$em = $this->getEntityManager();
|
|
|
|
$resourceNode = $resource->getResourceNode();
|
|
$resourceName = $resource->getResourceName();
|
|
|
|
foreach ($resourceNode->getResourceFiles() as $resourceFile) {
|
|
if (null !== $resourceFile) {
|
|
$originalName = $resourceFile->getOriginalName();
|
|
$originalExtension = pathinfo($originalName, PATHINFO_EXTENSION);
|
|
|
|
// $originalBasename = \basename($resourceName, $originalExtension);
|
|
/*$slug = sprintf(
|
|
'%s.%s',
|
|
$this->slugify->slugify($originalBasename),
|
|
$this->slugify->slugify($originalExtension)
|
|
);*/
|
|
|
|
$newOriginalName = \sprintf('%s.%s', $resourceName, $originalExtension);
|
|
$resourceFile->setOriginalName($newOriginalName);
|
|
|
|
$em->persist($resourceFile);
|
|
}
|
|
}
|
|
// $slug = $this->slugify->slugify($resourceName);
|
|
|
|
$resourceNode->setTitle($resourceName);
|
|
// $resourceNode->setSlug($slug);
|
|
|
|
$em->persist($resourceNode);
|
|
$em->persist($resource);
|
|
|
|
$em->flush();
|
|
|
|
return $resourceNode;
|
|
}
|
|
|
|
public function findCourseResourceByTitle(
|
|
string $title,
|
|
ResourceNode $parentNode,
|
|
Course $course,
|
|
?Session $session = null,
|
|
?CGroup $group = null
|
|
): ?ResourceInterface {
|
|
$qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
|
|
$this->addTitleQueryBuilder($title, $qb);
|
|
$qb->setMaxResults(1);
|
|
|
|
return $qb->getQuery()->getOneOrNullResult();
|
|
}
|
|
|
|
public function findCourseResourceBySlug(
|
|
string $title,
|
|
ResourceNode $parentNode,
|
|
Course $course,
|
|
?Session $session = null,
|
|
?CGroup $group = null
|
|
): ?ResourceInterface {
|
|
$qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
|
|
$this->addSlugQueryBuilder($title, $qb);
|
|
$qb->setMaxResults(1);
|
|
|
|
return $qb->getQuery()->getOneOrNullResult();
|
|
}
|
|
|
|
/**
|
|
* Find resources ignoring the visibility.
|
|
*/
|
|
public function findCourseResourceBySlugIgnoreVisibility(
|
|
string $title,
|
|
ResourceNode $parentNode,
|
|
Course $course,
|
|
?Session $session = null,
|
|
?CGroup $group = null
|
|
): ?ResourceInterface {
|
|
$qb = $this->getResourcesByCourseIgnoreVisibility($course, $session, $group, $parentNode);
|
|
$this->addSlugQueryBuilder($title, $qb);
|
|
$qb->setMaxResults(1);
|
|
|
|
return $qb->getQuery()->getOneOrNullResult();
|
|
}
|
|
|
|
/**
|
|
* @return ResourceInterface[]
|
|
*/
|
|
public function findCourseResourcesByTitle(
|
|
string $title,
|
|
ResourceNode $parentNode,
|
|
Course $course,
|
|
?Session $session = null,
|
|
?CGroup $group = null
|
|
) {
|
|
$qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
|
|
$this->addTitleQueryBuilder($title, $qb);
|
|
|
|
return $qb->getQuery()->getResult();
|
|
}
|
|
|
|
/**
|
|
* @todo clean path
|
|
*/
|
|
public function addFileFromPath(ResourceInterface $resource, string $fileName, string $path, bool $flush = true): ?ResourceFile
|
|
{
|
|
if (!empty($path) && file_exists($path) && !is_dir($path)) {
|
|
$mimeType = mime_content_type($path);
|
|
$file = new UploadedFile($path, $fileName, $mimeType, null, true);
|
|
|
|
return $this->addFile($resource, $file, '', $flush);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function addFileFromString(ResourceInterface $resource, string $fileName, string $mimeType, string $content, bool $flush = true): ?ResourceFile
|
|
{
|
|
$file = CreateUploadedFile::fromString($fileName, $mimeType, $content);
|
|
|
|
return $this->addFile($resource, $file, '', $flush);
|
|
}
|
|
|
|
public function addFileFromFileRequest(ResourceInterface $resource, string $fileKey, bool $flush = true): ?ResourceFile
|
|
{
|
|
$request = $this->getRequest();
|
|
if ($request->files->has($fileKey)) {
|
|
$file = $request->files->get($fileKey);
|
|
if (null !== $file) {
|
|
$resourceFile = $this->addFile($resource, $file);
|
|
if ($flush) {
|
|
$this->getEntityManager()->flush();
|
|
}
|
|
|
|
return $resourceFile;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function addFile(ResourceInterface $resource, UploadedFile $file, string $description = '', bool $flush = false): ?ResourceFile
|
|
{
|
|
$resourceNode = $resource->getResourceNode();
|
|
|
|
if (null === $resourceNode) {
|
|
throw new LogicException('Resource node is null');
|
|
}
|
|
|
|
$em = $this->getEntityManager();
|
|
|
|
$resourceFile = new ResourceFile();
|
|
$resourceFile
|
|
->setFile($file)
|
|
->setDescription($description)
|
|
->setTitle($resource->getResourceName())
|
|
->setResourceNode($resourceNode)
|
|
;
|
|
$resourceNode->addResourceFile($resourceFile);
|
|
$em->persist($resourceNode);
|
|
|
|
if ($flush) {
|
|
$em->flush();
|
|
}
|
|
|
|
return $resourceFile;
|
|
}
|
|
|
|
public function getResourceType(): ResourceType
|
|
{
|
|
$resourceTypeName = $this->toolChain->getResourceTypeNameByEntity($this->getClassName());
|
|
$repo = $this->getEntityManager()->getRepository(ResourceType::class);
|
|
|
|
return $repo->findOneBy([
|
|
'title' => $resourceTypeName,
|
|
]);
|
|
}
|
|
|
|
public function addVisibilityQueryBuilder(?QueryBuilder $qb = null, bool $checkStudentView = false, bool $displayOnlyPublished = true): QueryBuilder
|
|
{
|
|
$qb = $this->getOrCreateQueryBuilder($qb);
|
|
|
|
// TODO Avoid global assumption for a request, and inject
|
|
// the request stack instead.
|
|
$request = $this->getRequest();
|
|
$sessionStudentView = null;
|
|
if (null !== $request) {
|
|
$sessionStudentView = $request->getSession()->get('studentview');
|
|
}
|
|
|
|
$checker = $this->getAuthorizationChecker();
|
|
$isAdminOrTeacher =
|
|
$checker->isGranted('ROLE_ADMIN')
|
|
|| $checker->isGranted('ROLE_CURRENT_COURSE_TEACHER');
|
|
|
|
if ($displayOnlyPublished) {
|
|
if (!$isAdminOrTeacher
|
|
|| ($checkStudentView && 'studentview' === $sessionStudentView)
|
|
) {
|
|
$qb
|
|
->andWhere('links.visibility = :visibility')
|
|
->setParameter('visibility', ResourceLink::VISIBILITY_PUBLISHED, Types::INTEGER)
|
|
;
|
|
}
|
|
}
|
|
|
|
// @todo Add start/end visibility restrictions.
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function addCourseQueryBuilder(Course $course, QueryBuilder $qb): QueryBuilder
|
|
{
|
|
$qb
|
|
->andWhere('links.course = :course')
|
|
->setParameter('course', $course)
|
|
;
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function addCourseSessionGroupQueryBuilder(Course $course, ?Session $session = null, ?CGroup $group = null, ?QueryBuilder $qb = null): QueryBuilder
|
|
{
|
|
$reflectionClass = $this->getClassMetadata()->getReflectionClass();
|
|
|
|
// Check if this resource type requires to load the base course resources when using a session
|
|
$loadBaseSessionContent = \in_array(
|
|
ResourceShowCourseResourcesInSessionInterface::class,
|
|
$reflectionClass->getInterfaceNames(),
|
|
true
|
|
);
|
|
|
|
$this->addCourseQueryBuilder($course, $qb);
|
|
|
|
if (null === $session) {
|
|
$qb->andWhere(
|
|
$qb->expr()->orX(
|
|
$qb->expr()->isNull('links.session'),
|
|
$qb->expr()->eq('links.session', 0)
|
|
)
|
|
);
|
|
} elseif ($loadBaseSessionContent) {
|
|
// Load course base content.
|
|
$qb->andWhere('links.session = :session OR links.session IS NULL');
|
|
$qb->setParameter('session', $session);
|
|
} else {
|
|
// Load only session resources.
|
|
$qb->andWhere('links.session = :session');
|
|
$qb->setParameter('session', $session);
|
|
}
|
|
|
|
if (null === $group) {
|
|
$qb->andWhere(
|
|
$qb->expr()->orX(
|
|
$qb->expr()->isNull('links.group'),
|
|
$qb->expr()->eq('links.group', 0)
|
|
)
|
|
);
|
|
} else {
|
|
$qb->andWhere('links.group = :group');
|
|
$qb->setParameter('group', $group);
|
|
}
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function getResourceTypeName(): string
|
|
{
|
|
return $this->toolChain->getResourceTypeNameByEntity($this->getClassName());
|
|
}
|
|
|
|
public function getResources(?ResourceNode $parentNode = null): QueryBuilder
|
|
{
|
|
$resourceTypeName = $this->getResourceTypeName();
|
|
|
|
$qb = $this->createQueryBuilder('resource')
|
|
->select('resource')
|
|
->innerJoin('resource.resourceNode', 'node')
|
|
->innerJoin('node.resourceLinks', 'links')
|
|
->innerJoin('node.resourceType', 'type')
|
|
->leftJoin('node.resourceFiles', 'file')
|
|
->where('type.title = :type')
|
|
->setParameter('type', $resourceTypeName, Types::STRING)
|
|
->addSelect('node')
|
|
->addSelect('links')
|
|
->addSelect('type')
|
|
->addSelect('file')
|
|
;
|
|
|
|
if (null !== $parentNode) {
|
|
$qb->andWhere('node.parent = :parentNode');
|
|
$qb->setParameter('parentNode', $parentNode);
|
|
}
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function getResourcesByCourse(Course $course, ?Session $session = null, ?CGroup $group = null, ?ResourceNode $parentNode = null, bool $displayOnlyPublished = true, bool $displayOrder = false): QueryBuilder
|
|
{
|
|
$qb = $this->getResources($parentNode);
|
|
$this->addVisibilityQueryBuilder($qb, true, $displayOnlyPublished);
|
|
$this->addCourseSessionGroupQueryBuilder($course, $session, $group, $qb);
|
|
|
|
if ($displayOrder) {
|
|
$qb->orderBy('links.displayOrder', 'ASC');
|
|
}
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function getResourcesByCourseIgnoreVisibility(Course $course, ?Session $session = null, ?CGroup $group = null, ?ResourceNode $parentNode = null): QueryBuilder
|
|
{
|
|
$qb = $this->getResources($parentNode);
|
|
$this->addCourseSessionGroupQueryBuilder($course, $session, $group, $qb);
|
|
|
|
return $qb;
|
|
}
|
|
|
|
/**
|
|
* Get resources only from the base course.
|
|
*/
|
|
public function getResourcesByCourseOnly(Course $course, ?ResourceNode $parentNode = null): QueryBuilder
|
|
{
|
|
$qb = $this->getResources($parentNode);
|
|
$this->addCourseQueryBuilder($course, $qb);
|
|
$this->addVisibilityQueryBuilder($qb);
|
|
|
|
$qb->andWhere('links.session IS NULL');
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function getResourceByCreatorFromTitle(
|
|
string $title,
|
|
User $user,
|
|
ResourceNode $parentNode
|
|
): ?ResourceInterface {
|
|
$qb = $this->getResourcesByCreator($user, $parentNode);
|
|
$this->addTitleQueryBuilder($title, $qb);
|
|
$qb->setMaxResults(1);
|
|
|
|
return $qb->getQuery()->getOneOrNullResult();
|
|
}
|
|
|
|
public function getResourcesByCreator(User $user, ?ResourceNode $parentNode = null): QueryBuilder
|
|
{
|
|
$qb = $this->createQueryBuilder('resource')
|
|
->select('resource')
|
|
->innerJoin('resource.resourceNode', 'node')
|
|
;
|
|
|
|
if (null !== $parentNode) {
|
|
$qb->andWhere('node.parent = :parentNode');
|
|
$qb->setParameter('parentNode', $parentNode);
|
|
}
|
|
|
|
$this->addCreatorQueryBuilder($user, $qb);
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function getResourcesByCourseLinkedToUser(
|
|
User $user,
|
|
Course $course,
|
|
?Session $session = null,
|
|
?CGroup $group = null,
|
|
?ResourceNode $parentNode = null
|
|
): QueryBuilder {
|
|
$qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
|
|
$qb->andWhere('node.creator = :user OR (links.user = :user OR links.user IS NULL)');
|
|
$qb->setParameter('user', $user);
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function getResourcesByLinkedUser(User $user, ?ResourceNode $parentNode = null): QueryBuilder
|
|
{
|
|
$qb = $this->getResources($parentNode);
|
|
$qb
|
|
->andWhere('links.user = :user')
|
|
->setParameter('user', $user)
|
|
;
|
|
|
|
$this->addVisibilityQueryBuilder($qb);
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function getResourceFromResourceNode(int $resourceNodeId): ?ResourceInterface
|
|
{
|
|
$qb = $this->createQueryBuilder('resource')
|
|
->select('resource')
|
|
->addSelect('node')
|
|
->addSelect('links')
|
|
->innerJoin('resource.resourceNode', 'node')
|
|
// ->innerJoin('node.creator', 'userCreator')
|
|
->leftJoin('node.resourceLinks', 'links')
|
|
->where('node.id = :id')
|
|
->setParameters([
|
|
'id' => $resourceNodeId,
|
|
])
|
|
;
|
|
|
|
return $qb->getQuery()->getOneOrNullResult();
|
|
}
|
|
|
|
public function delete(ResourceInterface $resource): void
|
|
{
|
|
$em = $this->getEntityManager();
|
|
$children = $resource->getResourceNode()->getChildren();
|
|
foreach ($children as $child) {
|
|
foreach ($child->getResourceFiles() as $resourceFile) {
|
|
$em->remove($resourceFile);
|
|
}
|
|
$resourceNode = $this->getResourceFromResourceNode($child->getId());
|
|
if (null !== $resourceNode) {
|
|
$this->delete($resourceNode);
|
|
}
|
|
}
|
|
|
|
$em->remove($resource);
|
|
$em->flush();
|
|
}
|
|
|
|
/**
|
|
* Deletes several entities: AbstractResource (Ex: CDocument, CQuiz), ResourceNode,
|
|
* ResourceLinks and ResourceFile (including files via Flysystem).
|
|
*/
|
|
public function hardDelete(AbstractResource $resource): void
|
|
{
|
|
$em = $this->getEntityManager();
|
|
$em->remove($resource);
|
|
$em->flush();
|
|
}
|
|
|
|
public function getResourceFileContent(AbstractResource $resource): string
|
|
{
|
|
try {
|
|
$resourceNode = $resource->getResourceNode();
|
|
|
|
return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
|
|
} catch (Throwable $throwable) {
|
|
throw new FileNotFoundException($resource->getResourceName());
|
|
}
|
|
}
|
|
|
|
public function getResourceNodeFileContent(ResourceNode $resourceNode): string
|
|
{
|
|
return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
|
|
}
|
|
|
|
/**
|
|
* @return false|resource
|
|
*/
|
|
public function getResourceNodeFileStream(ResourceNode $resourceNode)
|
|
{
|
|
return $this->resourceNodeRepository->getResourceNodeFileStream($resourceNode);
|
|
}
|
|
|
|
public function getResourceFileDownloadUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
|
|
{
|
|
$extraParams['mode'] = 'download';
|
|
|
|
return $this->getResourceFileUrl($resource, $extraParams, $referenceType);
|
|
}
|
|
|
|
public function getResourceFileUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
|
|
{
|
|
return $this->getResourceNodeRepository()->getResourceFileUrl(
|
|
$resource->getResourceNode(),
|
|
$extraParams,
|
|
$referenceType
|
|
);
|
|
}
|
|
|
|
public function updateResourceFileContent(AbstractResource $resource, string $content): bool
|
|
{
|
|
$resourceNode = $resource->getResourceNode();
|
|
if ($resourceNode->hasResourceFile()) {
|
|
$resourceNode->setContent($content);
|
|
foreach ($resourceNode->getResourceFiles() as $resourceFile) {
|
|
$resourceFile->setSize(\strlen($content));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function setResourceName(AbstractResource $resource, $title): void
|
|
{
|
|
if (!empty($title)) {
|
|
$resource->setResourceName($title);
|
|
$resourceNode = $resource->getResourceNode();
|
|
$resourceNode->setTitle($title);
|
|
}
|
|
}
|
|
|
|
public function toggleVisibilityPublishedDraft(
|
|
AbstractResource $resource,
|
|
?Course $course = null,
|
|
?Session $session = null
|
|
): void {
|
|
$firstLink = $resource->getFirstResourceLink();
|
|
|
|
if (ResourceLink::VISIBILITY_PUBLISHED === $firstLink->getVisibility()) {
|
|
$this->setVisibilityDraft($resource, $course, $session);
|
|
|
|
return;
|
|
}
|
|
|
|
if (ResourceLink::VISIBILITY_DRAFT === $firstLink->getVisibility()) {
|
|
$this->setVisibilityPublished($resource, $course, $session);
|
|
}
|
|
}
|
|
|
|
public function setVisibilityPublished(
|
|
AbstractResource $resource,
|
|
?Course $course = null,
|
|
?Session $session = null,
|
|
): void {
|
|
$this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PUBLISHED, true, $course, $session);
|
|
}
|
|
|
|
public function setVisibilityDraft(
|
|
AbstractResource $resource,
|
|
?Course $course = null,
|
|
?Session $session = null,
|
|
): void {
|
|
$this->setLinkVisibility($resource, ResourceLink::VISIBILITY_DRAFT, true, $course, $session);
|
|
}
|
|
|
|
public function setVisibilityPending(
|
|
AbstractResource $resource,
|
|
?Course $course = null,
|
|
?Session $session = null,
|
|
): void {
|
|
$this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PENDING, true, $course, $session);
|
|
}
|
|
|
|
public function addResourceNode(
|
|
ResourceInterface $resource,
|
|
User $creator,
|
|
ResourceInterface $parentResource,
|
|
?ResourceType $resourceType = null,
|
|
): ResourceNode {
|
|
$parentResourceNode = $parentResource->getResourceNode();
|
|
|
|
return $this->createNodeForResource(
|
|
$resource,
|
|
$creator,
|
|
$parentResourceNode,
|
|
null,
|
|
$resourceType,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @todo remove this function and merge it with addResourceNode()
|
|
*/
|
|
public function createNodeForResource(
|
|
ResourceInterface $resource,
|
|
User $creator,
|
|
ResourceNode $parentNode,
|
|
?UploadedFile $file = null,
|
|
?ResourceType $resourceType = null,
|
|
): ResourceNode {
|
|
$em = $this->getEntityManager();
|
|
|
|
$resourceType = $resourceType ?: $this->getResourceType();
|
|
$resourceName = $resource->getResourceName();
|
|
$extension = $this->slugify->slugify(pathinfo($resourceName, PATHINFO_EXTENSION));
|
|
|
|
if (empty($extension)) {
|
|
$slug = $this->slugify->slugify($resourceName);
|
|
} else {
|
|
$originalExtension = pathinfo($resourceName, PATHINFO_EXTENSION);
|
|
$originalBasename = basename($resourceName, $originalExtension);
|
|
$slug = \sprintf('%s.%s', $this->slugify->slugify($originalBasename), $originalExtension);
|
|
}
|
|
|
|
$resourceNode = new ResourceNode();
|
|
$resourceNode
|
|
->setTitle($resourceName)
|
|
->setSlug($slug)
|
|
->setResourceType($resourceType)
|
|
;
|
|
|
|
$creator->addResourceNode($resourceNode);
|
|
|
|
$parentNode?->addChild($resourceNode);
|
|
|
|
$resource->setResourceNode($resourceNode);
|
|
$em->persist($resourceNode);
|
|
$em->persist($resource);
|
|
|
|
if (null !== $file) {
|
|
$this->addFile($resource, $file);
|
|
}
|
|
|
|
return $resourceNode;
|
|
}
|
|
|
|
/**
|
|
* This is only used during installation for the special nodes (admin and AccessUrl).
|
|
*/
|
|
public function createNodeForResourceWithNoParent(ResourceInterface $resource, User $creator): ResourceNode
|
|
{
|
|
$em = $this->getEntityManager();
|
|
|
|
$resourceType = $this->getResourceType();
|
|
$resourceName = $resource->getResourceName();
|
|
$slug = $this->slugify->slugify($resourceName);
|
|
$resourceNode = new ResourceNode();
|
|
$resourceNode
|
|
->setTitle($resourceName)
|
|
->setSlug($slug)
|
|
->setCreator($creator)
|
|
->setResourceType($resourceType)
|
|
;
|
|
$resource->setResourceNode($resourceNode);
|
|
$em->persist($resourceNode);
|
|
$em->persist($resource);
|
|
|
|
return $resourceNode;
|
|
}
|
|
|
|
public function getTotalSpaceByCourse(Course $course, ?CGroup $group = null, ?Session $session = null): int
|
|
{
|
|
$qb = $this->createQueryBuilder('resource');
|
|
$qb
|
|
->select('SUM(file.size) as total')
|
|
->innerJoin('resource.resourceNode', 'node')
|
|
->innerJoin('node.resourceLinks', 'l')
|
|
->innerJoin('node.resourceFiles', 'file')
|
|
->where('l.course = :course')
|
|
->andWhere('file IS NOT NULL')
|
|
->setParameters(
|
|
[
|
|
'course' => $course,
|
|
]
|
|
)
|
|
;
|
|
|
|
if (null === $group) {
|
|
$qb->andWhere('l.group IS NULL');
|
|
} else {
|
|
$qb
|
|
->andWhere('l.group = :group')
|
|
->setParameter('group', $group)
|
|
;
|
|
}
|
|
|
|
if (null === $session) {
|
|
$qb->andWhere('l.session IS NULL');
|
|
} else {
|
|
$qb
|
|
->andWhere('l.session = :session')
|
|
->setParameter('session', $session)
|
|
;
|
|
}
|
|
|
|
$query = $qb->getQuery();
|
|
|
|
return (int) $query->getSingleScalarResult();
|
|
}
|
|
|
|
public function addTitleDecoration(AbstractResource $resource, Course $course, ?Session $session = null): string
|
|
{
|
|
if (null === $session) {
|
|
return '';
|
|
}
|
|
|
|
$link = $resource->getFirstResourceLinkFromCourseSession($course, $session);
|
|
if (null === $link) {
|
|
return '';
|
|
}
|
|
|
|
return '<img title="'.$session->getTitle().'" src="/img/icons/22/star.png" />';
|
|
}
|
|
|
|
public function isGranted(string $subject, AbstractResource $resource): bool
|
|
{
|
|
return $this->getAuthorizationChecker()->isGranted($subject, $resource->getResourceNode());
|
|
}
|
|
|
|
/**
|
|
* Changes the visibility of the children that matches the exact same link.
|
|
*/
|
|
public function copyVisibilityToChildren(ResourceNode $resourceNode, ResourceLink $link): bool
|
|
{
|
|
$children = $resourceNode->getChildren();
|
|
|
|
if (0 === $children->count()) {
|
|
return false;
|
|
}
|
|
|
|
$em = $this->getEntityManager();
|
|
|
|
/** @var ResourceNode $child */
|
|
foreach ($children as $child) {
|
|
if ($child->getChildren()->count() > 0) {
|
|
$this->copyVisibilityToChildren($child, $link);
|
|
}
|
|
|
|
$links = $child->getResourceLinks();
|
|
foreach ($links as $linkItem) {
|
|
if ($linkItem->getUser() === $link->getUser()
|
|
&& $linkItem->getSession() === $link->getSession()
|
|
&& $linkItem->getCourse() === $link->getCourse()
|
|
&& $linkItem->getUserGroup() === $link->getUserGroup()
|
|
) {
|
|
$linkItem->setVisibility($link->getVisibility());
|
|
$em->persist($linkItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function addSlugQueryBuilder(?string $slug, ?QueryBuilder $qb = null): QueryBuilder
|
|
{
|
|
$qb = $this->getOrCreateQueryBuilder($qb);
|
|
if (null === $slug) {
|
|
return $qb;
|
|
}
|
|
|
|
$qb
|
|
->andWhere('node.slug = :slug OR node.slug LIKE :slug2')
|
|
->setParameter('slug', $slug) // normal slug = title
|
|
->setParameter('slug2', $slug.'%-%') // slug with a counter = title-1
|
|
;
|
|
|
|
return $qb;
|
|
}
|
|
|
|
protected function addTitleQueryBuilder(?string $title, ?QueryBuilder $qb = null): QueryBuilder
|
|
{
|
|
$qb = $this->getOrCreateQueryBuilder($qb);
|
|
if (null === $title) {
|
|
return $qb;
|
|
}
|
|
|
|
$qb
|
|
->andWhere('node.title = :title')
|
|
->setParameter('title', $title)
|
|
;
|
|
|
|
return $qb;
|
|
}
|
|
|
|
protected function addCreatorQueryBuilder(?User $user, ?QueryBuilder $qb = null): QueryBuilder
|
|
{
|
|
$qb = $this->getOrCreateQueryBuilder($qb);
|
|
if (null === $user) {
|
|
return $qb;
|
|
}
|
|
|
|
$qb
|
|
->andWhere('node.creator = :creator')
|
|
->setParameter('creator', $user)
|
|
;
|
|
|
|
return $qb;
|
|
}
|
|
|
|
private function setLinkVisibility(
|
|
AbstractResource $resource,
|
|
int $visibility,
|
|
bool $recursive = true,
|
|
?Course $course = null,
|
|
?Session $session = null,
|
|
?CGroup $group = null,
|
|
?User $user = null,
|
|
): bool {
|
|
$resourceNode = $resource->getResourceNode();
|
|
|
|
if (null === $resourceNode) {
|
|
return false;
|
|
}
|
|
|
|
$em = $this->getEntityManager();
|
|
if ($recursive) {
|
|
$children = $resourceNode->getChildren();
|
|
|
|
/** @var ResourceNode $child */
|
|
foreach ($children as $child) {
|
|
$criteria = [
|
|
'resourceNode' => $child,
|
|
];
|
|
$childDocument = $this->findOneBy($criteria);
|
|
if ($childDocument) {
|
|
$this->setLinkVisibility($childDocument, $visibility);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($resource instanceof ResourceShowCourseResourcesInSessionInterface) {
|
|
$link = $resource->getFirstResourceLinkFromCourseSession($course, $session);
|
|
|
|
if (!$link) {
|
|
$resource->parentResource = $course;
|
|
$resource->addCourseLink($course, $session);
|
|
}
|
|
|
|
$link = $resource->getFirstResourceLinkFromCourseSession($course, $session);
|
|
$links = [$link];
|
|
} else {
|
|
$links = $resourceNode->getResourceLinks();
|
|
}
|
|
|
|
/** @var ResourceLink $link */
|
|
foreach ($links as $link) {
|
|
$link->setVisibility($visibility);
|
|
if (ResourceLink::VISIBILITY_DRAFT === $visibility) {
|
|
$editorMask = ResourceNodeVoter::getEditorMask();
|
|
$resourceRight = (new ResourceRight())
|
|
->setMask($editorMask)
|
|
->setRole(ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER)
|
|
->setResourceLink($link)
|
|
;
|
|
$link->addResourceRight($resourceRight);
|
|
} else {
|
|
$link->setResourceRights(new ArrayCollection());
|
|
}
|
|
$em->persist($link);
|
|
}
|
|
|
|
$em->flush();
|
|
|
|
return true;
|
|
}
|
|
|
|
public function findByTitleAndParentResourceNode(string $title, int $parentResourceNodeId): ?AbstractResource
|
|
{
|
|
return $this->createQueryBuilder('d')
|
|
->innerJoin('d.resourceNode', 'node')
|
|
->andWhere('d.title = :title')
|
|
->andWhere('node.parent = :parentResourceNodeId')
|
|
->setParameter('title', $title)
|
|
->setParameter('parentResourceNodeId', $parentResourceNodeId)
|
|
->setMaxResults(1)
|
|
->getQuery()
|
|
->getOneOrNullResult()
|
|
;
|
|
}
|
|
|
|
public function findResourceByTitleInCourse(
|
|
string $title,
|
|
Course $course,
|
|
?Session $session = null,
|
|
?CGroup $group = null
|
|
): ?ResourceInterface {
|
|
$qb = $this->getResourcesByCourse($course, $session, $group);
|
|
|
|
$this->addTitleQueryBuilder($title, $qb);
|
|
|
|
$qb->setMaxResults(1);
|
|
|
|
return $qb->getQuery()->getOneOrNullResult();
|
|
}
|
|
}
|
|
|