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

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

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

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

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

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

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

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

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