Passed
Pull Request — master (#7119)
by
unknown
09:22
created

ResourceRepository::getResourceFileContent()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

584
            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...
585
        } catch (Throwable $throwable) {
586
            // Fallback: editable text content (for resources stored without a physical file)
587
            if (method_exists($resourceNode, 'hasEditableTextContent')
588
                && $resourceNode->hasEditableTextContent()
589
                && method_exists($resourceNode, 'getEditableTextContent')
590
            ) {
591
                $editable = (string) $resourceNode->getEditableTextContent();
592
                if ('' !== trim($editable)) {
593
                    return $editable;
594
                }
595
            }
596
597
            // If there is no file, returning an empty string avoids fatal errors for non-file resources.
598
            if (method_exists($resourceNode, 'hasResourceFile') && !$resourceNode->hasResourceFile()) {
599
                return '';
600
            }
601
602
            throw new FileNotFoundException($resource->getResourceName(), 0, $throwable);
603
        }
604
    }
605
606
    public function getResourceNodeFileContent(ResourceNode $resourceNode): string
607
    {
608
        return $this->resourceNodeRepository->getResourceNodeFileContent($resourceNode);
609
    }
610
611
    public function getResourceFileDownloadUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
612
    {
613
        $extraParams['mode'] = 'download';
614
615
        return $this->getResourceFileUrl($resource, $extraParams, $referenceType);
616
    }
617
618
    public function getResourceFileUrl(AbstractResource $resource, array $extraParams = [], ?int $referenceType = null): string
619
    {
620
        return $this->getResourceNodeRepository()->getResourceFileUrl(
621
            $resource->getResourceNode(),
622
            $extraParams,
623
            $referenceType
624
        );
625
    }
626
627
    public function updateResourceFileContent(AbstractResource $resource, string $content): bool
628
    {
629
        $resourceNode = $resource->getResourceNode();
630
        if ($resourceNode->hasResourceFile()) {
631
            $resourceNode->setContent($content);
632
            foreach ($resourceNode->getResourceFiles() as $resourceFile) {
633
                $resourceFile->setSize(\strlen($content));
634
            }
635
636
            return true;
637
        }
638
639
        return false;
640
    }
641
642
    public function setResourceName(AbstractResource $resource, $title): void
643
    {
644
        if (!empty($title)) {
645
            $resource->setResourceName($title);
646
            $resourceNode = $resource->getResourceNode();
647
            $resourceNode->setTitle($title);
648
        }
649
    }
650
651
    public function toggleVisibilityPublishedDraft(
652
        AbstractResource $resource,
653
        ?Course $course = null,
654
        ?Session $session = null
655
    ): void {
656
        $firstLink = null;
657
658
        if (null !== $course) {
659
            $firstLink = $resource->getFirstResourceLinkFromCourseSession($course, $session);
660
        }
661
662
        $firstLink ??= $resource->getFirstResourceLink();
663
664
        if (null === $firstLink) {
665
            return;
666
        }
667
668
        if (ResourceLink::VISIBILITY_PUBLISHED === $firstLink->getVisibility()) {
669
            $this->setVisibilityDraft($resource, $course, $session);
670
671
            return;
672
        }
673
674
        if (ResourceLink::VISIBILITY_DRAFT === $firstLink->getVisibility()) {
675
            $this->setVisibilityPublished($resource, $course, $session);
676
        }
677
    }
678
679
    public function setVisibilityPublished(
680
        AbstractResource $resource,
681
        ?Course $course = null,
682
        ?Session $session = null,
683
    ): void {
684
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PUBLISHED, true, $course, $session);
685
    }
686
687
    public function setVisibilityDraft(
688
        AbstractResource $resource,
689
        ?Course $course = null,
690
        ?Session $session = null,
691
    ): void {
692
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_DRAFT, true, $course, $session);
693
    }
694
695
    public function setVisibilityPending(
696
        AbstractResource $resource,
697
        ?Course $course = null,
698
        ?Session $session = null,
699
    ): void {
700
        $this->setLinkVisibility($resource, ResourceLink::VISIBILITY_PENDING, true, $course, $session);
701
    }
702
703
    public function addResourceNode(
704
        ResourceInterface $resource,
705
        User $creator,
706
        ResourceInterface $parentResource,
707
        ?ResourceType $resourceType = null,
708
    ): ResourceNode {
709
        $parentResourceNode = $parentResource->getResourceNode();
710
711
        return $this->createNodeForResource(
712
            $resource,
713
            $creator,
714
            $parentResourceNode,
715
            null,
716
            $resourceType,
717
        );
718
    }
719
720
    /**
721
     * @todo remove this function and merge it with addResourceNode()
722
     */
723
    public function createNodeForResource(
724
        ResourceInterface $resource,
725
        User $creator,
726
        ResourceNode $parentNode,
727
        ?UploadedFile $file = null,
728
        ?ResourceType $resourceType = null,
729
    ): ResourceNode {
730
        $em = $this->getEntityManager();
731
732
        $resourceType = $resourceType ?: $this->getResourceType();
733
        $resourceName = $resource->getResourceName();
734
        $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

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