Passed
Pull Request — master (#5955)
by
unknown
16:11
created

ResourceRepository::addCourseQueryBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 8
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\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
        foreach ($resourceNode->getResourceFiles() as $resourceFile) {
99
            if (null !== $resourceFile) {
100
                $originalName = $resourceFile->getOriginalName();
101
                $originalExtension = pathinfo($originalName, PATHINFO_EXTENSION);
102
103
                // $originalBasename = \basename($resourceName, $originalExtension);
104
                /*$slug = sprintf(
105
                    '%s.%s',
106
                    $this->slugify->slugify($originalBasename),
107
                    $this->slugify->slugify($originalExtension)
108
                );*/
109
110
                $newOriginalName = \sprintf('%s.%s', $resourceName, $originalExtension);
111
                $resourceFile->setOriginalName($newOriginalName);
112
113
                $em->persist($resourceFile);
114
            }
115
        }
116
        // $slug = $this->slugify->slugify($resourceName);
117
118
        $resourceNode->setTitle($resourceName);
119
        // $resourceNode->setSlug($slug);
120
121
        $em->persist($resourceNode);
122
        $em->persist($resource);
123
124
        $em->flush();
125
126
        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...
127
    }
128
129
    public function findCourseResourceByTitle(
130
        string $title,
131
        ResourceNode $parentNode,
132
        Course $course,
133
        ?Session $session = null,
134
        ?CGroup $group = null
135
    ): ?ResourceInterface {
136
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
137
        $this->addTitleQueryBuilder($title, $qb);
138
        $qb->setMaxResults(1);
139
140
        return $qb->getQuery()->getOneOrNullResult();
141
    }
142
143
    public function findCourseResourceBySlug(
144
        string $title,
145
        ResourceNode $parentNode,
146
        Course $course,
147
        ?Session $session = null,
148
        ?CGroup $group = null
149
    ): ?ResourceInterface {
150
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
151
        $this->addSlugQueryBuilder($title, $qb);
152
        $qb->setMaxResults(1);
153
154
        return $qb->getQuery()->getOneOrNullResult();
155
    }
156
157
    /**
158
     * Find resources ignoring the visibility.
159
     */
160
    public function findCourseResourceBySlugIgnoreVisibility(
161
        string $title,
162
        ResourceNode $parentNode,
163
        Course $course,
164
        ?Session $session = null,
165
        ?CGroup $group = null
166
    ): ?ResourceInterface {
167
        $qb = $this->getResourcesByCourseIgnoreVisibility($course, $session, $group, $parentNode);
168
        $this->addSlugQueryBuilder($title, $qb);
169
        $qb->setMaxResults(1);
170
171
        return $qb->getQuery()->getOneOrNullResult();
172
    }
173
174
    /**
175
     * @return ResourceInterface[]
176
     */
177
    public function findCourseResourcesByTitle(
178
        string $title,
179
        ResourceNode $parentNode,
180
        Course $course,
181
        ?Session $session = null,
182
        ?CGroup $group = null
183
    ) {
184
        $qb = $this->getResourcesByCourse($course, $session, $group, $parentNode);
185
        $this->addTitleQueryBuilder($title, $qb);
186
187
        return $qb->getQuery()->getResult();
188
    }
189
190
    /**
191
     * @todo clean path
192
     */
193
    public function addFileFromPath(ResourceInterface $resource, string $fileName, string $path, bool $flush = true): ?ResourceFile
194
    {
195
        if (!empty($path) && file_exists($path) && !is_dir($path)) {
196
            $mimeType = mime_content_type($path);
197
            $file = new UploadedFile($path, $fileName, $mimeType, null, true);
198
199
            return $this->addFile($resource, $file, '', $flush);
200
        }
201
202
        return null;
203
    }
204
205
    public function addFileFromString(ResourceInterface $resource, string $fileName, string $mimeType, string $content, bool $flush = true): ?ResourceFile
206
    {
207
        $file = CreateUploadedFile::fromString($fileName, $mimeType, $content);
208
209
        return $this->addFile($resource, $file, '', $flush);
210
    }
211
212
    public function addFileFromFileRequest(ResourceInterface $resource, string $fileKey, bool $flush = true): ?ResourceFile
213
    {
214
        $request = $this->getRequest();
215
        if ($request->files->has($fileKey)) {
216
            $file = $request->files->get($fileKey);
217
            if (null !== $file) {
218
                $resourceFile = $this->addFile($resource, $file);
219
                if ($flush) {
220
                    $this->getEntityManager()->flush();
221
                }
222
223
                return $resourceFile;
224
            }
225
        }
226
227
        return null;
228
    }
229
230
    public function addFile(ResourceInterface $resource, UploadedFile $file, string $description = '', bool $flush = false): ?ResourceFile
231
    {
232
        $resourceNode = $resource->getResourceNode();
233
234
        if (null === $resourceNode) {
235
            throw new LogicException('Resource node is null');
236
        }
237
238
        $em = $this->getEntityManager();
239
240
        $resourceFile = new ResourceFile();
241
        $resourceFile
242
            ->setFile($file)
243
            ->setDescription($description)
244
            ->setTitle($resource->getResourceName())
245
            ->setResourceNode($resourceNode)
246
        ;
247
        $resourceNode->addResourceFile($resourceFile);
248
        $em->persist($resourceNode);
249
250
        if ($flush) {
251
            $em->flush();
252
        }
253
254
        return $resourceFile;
255
    }
256
257
    public function getResourceType(): ResourceType
258
    {
259
        $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

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

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

529
            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...
530
        } catch (Throwable $throwable) {
531
            throw new FileNotFoundException($resource->getResourceName());
532
        }
533
    }
534
535
    public function getResourceNodeFileContent(ResourceNode $resourceNode): string
536
    {
537
        return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
538
    }
539
540
    /**
541
     * @return false|resource
542
     */
543
    public function getResourceNodeFileStream(ResourceNode $resourceNode)
544
    {
545
        return $this->resourceNodeRepository->getResourceNodeFileStream($resourceNode);
546
    }
547
548
    public function getResourceFileDownloadUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
549
    {
550
        $extraParams['mode'] = 'download';
551
552
        return $this->getResourceFileUrl($resource, $extraParams, $referenceType);
553
    }
554
555
    public function getResourceFileUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
556
    {
557
        return $this->getResourceNodeRepository()->getResourceFileUrl(
558
            $resource->getResourceNode(),
559
            $extraParams,
560
            $referenceType
561
        );
562
    }
563
564
    public function updateResourceFileContent(AbstractResource $resource, string $content): bool
565
    {
566
        $resourceNode = $resource->getResourceNode();
567
        if ($resourceNode->hasResourceFile()) {
568
            $resourceNode->setContent($content);
569
            foreach ($resourceNode->getResourceFiles() as $resourceFile) {
570
                $resourceFile->setSize(\strlen($content));
571
            }
572
573
            return true;
574
        }
575
576
        return false;
577
    }
578
579
    public function setResourceName(AbstractResource $resource, $title): void
580
    {
581
        if (!empty($title)) {
582
            $resource->setResourceName($title);
583
            $resourceNode = $resource->getResourceNode();
584
            $resourceNode->setTitle($title);
585
        }
586
    }
587
588
    public function toggleVisibilityPublishedDraft(AbstractResource $resource): void
589
    {
590
        $firstLink = $resource->getFirstResourceLink();
591
592
        if (ResourceLink::VISIBILITY_PUBLISHED === $firstLink->getVisibility()) {
593
            $this->setVisibilityDraft($resource);
594
595
            return;
596
        }
597
598
        if (ResourceLink::VISIBILITY_DRAFT === $firstLink->getVisibility()) {
599
            $this->setVisibilityPublished($resource);
600
        }
601
    }
602
603
    public function setVisibilityPublished(
604
        AbstractResource $resource,
605
        ?Course $course = null,
606
        ?Session $session = null,
607
    ): void {
608
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PUBLISHED, true, $course, $session);
609
    }
610
611
    public function setVisibilityDraft(
612
        AbstractResource $resource,
613
        ?Course $course = null,
614
        ?Session $session = null,
615
    ): void {
616
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_DRAFT, true, $course, $session);
617
    }
618
619
    public function setVisibilityPending(
620
        AbstractResource $resource,
621
        ?Course $course = null,
622
        ?Session $session = null,
623
    ): void {
624
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PENDING, true, $course, $session);
625
    }
626
627
    public function addResourceNode(
628
        ResourceInterface $resource,
629
        User $creator,
630
        ResourceInterface $parentResource,
631
        ?ResourceType $resourceType = null,
632
    ): ResourceNode {
633
        $parentResourceNode = $parentResource->getResourceNode();
634
635
        return $this->createNodeForResource(
636
            $resource,
637
            $creator,
638
            $parentResourceNode,
639
            null,
640
            $resourceType,
641
        );
642
    }
643
644
    /**
645
     * @todo remove this function and merge it with addResourceNode()
646
     */
647
    public function createNodeForResource(
648
        ResourceInterface $resource,
649
        User $creator,
650
        ResourceNode $parentNode,
651
        ?UploadedFile $file = null,
652
        ?ResourceType $resourceType = null,
653
    ): ResourceNode {
654
        $em = $this->getEntityManager();
655
656
        $resourceType = $resourceType ?: $this->getResourceType();
657
        $resourceName = $resource->getResourceName();
658
        $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

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