Passed
Push — master ( f10e67...52beba )
by Julito
07:56
created

ResourceNode::setPublic()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Entity;
8
9
use ApiPlatform\Core\Annotation\ApiFilter;
10
use ApiPlatform\Core\Annotation\ApiResource;
11
use ApiPlatform\Core\Annotation\ApiSubresource;
12
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;
13
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
14
use ApiPlatform\Core\Serializer\Filter\PropertyFilter;
15
use Chamilo\CoreBundle\Traits\TimestampableAgoTrait;
16
use Chamilo\CoreBundle\Traits\TimestampableTypedEntity;
17
use Chamilo\CourseBundle\Entity\CShortcut;
18
use DateTime;
19
use Doctrine\Common\Collections\ArrayCollection;
20
use Doctrine\Common\Collections\Collection;
21
use Doctrine\Common\Collections\Criteria;
22
use Doctrine\ORM\Mapping as ORM;
23
use Gedmo\Mapping\Annotation as Gedmo;
24
use InvalidArgumentException;
25
use Symfony\Component\Routing\RouterInterface;
26
use Symfony\Component\Serializer\Annotation\Groups;
27
use Symfony\Component\Uid\Uuid;
28
use Symfony\Component\Uid\UuidV4;
29
use Symfony\Component\Validator\Constraints as Assert;
30
31
//*     attributes={"security"="is_granted('ROLE_ADMIN')"},
32
/**
33
 * Base entity for all resources.
34
 *
35
 * @ApiResource(
36
 *     collectionOperations={"get"},
37
 *     normalizationContext={"groups"={"resource_node:read", "document:read"}},
38
 *     denormalizationContext={"groups"={"resource_node:write", "document:write"}}
39
 * )
40
 * @ApiFilter(SearchFilter::class, properties={"title":"partial"})
41
 * @ApiFilter(PropertyFilter::class)
42
 * @ApiFilter(OrderFilter::class, properties={"id", "title", "resourceFile", "createdAt", "updatedAt"})
43
 * @ORM\Entity(repositoryClass="Chamilo\CoreBundle\Repository\ResourceNodeRepository")
44
 *
45
 * @ORM\HasLifecycleCallbacks
46
 * @ORM\Table(name="resource_node")
47
 * @ORM\EntityListeners({"Chamilo\CoreBundle\Entity\Listener\ResourceNodeListener"})
48
 *
49
 * @Gedmo\Tree(type="materializedPath")
50
 */
51
class ResourceNode
52
{
53
    use TimestampableTypedEntity;
54
    use TimestampableAgoTrait;
55
56
    public const PATH_SEPARATOR = '/';
57
58
    /**
59
     * @Groups({"resource_node:read", "document:read", "ctool:read", "user_json:read"})
60
     * @ORM\Id
61
     * @ORM\Column(type="bigint")
62
     * @ORM\GeneratedValue(strategy="AUTO")
63
     */
64
    protected ?int $id = null;
65
66
    /**
67
     * @Assert\NotBlank()
68
     * @Groups({"resource_node:read", "resource_node:write", "document:read", "document:write"})
69
     * @Gedmo\TreePathSource
70
     *
71
     * @ORM\Column(name="title", type="string", length=255, nullable=false)
72
     */
73
    protected string $title;
74
75
    /**
76
     * @Assert\NotBlank()
77
     *
78
     * @Gedmo\Slug(fields={"title"})
79
     * @ORM\Column(name="slug", type="string", length=255, nullable=false)
80
     */
81
    protected string $slug;
82
83
    /**
84
     * @ORM\ManyToOne(targetEntity="ResourceType", inversedBy="resourceNodes")
85
     * @ORM\JoinColumn(name="resource_type_id", referencedColumnName="id", nullable=false)
86
     */
87
    protected ResourceType $resourceType;
88
89
    /**
90
     * @ApiSubresource()
91
     * @Groups({"ctool:read"})
92
     *
93
     * @var Collection|ResourceLink[]
94
     *
95
     * @ORM\OneToMany(targetEntity="ResourceLink", mappedBy="resourceNode", cascade={"persist", "remove"})
96
     */
97
    protected Collection $resourceLinks;
98
99
    /**
100
     * ResourceFile available file for this node.
101
     *
102
     * @Groups({"resource_node:read", "resource_node:write", "document:read", "document:write"})
103
     *
104
     * @ORM\OneToOne(targetEntity="ResourceFile", inversedBy="resourceNode", orphanRemoval=true)
105
     * @ORM\JoinColumn(name="resource_file_id", referencedColumnName="id", onDelete="CASCADE")
106
     */
107
    protected ?ResourceFile $resourceFile = null;
108
109
    /**
110
     * @Assert\Valid()
111
     * @Groups({"resource_node:read", "resource_node:write", "document:write"})
112
     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\User", inversedBy="resourceNodes")
113
     * @ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
114
     */
115
    protected User $creator;
116
117
    /**
118
     * @ApiSubresource()
119
     *
120
     * @Gedmo\TreeParent
121
     * @ORM\ManyToOne(
122
     *     targetEntity="ResourceNode",
123
     *     inversedBy="children"
124
     * )
125
     * @ORM\JoinColumns({
126
     *     @ORM\JoinColumn(name="parent_id", onDelete="CASCADE")
127
     * })
128
     */
129
    protected ?ResourceNode $parent = null;
130
131
    /**
132
     * @var Collection|ResourceNode[]
133
     *
134
     * @ORM\OneToMany(targetEntity="ResourceNode", mappedBy="parent")
135
     * @ORM\OrderBy({"id"="ASC"})
136
     */
137
    protected Collection $children;
138
139
    /**
140
     * @Gedmo\TreeLevel
141
     *
142
     * @ORM\Column(name="level", type="integer", nullable=true)
143
     */
144
    protected ?int $level = null;
145
146
    /**
147
     * @Groups({"resource_node:read", "document:read"})
148
     * @Gedmo\TreePath(appendId=true, separator="/")
149
     *
150
     * @ORM\Column(name="path", type="text", nullable=true)
151
     */
152
    protected ?string $path = null;
153
154
    /**
155
     * Shortcut to access Course resource from ResourceNode.
156
     * Groups({"resource_node:read", "course:read"}).
157
     *
158
     * ORM\OneToOne(targetEntity="Chamilo\CoreBundle\Entity\Illustration", mappedBy="resourceNode")
159
     */
160
    //protected $illustration;
161
162
    /**
163
     * @var Collection|ResourceComment[]
164
     *
165
     * @ORM\OneToMany(targetEntity="ResourceComment", mappedBy="resourceNode", cascade={"persist", "remove"})
166
     */
167
    protected Collection $comments;
168
169
    /**
170
     * @Groups({"resource_node:read", "document:read"})
171
     * @Gedmo\Timestampable(on="create")
172
     * @ORM\Column(type="datetime")
173
     */
174
    protected DateTime $createdAt;
175
176
    /**
177
     * @Groups({"resource_node:read", "document:read"})
178
     * @Gedmo\Timestampable(on="update")
179
     * @ORM\Column(type="datetime")
180
     */
181
    protected DateTime $updatedAt;
182
183
    #[Groups(['resource_node:read', 'document:read'])]
184
    protected bool $fileEditableText;
185
186
    /**
187
     * @ORM\Column(type="boolean")
188
     */
189
    #[Groups(['resource_node:read', 'document:read'])]
190
    protected bool $public;
191
192
    protected ?string $content = null;
193
194
    /**
195
     * @ORM\OneToOne(
196
     *     targetEntity="Chamilo\CourseBundle\Entity\CShortcut",
197
     *     mappedBy="shortCutNode",
198
     *     cascade={"persist", "remove"}
199
     * )
200
     */
201
    protected ?CShortcut $shortCut = null;
202
203
    /**
204
     * @ORM\Column(type="uuid", unique=true)
205
     */
206
    protected UuidV4 $uuid;
207
208
    public function __construct()
209
    {
210
        $this->public = false;
211
        $this->uuid = Uuid::v4();
212
        $this->children = new ArrayCollection();
213
        $this->resourceLinks = new ArrayCollection();
214
        $this->comments = new ArrayCollection();
215
        $this->createdAt = new DateTime();
216
        $this->fileEditableText = false;
217
    }
218
219
    public function __toString(): string
220
    {
221
        return $this->getPathForDisplay();
222
    }
223
224
    /**
225
     * Returns the resource id.
226
     *
227
     * @return int
228
     */
229
    public function getId()
230
    {
231
        return $this->id;
232
    }
233
234
    public function hasCreator(): bool
235
    {
236
        return null !== $this->creator;
237
    }
238
239
    public function getCreator(): ?User
240
    {
241
        return $this->creator;
242
    }
243
244
    public function setCreator(User $creator): self
245
    {
246
        $this->creator = $creator;
247
248
        return $this;
249
    }
250
251
    /**
252
     * Returns the children resource instances.
253
     *
254
     * @return Collection|ResourceNode[]
255
     */
256
    public function getChildren()
257
    {
258
        return $this->children;
259
    }
260
261
    /**
262
     * Sets the parent resource.
263
     */
264
    public function setParent(self $parent = null): self
265
    {
266
        $this->parent = $parent;
267
268
        return $this;
269
    }
270
271
    /**
272
     * Returns the parent resource.
273
     *
274
     * @return null|ResourceNode
275
     */
276
    public function getParent()
277
    {
278
        return $this->parent;
279
    }
280
281
    /**
282
     * Return the lvl value of the resource in the tree.
283
     */
284
    public function getLevel(): int
285
    {
286
        return $this->level;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->level could return the type null which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
287
    }
288
289
    /**
290
     * Returns the "raw" path of the resource
291
     * (the path merge names and ids of all items).
292
     * Eg.: "Root-1/subdir-2/file.txt-3/".
293
     *
294
     * @return string
295
     */
296
    public function getPath()
297
    {
298
        return $this->path;
299
    }
300
301
    /**
302
     * @return Collection|ResourceComment[]
303
     */
304
    public function getComments()
305
    {
306
        return $this->comments;
307
    }
308
309
    public function addComment(ResourceComment $comment): self
310
    {
311
        $comment->setResourceNode($this);
312
313
        $this->comments->add($comment);
314
315
        return $this;
316
    }
317
318
    /**
319
     * Returns the path cleaned from its ids.
320
     * Eg.: "Root/subdir/file.txt".
321
     */
322
    public function getPathForDisplay(): string
323
    {
324
        return $this->path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->path 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...
325
        //return $this->convertPathForDisplay($this->path);
326
    }
327
328
    public function getPathForDisplayToArray(?int $baseRoot = null): array
329
    {
330
        $parts = explode(self::PATH_SEPARATOR, $this->path);
331
        $list = [];
332
        foreach ($parts as $part) {
333
            $parts = explode('-', $part);
334
            if (empty($parts[1])) {
335
                continue;
336
            }
337
338
            $value = $parts[0];
339
            $id = $parts[1];
340
341
            if (!empty($baseRoot) && $id < $baseRoot) {
342
                continue;
343
            }
344
            $list[$id] = $value;
345
        }
346
347
        return $list;
348
    }
349
350
    public function getPathForDisplayRemoveBase(string $base): string
351
    {
352
        $path = str_replace($base, '', $this->path);
353
354
        return $this->convertPathForDisplay($path);
355
    }
356
357
    public function getSlug(): string
358
    {
359
        return $this->slug;
360
    }
361
362
    public function getTitle(): string
363
    {
364
        return $this->title;
365
    }
366
367
    public function setTitle(string $title): self
368
    {
369
        $title = str_replace('/', '-', $title);
370
371
        $this->title = $title;
372
373
        return $this;
374
    }
375
376
    public function setSlug(string $slug): self
377
    {
378
        if (false !== strpos(self::PATH_SEPARATOR, $slug)) {
379
            throw new InvalidArgumentException('Invalid character "'.self::PATH_SEPARATOR.'" in resource name.');
380
        }
381
382
        $this->slug = $slug;
383
384
        return $this;
385
    }
386
387
    /**
388
     * Convert a path for display: remove ids.
389
     */
390
    public function convertPathForDisplay(string $path): string
391
    {
392
        /*$pathForDisplay = preg_replace(
393
            '/-\d+'.self::PATH_SEPARATOR.'/',
394
            ' / ',
395
            $path
396
        );
397
        if ($pathForDisplay !== null && strlen($pathForDisplay) > 0) {
398
            $pathForDisplay = substr_replace($pathForDisplay, '', -3);
399
        }
400
        */
401
        //var_dump($this->getTitle(), $path);
402
        $pathForDisplay = preg_replace(
403
            '/-\d+\\'.self::PATH_SEPARATOR.'/',
404
            '/',
405
            $path
406
        );
407
408
        if (null !== $pathForDisplay && '' !== $pathForDisplay) {
409
            $pathForDisplay = substr_replace($pathForDisplay, '', -1);
410
        }
411
412
        return $pathForDisplay;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $pathForDisplay could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
413
    }
414
415
    public function getResourceType(): ResourceType
416
    {
417
        return $this->resourceType;
418
    }
419
420
    public function setResourceType(ResourceType $resourceType): self
421
    {
422
        $this->resourceType = $resourceType;
423
424
        return $this;
425
    }
426
427
    /**
428
     * @return Collection|ResourceLink[]
429
     */
430
    public function getResourceLinks()
431
    {
432
        return $this->resourceLinks;
433
    }
434
435
    public function addResourceLink(ResourceLink $link): self
436
    {
437
        $link->setResourceNode($this);
438
        $this->resourceLinks[] = $link;
439
440
        return $this;
441
    }
442
443
    /**
444
     * @param Collection|ResourceLink[] $resourceLinks
445
     */
446
    public function setResourceLinks($resourceLinks): self
447
    {
448
        $this->resourceLinks = $resourceLinks;
449
450
        return $this;
451
    }
452
453
    /**
454
     * @return Collection|ResourceLink[]
455
     */
456
    public function hasSession(Session $session = null)
457
    {
458
        $links = $this->getResourceLinks();
459
        $criteria = Criteria::create();
460
461
        $criteria->andWhere(
462
            Criteria::expr()->eq('session', $session)
463
        );
464
465
        return $links->matching($criteria);
466
    }
467
468
    public function hasResourceFile(): bool
469
    {
470
        return null !== $this->resourceFile;
471
    }
472
473
    public function getResourceFile(): ?ResourceFile
474
    {
475
        return $this->resourceFile;
476
    }
477
478
    public function hasEditableTextContent(): bool
479
    {
480
        if ($this->hasResourceFile()) {
481
            $mimeType = $this->getResourceFile()->getMimeType();
482
            if (str_contains($mimeType, 'text')) {
483
                return true;
484
            }
485
        }
486
487
        return false;
488
    }
489
490
    public function isResourceFileAnImage(): bool
491
    {
492
        if ($this->hasResourceFile()) {
493
            $mimeType = $this->getResourceFile()->getMimeType();
494
            if (str_contains($mimeType, 'image')) {
495
                return true;
496
            }
497
        }
498
499
        return false;
500
    }
501
502
    public function isResourceFileAVideo(): bool
503
    {
504
        if ($this->hasResourceFile()) {
505
            $mimeType = $this->getResourceFile()->getMimeType();
506
            if (str_contains($mimeType, 'video')) {
507
                return true;
508
            }
509
        }
510
511
        return false;
512
    }
513
514
    public function setResourceFile(?ResourceFile $resourceFile = null): self
515
    {
516
        $this->resourceFile = $resourceFile;
517
        if (null !== $resourceFile) {
518
            $resourceFile->setResourceNode($this);
519
        }
520
521
        return $this;
522
    }
523
524
    public function getIcon(): string
525
    {
526
        $class = 'fa fa-folder';
527
        if ($this->hasResourceFile()) {
528
            $class = 'far fa-file';
529
530
            if ($this->isResourceFileAnImage()) {
531
                $class = 'far fa-file-image';
532
            }
533
            if ($this->isResourceFileAVideo()) {
534
                $class = 'far fa-file-video';
535
            }
536
        }
537
538
        return '<i class="'.$class.'"></i>';
539
    }
540
541
    public function getThumbnail(RouterInterface $router): string
542
    {
543
        $size = 'fa-3x';
544
        $class = sprintf('fa fa-folder %s', $size);
545
        if ($this->hasResourceFile()) {
546
            $class = sprintf('far fa-file %s', $size);
547
548
            if ($this->isResourceFileAnImage()) {
549
                $class = sprintf('far fa-file-image %s', $size);
550
551
                $params = [
552
                    'id' => $this->getId(),
553
                    'tool' => $this->getResourceType()->getTool(),
554
                    'type' => $this->getResourceType()->getName(),
555
                    'filter' => 'editor_thumbnail',
556
                ];
557
                $url = $router->generate(
558
                    'chamilo_core_resource_view',
559
                    $params
560
                );
561
562
                return sprintf("<img src='%s'/>", $url);
563
            }
564
            if ($this->isResourceFileAVideo()) {
565
                $class = sprintf('far fa-file-video %s', $size);
566
            }
567
        }
568
569
        return '<i class="'.$class.'"></i>';
570
    }
571
572
    public function getContent(): ?string
573
    {
574
        return $this->content;
575
    }
576
577
    public function setContent(string $content): self
578
    {
579
        $this->content = $content;
580
581
        return $this;
582
    }
583
584
    public function getShortCut(): ?CShortcut
585
    {
586
        return $this->shortCut;
587
    }
588
589
    public function setShortCut(?CShortcut $shortCut): self
590
    {
591
        $this->shortCut = $shortCut;
592
593
        return $this;
594
    }
595
596
    public function isPublic(): bool
597
    {
598
        return $this->public;
599
    }
600
601
    public function setPublic(bool $public): self
602
    {
603
        $this->public = $public;
604
605
        return $this;
606
    }
607
}
608