CDocument::setResourceName()   A
last analyzed

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
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CourseBundle\Entity;
8
9
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
10
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
11
use ApiPlatform\Metadata\ApiFilter;
12
use ApiPlatform\Metadata\ApiProperty;
13
use ApiPlatform\Metadata\ApiResource;
14
use ApiPlatform\Metadata\Delete;
15
use ApiPlatform\Metadata\Get;
16
use ApiPlatform\Metadata\GetCollection;
17
use ApiPlatform\Metadata\Post;
18
use ApiPlatform\Metadata\Put;
19
use ApiPlatform\Metadata\QueryParameter;
20
use ApiPlatform\OpenApi\Model\Operation;
21
use ApiPlatform\OpenApi\Model\Parameter;
22
use ApiPlatform\OpenApi\Model\RequestBody;
23
use ApiPlatform\Serializer\Filter\PropertyFilter;
24
use ArrayObject;
25
use Chamilo\CoreBundle\Controller\Api\CreateDocumentFileAction;
26
use Chamilo\CoreBundle\Controller\Api\DocumentLearningPathUsageAction;
27
use Chamilo\CoreBundle\Controller\Api\DocumentUsageAction;
28
use Chamilo\CoreBundle\Controller\Api\DownloadSelectedDocumentsAction;
29
use Chamilo\CoreBundle\Controller\Api\MoveDocumentAction;
30
use Chamilo\CoreBundle\Controller\Api\ReplaceDocumentFileAction;
31
use Chamilo\CoreBundle\Controller\Api\UpdateDocumentFileAction;
32
use Chamilo\CoreBundle\Controller\Api\UpdateVisibilityDocument;
33
use Chamilo\CoreBundle\Entity\AbstractResource;
34
use Chamilo\CoreBundle\Entity\GradebookCategory;
35
use Chamilo\CoreBundle\Entity\Listener\ResourceListener;
36
use Chamilo\CoreBundle\Entity\ResourceInterface;
37
use Chamilo\CoreBundle\Entity\ResourceNode;
38
use Chamilo\CoreBundle\Entity\ResourceShowCourseResourcesInSessionInterface;
39
use Chamilo\CoreBundle\Filter\CidFilter;
40
use Chamilo\CoreBundle\Filter\SidFilter;
41
use Chamilo\CoreBundle\State\DocumentCollectionStateProvider;
42
use Chamilo\CourseBundle\Repository\CDocumentRepository;
43
use Doctrine\Common\Collections\ArrayCollection;
44
use Doctrine\Common\Collections\Collection;
45
use Doctrine\ORM\Mapping as ORM;
46
use Stringable;
47
use Symfony\Component\Serializer\Annotation\Groups;
48
use Symfony\Component\Uid\Uuid;
49
use Symfony\Component\Validator\Constraints as Assert;
50
51
#[ApiResource(
52
    shortName: 'Document',
53
    operations: [
54
        new Put(
55
            controller: UpdateDocumentFileAction::class,
56
            security: "is_granted('EDIT', object.resourceNode)",
57
            validationContext: [
58
                'groups' => ['media_object_create', 'document:write'],
59
            ],
60
            deserialize: false
61
        ),
62
        new Put(
63
            uriTemplate: '/documents/{iid}/toggle_visibility',
64
            controller: UpdateVisibilityDocument::class,
65
            openapi: new Operation(
66
                summary: 'Change document visibility (visible/invisible to learners)'
67
            ),
68
            security: "is_granted('EDIT', object.resourceNode)",
69
            deserialize: false
70
        ),
71
        new Put(
72
            uriTemplate: '/documents/{iid}/move',
73
            controller: MoveDocumentAction::class,
74
            openapi: new Operation(summary: 'Move document (context-aware using ResourceLink.parent)'),
75
            security: "is_granted('EDIT', object.resourceNode)",
76
            deserialize: false
77
        ),
78
        new Post(
79
            uriTemplate: '/documents/{iid}/replace',
80
            controller: ReplaceDocumentFileAction::class,
81
            openapi: new Operation(
82
                summary: 'Replace a document file, maintaining the same IDs.',
83
                requestBody: new RequestBody(
84
                    content: new ArrayObject([
85
                        'multipart/form-data' => [
86
                            'schema' => [
87
                                'type' => 'object',
88
                                'properties' => [
89
                                    'file' => [
90
                                        'type' => 'string',
91
                                        'format' => 'binary',
92
                                    ],
93
                                ],
94
                            ],
95
                        ],
96
                    ]),
97
                ),
98
            ),
99
            security: "is_granted('ROLE_CURRENT_COURSE_TEACHER') or is_granted('ROLE_CURRENT_COURSE_SESSION_TEACHER') or is_granted('ROLE_TEACHER')",
100
            validationContext: ['groups' => ['Default', 'media_object_create', 'document:write']],
101
            deserialize: false
102
        ),
103
        new Get(security: "is_granted('VIEW', object.resourceNode)"),
104
        new Get(
105
            uriTemplate: '/documents/{iid}/lp-usage',
106
            controller: DocumentLearningPathUsageAction::class,
107
            openapi: new Operation(
108
                summary: 'Get a list of learning paths where a document is used'
109
            ),
110
            security: "is_granted('ROLE_USER')",
111
            read: false,
112
            name: 'api_documents_lp_usage'
113
        ),
114
        new Delete(security: "is_granted('DELETE', object.resourceNode)"),
115
        new Post(
116
            controller: CreateDocumentFileAction::class,
117
            openapi: new Operation(
118
                requestBody: new RequestBody(
119
                    content: new ArrayObject([
120
                        'multipart/form-data' => [
121
                            'schema' => [
122
                                'type' => 'object',
123
                                'properties' => [
124
                                    'title' => ['type' => 'string'],
125
                                    'filetype' => [
126
                                        'type' => 'string',
127
                                        'enum' => ['folder', 'file'],
128
                                    ],
129
                                    'comment' => ['type' => 'string'],
130
                                    'contentFile' => ['type' => 'string'],
131
                                    'uploadFile' => [
132
                                        'type' => 'string',
133
                                        'format' => 'binary',
134
                                    ],
135
                                    'parentResourceNodeId' => ['type' => 'integer'],
136
                                    'resourceLinkList' => [
137
                                        'type' => 'array',
138
                                        'items' => [
139
                                            'type' => 'object',
140
                                            'properties' => [
141
                                                'visibility' => ['type' => 'integer'],
142
                                                'cid' => ['type' => 'integer'],
143
                                                'gid' => ['type' => 'integer'],
144
                                                'sid' => ['type' => 'integer'],
145
                                            ],
146
                                        ],
147
                                    ],
148
                                    'isUncompressZipEnabled' => ['type' => 'boolean'],
149
                                    'fileExistsOption' => [
150
                                        'type' => 'string',
151
                                        'enum' => ['overwrite', 'skip', 'rename'],
152
                                    ],
153
                                ],
154
                            ],
155
                        ],
156
                    ]),
157
                ),
158
            ),
159
            security: "is_granted('ROLE_CURRENT_COURSE_TEACHER') or is_granted('ROLE_CURRENT_COURSE_SESSION_TEACHER') or is_granted('ROLE_TEACHER')",
160
            validationContext: ['groups' => ['Default', 'media_object_create', 'document:write']],
161
            deserialize: false
162
        ),
163
        new Post(
164
            uriTemplate: '/documents/download-selected',
165
            outputFormats: ['zip' => DownloadSelectedDocumentsAction::CONTENT_TYPE],
166
            controller: DownloadSelectedDocumentsAction::class,
167
            parameters: [
168
                'cid' => new QueryParameter(
169
                    schema: ['type' => 'integer'],
170
                    description: 'Course identifier',
171
                ),
172
                'sid' => new QueryParameter(
173
                    schema: ['type' => 'integer'],
174
                    description: 'Session identifier',
175
                ),
176
                'gid' => new QueryParameter(
177
                    schema: ['type' => 'integer'],
178
                    description: 'Course grou identifier',
179
                ),
180
            ],
181
            openapi: new Operation(
182
                summary: 'Download selected documents as a ZIP file.',
183
                requestBody: new RequestBody(
184
                    content: new ArrayObject([
185
                        'application/json' => [
186
                            'schema' => [
187
                                'type' => 'object',
188
                                'properties' => [
189
                                    'ids' => [
190
                                        'type' => 'array',
191
                                        'items' => ['type' => 'integer'],
192
                                    ],
193
                                ],
194
                            ],
195
                        ],
196
                    ]),
197
                ),
198
            ),
199
            security: "is_granted('ROLE_USER')",
200
        ),
201
        new GetCollection(
202
            openapi: new Operation(
203
                parameters: [
204
                    new Parameter(
205
                        name: 'resourceNode.parent',
206
                        in: 'query',
207
                        description: 'Resource node Parent',
208
                        required: true,
209
                        schema: ['type' => 'integer'],
210
                    ),
211
                ],
212
            ),
213
            provider: DocumentCollectionStateProvider::class
214
        ),
215
        new Get(
216
            uriTemplate: '/documents/{cid}/usage',
217
            controller: DocumentUsageAction::class,
218
            openapiContext: [
219
                'summary' => 'Get usage/quota information for documents.',
220
            ],
221
            security: "is_granted('ROLE_USER')",
222
            read: false,
223
            name: 'api_documents_usage'
224
        ),
225
    ],
226
    normalizationContext: [
227
        'groups' => ['document:read', 'resource_node:read'],
228
    ],
229
    denormalizationContext: [
230
        'groups' => ['document:write'],
231
    ]
232
)]
233
#[ORM\Table(name: 'c_document')]
234
#[ORM\Index(columns: ['filetype'], name: 'idx_cdoc_type')]
235
#[ORM\Entity(repositoryClass: CDocumentRepository::class)]
236
#[ORM\EntityListeners([ResourceListener::class])]
237
#[ApiFilter(filterClass: PropertyFilter::class)]
238
#[ApiFilter(filterClass: SearchFilter::class, properties: ['title' => 'partial', 'filetype' => 'exact'])]
239
#[ApiFilter(
240
    filterClass: OrderFilter::class,
241
    properties: [
242
        'iid',
243
        'filetype',
244
        'resourceNode.title',
245
        'resourceNode.createdAt',
246
        'resourceNode.firstResourceFile.size',
247
        'resourceNode.updatedAt',
248
    ]
249
)]
250
#[ApiFilter(filterClass: CidFilter::class)]
251
#[ApiFilter(filterClass: SidFilter::class)]
252
class CDocument extends AbstractResource implements ResourceInterface, ResourceShowCourseResourcesInSessionInterface, Stringable
253
{
254
    #[ApiProperty(identifier: true)]
255
    #[Groups(['document:read'])]
256
    #[ORM\Column(name: 'iid', type: 'integer')]
257
    #[ORM\Id]
258
    #[ORM\GeneratedValue]
259
    protected ?int $iid = null;
260
261
    #[Groups(['document:read', 'document:write', 'document:browse', 'student_publication_rel_document:read'])]
262
    #[Assert\NotBlank]
263
    #[ORM\Column(name: 'title', type: 'string', length: 255, nullable: false)]
264
    protected string $title;
265
266
    #[Groups(['document:read', 'document:write'])]
267
    #[ORM\Column(name: 'comment', type: 'text', nullable: true)]
268
    protected ?string $comment;
269
270
    #[Groups(['document:read', 'document:write'])]
271
    #[Assert\Choice(['folder', 'file', 'certificate', 'video'], message: 'Choose a valid filetype.')]
272
    #[ORM\Column(name: 'filetype', type: 'string', length: 15, nullable: false)]
273
    protected string $filetype;
274
275
    #[ORM\Column(name: 'readonly', type: 'boolean', nullable: false)]
276
    protected bool $readonly;
277
278
    #[Groups(['document:read', 'document:write'])]
279
    #[ORM\Column(name: 'template', type: 'boolean', nullable: false)]
280
    protected bool $template;
281
282
    #[Groups(['document:read'])]
283
    #[ORM\OneToMany(mappedBy: 'document', targetEntity: GradebookCategory::class)]
284
    private Collection $gradebookCategories;
285
286
    public function __construct()
287
    {
288
        $this->comment = '';
289
        $this->filetype = 'folder';
290
        $this->readonly = false;
291
        $this->template = false;
292
        $this->gradebookCategories = new ArrayCollection();
293
    }
294
295
    public function __toString(): string
296
    {
297
        return $this->getTitle();
298
    }
299
300
    public function getTitle(): string
301
    {
302
        return $this->title;
303
    }
304
305
    public function setTitle(string $title): self
306
    {
307
        $this->title = $title;
308
309
        return $this;
310
    }
311
312
    public function isTemplate(): bool
313
    {
314
        return $this->template;
315
    }
316
317
    public function setTemplate(bool $template): self
318
    {
319
        $this->template = $template;
320
321
        return $this;
322
    }
323
324
    public function getComment(): ?string
325
    {
326
        return $this->comment;
327
    }
328
329
    public function setComment(?string $comment): self
330
    {
331
        $this->comment = $comment;
332
333
        return $this;
334
    }
335
336
    public function getFiletype(): string
337
    {
338
        return $this->filetype;
339
    }
340
341
    public function setFiletype(string $filetype): self
342
    {
343
        $this->filetype = $filetype;
344
345
        return $this;
346
    }
347
348
    public function getReadonly(): bool
349
    {
350
        return $this->readonly;
351
    }
352
353
    public function setReadonly(bool $readonly): self
354
    {
355
        $this->readonly = $readonly;
356
357
        return $this;
358
    }
359
360
    public function getResourceIdentifier(): int|Uuid
361
    {
362
        return $this->getIid();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getIid() could return the type null which is incompatible with the type-hinted return Symfony\Component\Uid\Uuid|integer. Consider adding an additional type-check to rule them out.
Loading history...
363
    }
364
365
    public function getIid(): ?int
366
    {
367
        return $this->iid;
368
    }
369
370
    public function getResourceName(): string
371
    {
372
        return $this->getTitle();
373
    }
374
375
    public function setResourceName(string $name): self
376
    {
377
        return $this->setTitle($name);
378
    }
379
380
    /**
381
     * @return Collection<int, GradebookCategory>
382
     */
383
    public function getGradebookCategories(): Collection
384
    {
385
        return $this->gradebookCategories;
386
    }
387
388
    public function addGradebookCategory(GradebookCategory $gradebookCategory): static
389
    {
390
        if (!$this->gradebookCategories->contains($gradebookCategory)) {
391
            $this->gradebookCategories->add($gradebookCategory);
392
            $gradebookCategory->setDocument($this);
393
        }
394
395
        return $this;
396
    }
397
398
    public function removeGradebookCategory(GradebookCategory $gradebookCategory): static
399
    {
400
        if ($this->gradebookCategories->removeElement($gradebookCategory)) {
401
            // set the owning side to null (unless already changed)
402
            if ($gradebookCategory->getDocument() === $this) {
403
                $gradebookCategory->setDocument(null);
404
            }
405
        }
406
407
        return $this;
408
    }
409
410
    #[Groups(['document:read', 'document:fullPath'])]
411
    public function getFullPath(): string
412
    {
413
        $pathParts = [$this->getTitle()];
414
415
        $parent = $this->getParent();
416
        while ($parent instanceof ResourceNode) {
417
            array_unshift($pathParts, $parent->getTitle());
418
            $parent = $parent->getParent();
419
        }
420
421
        return implode('/', $pathParts);
422
    }
423
}
424