Passed
Push — master ( 843deb...2912c8 )
by Angel Fernando Quiroz
08:56
created

ResourceRepository::getResourcesByLinkedUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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