Passed
Push — master ( 8c4b93...c6401d )
by Julito
21:04
created

ResourceNode::getShortCut()   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"})
60
     * @ORM\Id
61
     * @ORM\Column(type="integer")
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
     *
92
     * @var Collection|ResourceLink[]
93
     *
94
     * @ORM\OneToMany(targetEntity="ResourceLink", mappedBy="resourceNode", cascade={"persist", "remove"})
95
     */
96
    protected Collection $resourceLinks;
97
98
    /**
99
     * ResourceFile available file for this node.
100
     *
101
     * @Groups({"resource_node:read", "resource_node:write", "document:read", "document:write"})
102
     *
103
     * @ORM\OneToOne(targetEntity="ResourceFile", inversedBy="resourceNode", orphanRemoval=true)
104
     * @ORM\JoinColumn(name="resource_file_id", referencedColumnName="id", onDelete="CASCADE")
105
     */
106
    protected ?ResourceFile $resourceFile = null;
107
108
    /**
109
     * @Assert\Valid()
110
     * @Groups({"resource_node:read", "resource_node:write", "document:write"})
111
     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\User", inversedBy="resourceNodes")
112
     * @ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
113
     */
114
    protected User $creator;
115
116
    /**
117
     * @ApiSubresource()
118
     *
119
     * @Gedmo\TreeParent
120
     * @ORM\ManyToOne(
121
     *     targetEntity="ResourceNode",
122
     *     inversedBy="children"
123
     * )
124
     * @ORM\JoinColumns({
125
     *     @ORM\JoinColumn(onDelete="CASCADE")
126
     * })
127
     */
128
    protected ?ResourceNode $parent = null;
129
130
    /**
131
     * @Gedmo\TreeLevel
132
     *
133
     * @ORM\Column(name="level", type="integer", nullable=true)
134
     */
135
    protected ?int $level = null;
136
137
    /**
138
     * @var Collection|ResourceNode[]
139
     *
140
     * @ORM\OneToMany(
141
     *     targetEntity="ResourceNode",
142
     *     mappedBy="parent"
143
     * )
144
     * @ORM\OrderBy({"id"="ASC"})
145
     */
146
    protected Collection $children;
147
148
    /**
149
     * @Groups({"resource_node:read", "document:read"})
150
     * @Gedmo\TreePath(appendId=true, separator="/")
151
     *
152
     * @ORM\Column(name="path", type="text", nullable=true)
153
     */
154
    protected ?string $path = null;
155
156
    /**
157
     * Shortcut to access Course resource from ResourceNode.
158
     *
159
     * ORM\OneToOne(targetEntity="Chamilo\CoreBundle\Entity\Illustration", mappedBy="resourceNode")
160
     */
161
    //protected $illustration;
162
163
    /**
164
     * @var Collection|ResourceComment[]
165
     *
166
     * @ORM\OneToMany(targetEntity="ResourceComment", mappedBy="resourceNode", cascade={"persist", "remove"})
167
     */
168
    protected Collection $comments;
169
170
    /**
171
     * @Groups({"resource_node:read", "document:read"})
172
     * @Gedmo\Timestampable(on="create")
173
     * @ORM\Column(type="datetime")
174
     */
175
    protected DateTime $createdAt;
176
177
    /**
178
     * @Groups({"resource_node:read", "document:read"})
179
     * @Gedmo\Timestampable(on="update")
180
     * @ORM\Column(type="datetime")
181
     */
182
    protected DateTime $updatedAt;
183
184
    /**
185
     * @Groups({"resource_node:read", "document:read"})
186
     */
187
    protected bool $fileEditableText;
188
189
    protected ?string $content = null;
190
191
    /**
192
     * @ORM\OneToOne(
193
     *     targetEntity="Chamilo\CourseBundle\Entity\CShortcut",
194
     *     mappedBy="shortCutNode",
195
     *     cascade={"persist", "remove"}
196
     * )
197
     */
198
    protected ?CShortcut $shortCut = null;
199
200
    /**
201
     * @ORM\Column(type="uuid", unique=true)
202
     */
203
    protected UuidV4 $uuid;
204
205
    public function __construct()
206
    {
207
        $this->uuid = Uuid::v4();
208
        $this->children = new ArrayCollection();
209
        $this->resourceLinks = new ArrayCollection();
210
        $this->comments = new ArrayCollection();
211
        $this->createdAt = new DateTime();
212
        $this->fileEditableText = false;
213
    }
214
215
    /**
216
     * @return string
217
     */
218
    public function __toString()
219
    {
220
        return (string) $this->getPathForDisplay();
221
    }
222
223
    /**
224
     * Returns the resource id.
225
     *
226
     * @return int
227
     */
228
    public function getId()
229
    {
230
        return $this->id;
231
    }
232
233
    /**
234
     * Returns the resource creator.
235
     */
236
    public function getCreator(): ?User
237
    {
238
        return $this->creator;
239
    }
240
241
    public function setCreator(User $creator = null): self
242
    {
243
        $this->creator = $creator;
244
245
        return $this;
246
    }
247
248
    /**
249
     * Returns the children resource instances.
250
     *
251
     * @return ArrayCollection|ResourceNode[]
252
     */
253
    public function getChildren()
254
    {
255
        return $this->children;
256
    }
257
258
    /**
259
     * Sets the parent resource.
260
     */
261
    public function setParent(self $parent = null): self
262
    {
263
        $this->parent = $parent;
264
265
        return $this;
266
    }
267
268
    /**
269
     * Returns the parent resource.
270
     *
271
     * @return null|ResourceNode
272
     */
273
    public function getParent()
274
    {
275
        return $this->parent;
276
    }
277
278
    /**
279
     * Return the lvl value of the resource in the tree.
280
     */
281
    public function getLevel(): int
282
    {
283
        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...
284
    }
285
286
    /**
287
     * Returns the "raw" path of the resource
288
     * (the path merge names and ids of all items).
289
     * Eg.: "Root-1/subdir-2/file.txt-3/".
290
     *
291
     * @return string
292
     */
293
    public function getPath()
294
    {
295
        return $this->path;
296
    }
297
298
    /**
299
     * @return ArrayCollection|ResourceComment[]
300
     */
301
    public function getComments()
302
    {
303
        return $this->comments;
304
    }
305
306
    public function addComment(ResourceComment $comment)
307
    {
308
        $comment->setResourceNode($this);
309
310
        return $this->comments->add($comment);
311
    }
312
313
    /**
314
     * Returns the path cleaned from its ids.
315
     * Eg.: "Root/subdir/file.txt".
316
     *
317
     * @return string
318
     */
319
    public function getPathForDisplay()
320
    {
321
        return $this->path;
322
        //return $this->convertPathForDisplay($this->path);
323
    }
324
325
    public function getPathForDisplayToArray($baseRoot = null)
326
    {
327
        $parts = explode(self::PATH_SEPARATOR, $this->path);
328
        $list = [];
329
        foreach ($parts as $part) {
330
            $parts = explode('-', $part);
331
            if (empty($parts[1])) {
332
                continue;
333
            }
334
335
            $value = $parts[0];
336
            $id = $parts[1];
337
338
            if (!empty($baseRoot) && $id < $baseRoot) {
339
                continue;
340
            }
341
            $list[$id] = $value;
342
        }
343
344
        return $list;
345
    }
346
347
    public function getPathForDisplayRemoveBase(string $base): string
348
    {
349
        $path = str_replace($base, '', $this->path);
350
351
        return $this->convertPathForDisplay($path);
352
    }
353
354
    public function getSlug()
355
    {
356
        return $this->slug;
357
    }
358
359
    public function getTitle()
360
    {
361
        return $this->title;
362
    }
363
364
    public function setTitle(string $title)
365
    {
366
        $title = str_replace('/', '-', $title);
367
368
        $this->title = $title;
369
370
        return $this;
371
    }
372
373
    public function setSlug(string $slug): self
374
    {
375
        if (false !== strpos(self::PATH_SEPARATOR, $slug)) {
376
            throw new InvalidArgumentException('Invalid character "'.self::PATH_SEPARATOR.'" in resource name.');
377
        }
378
379
        $this->slug = $slug;
380
381
        return $this;
382
    }
383
384
    /**
385
     * Convert a path for display: remove ids.
386
     *
387
     * @return string
388
     */
389
    public function convertPathForDisplay(string $path)
390
    {
391
        /*$pathForDisplay = preg_replace(
392
            '/-\d+'.self::PATH_SEPARATOR.'/',
393
            ' / ',
394
            $path
395
        );
396
        if ($pathForDisplay !== null && strlen($pathForDisplay) > 0) {
397
            $pathForDisplay = substr_replace($pathForDisplay, '', -3);
398
        }
399
        */
400
        //var_dump($this->getTitle(), $path);
401
        $pathForDisplay = preg_replace(
402
            '/-\d+\\'.self::PATH_SEPARATOR.'/',
403
            '/',
404
            $path
405
        );
406
407
        if (null !== $pathForDisplay && '' !== $pathForDisplay) {
408
            $pathForDisplay = substr_replace($pathForDisplay, '', -1);
409
        }
410
411
        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...
412
    }
413
414
    public function getResourceType(): ResourceType
415
    {
416
        return $this->resourceType;
417
    }
418
419
    public function setResourceType(ResourceType $resourceType): self
420
    {
421
        $this->resourceType = $resourceType;
422
423
        return $this;
424
    }
425
426
    /**
427
     * @return ArrayCollection|ResourceLink[]
428
     */
429
    public function getResourceLinks()
430
    {
431
        return $this->resourceLinks;
432
    }
433
434
    public function addResourceLink(ResourceLink $link): self
435
    {
436
        $link->setResourceNode($this);
437
        $this->resourceLinks[] = $link;
438
439
        return $this;
440
    }
441
442
    public function setResourceLinks($resourceLinks): self
443
    {
444
        $this->resourceLinks = $resourceLinks;
445
446
        return $this;
447
    }
448
449
    /**
450
     * @return ArrayCollection|ResourceLink[]
451
     */
452
    public function hasSession(Session $session = null)
453
    {
454
        $links = $this->getResourceLinks();
455
        $criteria = Criteria::create();
456
457
        $criteria->andWhere(
458
            Criteria::expr()->eq('session', $session)
459
        );
460
461
        return $links->matching($criteria);
462
    }
463
464
    public function hasResourceFile(): bool
465
    {
466
        return null !== $this->resourceFile;
467
    }
468
469
    public function getResourceFile(): ?ResourceFile
470
    {
471
        return $this->resourceFile;
472
    }
473
474
    public function hasEditableTextContent(): bool
475
    {
476
        if ($this->hasResourceFile()) {
477
            $mimeType = $this->getResourceFile()->getMimeType();
478
            if (false !== strpos($mimeType, 'text')) {
479
                return true;
480
            }
481
        }
482
483
        return false;
484
    }
485
486
    public function isResourceFileAnImage(): bool
487
    {
488
        if ($this->hasResourceFile()) {
489
            $mimeType = $this->getResourceFile()->getMimeType();
490
            if (false !== strpos($mimeType, 'image')) {
491
                return true;
492
            }
493
        }
494
495
        return false;
496
    }
497
498
    public function isResourceFileAVideo(): bool
499
    {
500
        if ($this->hasResourceFile()) {
501
            $mimeType = $this->getResourceFile()->getMimeType();
502
            if (false !== strpos($mimeType, 'video')) {
503
                return true;
504
            }
505
        }
506
507
        return false;
508
    }
509
510
    public function setResourceFile(?ResourceFile $resourceFile = null): self
511
    {
512
        $this->resourceFile = $resourceFile;
513
        if (null !== $resourceFile) {
514
            $resourceFile->setResourceNode($this);
515
        }
516
517
        return $this;
518
    }
519
520
    public function getIcon(): string
521
    {
522
        $class = 'fa fa-folder';
523
        if ($this->hasResourceFile()) {
524
            $class = 'far fa-file';
525
526
            if ($this->isResourceFileAnImage()) {
527
                $class = 'far fa-file-image';
528
            }
529
            if ($this->isResourceFileAVideo()) {
530
                $class = 'far fa-file-video';
531
            }
532
        }
533
534
        return '<i class="'.$class.'"></i>';
535
    }
536
537
    public function getThumbnail(RouterInterface $router): string
538
    {
539
        $size = 'fa-3x';
540
        $class = sprintf('fa fa-folder %s', $size);
541
        if ($this->hasResourceFile()) {
542
            $class = sprintf('far fa-file %s', $size);
543
544
            if ($this->isResourceFileAnImage()) {
545
                $class = sprintf('far fa-file-image %s', $size);
546
547
                $params = [
548
                    'id' => $this->getId(),
549
                    'tool' => $this->getResourceType()->getTool(),
550
                    'type' => $this->getResourceType()->getName(),
551
                    'filter' => 'editor_thumbnail',
552
                ];
553
                $url = $router->generate(
554
                    'chamilo_core_resource_view',
555
                    $params
556
                );
557
558
                return sprintf("<img src='%s'/>", $url);
559
            }
560
            if ($this->isResourceFileAVideo()) {
561
                $class = sprintf('far fa-file-video %s', $size);
562
            }
563
        }
564
565
        return '<i class="'.$class.'"></i>';
566
    }
567
568
    public function getContent()
569
    {
570
        return $this->content;
571
    }
572
573
    public function setContent(string $content): self
574
    {
575
        $this->content = $content;
576
577
        return $this;
578
    }
579
580
    public function getShortCut(): ?CShortcut
581
    {
582
        return $this->shortCut;
583
    }
584
585
    public function setShortCut(?CShortcut $shortCut): self
586
    {
587
        $this->shortCut = $shortCut;
588
589
        return $this;
590
    }
591
}
592