Passed
Push — master ( 1c618d...c1c6b0 )
by Yannick
10:36 queued 02:52
created

ResourceRepository::addVisibilityQueryBuilder()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 12
nop 3
dl 0
loc 31
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Repository;
8
9
use Chamilo\CoreBundle\Component\Utils\CreateUploadedFile;
10
use Chamilo\CoreBundle\Entity\AbstractResource;
11
use Chamilo\CoreBundle\Entity\Course;
12
use Chamilo\CoreBundle\Entity\ResourceFile;
13
use Chamilo\CoreBundle\Entity\ResourceInterface;
14
use Chamilo\CoreBundle\Entity\ResourceLink;
15
use Chamilo\CoreBundle\Entity\ResourceNode;
16
use Chamilo\CoreBundle\Entity\ResourceRight;
17
use Chamilo\CoreBundle\Entity\ResourceShowCourseResourcesInSessionInterface;
18
use Chamilo\CoreBundle\Entity\ResourceType;
19
use Chamilo\CoreBundle\Entity\Session;
20
use Chamilo\CoreBundle\Entity\User;
21
use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter;
22
use Chamilo\CoreBundle\Traits\NonResourceRepository;
23
use Chamilo\CoreBundle\Traits\Repository\RepositoryQueryBuilderTrait;
24
use Chamilo\CourseBundle\Entity\CDocument;
25
use Chamilo\CourseBundle\Entity\CGroup;
26
use DateTime;
27
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
28
use Doctrine\Common\Collections\ArrayCollection;
29
use Doctrine\DBAL\Types\Types;
30
use Doctrine\ORM\EntityRepository;
31
use Doctrine\ORM\QueryBuilder;
32
use Exception;
33
use LogicException;
34
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
35
use Symfony\Component\HttpFoundation\File\UploadedFile;
36
use Throwable;
37
38
use const PATHINFO_EXTENSION;
39
40
/**
41
 * Extends Resource EntityRepository.
42
 */
43
abstract class ResourceRepository extends ServiceEntityRepository
44
{
45
    use NonResourceRepository;
46
    use RepositoryQueryBuilderTrait;
47
48
    protected ?ResourceType $resourceType = null;
49
50
    public function getCount(QueryBuilder $qb): int
51
    {
52
        $qb
53
            ->select('count(resource)')
54
            ->setMaxResults(1)
55
            ->setFirstResult(null)
56
        ;
57
58
        return (int) $qb->getQuery()->getSingleScalarResult();
59
    }
60
61
    public function getResourceByResourceNode(ResourceNode $resourceNode): ?ResourceInterface
62
    {
63
        return $this->findOneBy([
64
            'resourceNode' => $resourceNode,
65
        ]);
66
    }
67
68
    public function create(AbstractResource $resource): void
69
    {
70
        $this->getEntityManager()->persist($resource);
71
        $this->getEntityManager()->flush();
72
    }
73
74
    public function update(AbstractResource|User $resource, bool $andFlush = true): void
75
    {
76
        if (!$resource->hasResourceNode()) {
77
            throw new Exception('Resource needs a resource node');
78
        }
79
80
        $em = $this->getEntityManager();
81
82
        $resource->getResourceNode()->setUpdatedAt(new DateTime());
83
        $resource->getResourceNode()->setTitle($resource->getResourceName());
84
        $em->persist($resource);
85
86
        if ($andFlush) {
87
            $em->flush();
88
        }
89
    }
90
91
    public function updateNodeForResource(ResourceInterface $resource): ResourceNode
92
    {
93
        $em = $this->getEntityManager();
94
95
        $resourceNode = $resource->getResourceNode();
96
        $resourceName = $resource->getResourceName();
97
98
        if ($resourceNode->hasResourceFile()) {
99
            $resourceFile = $resourceNode->getResourceFile();
100
            if (null !== $resourceFile) {
101
                $originalName = $resourceFile->getOriginalName();
102
                $originalExtension = pathinfo($originalName, PATHINFO_EXTENSION);
103
104
                // $originalBasename = \basename($resourceName, $originalExtension);
105
                /*$slug = sprintf(
106
                    '%s.%s',
107
                    $this->slugify->slugify($originalBasename),
108
                    $this->slugify->slugify($originalExtension)
109
                );*/
110
111
                $newOriginalName = sprintf('%s.%s', $resourceName, $originalExtension);
112
                $resourceFile->setOriginalName($newOriginalName);
113
114
                $em->persist($resourceFile);
115
            }
116
        }
117
        // $slug = $this->slugify->slugify($resourceName);
118
119
        $resourceNode->setTitle($resourceName);
120
        // $resourceNode->setSlug($slug);
121
122
        $em->persist($resourceNode);
123
        $em->persist($resource);
124
125
        $em->flush();
126
127
        return $resourceNode;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $resourceNode could return the type null which is incompatible with the type-hinted return Chamilo\CoreBundle\Entity\ResourceNode. Consider adding an additional type-check to rule them out.
Loading history...
128
    }
129
130
    public function findCourseResourceByTitle(
131
        string $title,
132
        ResourceNode $parentNode,
133
        Course $course,
134
        ?Session $session = null,
135
        ?CGroup $group = null
136
    ): ?ResourceInterface {
137
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
138
        $this->addTitleQueryBuilder($title, $qb);
139
        $qb->setMaxResults(1);
140
141
        return $qb->getQuery()->getOneOrNullResult();
142
    }
143
144
    public function findCourseResourceBySlug(
145
        string $title,
146
        ResourceNode $parentNode,
147
        Course $course,
148
        ?Session $session = null,
149
        ?CGroup $group = null
150
    ): ?ResourceInterface {
151
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
152
        $this->addSlugQueryBuilder($title, $qb);
153
        $qb->setMaxResults(1);
154
155
        return $qb->getQuery()->getOneOrNullResult();
156
    }
157
158
    /**
159
     * Find resources ignoring the visibility.
160
     */
161
    public function findCourseResourceBySlugIgnoreVisibility(
162
        string $title,
163
        ResourceNode $parentNode,
164
        Course $course,
165
        ?Session $session = null,
166
        ?CGroup $group = null
167
    ): ?ResourceInterface {
168
        $qb = $this->getResourcesByCourseIgnoreVisibility($course, $session, $group, $parentNode);
169
        $this->addSlugQueryBuilder($title, $qb);
170
        $qb->setMaxResults(1);
171
172
        return $qb->getQuery()->getOneOrNullResult();
173
    }
174
175
    /**
176
     * @return ResourceInterface[]
177
     */
178
    public function findCourseResourcesByTitle(
179
        string $title,
180
        ResourceNode $parentNode,
181
        Course $course,
182
        ?Session $session = null,
183
        ?CGroup $group = null
184
    ) {
185
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
186
        $this->addTitleQueryBuilder($title, $qb);
187
188
        return $qb->getQuery()->getResult();
189
    }
190
191
    /**
192
     * @todo clean path
193
     */
194
    public function addFileFromPath(ResourceInterface $resource, string $fileName, string $path, bool $flush = true): ?ResourceFile
195
    {
196
        if (!empty($path) && file_exists($path) && !is_dir($path)) {
197
            $mimeType = mime_content_type($path);
198
            $file = new UploadedFile($path, $fileName, $mimeType, null, true);
199
200
            return $this->addFile($resource, $file, '', $flush);
201
        }
202
203
        return null;
204
    }
205
206
    public function addFileFromString(ResourceInterface $resource, string $fileName, string $mimeType, string $content, bool $flush = true): ?ResourceFile
207
    {
208
        $file = CreateUploadedFile::fromString($fileName, $mimeType, $content);
209
210
        return $this->addFile($resource, $file, '', $flush);
211
    }
212
213
    public function addFileFromFileRequest(ResourceInterface $resource, string $fileKey, bool $flush = true): ?ResourceFile
214
    {
215
        $request = $this->getRequest();
216
        if ($request->files->has($fileKey)) {
217
            $file = $request->files->get($fileKey);
218
            if (null !== $file) {
219
                $resourceFile = $this->addFile($resource, $file);
220
                if ($flush) {
221
                    $this->getEntityManager()->flush();
222
                }
223
224
                return $resourceFile;
225
            }
226
        }
227
228
        return null;
229
    }
230
231
    public function addFile(ResourceInterface $resource, UploadedFile $file, string $description = '', bool $flush = false): ?ResourceFile
232
    {
233
        $resourceNode = $resource->getResourceNode();
234
235
        if (null === $resourceNode) {
236
            throw new LogicException('Resource node is null');
237
        }
238
239
        $resourceFile = $resourceNode->getResourceFile();
240
        if (null === $resourceFile) {
241
            $resourceFile = new ResourceFile();
242
        }
243
244
        $em = $this->getEntityManager();
245
        $resourceFile
246
            ->setFile($file)
247
            ->setDescription($description)
248
            ->setTitle($resource->getResourceName())
249
            ->setResourceNode($resourceNode)
250
        ;
251
        $em->persist($resourceFile);
252
        $resourceNode->setResourceFile($resourceFile);
253
        $em->persist($resourceNode);
254
255
        if ($flush) {
256
            $em->flush();
257
        }
258
259
        return $resourceFile;
260
    }
261
262
    public function getResourceType(): ResourceType
263
    {
264
        $resourceTypeName = $this->toolChain->getResourceTypeNameByEntity($this->getClassName());
0 ignored issues
show
Bug introduced by
The method getResourceTypeNameByEntity() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

264
        /** @scrutinizer ignore-call */ 
265
        $resourceTypeName = $this->toolChain->getResourceTypeNameByEntity($this->getClassName());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
265
        $repo = $this->getEntityManager()->getRepository(ResourceType::class);
266
267
        return $repo->findOneBy([
268
            'title' => $resourceTypeName,
269
        ]);
270
    }
271
272
    public function addVisibilityQueryBuilder(?QueryBuilder $qb = null, bool $checkStudentView = false, bool $displayOnlyPublished = true): QueryBuilder
273
    {
274
        $qb = $this->getOrCreateQueryBuilder($qb);
275
276
        // TODO Avoid global assumption for a request, and inject
277
        // the request stack instead.
278
        $request = $this->getRequest();
279
        $sessionStudentView = null;
280
        if (null !== $request) {
281
            $sessionStudentView = $request->getSession()->get('studentview');
282
        }
283
284
        $checker = $this->getAuthorizationChecker();
285
        $isAdminOrTeacher =
286
            $checker->isGranted('ROLE_ADMIN')
287
            || $checker->isGranted('ROLE_CURRENT_COURSE_TEACHER');
288
289
        if ($displayOnlyPublished) {
290
            if (!$isAdminOrTeacher
291
                || ($checkStudentView && 'studentview' === $sessionStudentView)
292
            ) {
293
                $qb
294
                    ->andWhere('links.visibility = :visibility')
295
                    ->setParameter('visibility', ResourceLink::VISIBILITY_PUBLISHED, Types::INTEGER)
296
                ;
297
            }
298
        }
299
300
        // @todo Add start/end visibility restrictions.
301
302
        return $qb;
303
    }
304
305
    public function addCourseQueryBuilder(Course $course, QueryBuilder $qb): QueryBuilder
306
    {
307
        $qb
308
            ->andWhere('links.course = :course')
309
            ->setParameter('course', $course)
310
        ;
311
312
        return $qb;
313
    }
314
315
    public function addCourseSessionGroupQueryBuilder(Course $course, ?Session $session = null, ?CGroup $group = null, ?QueryBuilder $qb = null): QueryBuilder
316
    {
317
        $reflectionClass = $this->getClassMetadata()->getReflectionClass();
318
319
        // Check if this resource type requires to load the base course resources when using a session
320
        $loadBaseSessionContent = \in_array(
321
            ResourceShowCourseResourcesInSessionInterface::class,
322
            $reflectionClass->getInterfaceNames(),
323
            true
324
        );
325
326
        $this->addCourseQueryBuilder($course, $qb);
327
328
        if (null === $session) {
329
            $qb->andWhere(
330
                $qb->expr()->orX(
0 ignored issues
show
Bug introduced by
The method expr() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

330
                $qb->/** @scrutinizer ignore-call */ 
331
                     expr()->orX(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
331
                    $qb->expr()->isNull('links.session'),
332
                    $qb->expr()->eq('links.session', 0)
333
                )
334
            );
335
        } elseif ($loadBaseSessionContent) {
336
            // Load course base content.
337
            $qb->andWhere('links.session = :session OR links.session IS NULL');
338
            $qb->setParameter('session', $session);
339
        } else {
340
            // Load only session resources.
341
            $qb->andWhere('links.session = :session');
342
            $qb->setParameter('session', $session);
343
        }
344
345
        if (null === $group) {
346
            $qb->andWhere(
347
                $qb->expr()->orX(
348
                    $qb->expr()->isNull('links.group'),
349
                    $qb->expr()->eq('links.group', 0)
350
                )
351
            );
352
        } else {
353
            $qb->andWhere('links.group = :group');
354
            $qb->setParameter('group', $group);
355
        }
356
357
        return $qb;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $qb could return the type null which is incompatible with the type-hinted return Doctrine\ORM\QueryBuilder. Consider adding an additional type-check to rule them out.
Loading history...
358
    }
359
360
    public function getResourceTypeName(): string
361
    {
362
        return $this->toolChain->getResourceTypeNameByEntity($this->getClassName());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->toolChain-...($this->getClassName()) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
363
    }
364
365
    public function getResources(?ResourceNode $parentNode = null): QueryBuilder
366
    {
367
        $resourceTypeName = $this->getResourceTypeName();
368
369
        $qb = $this->createQueryBuilder('resource')
370
            ->select('resource')
371
            ->innerJoin('resource.resourceNode', 'node')
372
            ->innerJoin('node.resourceLinks', 'links')
373
            ->innerJoin('node.resourceType', 'type')
374
            ->leftJoin('node.resourceFile', 'file')
375
            ->where('type.title = :type')
376
            ->setParameter('type', $resourceTypeName, Types::STRING)
377
            ->addSelect('node')
378
            ->addSelect('links')
379
            ->addSelect('type')
380
            ->addSelect('file')
381
        ;
382
383
        if (null !== $parentNode) {
384
            $qb->andWhere('node.parent = :parentNode');
385
            $qb->setParameter('parentNode', $parentNode);
386
        }
387
388
        return $qb;
389
    }
390
391
    public function getResourcesByCourse(Course $course, ?Session $session = null, ?CGroup $group = null, ?ResourceNode $parentNode = null, bool $displayOnlyPublished = true, bool $displayOrder = false): QueryBuilder
392
    {
393
        $qb = $this->getResources($parentNode);
394
        $this->addVisibilityQueryBuilder($qb, true, $displayOnlyPublished);
395
        $this->addCourseSessionGroupQueryBuilder($course, $session, $group, $qb);
396
397
        if ($displayOrder) {
398
            $qb->orderBy('links.displayOrder', 'ASC');
399
        }
400
401
        return $qb;
402
    }
403
404
    public function getResourcesByCourseIgnoreVisibility(Course $course, ?Session $session = null, ?CGroup $group = null, ?ResourceNode $parentNode = null): QueryBuilder
405
    {
406
        $qb = $this->getResources($parentNode);
407
        $this->addCourseSessionGroupQueryBuilder($course, $session, $group, $qb);
408
409
        return $qb;
410
    }
411
412
    /**
413
     * Get resources only from the base course.
414
     */
415
    public function getResourcesByCourseOnly(Course $course, ?ResourceNode $parentNode = null): QueryBuilder
416
    {
417
        $qb = $this->getResources($parentNode);
418
        $this->addCourseQueryBuilder($course, $qb);
419
        $this->addVisibilityQueryBuilder($qb);
420
421
        $qb->andWhere('links.session IS NULL');
422
423
        return $qb;
424
    }
425
426
    public function getResourceByCreatorFromTitle(
427
        string $title,
428
        User $user,
429
        ResourceNode $parentNode
430
    ): ?ResourceInterface {
431
        $qb = $this->getResourcesByCreator($user, $parentNode);
432
        $this->addTitleQueryBuilder($title, $qb);
433
        $qb->setMaxResults(1);
434
435
        return $qb->getQuery()->getOneOrNullResult();
436
    }
437
438
    public function getResourcesByCreator(User $user, ?ResourceNode $parentNode = null): QueryBuilder
439
    {
440
        $qb = $this->createQueryBuilder('resource')
441
            ->select('resource')
442
            ->innerJoin('resource.resourceNode', 'node')
443
        ;
444
445
        if (null !== $parentNode) {
446
            $qb->andWhere('node.parent = :parentNode');
447
            $qb->setParameter('parentNode', $parentNode);
448
        }
449
450
        $this->addCreatorQueryBuilder($user, $qb);
451
452
        return $qb;
453
    }
454
455
    public function getResourcesByCourseLinkedToUser(
456
        User $user,
457
        Course $course,
458
        ?Session $session = null,
459
        ?CGroup $group = null,
460
        ?ResourceNode $parentNode = null
461
    ): QueryBuilder {
462
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
463
        $qb->andWhere('node.creator = :user OR (links.user = :user OR links.user IS NULL)');
464
        $qb->setParameter('user', $user);
465
466
        return $qb;
467
    }
468
469
    public function getResourcesByLinkedUser(User $user, ?ResourceNode $parentNode = null): QueryBuilder
470
    {
471
        $qb = $this->getResources($parentNode);
472
        $qb
473
            ->andWhere('links.user = :user')
474
            ->setParameter('user', $user)
475
        ;
476
477
        $this->addVisibilityQueryBuilder($qb);
478
479
        return $qb;
480
    }
481
482
    public function getResourceFromResourceNode(int $resourceNodeId): ?ResourceInterface
483
    {
484
        $qb = $this->createQueryBuilder('resource')
485
            ->select('resource')
486
            ->addSelect('node')
487
            ->addSelect('links')
488
            ->innerJoin('resource.resourceNode', 'node')
489
        //    ->innerJoin('node.creator', 'userCreator')
490
            ->leftJoin('node.resourceLinks', 'links')
491
//            ->leftJoin('node.resourceFile', 'file')
492
            ->where('node.id = :id')
493
            ->setParameters([
494
                'id' => $resourceNodeId,
495
            ])
496
        ;
497
498
        return $qb->getQuery()->getOneOrNullResult();
499
    }
500
501
    public function delete(ResourceInterface $resource): void
502
    {
503
        $em = $this->getEntityManager();
504
        $children = $resource->getResourceNode()->getChildren();
505
        foreach ($children as $child) {
506
            if ($child->hasResourceFile()) {
507
                $em->remove($child->getResourceFile());
508
            }
509
            $resourceNode = $this->getResourceFromResourceNode($child->getId());
510
            if (null !== $resourceNode) {
511
                $this->delete($resourceNode);
512
            }
513
        }
514
515
        $em->remove($resource);
516
        $em->flush();
517
    }
518
519
    /**
520
     * Deletes several entities: AbstractResource (Ex: CDocument, CQuiz), ResourceNode,
521
     * ResourceLinks and ResourceFile (including files via Flysystem).
522
     */
523
    public function hardDelete(AbstractResource $resource): void
524
    {
525
        $em = $this->getEntityManager();
526
        $em->remove($resource);
527
        $em->flush();
528
    }
529
530
    public function getResourceFileContent(AbstractResource $resource): string
531
    {
532
        try {
533
            $resourceNode = $resource->getResourceNode();
534
535
            return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
0 ignored issues
show
Bug introduced by
The method getResourceNodeFileContent() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

535
            return $this->resourceNodeRepository->/** @scrutinizer ignore-call */ getResourceNodeFileContent($resourceNode);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
536
        } catch (Throwable $throwable) {
537
            throw new FileNotFoundException($resource->getResourceName());
538
        }
539
    }
540
541
    public function getResourceNodeFileContent(ResourceNode $resourceNode): string
542
    {
543
        return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
544
    }
545
546
    /**
547
     * @return false|resource
548
     */
549
    public function getResourceNodeFileStream(ResourceNode $resourceNode)
550
    {
551
        return $this->resourceNodeRepository->getResourceNodeFileStream($resourceNode);
552
    }
553
554
    public function getResourceFileDownloadUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
555
    {
556
        $extraParams['mode'] = 'download';
557
558
        return $this->getResourceFileUrl($resource, $extraParams, $referenceType);
559
    }
560
561
    public function getResourceFileUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
562
    {
563
        return $this->getResourceNodeRepository()->getResourceFileUrl(
564
            $resource->getResourceNode(),
565
            $extraParams,
566
            $referenceType
567
        );
568
    }
569
570
    public function updateResourceFileContent(AbstractResource $resource, string $content): bool
571
    {
572
        $resourceNode = $resource->getResourceNode();
573
        if ($resourceNode->hasResourceFile()) {
574
            $resourceNode->setContent($content);
575
            $resourceNode->getResourceFile()->setSize(\strlen($content));
576
577
            return true;
578
        }
579
580
        return false;
581
    }
582
583
    public function setResourceName(AbstractResource $resource, $title): void
584
    {
585
        if (!empty($title)) {
586
            $resource->setResourceName($title);
587
            $resourceNode = $resource->getResourceNode();
588
            $resourceNode->setTitle($title);
589
        }
590
591
        // if ($resourceNode->hasResourceFile()) {
592
        // $resourceNode->getResourceFile()->getFile()->
593
        // $resourceNode->getResourceFile()->setTitle($title);
594
        // $resourceFile->setTitle($title);
595
596
        /*$fileName = $this->getResourceNodeRepository()->getFilename($resourceFile);
597
        error_log('$fileName');
598
        error_log($fileName);
599
        error_log($title);
600
        $this->getResourceNodeRepository()->getFileSystem()->rename($fileName, $title);
601
        $resourceFile->setTitle($title);
602
        $resourceFile->setOriginalName($title);*/
603
        // }
604
    }
605
606
    public function toggleVisibilityPublishedDraft(AbstractResource $resource): void
607
    {
608
        $firstLink = $resource->getFirstResourceLink();
609
610
        if (ResourceLink::VISIBILITY_PUBLISHED === $firstLink->getVisibility()) {
611
            $this->setVisibilityDraft($resource);
612
613
            return;
614
        }
615
616
        if (ResourceLink::VISIBILITY_DRAFT === $firstLink->getVisibility()) {
617
            $this->setVisibilityPublished($resource);
618
        }
619
    }
620
621
    public function setVisibilityPublished(AbstractResource $resource): void
622
    {
623
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PUBLISHED);
624
    }
625
626
    public function setVisibilityDraft(AbstractResource $resource): void
627
    {
628
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_DRAFT);
629
    }
630
631
    public function setVisibilityPending(AbstractResource $resource): void
632
    {
633
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PENDING);
634
    }
635
636
    public function addResourceNode(ResourceInterface $resource, User $creator, ResourceInterface $parentResource): ResourceNode
637
    {
638
        $parentResourceNode = $parentResource->getResourceNode();
639
640
        return $this->createNodeForResource($resource, $creator, $parentResourceNode);
641
    }
642
643
    /**
644
     * @todo remove this function and merge it with addResourceNode()
645
     */
646
    public function createNodeForResource(ResourceInterface $resource, User $creator, ResourceNode $parentNode, ?UploadedFile $file = null): ResourceNode
647
    {
648
        $em = $this->getEntityManager();
649
650
        $resourceType = $this->getResourceType();
651
        $resourceName = $resource->getResourceName();
652
        $extension = $this->slugify->slugify(pathinfo($resourceName, PATHINFO_EXTENSION));
0 ignored issues
show
Bug introduced by
The method slugify() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

652
        /** @scrutinizer ignore-call */ 
653
        $extension = $this->slugify->slugify(pathinfo($resourceName, PATHINFO_EXTENSION));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
653
654
        if (empty($extension)) {
655
            $slug = $this->slugify->slugify($resourceName);
656
        } else {
657
            $originalExtension = pathinfo($resourceName, PATHINFO_EXTENSION);
658
            $originalBasename = basename($resourceName, $originalExtension);
659
            $slug = sprintf('%s.%s', $this->slugify->slugify($originalBasename), $originalExtension);
660
        }
661
662
        $resourceNode = new ResourceNode();
663
        $resourceNode
664
            ->setTitle($resourceName)
665
            ->setSlug($slug)
666
            ->setResourceType($resourceType)
667
        ;
668
669
        $creator->addResourceNode($resourceNode);
670
671
        $parentNode?->addChild($resourceNode);
672
673
        $resource->setResourceNode($resourceNode);
674
        $em->persist($resourceNode);
675
        $em->persist($resource);
676
677
        if (null !== $file) {
678
            $this->addFile($resource, $file);
679
        }
680
681
        return $resourceNode;
682
    }
683
684
    /**
685
     * This is only used during installation for the special nodes (admin and AccessUrl).
686
     */
687
    public function createNodeForResourceWithNoParent(ResourceInterface $resource, User $creator): ResourceNode
688
    {
689
        $em = $this->getEntityManager();
690
691
        $resourceType = $this->getResourceType();
692
        $resourceName = $resource->getResourceName();
693
        $slug = $this->slugify->slugify($resourceName);
694
        $resourceNode = new ResourceNode();
695
        $resourceNode
696
            ->setTitle($resourceName)
697
            ->setSlug($slug)
698
            ->setCreator($creator)
699
            ->setResourceType($resourceType)
700
        ;
701
        $resource->setResourceNode($resourceNode);
702
        $em->persist($resourceNode);
703
        $em->persist($resource);
704
705
        return $resourceNode;
706
    }
707
708
    public function getTotalSpaceByCourse(Course $course, ?CGroup $group = null, ?Session $session = null): int
709
    {
710
        $qb = $this->createQueryBuilder('resource');
711
        $qb
712
            ->select('SUM(file.size) as total')
713
            ->innerJoin('resource.resourceNode', 'node')
714
            ->innerJoin('node.resourceLinks', 'l')
715
            ->innerJoin('node.resourceFile', 'file')
716
            ->where('l.course = :course')
717
            ->andWhere('file IS NOT NULL')
718
            ->setParameters(
719
                [
720
                    'course' => $course,
721
                ]
722
            )
723
        ;
724
725
        if (null === $group) {
726
            $qb->andWhere('l.group IS NULL');
727
        } else {
728
            $qb
729
                ->andWhere('l.group = :group')
730
                ->setParameter('group', $group)
731
            ;
732
        }
733
734
        if (null === $session) {
735
            $qb->andWhere('l.session IS NULL');
736
        } else {
737
            $qb
738
                ->andWhere('l.session = :session')
739
                ->setParameter('session', $session)
740
            ;
741
        }
742
743
        $query = $qb->getQuery();
744
745
        return (int) $query->getSingleScalarResult();
746
    }
747
748
    public function addTitleDecoration(AbstractResource $resource, Course $course, ?Session $session = null): string
749
    {
750
        if (null === $session) {
751
            return '';
752
        }
753
754
        $link = $resource->getFirstResourceLinkFromCourseSession($course, $session);
755
        if (null === $link) {
756
            return '';
757
        }
758
759
        return '<img title="'.$session->getTitle().'" src="/img/icons/22/star.png" />';
760
    }
761
762
    public function isGranted(string $subject, AbstractResource $resource): bool
763
    {
764
        return $this->getAuthorizationChecker()->isGranted($subject, $resource->getResourceNode());
765
    }
766
767
    /**
768
     * Changes the visibility of the children that matches the exact same link.
769
     */
770
    public function copyVisibilityToChildren(ResourceNode $resourceNode, ResourceLink $link): bool
771
    {
772
        $children = $resourceNode->getChildren();
773
774
        if (0 === $children->count()) {
775
            return false;
776
        }
777
778
        $em = $this->getEntityManager();
779
780
        /** @var ResourceNode $child */
781
        foreach ($children as $child) {
782
            if ($child->getChildren()->count() > 0) {
783
                $this->copyVisibilityToChildren($child, $link);
784
            }
785
786
            $links = $child->getResourceLinks();
787
            foreach ($links as $linkItem) {
788
                if ($linkItem->getUser() === $link->getUser()
789
                    && $linkItem->getSession() === $link->getSession()
790
                    && $linkItem->getCourse() === $link->getCourse()
791
                    && $linkItem->getUserGroup() === $link->getUserGroup()
792
                ) {
793
                    $linkItem->setVisibility($link->getVisibility());
794
                    $em->persist($linkItem);
795
                }
796
            }
797
        }
798
799
        $em->flush();
800
801
        return true;
802
    }
803
804
    protected function addSlugQueryBuilder(?string $slug, ?QueryBuilder $qb = null): QueryBuilder
805
    {
806
        $qb = $this->getOrCreateQueryBuilder($qb);
807
        if (null === $slug) {
808
            return $qb;
809
        }
810
811
        $qb
812
            ->andWhere('node.slug = :slug OR node.slug LIKE :slug2')
813
            ->setParameter('slug', $slug) // normal slug = title
814
            ->setParameter('slug2', $slug.'%-%') // slug with a counter  = title-1
815
        ;
816
817
        return $qb;
818
    }
819
820
    protected function addTitleQueryBuilder(?string $title, ?QueryBuilder $qb = null): QueryBuilder
821
    {
822
        $qb = $this->getOrCreateQueryBuilder($qb);
823
        if (null === $title) {
824
            return $qb;
825
        }
826
827
        $qb
828
            ->andWhere('node.title = :title')
829
            ->setParameter('title', $title)
830
        ;
831
832
        return $qb;
833
    }
834
835
    protected function addCreatorQueryBuilder(?User $user, ?QueryBuilder $qb = null): QueryBuilder
836
    {
837
        $qb = $this->getOrCreateQueryBuilder($qb);
838
        if (null === $user) {
839
            return $qb;
840
        }
841
842
        $qb
843
            ->andWhere('node.creator = :creator')
844
            ->setParameter('creator', $user)
845
        ;
846
847
        return $qb;
848
    }
849
850
    private function setLinkVisibility(AbstractResource $resource, int $visibility, bool $recursive = true): bool
851
    {
852
        $resourceNode = $resource->getResourceNode();
853
854
        if (null === $resourceNode) {
855
            return false;
856
        }
857
858
        $em = $this->getEntityManager();
859
        if ($recursive) {
860
            $children = $resourceNode->getChildren();
861
862
            /** @var ResourceNode $child */
863
            foreach ($children as $child) {
864
                $criteria = [
865
                    'resourceNode' => $child,
866
                ];
867
                $childDocument = $this->findOneBy($criteria);
868
                if ($childDocument) {
869
                    $this->setLinkVisibility($childDocument, $visibility);
870
                }
871
            }
872
        }
873
874
        $links = $resourceNode->getResourceLinks();
875
876
        /** @var ResourceLink $link */
877
        foreach ($links as $link) {
878
            $link->setVisibility($visibility);
879
            if (ResourceLink::VISIBILITY_DRAFT === $visibility) {
880
                $editorMask = ResourceNodeVoter::getEditorMask();
881
                $resourceRight = (new ResourceRight())
882
                    ->setMask($editorMask)
883
                    ->setRole(ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER)
884
                    ->setResourceLink($link)
885
                ;
886
                $link->addResourceRight($resourceRight);
887
            } else {
888
                $link->setResourceRights(new ArrayCollection());
889
            }
890
            $em->persist($link);
891
        }
892
893
        $em->flush();
894
895
        return true;
896
    }
897
898
    public function findByTitleAndParentResourceNode(string $title, int $parentResourceNodeId): ?AbstractResource
899
    {
900
        return $this->createQueryBuilder('d')
901
            ->innerJoin('d.resourceNode', 'node')
902
            ->andWhere('d.title = :title')
903
            ->andWhere('node.parent = :parentResourceNodeId')
904
            ->setParameter('title', $title)
905
            ->setParameter('parentResourceNodeId', $parentResourceNodeId)
906
            ->setMaxResults(1)
907
            ->getQuery()
908
            ->getOneOrNullResult()
909
        ;
910
    }
911
}
912