Passed
Push — master ( d802b2...38677f )
by Julito
12:04
created

ResourceNode::hasCreator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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\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
    /**
184
     * @Groups({"resource_node:read", "document:read"})
185
     */
186
    protected bool $fileEditableText;
187
188
    protected ?string $content = null;
189
190
    /**
191
     * @ORM\OneToOne(
192
     *     targetEntity="Chamilo\CourseBundle\Entity\CShortcut",
193
     *     mappedBy="shortCutNode",
194
     *     cascade={"persist", "remove"}
195
     * )
196
     */
197
    protected ?CShortcut $shortCut = null;
198
199
    /**
200
     * @ORM\Column(type="uuid", unique=true)
201
     */
202
    protected UuidV4 $uuid;
203
204
    public function __construct()
205
    {
206
        $this->uuid = Uuid::v4();
207
        $this->children = new ArrayCollection();
208
        $this->resourceLinks = new ArrayCollection();
209
        $this->comments = new ArrayCollection();
210
        $this->createdAt = new DateTime();
211
        $this->fileEditableText = false;
212
    }
213
214
    public function __toString()
215
    {
216
        return (string) $this->getPathForDisplay();
217
    }
218
219
    /**
220
     * Returns the resource id.
221
     *
222
     * @return int
223
     */
224
    public function getId()
225
    {
226
        return $this->id;
227
    }
228
229
    public function hasCreator(): bool
230
    {
231
        return null !== $this->creator;
232
    }
233
234
    public function getCreator(): ?User
235
    {
236
        return $this->creator;
237
    }
238
239
    public function setCreator(User $creator): self
240
    {
241
        $this->creator = $creator;
242
243
        return $this;
244
    }
245
246
    /**
247
     * Returns the children resource instances.
248
     *
249
     * @return Collection|ResourceNode[]
250
     */
251
    public function getChildren()
252
    {
253
        return $this->children;
254
    }
255
256
    /**
257
     * Sets the parent resource.
258
     */
259
    public function setParent(self $parent = null): self
260
    {
261
        $this->parent = $parent;
262
263
        return $this;
264
    }
265
266
    /**
267
     * Returns the parent resource.
268
     *
269
     * @return null|ResourceNode
270
     */
271
    public function getParent()
272
    {
273
        return $this->parent;
274
    }
275
276
    /**
277
     * Return the lvl value of the resource in the tree.
278
     */
279
    public function getLevel(): int
280
    {
281
        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...
282
    }
283
284
    /**
285
     * Returns the "raw" path of the resource
286
     * (the path merge names and ids of all items).
287
     * Eg.: "Root-1/subdir-2/file.txt-3/".
288
     *
289
     * @return string
290
     */
291
    public function getPath()
292
    {
293
        return $this->path;
294
    }
295
296
    /**
297
     * @return Collection|ResourceComment[]
298
     */
299
    public function getComments()
300
    {
301
        return $this->comments;
302
    }
303
304
    public function addComment(ResourceComment $comment)
305
    {
306
        $comment->setResourceNode($this);
307
308
        return $this->comments->add($comment);
309
    }
310
311
    /**
312
     * Returns the path cleaned from its ids.
313
     * Eg.: "Root/subdir/file.txt".
314
     *
315
     * @return string
316
     */
317
    public function getPathForDisplay()
318
    {
319
        return $this->path;
320
        //return $this->convertPathForDisplay($this->path);
321
    }
322
323
    public function getPathForDisplayToArray($baseRoot = null)
324
    {
325
        $parts = explode(self::PATH_SEPARATOR, $this->path);
326
        $list = [];
327
        foreach ($parts as $part) {
328
            $parts = explode('-', $part);
329
            if (empty($parts[1])) {
330
                continue;
331
            }
332
333
            $value = $parts[0];
334
            $id = $parts[1];
335
336
            if (!empty($baseRoot) && $id < $baseRoot) {
337
                continue;
338
            }
339
            $list[$id] = $value;
340
        }
341
342
        return $list;
343
    }
344
345
    public function getPathForDisplayRemoveBase(string $base): string
346
    {
347
        $path = str_replace($base, '', $this->path);
348
349
        return $this->convertPathForDisplay($path);
350
    }
351
352
    public function getSlug()
353
    {
354
        return $this->slug;
355
    }
356
357
    public function getTitle()
358
    {
359
        return $this->title;
360
    }
361
362
    public function setTitle(string $title)
363
    {
364
        $title = str_replace('/', '-', $title);
365
366
        $this->title = $title;
367
368
        return $this;
369
    }
370
371
    public function setSlug(string $slug): self
372
    {
373
        if (false !== strpos(self::PATH_SEPARATOR, $slug)) {
374
            throw new InvalidArgumentException('Invalid character "'.self::PATH_SEPARATOR.'" in resource name.');
375
        }
376
377
        $this->slug = $slug;
378
379
        return $this;
380
    }
381
382
    /**
383
     * Convert a path for display: remove ids.
384
     *
385
     * @return string
386
     */
387
    public function convertPathForDisplay(string $path)
388
    {
389
        /*$pathForDisplay = preg_replace(
390
            '/-\d+'.self::PATH_SEPARATOR.'/',
391
            ' / ',
392
            $path
393
        );
394
        if ($pathForDisplay !== null && strlen($pathForDisplay) > 0) {
395
            $pathForDisplay = substr_replace($pathForDisplay, '', -3);
396
        }
397
        */
398
        //var_dump($this->getTitle(), $path);
399
        $pathForDisplay = preg_replace(
400
            '/-\d+\\'.self::PATH_SEPARATOR.'/',
401
            '/',
402
            $path
403
        );
404
405
        if (null !== $pathForDisplay && '' !== $pathForDisplay) {
406
            $pathForDisplay = substr_replace($pathForDisplay, '', -1);
407
        }
408
409
        return $pathForDisplay;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $pathForDisplay also could return the type array which is incompatible with the documented return type string.
Loading history...
410
    }
411
412
    public function getResourceType(): ResourceType
413
    {
414
        return $this->resourceType;
415
    }
416
417
    public function setResourceType(ResourceType $resourceType): self
418
    {
419
        $this->resourceType = $resourceType;
420
421
        return $this;
422
    }
423
424
    /**
425
     * @return Collection|ResourceLink[]
426
     */
427
    public function getResourceLinks()
428
    {
429
        return $this->resourceLinks;
430
    }
431
432
    public function addResourceLink(ResourceLink $link): self
433
    {
434
        $link->setResourceNode($this);
435
        $this->resourceLinks[] = $link;
436
437
        return $this;
438
    }
439
440
    public function setResourceLinks($resourceLinks): self
441
    {
442
        $this->resourceLinks = $resourceLinks;
443
444
        return $this;
445
    }
446
447
    /**
448
     * @return Collection|ResourceLink[]
449
     */
450
    public function hasSession(Session $session = null)
451
    {
452
        $links = $this->getResourceLinks();
453
        $criteria = Criteria::create();
454
455
        $criteria->andWhere(
456
            Criteria::expr()->eq('session', $session)
457
        );
458
459
        return $links->matching($criteria);
460
    }
461
462
    public function hasResourceFile(): bool
463
    {
464
        return null !== $this->resourceFile;
465
    }
466
467
    public function getResourceFile(): ?ResourceFile
468
    {
469
        return $this->resourceFile;
470
    }
471
472
    public function hasEditableTextContent(): bool
473
    {
474
        if ($this->hasResourceFile()) {
475
            $mimeType = $this->getResourceFile()->getMimeType();
476
            if (str_contains($mimeType, 'text')) {
477
                return true;
478
            }
479
        }
480
481
        return false;
482
    }
483
484
    public function isResourceFileAnImage(): bool
485
    {
486
        if ($this->hasResourceFile()) {
487
            $mimeType = $this->getResourceFile()->getMimeType();
488
            if (str_contains($mimeType, 'image')) {
489
                return true;
490
            }
491
        }
492
493
        return false;
494
    }
495
496
    public function isResourceFileAVideo(): bool
497
    {
498
        if ($this->hasResourceFile()) {
499
            $mimeType = $this->getResourceFile()->getMimeType();
500
            if (str_contains($mimeType, 'video')) {
501
                return true;
502
            }
503
        }
504
505
        return false;
506
    }
507
508
    public function setResourceFile(?ResourceFile $resourceFile = null): self
509
    {
510
        $this->resourceFile = $resourceFile;
511
        if (null !== $resourceFile) {
512
            $resourceFile->setResourceNode($this);
513
        }
514
515
        return $this;
516
    }
517
518
    public function getIcon(): string
519
    {
520
        $class = 'fa fa-folder';
521
        if ($this->hasResourceFile()) {
522
            $class = 'far fa-file';
523
524
            if ($this->isResourceFileAnImage()) {
525
                $class = 'far fa-file-image';
526
            }
527
            if ($this->isResourceFileAVideo()) {
528
                $class = 'far fa-file-video';
529
            }
530
        }
531
532
        return '<i class="'.$class.'"></i>';
533
    }
534
535
    public function getThumbnail(RouterInterface $router): string
536
    {
537
        $size = 'fa-3x';
538
        $class = sprintf('fa fa-folder %s', $size);
539
        if ($this->hasResourceFile()) {
540
            $class = sprintf('far fa-file %s', $size);
541
542
            if ($this->isResourceFileAnImage()) {
543
                $class = sprintf('far fa-file-image %s', $size);
544
545
                $params = [
546
                    'id' => $this->getId(),
547
                    'tool' => $this->getResourceType()->getTool(),
548
                    'type' => $this->getResourceType()->getName(),
549
                    'filter' => 'editor_thumbnail',
550
                ];
551
                $url = $router->generate(
552
                    'chamilo_core_resource_view',
553
                    $params
554
                );
555
556
                return sprintf("<img src='%s'/>", $url);
557
            }
558
            if ($this->isResourceFileAVideo()) {
559
                $class = sprintf('far fa-file-video %s', $size);
560
            }
561
        }
562
563
        return '<i class="'.$class.'"></i>';
564
    }
565
566
    public function getContent()
567
    {
568
        return $this->content;
569
    }
570
571
    public function setContent(string $content): self
572
    {
573
        $this->content = $content;
574
575
        return $this;
576
    }
577
578
    public function getShortCut(): ?CShortcut
579
    {
580
        return $this->shortCut;
581
    }
582
583
    public function setShortCut(?CShortcut $shortCut): self
584
    {
585
        $this->shortCut = $shortCut;
586
587
        return $this;
588
    }
589
}
590