Passed
Push — master ( ffb0a0...116c25 )
by Julito
08:25
created

ResourceRepository::createNodeForResource()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 37
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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