1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* For licensing terms, see /license.txt */ |
6
|
|
|
|
7
|
|
|
namespace Chamilo\CoreBundle\Controller\Api; |
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\ResourceLink; |
14
|
|
|
use Chamilo\CoreBundle\Entity\ResourceNode; |
15
|
|
|
use Chamilo\CoreBundle\Entity\ResourceRight; |
16
|
|
|
use Chamilo\CoreBundle\Entity\Session; |
17
|
|
|
use Chamilo\CoreBundle\Entity\User; |
18
|
|
|
use Chamilo\CoreBundle\Repository\ResourceRepository; |
19
|
|
|
use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter; |
20
|
|
|
use Chamilo\CourseBundle\Entity\CDocument; |
21
|
|
|
use Chamilo\CourseBundle\Entity\CGroup; |
22
|
|
|
use DateTime; |
23
|
|
|
use Doctrine\ORM\EntityManager; |
24
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
25
|
|
|
use Exception; |
26
|
|
|
use InvalidArgumentException; |
27
|
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
28
|
|
|
use Symfony\Component\HttpFoundation\Request; |
29
|
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
30
|
|
|
use Symfony\Component\HttpKernel\KernelInterface; |
31
|
|
|
use Symfony\Contracts\Translation\TranslatorInterface; |
32
|
|
|
use ZipArchive; |
33
|
|
|
|
34
|
|
|
class BaseResourceFileAction |
35
|
|
|
{ |
36
|
|
|
public static function setLinks(AbstractResource $resource, EntityManagerInterface $em): void |
37
|
|
|
{ |
38
|
|
|
$resourceNode = $resource->getResourceNode(); |
39
|
|
|
$links = $resource->getResourceLinkArray(); |
40
|
|
|
if ($links) { |
|
|
|
|
41
|
|
|
$groupRepo = $em->getRepository(CGroup::class); |
42
|
|
|
$courseRepo = $em->getRepository(Course::class); |
43
|
|
|
$sessionRepo = $em->getRepository(Session::class); |
44
|
|
|
$userRepo = $em->getRepository(User::class); |
45
|
|
|
|
46
|
|
|
foreach ($links as $link) { |
47
|
|
|
$resourceLink = new ResourceLink(); |
48
|
|
|
$linkSet = false; |
49
|
|
|
if (isset($link['cid']) && !empty($link['cid'])) { |
50
|
|
|
$course = $courseRepo->find($link['cid']); |
51
|
|
|
if (null !== $course) { |
52
|
|
|
$linkSet = true; |
53
|
|
|
$resourceLink->setCourse($course); |
54
|
|
|
} else { |
55
|
|
|
throw new InvalidArgumentException(\sprintf('Course #%s does not exists', $link['cid'])); |
56
|
|
|
} |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
if (isset($link['sid']) && !empty($link['sid'])) { |
60
|
|
|
$session = $sessionRepo->find($link['sid']); |
61
|
|
|
if (null !== $session) { |
62
|
|
|
$linkSet = true; |
63
|
|
|
$resourceLink->setSession($session); |
64
|
|
|
} else { |
65
|
|
|
throw new InvalidArgumentException(\sprintf('Session #%s does not exists', $link['sid'])); |
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
if (isset($link['gid']) && !empty($link['gid'])) { |
70
|
|
|
$group = $groupRepo->find($link['gid']); |
71
|
|
|
if (null !== $group) { |
72
|
|
|
$linkSet = true; |
73
|
|
|
$resourceLink->setGroup($group); |
74
|
|
|
} else { |
75
|
|
|
throw new InvalidArgumentException(\sprintf('Group #%s does not exists', $link['gid'])); |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
if (isset($link['uid']) && !empty($link['uid'])) { |
80
|
|
|
$user = $userRepo->find($link['uid']); |
81
|
|
|
if (null !== $user) { |
82
|
|
|
$linkSet = true; |
83
|
|
|
$resourceLink->setUser($user); |
84
|
|
|
} else { |
85
|
|
|
throw new InvalidArgumentException(\sprintf('User #%s does not exists', $link['uid'])); |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
if (isset($link['visibility'])) { |
90
|
|
|
$resourceLink->setVisibility((int) $link['visibility']); |
91
|
|
|
} else { |
92
|
|
|
throw new InvalidArgumentException('Link needs a visibility key'); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
if ($linkSet) { |
96
|
|
|
$em->persist($resourceLink); |
97
|
|
|
$resourceNode->addResourceLink($resourceLink); |
98
|
|
|
// $em->persist($resourceNode); |
99
|
|
|
// $em->persist($resource->getResourceNode()); |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
// Use by Chamilo not api platform. |
105
|
|
|
$links = $resource->getResourceLinkEntityList(); |
106
|
|
|
if ($links) { |
|
|
|
|
107
|
|
|
// error_log('$resource->getResourceLinkEntityList()'); |
108
|
|
|
foreach ($links as $link) { |
109
|
|
|
/*$rights = []; |
110
|
|
|
* switch ($link->getVisibility()) { |
111
|
|
|
* case ResourceLink::VISIBILITY_PENDING: |
112
|
|
|
* case ResourceLink::VISIBILITY_DRAFT: |
113
|
|
|
* $editorMask = ResourceNodeVoter::getEditorMask(); |
114
|
|
|
* $resourceRight = new ResourceRight(); |
115
|
|
|
* $resourceRight |
116
|
|
|
* ->setMask($editorMask) |
117
|
|
|
* ->setRole(ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER) |
118
|
|
|
* ; |
119
|
|
|
* $rights[] = $resourceRight; |
120
|
|
|
* break; |
121
|
|
|
* } |
122
|
|
|
* if (!empty($rights)) { |
123
|
|
|
* foreach ($rights as $right) { |
124
|
|
|
* $link->addResourceRight($right); |
125
|
|
|
* } |
126
|
|
|
* }*/ |
127
|
|
|
// error_log('link adding to node: '.$resource->getResourceNode()->getId()); |
128
|
|
|
// error_log('link with user : '.$link->getUser()->getUsername()); |
129
|
|
|
$resource->getResourceNode()->addResourceLink($link); |
130
|
|
|
|
131
|
|
|
$em->persist($link); |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @todo use this function inside handleCreateFileRequest |
138
|
|
|
*/ |
139
|
|
|
protected function handleCreateRequest(AbstractResource $resource, ResourceRepository $resourceRepository, Request $request): array |
140
|
|
|
{ |
141
|
|
|
$contentData = $request->getContent(); |
142
|
|
|
|
143
|
|
|
if (!empty($contentData)) { |
144
|
|
|
$contentData = json_decode($contentData, true); |
145
|
|
|
$title = $contentData['title'] ?? ''; |
146
|
|
|
$parentResourceNodeId = (int) ($contentData['parentResourceNodeId'] ?? 0); |
147
|
|
|
$resourceLinkList = $contentData['resourceLinkList'] ?? []; |
148
|
|
|
if (empty($resourceLinkList)) { |
149
|
|
|
$resourceLinkList = $contentData['resourceLinkListFromEntity'] ?? []; |
150
|
|
|
} |
151
|
|
|
} else { |
152
|
|
|
$contentData = $request->request->all(); |
153
|
|
|
$title = $request->get('title'); |
154
|
|
|
$parentResourceNodeId = (int) $request->get('parentResourceNodeId'); |
155
|
|
|
$resourceLinkList = $request->get('resourceLinkList', []); |
156
|
|
|
if (!empty($resourceLinkList)) { |
157
|
|
|
$resourceLinkList = !str_contains($resourceLinkList, '[') ? json_decode('['.$resourceLinkList.']', true) : json_decode($resourceLinkList, true); |
158
|
|
|
if (empty($resourceLinkList)) { |
159
|
|
|
$message = 'resourceLinkList is not a valid json. Use for example: [{"cid":1, "visibility":1}]'; |
160
|
|
|
|
161
|
|
|
throw new InvalidArgumentException($message); |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
if (0 === $parentResourceNodeId) { |
167
|
|
|
throw new Exception('Parameter parentResourceNodeId int value is needed'); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
$resource->setParentResourceNode($parentResourceNodeId); |
171
|
|
|
|
172
|
|
|
if (empty($title)) { |
173
|
|
|
throw new InvalidArgumentException('title is required'); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
$resource->setResourceName($title); |
177
|
|
|
|
178
|
|
|
// Set resource link list if exists. |
179
|
|
|
if (!empty($resourceLinkList)) { |
180
|
|
|
$resource->setResourceLinkArray($resourceLinkList); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
return $contentData; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Handles the creation logic for a student publication comment resource. |
188
|
|
|
*/ |
189
|
|
|
public function handleCreateCommentRequest( |
190
|
|
|
AbstractResource $resource, |
191
|
|
|
ResourceRepository $resourceRepository, |
192
|
|
|
Request $request, |
193
|
|
|
EntityManager $em, |
194
|
|
|
string $fileExistsOption = '', |
195
|
|
|
?TranslatorInterface $translator = null |
196
|
|
|
): array { |
197
|
|
|
$title = $request->get('comment', ''); |
198
|
|
|
$parentResourceNodeId = (int) $request->get('parentResourceNodeId'); |
199
|
|
|
$fileType = $request->get('filetype'); |
200
|
|
|
$uploadedFile = null; |
201
|
|
|
|
202
|
|
|
if (empty($fileType)) { |
203
|
|
|
throw new Exception('filetype needed: folder or file'); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
if (0 === $parentResourceNodeId) { |
207
|
|
|
throw new Exception('parentResourceNodeId int value needed'); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
$resource->setParentResourceNode($parentResourceNodeId); |
211
|
|
|
|
212
|
|
|
if ($request->files->count() > 0 && $request->files->has('uploadFile')) { |
213
|
|
|
/** @var UploadedFile $uploadedFile */ |
214
|
|
|
$uploadedFile = $request->files->get('uploadFile'); |
215
|
|
|
$resource->setUploadFile($uploadedFile); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
return [ |
219
|
|
|
'title' => $title, |
220
|
|
|
'filename' => $uploadedFile?->getClientOriginalName(), |
221
|
|
|
'filetype' => $fileType, |
222
|
|
|
]; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Function loaded when creating a resource using the api, then the ResourceListener is executed. |
227
|
|
|
*/ |
228
|
|
|
public function handleCreateFileRequest( |
229
|
|
|
AbstractResource $resource, |
230
|
|
|
ResourceRepository $resourceRepository, |
231
|
|
|
Request $request, |
232
|
|
|
EntityManager $em, |
233
|
|
|
string $fileExistsOption = '', |
234
|
|
|
?TranslatorInterface $translator = null |
235
|
|
|
): array { |
236
|
|
|
$contentData = $request->getContent(); |
237
|
|
|
|
238
|
|
|
if (!empty($contentData)) { |
239
|
|
|
$contentData = json_decode($contentData, true); |
240
|
|
|
$title = $contentData['title'] ?? ''; |
241
|
|
|
$comment = $contentData['comment'] ?? ''; |
242
|
|
|
$parentResourceNodeId = (int) ($contentData['parentResourceNodeId'] ?? 0); |
243
|
|
|
$fileType = $contentData['filetype'] ?? ''; |
244
|
|
|
$resourceLinkList = $contentData['resourceLinkList'] ?? []; |
245
|
|
|
} else { |
246
|
|
|
$title = $request->get('title'); |
247
|
|
|
$comment = $request->get('comment'); |
248
|
|
|
$parentResourceNodeId = (int) $request->get('parentResourceNodeId'); |
249
|
|
|
$fileType = $request->get('filetype'); |
250
|
|
|
$resourceLinkList = $request->get('resourceLinkList', []); |
251
|
|
|
if (!empty($resourceLinkList)) { |
252
|
|
|
$resourceLinkList = !str_contains($resourceLinkList, '[') ? json_decode('['.$resourceLinkList.']', true) : json_decode($resourceLinkList, true); |
253
|
|
|
if (empty($resourceLinkList)) { |
254
|
|
|
$message = 'resourceLinkList is not a valid json. Use for example: [{"cid":1, "visibility":1}]'; |
255
|
|
|
|
256
|
|
|
throw new InvalidArgumentException($message); |
257
|
|
|
} |
258
|
|
|
} |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
if (empty($fileType)) { |
262
|
|
|
throw new Exception('filetype needed: folder or file'); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
if (0 === $parentResourceNodeId) { |
266
|
|
|
throw new Exception('parentResourceNodeId int value needed'); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
$resource->setParentResourceNode($parentResourceNodeId); |
270
|
|
|
|
271
|
|
|
switch ($fileType) { |
272
|
|
|
case 'certificate': |
273
|
|
|
case 'file': |
274
|
|
|
$content = ''; |
275
|
|
|
if ($request->request->has('contentFile')) { |
276
|
|
|
$content = $request->request->get('contentFile'); |
277
|
|
|
} |
278
|
|
|
$fileParsed = false; |
279
|
|
|
// File upload. |
280
|
|
|
if ($request->files->count() > 0) { |
281
|
|
|
if (!$request->files->has('uploadFile')) { |
282
|
|
|
throw new BadRequestHttpException('"uploadFile" is required'); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** @var UploadedFile $uploadedFile */ |
286
|
|
|
$uploadedFile = $request->files->get('uploadFile'); |
287
|
|
|
$title = $uploadedFile->getClientOriginalName(); |
288
|
|
|
|
289
|
|
|
if (empty($title)) { |
290
|
|
|
throw new InvalidArgumentException('title is required'); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
// Handle the appropriate action based on the fileExistsOption |
294
|
|
|
if (!empty($fileExistsOption)) { |
295
|
|
|
// Check if a document with the same title and parent resource node already exists |
296
|
|
|
$existingDocument = $resourceRepository->findByTitleAndParentResourceNode($title, $parentResourceNodeId); |
297
|
|
|
if ($existingDocument) { |
298
|
|
|
if ('overwrite' === $fileExistsOption) { |
299
|
|
|
$existingDocument->setTitle($title); |
|
|
|
|
300
|
|
|
$existingDocument->setComment($comment); |
|
|
|
|
301
|
|
|
|
302
|
|
|
$resourceNode = $existingDocument->getResourceNode(); |
303
|
|
|
|
304
|
|
|
$resourceFile = $resourceNode->getFirstResourceFile(); |
305
|
|
|
if ($resourceFile instanceof ResourceFile) { |
306
|
|
|
$resourceFile->setFile($uploadedFile); |
307
|
|
|
$em->persist($resourceFile); |
308
|
|
|
} else { |
309
|
|
|
$existingDocument->setUploadFile($uploadedFile); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
$resourceNode->setUpdatedAt(new DateTime()); |
313
|
|
|
$existingDocument->setResourceNode($resourceNode); |
314
|
|
|
|
315
|
|
|
$em->persist($existingDocument); |
316
|
|
|
$em->flush(); |
317
|
|
|
|
318
|
|
|
// Return any data you need for further processing |
319
|
|
|
return [ |
320
|
|
|
'title' => $title, |
321
|
|
|
'filetype' => 'file', |
322
|
|
|
'comment' => $comment, |
323
|
|
|
]; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
if ('rename' == $fileExistsOption) { |
327
|
|
|
// Perform actions when file exists and 'rename' option is selected |
328
|
|
|
$newTitle = $this->generateUniqueTitle($title); // Generate a unique title |
329
|
|
|
$resource->setResourceName($newTitle); |
330
|
|
|
$resource->setUploadFile($uploadedFile); |
331
|
|
|
if (!empty($resourceLinkList)) { |
332
|
|
|
$resource->setResourceLinkArray($resourceLinkList); |
333
|
|
|
} |
334
|
|
|
$em->persist($resource); |
335
|
|
|
$em->flush(); |
336
|
|
|
|
337
|
|
|
// Return any data you need for further processing |
338
|
|
|
return [ |
339
|
|
|
'title' => $newTitle, |
340
|
|
|
'filetype' => 'file', |
341
|
|
|
'comment' => $comment, |
342
|
|
|
]; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
if ('nothing' == $fileExistsOption) { |
346
|
|
|
// Perform actions when file exists and 'nothing' option is selected |
347
|
|
|
// Display a message indicating that the file already exists |
348
|
|
|
// or perform any other desired actions based on your application's requirements |
349
|
|
|
$resource->setResourceName($title); |
350
|
|
|
$flashBag = $request->getSession()->getFlashBag(); |
351
|
|
|
$message = $translator ? $translator->trans('upload.already_exists') : 'Upload Already Exists'; |
352
|
|
|
$flashBag->add('warning', $message); |
353
|
|
|
|
354
|
|
|
throw new BadRequestHttpException($translator ? $translator->trans('file.already_exists') : 'The file already exists and was not uploaded.'); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
throw new InvalidArgumentException('Invalid fileExistsOption'); |
358
|
|
|
} else { |
359
|
|
|
$resource->setResourceName($title); |
360
|
|
|
$resource->setUploadFile($uploadedFile); |
361
|
|
|
$fileParsed = true; |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
// Get data in content and create a HTML file. |
367
|
|
|
if (!$fileParsed && $content) { |
368
|
|
|
$uploadedFile = CreateUploadedFile::fromString($title.'.html', 'text/html', $content); |
369
|
|
|
$resource->setUploadFile($uploadedFile); |
370
|
|
|
$fileParsed = true; |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
if (!$fileParsed) { |
374
|
|
|
throw new InvalidArgumentException('filetype was set to "file" but no upload file found'); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
break; |
378
|
|
|
|
379
|
|
|
case 'folder': |
380
|
|
|
break; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
// Set resource link list if exists. |
384
|
|
|
if (!empty($resourceLinkList)) { |
385
|
|
|
$resource->setResourceLinkArray($resourceLinkList); |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
return [ |
389
|
|
|
'title' => $title, |
390
|
|
|
'filetype' => $fileType, |
391
|
|
|
'comment' => $comment, |
392
|
|
|
]; |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
protected function handleCreateFileRequestUncompress(AbstractResource $resource, Request $request, EntityManager $em, KernelInterface $kernel): array |
396
|
|
|
{ |
397
|
|
|
// Get the parameters from the request |
398
|
|
|
$parentResourceNodeId = (int) $request->get('parentResourceNodeId'); |
399
|
|
|
$fileType = $request->get('filetype'); |
400
|
|
|
$resourceLinkList = $request->get('resourceLinkList', []); |
401
|
|
|
if (!empty($resourceLinkList)) { |
402
|
|
|
$resourceLinkList = !str_contains($resourceLinkList, '[') ? json_decode('['.$resourceLinkList.']', true) : json_decode($resourceLinkList, true); |
403
|
|
|
if (empty($resourceLinkList)) { |
404
|
|
|
$message = 'resourceLinkList is not a valid json. Use for example: [{"cid":1, "visibility":1}]'; |
405
|
|
|
|
406
|
|
|
throw new InvalidArgumentException($message); |
407
|
|
|
} |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
if (empty($fileType)) { |
411
|
|
|
throw new Exception('filetype needed: folder or file'); |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
if (0 === $parentResourceNodeId) { |
415
|
|
|
throw new Exception('parentResourceNodeId int value needed'); |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
if ('file' == $fileType && $request->files->count() > 0) { |
419
|
|
|
if (!$request->files->has('uploadFile')) { |
420
|
|
|
throw new BadRequestHttpException('"uploadFile" is required'); |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
$uploadedFile = $request->files->get('uploadFile'); |
424
|
|
|
$resourceTitle = $uploadedFile->getClientOriginalName(); |
425
|
|
|
$resource->setResourceName($resourceTitle); |
426
|
|
|
$resource->setUploadFile($uploadedFile); |
427
|
|
|
|
428
|
|
|
if ('zip' === $uploadedFile->getClientOriginalExtension()) { |
429
|
|
|
// Extract the files and subdirectories |
430
|
|
|
$extractedData = $this->extractZipFile($uploadedFile, $kernel); |
431
|
|
|
$folderStructure = $extractedData['folderStructure']; |
432
|
|
|
$extractPath = $extractedData['extractPath']; |
433
|
|
|
$documents = $this->saveZipContentsAsDocuments($folderStructure, $em, $resourceLinkList, $parentResourceNodeId, '', $extractPath, $processedItems); |
434
|
|
|
} |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
$resource->setParentResourceNode($parentResourceNodeId); |
438
|
|
|
|
439
|
|
|
return [ |
440
|
|
|
'filetype' => $fileType, |
441
|
|
|
'comment' => 'Uncompressed', |
442
|
|
|
]; |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
protected function handleUpdateRequest(AbstractResource $resource, ResourceRepository $repo, Request $request, EntityManager $em): AbstractResource |
446
|
|
|
{ |
447
|
|
|
$contentData = $request->getContent(); |
448
|
|
|
$resourceLinkList = []; |
449
|
|
|
if (!empty($contentData)) { |
450
|
|
|
$contentData = json_decode($contentData, true); |
451
|
|
|
if (isset($contentData['parentResourceNodeId']) && 1 === \count($contentData)) { |
452
|
|
|
$parentResourceNodeId = (int) $contentData['parentResourceNodeId']; |
453
|
|
|
} |
454
|
|
|
$title = $contentData['title'] ?? ''; |
455
|
|
|
$content = $contentData['contentFile'] ?? ''; |
456
|
|
|
$resourceLinkList = $contentData['resourceLinkListFromEntity'] ?? []; |
457
|
|
|
} else { |
458
|
|
|
$title = $request->get('title'); |
459
|
|
|
$content = $request->request->get('contentFile'); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
$repo->setResourceName($resource, $title); |
463
|
|
|
|
464
|
|
|
$resourceNode = $resource->getResourceNode(); |
465
|
|
|
$hasFile = $resourceNode->hasResourceFile(); |
466
|
|
|
|
467
|
|
|
if ($hasFile && !empty($content)) { |
468
|
|
|
// The content is updated by the ResourceNodeListener.php |
469
|
|
|
$resourceNode->setContent($content); |
470
|
|
|
foreach ($resourceNode->getResourceFiles() as $resourceFile) { |
471
|
|
|
$resourceFile->setSize(\strlen($content)); |
472
|
|
|
} |
473
|
|
|
$resource->setResourceNode($resourceNode); |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
$link = null; |
477
|
|
|
if (!empty($resourceLinkList)) { |
478
|
|
|
foreach ($resourceLinkList as $key => &$linkArray) { |
479
|
|
|
// Find the exact link. |
480
|
|
|
$linkId = $linkArray['id'] ?? 0; |
481
|
|
|
if (!empty($linkId)) { |
482
|
|
|
/** @var ResourceLink $link */ |
483
|
|
|
$link = $resourceNode->getResourceLinks()->filter(fn ($link) => $link->getId() === $linkId)->first(); |
484
|
|
|
|
485
|
|
|
if (null !== $link) { |
486
|
|
|
$link->setVisibility((int) $linkArray['visibility']); |
487
|
|
|
unset($resourceLinkList[$key]); |
488
|
|
|
|
489
|
|
|
$em->persist($link); |
490
|
|
|
} |
491
|
|
|
} |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
$resource->setResourceLinkArray($resourceLinkList); |
495
|
|
|
self::setLinks($resource, $em); |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
$isRecursive = !$hasFile; |
499
|
|
|
// If it's a folder then change the visibility to the children (That have the same link). |
500
|
|
|
if ($isRecursive && null !== $link) { |
501
|
|
|
$repo->copyVisibilityToChildren($resource->getResourceNode(), $link); |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
if (!empty($parentResourceNodeId)) { |
505
|
|
|
$parentResourceNode = $em->getRepository(ResourceNode::class)->find($parentResourceNodeId); |
506
|
|
|
if ($parentResourceNode) { |
507
|
|
|
$resourceNode->setParent($parentResourceNode); |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
$resourceNode->setUpdatedAt(new DateTime()); |
512
|
|
|
|
513
|
|
|
return $resource; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
private function saveZipContentsAsDocuments(array $folderStructure, EntityManager $em, $resourceLinkList = [], $parentResourceId = null, $currentPath = '', $extractPath = '', &$processedItems = []): array |
517
|
|
|
{ |
518
|
|
|
$documents = []; |
519
|
|
|
|
520
|
|
|
foreach ($folderStructure as $key => $item) { |
521
|
|
|
if (\is_array($item)) { |
522
|
|
|
$folderName = $key; |
523
|
|
|
$subFolderStructure = $item; |
524
|
|
|
|
525
|
|
|
$document = new CDocument(); |
526
|
|
|
$document->setTitle($folderName); |
527
|
|
|
$document->setFiletype('folder'); |
528
|
|
|
|
529
|
|
|
if (null !== $parentResourceId) { |
530
|
|
|
$document->setParentResourceNode($parentResourceId); |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
if (!empty($resourceLinkList)) { |
534
|
|
|
$document->setResourceLinkArray($resourceLinkList); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
$em->persist($document); |
538
|
|
|
$em->flush(); |
539
|
|
|
|
540
|
|
|
$documentId = $document->getResourceNode()->getId(); |
541
|
|
|
$documents[$documentId] = [ |
542
|
|
|
'name' => $document->getTitle(), |
543
|
|
|
'files' => [], |
544
|
|
|
]; |
545
|
|
|
|
546
|
|
|
$subDocuments = $this->saveZipContentsAsDocuments($subFolderStructure, $em, $resourceLinkList, $documentId, $currentPath.$folderName.'/', $extractPath, $processedItems); |
547
|
|
|
$documents[$documentId]['files'] = $subDocuments; |
548
|
|
|
} else { |
549
|
|
|
$fileName = $item; |
550
|
|
|
|
551
|
|
|
$document = new CDocument(); |
552
|
|
|
$document->setTitle($fileName); |
553
|
|
|
$document->setFiletype('file'); |
554
|
|
|
|
555
|
|
|
if (null !== $parentResourceId) { |
556
|
|
|
$document->setParentResourceNode($parentResourceId); |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
if (!empty($resourceLinkList)) { |
560
|
|
|
$document->setResourceLinkArray($resourceLinkList); |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
$filePath = $extractPath.'/'.$currentPath.$fileName; |
564
|
|
|
|
565
|
|
|
if (file_exists($filePath)) { |
566
|
|
|
$uploadedFile = new UploadedFile( |
567
|
|
|
$filePath, |
568
|
|
|
$fileName |
569
|
|
|
); |
570
|
|
|
|
571
|
|
|
$document->setUploadFile($uploadedFile); |
572
|
|
|
$em->persist($document); |
573
|
|
|
$em->flush(); |
574
|
|
|
|
575
|
|
|
$documentId = $document->getResourceNode()->getId(); |
576
|
|
|
$documents[$documentId] = [ |
577
|
|
|
'name' => $document->getTitle(), |
578
|
|
|
'files' => [], |
579
|
|
|
]; |
580
|
|
|
} else { |
581
|
|
|
error_log('File does not exist: '.$filePath); |
582
|
|
|
|
583
|
|
|
continue; |
584
|
|
|
} |
585
|
|
|
} |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
return $documents; |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
private function extractZipFile(UploadedFile $file, KernelInterface $kernel): array |
592
|
|
|
{ |
593
|
|
|
// Get the temporary path of the ZIP file |
594
|
|
|
$zipFilePath = $file->getRealPath(); |
595
|
|
|
|
596
|
|
|
// Create an instance of the ZipArchive class |
597
|
|
|
$zip = new ZipArchive(); |
598
|
|
|
$zip->open($zipFilePath); |
599
|
|
|
|
600
|
|
|
$cacheDirectory = $kernel->getCacheDir(); |
601
|
|
|
$extractPath = $cacheDirectory.'/'.uniqid('extracted_', true); |
602
|
|
|
mkdir($extractPath); |
603
|
|
|
|
604
|
|
|
// Extract the contents of the ZIP file |
605
|
|
|
$zip->extractTo($extractPath); |
606
|
|
|
|
607
|
|
|
// Array to store the sorted extracted paths |
608
|
|
|
$extractedPaths = []; |
609
|
|
|
|
610
|
|
|
// Iterate over each file or directory in the ZIP file |
611
|
|
|
for ($i = 0; $i < $zip->numFiles; $i++) { |
612
|
|
|
$filename = $zip->getNameIndex($i); |
613
|
|
|
$extractedPaths[] = $extractPath.'/'.$filename; |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
// Close the ZIP file |
617
|
|
|
$zip->close(); |
618
|
|
|
|
619
|
|
|
// Build the folder structure and file associations |
620
|
|
|
$folderStructure = $this->buildFolderStructure($extractedPaths, $extractPath); |
621
|
|
|
|
622
|
|
|
// Return the array of folder structure and the extraction path |
623
|
|
|
return [ |
624
|
|
|
'folderStructure' => $folderStructure, |
625
|
|
|
'extractPath' => $extractPath, |
626
|
|
|
]; |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
private function buildFolderStructure(array $paths, string $extractPath): array |
630
|
|
|
{ |
631
|
|
|
$folderStructure = []; |
632
|
|
|
|
633
|
|
|
foreach ($paths as $path) { |
634
|
|
|
$relativePath = str_replace($extractPath.'/', '', $path); |
635
|
|
|
$parts = explode('/', $relativePath); |
636
|
|
|
|
637
|
|
|
$currentLevel = &$folderStructure; |
638
|
|
|
|
639
|
|
|
foreach ($parts as $part) { |
640
|
|
|
if (!isset($currentLevel[$part])) { |
641
|
|
|
$currentLevel[$part] = []; |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
$currentLevel = &$currentLevel[$part]; |
645
|
|
|
} |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
return $this->formatFolderStructure($folderStructure); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
private function formatFolderStructure(array $folderStructure): array |
652
|
|
|
{ |
653
|
|
|
$result = []; |
654
|
|
|
|
655
|
|
|
foreach ($folderStructure as $folder => $contents) { |
656
|
|
|
$formattedContents = $this->formatFolderStructure($contents); |
657
|
|
|
|
658
|
|
|
if (!empty($formattedContents)) { |
659
|
|
|
$result[$folder] = $formattedContents; |
660
|
|
|
} elseif (!empty($folder)) { |
661
|
|
|
$result[] = $folder; |
662
|
|
|
} |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
return $result; |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
/** |
669
|
|
|
* Generates a unique filename by appending a random suffix. |
670
|
|
|
*/ |
671
|
|
|
private function generateUniqueTitle(string $title): string |
672
|
|
|
{ |
673
|
|
|
$info = pathinfo($title); |
674
|
|
|
$filename = $info['filename']; |
675
|
|
|
$extension = isset($info['extension']) ? '.'.$info['extension'] : ''; |
676
|
|
|
|
677
|
|
|
return $filename.'_'.uniqid().$extension; |
678
|
|
|
} |
679
|
|
|
} |
680
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.