Passed
Push — master ( 51c6f6...71ef0e )
by Angel Fernando Quiroz
08:51
created

getResourcesByCourseIgnoreVisibility()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

257
        /** @scrutinizer ignore-call */ 
258
        $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...
258
        $repo = $this->getEntityManager()->getRepository(ResourceType::class);
259
260
        return $repo->findOneBy([
261
            'title' => $resourceTypeName,
262
        ]);
263
    }
264
265
    public function addVisibilityQueryBuilder(?QueryBuilder $qb = null, bool $checkStudentView = false, bool $displayOnlyPublished = true): QueryBuilder
266
    {
267
        $qb = $this->getOrCreateQueryBuilder($qb);
268
269
        // TODO Avoid global assumption for a request, and inject
270
        // the request stack instead.
271
        $request = $this->getRequest();
272
        $sessionStudentView = null;
273
        if (null !== $request) {
274
            $sessionStudentView = $request->getSession()->get('studentview');
275
        }
276
277
        $checker = $this->getAuthorizationChecker();
278
        $isAdminOrTeacher =
279
            $checker->isGranted('ROLE_ADMIN')
280
            || $checker->isGranted('ROLE_CURRENT_COURSE_TEACHER');
281
282
        if ($displayOnlyPublished) {
283
            if (!$isAdminOrTeacher
284
                || ($checkStudentView && 'studentview' === $sessionStudentView)
285
            ) {
286
                $qb
287
                    ->andWhere('links.visibility = :visibility')
288
                    ->setParameter('visibility', ResourceLink::VISIBILITY_PUBLISHED, Types::INTEGER)
289
                ;
290
            }
291
        }
292
293
        // @todo Add start/end visibility restrictions.
294
295
        return $qb;
296
    }
297
298
    public function addCourseQueryBuilder(Course $course, QueryBuilder $qb): QueryBuilder
299
    {
300
        $qb
301
            ->andWhere('links.course = :course')
302
            ->setParameter('course', $course)
303
        ;
304
305
        return $qb;
306
    }
307
308
    public function addSessionNullQueryBuilder(QueryBuilder $qb): QueryBuilder
309
    {
310
        return $qb->andWhere(
311
            $qb->expr()->orX(
312
                $qb->expr()->isNull('links.session'),
313
                $qb->expr()->eq('links.session', 0)
314
            )
315
        );
316
    }
317
318
    public function addSessionAndBaseContentQueryBuilder(Session $session, QueryBuilder $qb): QueryBuilder
319
    {
320
        return $qb
321
            ->andWhere('links.session = :session OR links.session IS NULL')
322
            ->setParameter('session', $session)
323
        ;
324
    }
325
326
    public function addSessionOnlyQueryBuilder(Session $session, QueryBuilder $qb): QueryBuilder
327
    {
328
        return $qb
329
            ->andWhere('links.session = :session')
330
            ->setParameter('session', $session)
331
        ;
332
    }
333
334
    public function addCourseSessionGroupQueryBuilder(
335
        ?Course $course = null,
336
        ?Session $session = null,
337
        ?CGroup $group = null,
338
        ?QueryBuilder $qb = null
339
    ): QueryBuilder {
340
        $reflectionClass = $this->getClassMetadata()->getReflectionClass();
341
342
        // Check if this resource type requires to load the base course resources when using a session
343
        $loadBaseSessionContent = \in_array(
344
            ResourceShowCourseResourcesInSessionInterface::class,
345
            $reflectionClass->getInterfaceNames(),
346
            true
347
        );
348
349
        if ($course) {
350
            $this->addCourseQueryBuilder($course, $qb);
351
        }
352
353
        if (null === $session) {
354
            $this->addSessionNullQueryBuilder($qb);
355
        } elseif ($loadBaseSessionContent) {
356
            // Load course base content.
357
            $this->addSessionAndBaseContentQueryBuilder($session, $qb);
358
        } else {
359
            // Load only session resources.
360
            $this->addSessionOnlyQueryBuilder($session, $qb);
361
        }
362
363
        if (null === $group) {
364
            $qb->andWhere(
365
                $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

365
                $qb->/** @scrutinizer ignore-call */ 
366
                     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...
366
                    $qb->expr()->isNull('links.group'),
367
                    $qb->expr()->eq('links.group', 0)
368
                )
369
            );
370
        } else {
371
            $qb->andWhere('links.group = :group');
372
            $qb->setParameter('group', $group);
373
        }
374
375
        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...
376
    }
377
378
    public function getResourceTypeName(): string
379
    {
380
        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...
381
    }
382
383
    public function getResources(?ResourceNode $parentNode = null): QueryBuilder
384
    {
385
        $resourceTypeName = $this->getResourceTypeName();
386
387
        $qb = $this->createQueryBuilder('resource')
388
            ->select('resource')
389
            ->innerJoin('resource.resourceNode', 'node')
390
            ->innerJoin('node.resourceLinks', 'links')
391
            ->innerJoin('node.resourceType', 'type')
392
            ->leftJoin('node.resourceFiles', 'file')
393
            ->where('type.title = :type')
394
            ->setParameter('type', $resourceTypeName, Types::STRING)
395
            ->addSelect('node')
396
            ->addSelect('links')
397
            ->addSelect('type')
398
            ->addSelect('file')
399
        ;
400
401
        if (null !== $parentNode) {
402
            $qb->andWhere('node.parent = :parentNode');
403
            $qb->setParameter('parentNode', $parentNode);
404
        }
405
406
        return $qb;
407
    }
408
409
    public function getResourcesByCourse(
410
        ?Course $course = null,
411
        ?Session $session = null,
412
        ?CGroup $group = null,
413
        ?ResourceNode $parentNode = null,
414
        bool $displayOnlyPublished = true,
415
        bool $displayOrder = false
416
    ): QueryBuilder {
417
        $qb = $this->getResources($parentNode);
418
        $this->addVisibilityQueryBuilder($qb, true, $displayOnlyPublished);
419
        $this->addCourseSessionGroupQueryBuilder($course, $session, $group, $qb);
420
421
        if ($displayOrder) {
422
            $qb->orderBy('links.displayOrder', 'ASC');
423
        }
424
425
        return $qb;
426
    }
427
428
    public function getResourcesBySession(
429
        ?Session $session = null,
430
        ?ResourceNode $parentNode = null,
431
        bool $displayOnlyPublished = true,
432
        bool $displayOrder = false
433
    ): QueryBuilder {
434
        $qb = $this->getResources($parentNode);
435
        $this->addVisibilityQueryBuilder($qb, true, $displayOnlyPublished);
436
        $this->addCourseSessionGroupQueryBuilder(null, $session, null, $qb);
437
438
        if ($displayOrder) {
439
            $qb->orderBy('links.displayOrder', 'ASC');
440
        }
441
442
        return $qb;
443
    }
444
445
    public function getResourcesByCourseIgnoreVisibility(Course $course, ?Session $session = null, ?CGroup $group = null, ?ResourceNode $parentNode = null): QueryBuilder
446
    {
447
        $qb = $this->getResources($parentNode);
448
        $this->addCourseSessionGroupQueryBuilder($course, $session, $group, $qb);
449
450
        return $qb;
451
    }
452
453
    /**
454
     * Get resources only from the base course.
455
     */
456
    public function getResourcesByCourseOnly(Course $course, ?ResourceNode $parentNode = null): QueryBuilder
457
    {
458
        $qb = $this->getResources($parentNode);
459
        $this->addCourseQueryBuilder($course, $qb);
460
        $this->addVisibilityQueryBuilder($qb);
461
462
        $qb->andWhere('links.session IS NULL');
463
464
        return $qb;
465
    }
466
467
    public function getResourceByCreatorFromTitle(
468
        string $title,
469
        User $user,
470
        ResourceNode $parentNode
471
    ): ?ResourceInterface {
472
        $qb = $this->getResourcesByCreator($user, $parentNode);
473
        $this->addTitleQueryBuilder($title, $qb);
474
        $qb->setMaxResults(1);
475
476
        return $qb->getQuery()->getOneOrNullResult();
477
    }
478
479
    public function getResourcesByCreator(User $user, ?ResourceNode $parentNode = null): QueryBuilder
480
    {
481
        $qb = $this->createQueryBuilder('resource')
482
            ->select('resource')
483
            ->innerJoin('resource.resourceNode', 'node')
484
        ;
485
486
        if (null !== $parentNode) {
487
            $qb->andWhere('node.parent = :parentNode');
488
            $qb->setParameter('parentNode', $parentNode);
489
        }
490
491
        $this->addCreatorQueryBuilder($user, $qb);
492
493
        return $qb;
494
    }
495
496
    public function getResourcesByCourseLinkedToUser(
497
        User $user,
498
        Course $course,
499
        ?Session $session = null,
500
        ?CGroup $group = null,
501
        ?ResourceNode $parentNode = null
502
    ): QueryBuilder {
503
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
504
        $qb->andWhere('node.creator = :user OR (links.user = :user OR links.user IS NULL)');
505
        $qb->setParameter('user', $user);
506
507
        return $qb;
508
    }
509
510
    public function getResourcesByLinkedUser(User $user, ?ResourceNode $parentNode = null): QueryBuilder
511
    {
512
        $qb = $this->getResources($parentNode);
513
        $qb
514
            ->andWhere('links.user = :user')
515
            ->setParameter('user', $user)
516
        ;
517
518
        $this->addVisibilityQueryBuilder($qb);
519
520
        return $qb;
521
    }
522
523
    public function getResourceFromResourceNode(int $resourceNodeId): ?ResourceInterface
524
    {
525
        $qb = $this->createQueryBuilder('resource')
526
            ->select('resource')
527
            ->addSelect('node')
528
            ->addSelect('links')
529
            ->innerJoin('resource.resourceNode', 'node')
530
        //    ->innerJoin('node.creator', 'userCreator')
531
            ->leftJoin('node.resourceLinks', 'links')
532
            ->where('node.id = :id')
533
            ->setParameters([
534
                'id' => $resourceNodeId,
535
            ])
536
        ;
537
538
        return $qb->getQuery()->getOneOrNullResult();
539
    }
540
541
    public function delete(ResourceInterface $resource): void
542
    {
543
        $em = $this->getEntityManager();
544
        $children = $resource->getResourceNode()->getChildren();
545
        foreach ($children as $child) {
546
            foreach ($child->getResourceFiles() as $resourceFile) {
547
                $em->remove($resourceFile);
548
            }
549
            $resourceNode = $this->getResourceFromResourceNode($child->getId());
550
            if (null !== $resourceNode) {
551
                $this->delete($resourceNode);
552
            }
553
        }
554
555
        $em->remove($resource);
556
        $em->flush();
557
    }
558
559
    /**
560
     * Deletes several entities: AbstractResource (Ex: CDocument, CQuiz), ResourceNode,
561
     * ResourceLinks and ResourceFile (including files via Flysystem).
562
     */
563
    public function hardDelete(AbstractResource $resource): void
564
    {
565
        $em = $this->getEntityManager();
566
        $em->remove($resource);
567
        $em->flush();
568
    }
569
570
    public function getResourceFileContent(AbstractResource $resource): string
571
    {
572
        try {
573
            $resourceNode = $resource->getResourceNode();
574
575
            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

575
            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...
576
        } catch (Throwable $throwable) {
577
            throw new FileNotFoundException($resource->getResourceName());
578
        }
579
    }
580
581
    public function getResourceNodeFileContent(ResourceNode $resourceNode): string
582
    {
583
        return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
584
    }
585
586
    /**
587
     * @return false|resource
588
     */
589
    public function getResourceNodeFileStream(ResourceNode $resourceNode)
590
    {
591
        return $this->resourceNodeRepository->getResourceNodeFileStream($resourceNode);
592
    }
593
594
    public function getResourceFileDownloadUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
595
    {
596
        $extraParams['mode'] = 'download';
597
598
        return $this->getResourceFileUrl($resource, $extraParams, $referenceType);
599
    }
600
601
    public function getResourceFileUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
602
    {
603
        return $this->getResourceNodeRepository()->getResourceFileUrl(
604
            $resource->getResourceNode(),
605
            $extraParams,
606
            $referenceType
607
        );
608
    }
609
610
    public function updateResourceFileContent(AbstractResource $resource, string $content): bool
611
    {
612
        $resourceNode = $resource->getResourceNode();
613
        if ($resourceNode->hasResourceFile()) {
614
            $resourceNode->setContent($content);
615
            foreach ($resourceNode->getResourceFiles() as $resourceFile) {
616
                $resourceFile->setSize(\strlen($content));
617
            }
618
619
            return true;
620
        }
621
622
        return false;
623
    }
624
625
    public function setResourceName(AbstractResource $resource, $title): void
626
    {
627
        if (!empty($title)) {
628
            $resource->setResourceName($title);
629
            $resourceNode = $resource->getResourceNode();
630
            $resourceNode->setTitle($title);
631
        }
632
    }
633
634
    public function toggleVisibilityPublishedDraft(
635
        AbstractResource $resource,
636
        ?Course $course = null,
637
        ?Session $session = null
638
    ): void {
639
        $firstLink = $resource->getFirstResourceLink();
640
641
        if (ResourceLink::VISIBILITY_PUBLISHED === $firstLink->getVisibility()) {
642
            $this->setVisibilityDraft($resource, $course, $session);
643
644
            return;
645
        }
646
647
        if (ResourceLink::VISIBILITY_DRAFT === $firstLink->getVisibility()) {
648
            $this->setVisibilityPublished($resource, $course, $session);
649
        }
650
    }
651
652
    public function setVisibilityPublished(
653
        AbstractResource $resource,
654
        ?Course $course = null,
655
        ?Session $session = null,
656
    ): void {
657
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PUBLISHED, true, $course, $session);
658
    }
659
660
    public function setVisibilityDraft(
661
        AbstractResource $resource,
662
        ?Course $course = null,
663
        ?Session $session = null,
664
    ): void {
665
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_DRAFT, true, $course, $session);
666
    }
667
668
    public function setVisibilityPending(
669
        AbstractResource $resource,
670
        ?Course $course = null,
671
        ?Session $session = null,
672
    ): void {
673
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PENDING, true, $course, $session);
674
    }
675
676
    public function addResourceNode(
677
        ResourceInterface $resource,
678
        User $creator,
679
        ResourceInterface $parentResource,
680
        ?ResourceType $resourceType = null,
681
    ): ResourceNode {
682
        $parentResourceNode = $parentResource->getResourceNode();
683
684
        return $this->createNodeForResource(
685
            $resource,
686
            $creator,
687
            $parentResourceNode,
688
            null,
689
            $resourceType,
690
        );
691
    }
692
693
    /**
694
     * @todo remove this function and merge it with addResourceNode()
695
     */
696
    public function createNodeForResource(
697
        ResourceInterface $resource,
698
        User $creator,
699
        ResourceNode $parentNode,
700
        ?UploadedFile $file = null,
701
        ?ResourceType $resourceType = null,
702
    ): ResourceNode {
703
        $em = $this->getEntityManager();
704
705
        $resourceType = $resourceType ?: $this->getResourceType();
706
        $resourceName = $resource->getResourceName();
707
        $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

707
        /** @scrutinizer ignore-call */ 
708
        $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...
708
709
        if (empty($extension)) {
710
            $slug = $this->slugify->slugify($resourceName);
711
        } else {
712
            $originalExtension = pathinfo($resourceName, PATHINFO_EXTENSION);
713
            $originalBasename = basename($resourceName, $originalExtension);
714
            $slug = \sprintf('%s.%s', $this->slugify->slugify($originalBasename), $originalExtension);
715
        }
716
717
        $resourceNode = new ResourceNode();
718
        $resourceNode
719
            ->setTitle($resourceName)
720
            ->setSlug($slug)
721
            ->setResourceType($resourceType)
722
        ;
723
724
        $creator->addResourceNode($resourceNode);
725
726
        $parentNode?->addChild($resourceNode);
727
728
        $resource->setResourceNode($resourceNode);
729
        $em->persist($resourceNode);
730
        $em->persist($resource);
731
732
        if (null !== $file) {
733
            $this->addFile($resource, $file);
734
        }
735
736
        return $resourceNode;
737
    }
738
739
    /**
740
     * This is only used during installation for the special nodes (admin and AccessUrl).
741
     */
742
    public function createNodeForResourceWithNoParent(ResourceInterface $resource, User $creator): ResourceNode
743
    {
744
        $em = $this->getEntityManager();
745
746
        $resourceType = $this->getResourceType();
747
        $resourceName = $resource->getResourceName();
748
        $slug = $this->slugify->slugify($resourceName);
749
        $resourceNode = new ResourceNode();
750
        $resourceNode
751
            ->setTitle($resourceName)
752
            ->setSlug($slug)
753
            ->setCreator($creator)
754
            ->setResourceType($resourceType)
755
        ;
756
        $resource->setResourceNode($resourceNode);
757
        $em->persist($resourceNode);
758
        $em->persist($resource);
759
760
        return $resourceNode;
761
    }
762
763
    public function getTotalSpaceByCourse(Course $course, ?CGroup $group = null, ?Session $session = null): int
764
    {
765
        $qb = $this->createQueryBuilder('resource');
766
        $qb
767
            ->select('SUM(file.size) as total')
768
            ->innerJoin('resource.resourceNode', 'node')
769
            ->innerJoin('node.resourceLinks', 'l')
770
            ->innerJoin('node.resourceFiles', 'file')
771
            ->where('l.course = :course')
772
            ->andWhere('file IS NOT NULL')
773
            ->setParameters(
774
                [
775
                    'course' => $course,
776
                ]
777
            )
778
        ;
779
780
        if (null === $group) {
781
            $qb->andWhere('l.group IS NULL');
782
        } else {
783
            $qb
784
                ->andWhere('l.group = :group')
785
                ->setParameter('group', $group)
786
            ;
787
        }
788
789
        if (null === $session) {
790
            $qb->andWhere('l.session IS NULL');
791
        } else {
792
            $qb
793
                ->andWhere('l.session = :session')
794
                ->setParameter('session', $session)
795
            ;
796
        }
797
798
        $query = $qb->getQuery();
799
800
        return (int) $query->getSingleScalarResult();
801
    }
802
803
    public function addTitleDecoration(AbstractResource $resource, Course $course, ?Session $session = null): string
804
    {
805
        if (null === $session) {
806
            return '';
807
        }
808
809
        $link = $resource->getFirstResourceLinkFromCourseSession($course, $session);
810
        if (null === $link) {
811
            return '';
812
        }
813
814
        return '<img title="'.$session->getTitle().'" src="/img/icons/22/star.png" />';
815
    }
816
817
    public function isGranted(string $subject, AbstractResource $resource): bool
818
    {
819
        return $this->getAuthorizationChecker()->isGranted($subject, $resource->getResourceNode());
820
    }
821
822
    /**
823
     * Changes the visibility of the children that matches the exact same link.
824
     */
825
    public function copyVisibilityToChildren(ResourceNode $resourceNode, ResourceLink $link): bool
826
    {
827
        $children = $resourceNode->getChildren();
828
829
        if (0 === $children->count()) {
830
            return false;
831
        }
832
833
        $em = $this->getEntityManager();
834
835
        /** @var ResourceNode $child */
836
        foreach ($children as $child) {
837
            if ($child->getChildren()->count() > 0) {
838
                $this->copyVisibilityToChildren($child, $link);
839
            }
840
841
            $links = $child->getResourceLinks();
842
            foreach ($links as $linkItem) {
843
                if ($linkItem->getUser() === $link->getUser()
844
                    && $linkItem->getSession() === $link->getSession()
845
                    && $linkItem->getCourse() === $link->getCourse()
846
                    && $linkItem->getUserGroup() === $link->getUserGroup()
847
                ) {
848
                    $linkItem->setVisibility($link->getVisibility());
849
                    $em->persist($linkItem);
850
                }
851
            }
852
        }
853
854
        $em->flush();
855
856
        return true;
857
    }
858
859
    protected function addSlugQueryBuilder(?string $slug, ?QueryBuilder $qb = null): QueryBuilder
860
    {
861
        $qb = $this->getOrCreateQueryBuilder($qb);
862
        if (null === $slug) {
863
            return $qb;
864
        }
865
866
        $qb
867
            ->andWhere('node.slug = :slug OR node.slug LIKE :slug2')
868
            ->setParameter('slug', $slug) // normal slug = title
869
            ->setParameter('slug2', $slug.'%-%') // slug with a counter  = title-1
870
        ;
871
872
        return $qb;
873
    }
874
875
    protected function addTitleQueryBuilder(?string $title, ?QueryBuilder $qb = null): QueryBuilder
876
    {
877
        $qb = $this->getOrCreateQueryBuilder($qb);
878
        if (null === $title) {
879
            return $qb;
880
        }
881
882
        $qb
883
            ->andWhere('node.title = :title')
884
            ->setParameter('title', $title)
885
        ;
886
887
        return $qb;
888
    }
889
890
    protected function addCreatorQueryBuilder(?User $user, ?QueryBuilder $qb = null): QueryBuilder
891
    {
892
        $qb = $this->getOrCreateQueryBuilder($qb);
893
        if (null === $user) {
894
            return $qb;
895
        }
896
897
        $qb
898
            ->andWhere('node.creator = :creator')
899
            ->setParameter('creator', $user)
900
        ;
901
902
        return $qb;
903
    }
904
905
    private function setLinkVisibility(
906
        AbstractResource $resource,
907
        int $visibility,
908
        bool $recursive = true,
909
        ?Course $course = null,
910
        ?Session $session = null,
911
        ?CGroup $group = null,
912
        ?User $user = null,
913
    ): bool {
914
        $resourceNode = $resource->getResourceNode();
915
916
        if (null === $resourceNode) {
917
            return false;
918
        }
919
920
        $em = $this->getEntityManager();
921
        if ($recursive) {
922
            $children = $resourceNode->getChildren();
923
924
            /** @var ResourceNode $child */
925
            foreach ($children as $child) {
926
                $criteria = [
927
                    'resourceNode' => $child,
928
                ];
929
                $childDocument = $this->findOneBy($criteria);
930
                if ($childDocument) {
931
                    $this->setLinkVisibility($childDocument, $visibility);
932
                }
933
            }
934
        }
935
936
        if ($resource instanceof ResourceShowCourseResourcesInSessionInterface) {
937
            $link = $resource->getFirstResourceLinkFromCourseSession($course, $session);
938
939
            if (!$link) {
940
                $resource->parentResource = $course;
941
                $resource->addCourseLink($course, $session);
942
            }
943
944
            $link = $resource->getFirstResourceLinkFromCourseSession($course, $session);
945
            $links = [$link];
946
        } else {
947
            $links = $resourceNode->getResourceLinks();
948
        }
949
950
        /** @var ResourceLink $link */
951
        foreach ($links as $link) {
952
            $link->setVisibility($visibility);
953
            if (ResourceLink::VISIBILITY_DRAFT === $visibility) {
954
                $editorMask = ResourceNodeVoter::getEditorMask();
955
                $resourceRight = (new ResourceRight())
956
                    ->setMask($editorMask)
957
                    ->setRole(ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER)
958
                    ->setResourceLink($link)
959
                ;
960
                $link->addResourceRight($resourceRight);
961
            } else {
962
                $link->setResourceRights(new ArrayCollection());
963
            }
964
            $em->persist($link);
965
        }
966
967
        $em->flush();
968
969
        return true;
970
    }
971
972
    public function findByTitleAndParentResourceNode(string $title, int $parentResourceNodeId): ?AbstractResource
973
    {
974
        return $this->createQueryBuilder('d')
975
            ->innerJoin('d.resourceNode', 'node')
976
            ->andWhere('d.title = :title')
977
            ->andWhere('node.parent = :parentResourceNodeId')
978
            ->setParameter('title', $title)
979
            ->setParameter('parentResourceNodeId', $parentResourceNodeId)
980
            ->setMaxResults(1)
981
            ->getQuery()
982
            ->getOneOrNullResult()
983
        ;
984
    }
985
986
    public function findResourceByTitleInCourse(
987
        string $title,
988
        Course $course,
989
        ?Session $session = null,
990
        ?CGroup $group = null
991
    ): ?ResourceInterface {
992
        $qb = $this->getResourcesByCourse($course, $session, $group);
993
994
        $this->addTitleQueryBuilder($title, $qb);
995
996
        $qb->setMaxResults(1);
997
998
        return $qb->getQuery()->getOneOrNullResult();
999
    }
1000
}
1001