Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
57.09% |
165 / 289 |
|
41.18% |
14 / 34 |
CRAP | |
0.00% |
0 / 1 |
Workspace | |
57.09% |
165 / 289 |
|
41.18% |
14 / 34 |
1084.23 | |
0.00% |
0 / 1 |
getAll | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
hasFilesChanged | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getHashOfAllFiles | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getOrCreateWorkspacePath | |
57.14% |
4 / 7 |
|
0.00% |
0 / 1 |
6.97 | |||
getOrCreateSubFolderPath | |
55.56% |
5 / 9 |
|
0.00% |
0 / 1 |
9.16 | |||
getId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getWorkspacePath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
deleteFiles | |
80.00% |
16 / 20 |
|
0.00% |
0 / 1 |
6.29 | |||
deleteFileFromFs | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
deleteFileFromDb | |
55.56% |
5 / 9 |
|
0.00% |
0 / 1 |
7.19 | |||
isPathLegal | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
importUncategorizedFiles | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
sortAndValidateToplevelFiles | |
100.00% |
30 / 30 |
|
100.00% |
1 / 1 |
9 | |||
validateFilesForCategory | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
2 | |||
categorizeFile | |
32.50% |
13 / 40 |
|
0.00% |
0 / 1 |
33.91 | |||
unpackRootlevelZipArchive | |
70.00% |
7 / 10 |
|
0.00% |
0 / 1 |
4.43 | |||
getExtractionDirName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
deleteRootlevelFiles | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
getFileById | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
getFileByName | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
countFilesOfAllSubFolders | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
countFiles | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
delete | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
storeAllFiles | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
20 | |||
removeVanishedFilesFromDB | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
storeFileMeta | |
88.89% |
24 / 27 |
|
0.00% |
0 / 1 |
7.07 | |||
getRequestedAttachments | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
3.03 | |||
getFileRelations | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
updateDependentFiles | |
40.00% |
2 / 5 |
|
0.00% |
0 / 1 |
4.94 | |||
getBookletResourcePaths | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
getWorkspaceHash | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setWorkspaceHash | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
loadFilesIntoCache | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | |
3 | /** @noinspection PhpUnhandledExceptionInspection */ |
4 | declare(strict_types=1); |
5 | |
6 | class Workspace { |
7 | protected int $workspaceId = 0; |
8 | protected string $workspacePath = ''; |
9 | public WorkspaceDAO $workspaceDAO; |
10 | |
11 | // dont' change order, it's the order of possible dependencies |
12 | const subFolders = ['Resource', 'Unit', 'Booklet', 'Testtakers', 'SysCheck']; |
13 | |
14 | static function getAll(): array { |
15 | $workspaces = []; |
16 | $class = get_called_class(); |
17 | |
18 | foreach (Folder::glob(DATA_DIR, 'ws_*') as $workspaceDir) { |
19 | $workspaceFolderNameParts = explode('_', $workspaceDir); |
20 | $workspaceId = (int) array_pop($workspaceFolderNameParts); |
21 | if (!$workspaceId) { |
22 | continue; |
23 | } |
24 | $workspaces[$workspaceId] = new $class($workspaceId); |
25 | } |
26 | |
27 | return $workspaces; |
28 | } |
29 | |
30 | public function hasFilesChanged(string $currentHash): bool { |
31 | $originalHash = $this->workspaceDAO->getWorkspaceHash(); |
32 | return $currentHash !== $originalHash; |
33 | } |
34 | |
35 | private static function getHashOfAllFiles(string $dir): array { |
36 | $result = []; |
37 | |
38 | $files = scandir($dir); |
39 | |
40 | foreach ($files as $file) { |
41 | if ($file != '.' && $file != '..') { |
42 | $path = $dir . '/' . $file; |
43 | if (is_dir($path)) { |
44 | $result = array_merge($result, self::getHashOfAllFiles($path)); |
45 | } else { |
46 | $result[] = [ |
47 | 'filename' => $file, |
48 | 'filemtime' => filemtime($path), |
49 | 'filesize' => filesize($path) |
50 | ]; |
51 | } |
52 | } |
53 | } |
54 | |
55 | return $result; |
56 | } |
57 | |
58 | function __construct(int $workspaceId) { |
59 | $this->workspaceId = $workspaceId; |
60 | $this->workspacePath = $this->getOrCreateWorkspacePath(); |
61 | $this->workspaceDAO = new WorkspaceDAO($this->workspaceId, $this->workspacePath); |
62 | } |
63 | |
64 | protected function getOrCreateWorkspacePath(): string { |
65 | $workspacePath = DATA_DIR . '/ws_' . $this->workspaceId; |
66 | if (file_exists($workspacePath) and !is_dir($workspacePath)) { |
67 | throw new Exception("Workspace dir $this->workspaceId seems not to be a proper directory!"); |
68 | } |
69 | if (!file_exists($workspacePath)) { |
70 | if (!mkdir($workspacePath)) { |
71 | throw new Exception("Could not create workspace dir $this->workspaceId"); |
72 | } |
73 | } |
74 | return $workspacePath; |
75 | } |
76 | |
77 | public function getOrCreateSubFolderPath(string $type): string { |
78 | $subFolderPath = $this->workspacePath . '/' . $type; |
79 | if (!in_array($type, $this::subFolders)) { |
80 | throw new Exception("Invalid type `$type`!"); |
81 | } |
82 | if (file_exists($subFolderPath) and !is_dir($subFolderPath)) { |
83 | throw new Exception("Workspace dir `$subFolderPath` seems not to be a proper directory!"); |
84 | } |
85 | if (!file_exists($subFolderPath)) { |
86 | if (!mkdir($subFolderPath)) { |
87 | throw new Exception("Could not create workspace dir `$subFolderPath`"); |
88 | } |
89 | } |
90 | return $subFolderPath; |
91 | } |
92 | |
93 | public function getId(): int { |
94 | return $this->workspaceId; |
95 | } |
96 | |
97 | public function getWorkspacePath(): string { |
98 | return $this->workspacePath; |
99 | } |
100 | |
101 | public function deleteFiles(array $filesToDelete): FileDeletionReport { |
102 | $deletionReport = new FileDeletionReport(); |
103 | |
104 | $cachedFilesToDelete = $this->workspaceDAO->getFiles($filesToDelete, true); |
105 | $blockedFiles = $this->workspaceDAO->getBlockedFiles(array_merge(...array_values($cachedFilesToDelete))); |
106 | |
107 | foreach ($filesToDelete as $localFilePath) { |
108 | $pathParts = explode('/', $localFilePath, 2); |
109 | |
110 | if (count($pathParts) < 2) { |
111 | $deletionReport->incorrect_path[] = $localFilePath; |
112 | continue; |
113 | } |
114 | |
115 | list($type, $name) = $pathParts; |
116 | |
117 | $cachedFile = $cachedFilesToDelete[$type][$name] ?? null; |
118 | |
119 | // file does not exist in db means, it must be something not validatable like sysCheck-Reports |
120 | if ($cachedFile) { |
121 | if (isset($blockedFiles[$localFilePath])) { |
122 | $deletionReport->was_used[] = $localFilePath; |
123 | continue; |
124 | } |
125 | |
126 | if (!$this->deleteFileFromDb($cachedFile)) { |
127 | $deletionReport->error[] = $localFilePath; |
128 | continue; |
129 | } |
130 | } |
131 | $fieldName = $this->deleteFileFromFs($this->workspacePath . '/' . $localFilePath); |
132 | $deletionReport->$fieldName[] = $localFilePath; |
133 | } |
134 | |
135 | return $deletionReport; |
136 | } |
137 | |
138 | protected function deleteFileFromFs(string $fullPath): string { |
139 | if (!file_exists($fullPath)) { |
140 | return 'did_not_exist'; |
141 | } |
142 | |
143 | if ($this->isPathLegal($fullPath) and unlink($fullPath)) { |
144 | return 'deleted'; |
145 | } |
146 | |
147 | return 'not_allowed'; |
148 | } |
149 | |
150 | private function deleteFileFromDb(?File $file): bool { |
151 | try { |
152 | if (is_a($file, XMLFileTesttakers::class)) { |
153 | $this->workspaceDAO->deleteLoginSource($file->getName()); |
154 | } |
155 | |
156 | if (is_a($file, ResourceFile::class) and $file->isPackage()) { |
157 | $file->uninstallPackage(); |
158 | } |
159 | |
160 | $this->workspaceDAO->deleteFile($file); |
161 | |
162 | } catch (Exception $e) { |
163 | echo $e->getMessage(); |
164 | return false; |
165 | } |
166 | return true; |
167 | } |
168 | |
169 | protected function isPathLegal(string $path): bool { |
170 | return substr_count($path, '..') == 0; |
171 | } |
172 | |
173 | /** takes files from the workspace-dir toplevel and puts it to the correct subdir if valid |
174 | * @return array{ |
175 | * type: string, |
176 | * warning: array, |
177 | * error: array, |
178 | * info: array} |
179 | */ |
180 | public function importUncategorizedFiles(array $fileNames): array { |
181 | $toDeleteFilePaths = []; |
182 | $toSortInFilePaths = []; |
183 | foreach ($fileNames as $fileName) { |
184 | if (FileExt::has($fileName, 'ZIP') and !FileExt::has($fileName, 'ITCR.ZIP')) { |
185 | array_push($toSortInFilePaths, ...$this->unpackRootlevelZipArchive($fileName)); |
186 | array_push($toDeleteFilePaths, $fileName, $this->getExtractionDirName($fileName)); |
187 | } else { |
188 | $toSortInFilePaths[] = $fileName; |
189 | $toDeleteFilePaths[] = $fileName; |
190 | } |
191 | } |
192 | |
193 | $importedFiles = $this->sortAndValidateToplevelFiles($toSortInFilePaths); |
194 | $this->deleteRootlevelFiles($toDeleteFilePaths); |
195 | |
196 | return $importedFiles; |
197 | } |
198 | |
199 | private function sortAndValidateToplevelFiles(array $relativeFilePaths): array { |
200 | $filesAfterSorting = []; |
201 | $pathsPerType = array_fill_keys(Workspace::subFolders, []); |
202 | |
203 | foreach ($relativeFilePaths as $relativeFilePath) { |
204 | $pathsPerType[File::determineType($this->workspacePath . '/' . $relativeFilePath)][] = $relativeFilePath; |
205 | } |
206 | |
207 | $pathsPerType = [ |
208 | // Resource and Unit are merged in order to save one call to getFilesWhere(['type' => 'Resource']), as this is the |
209 | // most expensive call in the following loop (Unit depends on Resource anyway) |
210 | !empty($pathsPerType['Unit']) ? 'Unit' : 'Resource' => array_merge( |
211 | $pathsPerType['Resource'], |
212 | $pathsPerType['Unit'] |
213 | ), |
214 | 'Booklet' => $pathsPerType['Booklet'], |
215 | 'Testtakers' => $pathsPerType['Testtakers'], |
216 | 'SysCheck' => $pathsPerType['SysCheck'], |
217 | 'xml' => $pathsPerType['xml'] |
218 | ]; |
219 | |
220 | foreach ($pathsPerType as $type => $filePaths) { |
221 | if (!empty($filePaths)) { |
222 | // these files are all not valid from the start |
223 | if ($type === 'xml') { |
224 | foreach ($filePaths as $path) { |
225 | $file = File::get($this->workspacePath . '/' . $path); |
226 | $filesAfterSorting[$path] = ['type' => $file->getType(), ...$file->getValidationReport()]; |
227 | } |
228 | continue; |
229 | } |
230 | |
231 | $files = $this->validateFilesForCategory($filePaths, FileType::tryFrom($type)); |
232 | foreach ($files as $filePath => $file) { |
233 | if ($file->isValid()) { |
234 | $this->categorizeFile($filePath, $file); |
235 | $this->workspaceDAO->storeFile($file); |
236 | $this->storeFileMeta($file); |
237 | $this->updateDependentFiles($file); |
238 | } |
239 | |
240 | $filesAfterSorting[$filePath] = ['type' => $file->getType(), ...$file->getValidationReport()]; |
241 | } |
242 | } |
243 | } |
244 | |
245 | return $filesAfterSorting; |
246 | } |
247 | |
248 | protected function validateFilesForCategory(array $localFilePaths, FileType $highestTypeAmongFiles): array { |
249 | $filesPerType = []; |
250 | $workspaceCache = new WorkspaceCache($this); |
251 | $this->loadFilesIntoCache($workspaceCache, $highestTypeAmongFiles); |
252 | |
253 | foreach ($localFilePaths as $localFilePath) { |
254 | $file = File::get($this->workspacePath . '/' . $localFilePath); |
255 | $workspaceCache->addFile($file->getType(), $file, true); |
256 | $filesPerType[$localFilePath] = $file; |
257 | } |
258 | |
259 | $workspaceCache->validate($highestTypeAmongFiles->value); |
260 | |
261 | return $filesPerType; |
262 | } |
263 | |
264 | protected function categorizeFile(string $localFilePath, File $file): bool { |
265 | $targetFolder = $this->workspacePath . '/' . $file->getType(); |
266 | |
267 | if (!file_exists($targetFolder)) { |
268 | if (!mkdir($targetFolder)) { |
269 | $file->report('error', "Could not create folder: `$targetFolder`."); |
270 | return false; |
271 | } |
272 | } |
273 | |
274 | $targetFilePath = $targetFolder . '/' . basename($localFilePath); |
275 | |
276 | if (file_exists($targetFilePath)) { |
277 | $oldFile = File::get($targetFilePath, $file->getType()); |
278 | |
279 | if ($oldFile->getId() !== $file->getId()) { |
280 | $file->report( |
281 | 'error', |
282 | "File of name `{$oldFile->getName()}` did already exist. " |
283 | . "Overwriting was rejected since new file's ID (`{$file->getId()}`) differs from old one (`{$oldFile->getId()}`)." |
284 | ); |
285 | return false; |
286 | } |
287 | |
288 | if ($oldFile->getVeronaModuleId() !== $file->getVeronaModuleId()) { |
289 | $file->report( |
290 | 'error', |
291 | "File of name `{$oldFile->getName()}` did already exist. " |
292 | . "Overwriting was rejected since new file's Verona-Module-ID (`{$file->getVeronaModuleId()}`) differs from old one (`{$oldFile->getVeronaModuleId()}`)." |
293 | . "Filenames not according to the Verona-standard are a bad idea anyway and and will be forbidden in the future." |
294 | ); |
295 | return false; |
296 | } |
297 | |
298 | if (!Version::isCompatible($oldFile->getVersion(), $file->getVersion())) { |
299 | $file->report( |
300 | 'error', |
301 | "File of name `{$oldFile->getName()}` did already exist. " |
302 | . "Overwriting was rejected since version conflict between old ({$oldFile->getVersion()}) and new ({$file->getVersion()}) file." |
303 | . "Filenames not according to the Verona-standard are a bad idea anyway and and will be forbidden in the future." |
304 | ); |
305 | return false; |
306 | } |
307 | |
308 | if (!unlink($targetFilePath)) { |
309 | $file->report('error', "Could not delete file: `$targetFolder/$localFilePath`"); |
310 | return false; |
311 | } |
312 | |
313 | $file->report('warning', "File of name `{$oldFile->getName()}` did already exist and was overwritten."); |
314 | } |
315 | |
316 | if (!rename($this->workspacePath . '/' . $localFilePath, $targetFilePath)) { |
317 | $file->report('error', "Could not move file to `$targetFolder/$localFilePath`"); |
318 | return false; |
319 | } |
320 | |
321 | $file->readFileMeta($targetFilePath); |
322 | |
323 | return true; |
324 | } |
325 | |
326 | protected function unpackRootlevelZipArchive(string $fileName): array { |
327 | $filePath = "$this->workspacePath/$fileName"; |
328 | $extractionFolder = $this->getExtractionDirName($fileName); |
329 | $extractionPath = "$this->workspacePath/$extractionFolder"; |
330 | |
331 | // sometimes in error cases there are remains from previous attempts |
332 | if (file_exists($extractionPath) and is_dir($extractionPath)) { |
333 | Folder::deleteContentsRecursive($extractionPath); |
334 | rmdir($extractionPath); |
335 | } |
336 | |
337 | if (!mkdir($extractionPath)) { |
338 | throw new Exception("Could not create directory for extracted files: `$extractionPath`"); |
339 | } |
340 | |
341 | ZIP::extract($filePath, $extractionPath); |
342 | |
343 | return Folder::getContentsFlat($extractionPath, $extractionFolder); |
344 | } |
345 | |
346 | protected function getExtractionDirName(string $fileName): string { |
347 | return "{$fileName}_Extract"; |
348 | } |
349 | |
350 | protected function deleteRootlevelFiles(array $relativePaths): void { |
351 | foreach ($relativePaths as $relativePath) { |
352 | $filePath = "$this->workspacePath/$relativePath"; |
353 | if (is_dir($filePath)) { |
354 | Folder::deleteContentsRecursive($filePath); |
355 | rmdir($filePath); |
356 | } |
357 | if (is_file($filePath)) { |
358 | unlink($filePath); |
359 | } |
360 | } |
361 | } |
362 | |
363 | public function getFileById(string $type, string $fileId): File { |
364 | if ($file = $this->workspaceDAO->getFileById($fileId, $type)) { |
365 | if ($file->isValid()) { |
366 | return $file; |
367 | } |
368 | } |
369 | |
370 | throw new HttpError("No $type with id `$fileId` found on workspace `$this->workspaceId`!", 404); |
371 | } |
372 | |
373 | public function getFileByName(string $type, string $fileName): File { |
374 | $file = File::get("$this->workspacePath/$type/$fileName", $type); |
375 | |
376 | if ($file->isValid()) { |
377 | return $file; |
378 | } |
379 | |
380 | throw new HttpError("No $type with name `$fileName` found on workspace `$this->workspaceId`!", 404); |
381 | } |
382 | |
383 | public function countFilesOfAllSubFolders(): array { |
384 | $result = []; |
385 | |
386 | foreach ($this::subFolders as $type) { |
387 | $result[$type] = $this->countFiles($type); |
388 | } |
389 | |
390 | return $result; |
391 | } |
392 | |
393 | public function countFiles(string $type): int { |
394 | $pattern = ($type == 'Resource') ? "*.*" : "*.[xX][mM][lL]"; |
395 | return count(Folder::glob($this->getOrCreateSubFolderPath($type), $pattern)); |
396 | } |
397 | |
398 | public function delete(): void { |
399 | Folder::deleteContentsRecursive($this->workspacePath); |
400 | rmdir($this->workspacePath); |
401 | } |
402 | |
403 | // TODO unit-test |
404 | public function storeAllFiles(): array { |
405 | $workspaceCache = new WorkspaceCache($this); |
406 | $workspaceCache->loadFiles(); |
407 | |
408 | $typeStats = array_fill_keys(Workspace::subFolders, 0); |
409 | $loginStats = [ |
410 | 'added' => 0 |
411 | ]; |
412 | $invalidCount = 0; |
413 | |
414 | $loginStats['deleted'] = $this->removeVanishedFilesFromDB($workspaceCache); |
415 | |
416 | $workspaceCache->validate(); |
417 | |
418 | foreach ($workspaceCache->getFiles(true) as $file) { |
419 | /* @var File $file */ |
420 | |
421 | if (!$file->isValid()) { |
422 | $invalidCount++; |
423 | } |
424 | |
425 | $this->workspaceDAO->storeFile($file); |
426 | $typeStats[$file->getType()] += 1; |
427 | } |
428 | |
429 | foreach ($workspaceCache->getFiles(true) as $file) { |
430 | $stats = $this->storeFileMeta($file); |
431 | |
432 | $loginStats['deleted'] += $stats['logins_deleted']; |
433 | $loginStats['added'] += $stats['logins_added']; |
434 | } |
435 | |
436 | return [ |
437 | 'valid' => $typeStats, |
438 | 'invalid' => $invalidCount, |
439 | 'logins' => $loginStats |
440 | ]; |
441 | } |
442 | |
443 | private function removeVanishedFilesFromDB(WorkspaceCache $workspaceCache): int { |
444 | $filesInDb = $this->workspaceDAO->getAllFiles(); |
445 | $filesInFolder = $workspaceCache->getFiles(true); |
446 | $deletedLogins = 0; |
447 | |
448 | foreach ($filesInDb as $fileSet) { |
449 | foreach ($fileSet as $file) { |
450 | /* @var File $file */ |
451 | |
452 | if (!isset($filesInFolder[$file->getPath()])) { |
453 | $this->workspaceDAO->deleteFile($file); |
454 | $deletedLogins += $this->workspaceDAO->deleteLoginSource($file->getName()); |
455 | } |
456 | } |
457 | } |
458 | |
459 | return $deletedLogins; |
460 | } |
461 | |
462 | // TODO unit-test |
463 | private function storeFileMeta(File $file): ?array { |
464 | $stats = [ |
465 | 'logins_deleted' => 0, |
466 | 'logins_added' => 0, |
467 | 'resource_packages_installed' => 0, |
468 | 'attachments_noted' => 0, |
469 | 'resolved_relations' => 0, |
470 | 'relations_resolved' => 0, |
471 | 'relations_unresolved' => 0 |
472 | ]; |
473 | |
474 | if (!$file->isValid()) { |
475 | return $stats; |
476 | } |
477 | |
478 | if ($file::canBeRelationSubject) { |
479 | list($relationsUnresolved) = $this->workspaceDAO->storeRelations($file); |
480 | $stats['relations_resolved'] = count($file->getRelations()) - count($relationsUnresolved); |
481 | $stats['relations_unresolved'] = count($relationsUnresolved); |
482 | } |
483 | |
484 | if (is_a($file, XMLFileTesttakers::class)) { |
485 | list($deleted, $added) = $this->workspaceDAO->updateLoginSource($file->getName(), $file->getAllLogins()); |
486 | $stats['logins_deleted'] = $deleted; |
487 | $stats['logins_added'] = $added; |
488 | } |
489 | |
490 | if (is_a($file, ResourceFile::class) and $file->isPackage()) { |
491 | $file->installPackage(); |
492 | $stats['resource_packages_installed'] = 1; |
493 | } |
494 | |
495 | if (is_a($file, XMLFileBooklet::class)) { |
496 | $requestedAttachments = $this->getRequestedAttachments($file); |
497 | $this->workspaceDAO->updateUnitDefsAttachments($file->getId(), $requestedAttachments); |
498 | $stats['attachments_noted'] = count($requestedAttachments); |
499 | } |
500 | |
501 | return $stats; |
502 | } |
503 | |
504 | public function getRequestedAttachments(XMLFileBooklet $booklet): array { |
505 | if (!$booklet->isValid()) { |
506 | return []; |
507 | } |
508 | |
509 | $requestedAttachments = []; |
510 | foreach ($booklet->getUnitIds() as $uniId) { |
511 | $unit = $this->getFileById('Unit', $uniId); |
512 | /* @var $unit XMLFileUnit */ |
513 | $requestedAttachments = array_merge($requestedAttachments, $unit->getRequestedAttachments()); |
514 | } |
515 | return $requestedAttachments; |
516 | } |
517 | |
518 | public function getFileRelations(File $file): array { |
519 | return $this->workspaceDAO->getFileRelations($file->getName(), $file->getType()); |
520 | } |
521 | |
522 | /** checks files that are depending on the current file, upstream */ |
523 | private function updateDependentFiles(File $file): void { |
524 | $relatingFiles = $this->workspaceDAO->getDependentFilesByTypes($file, ['Booklet']); |
525 | |
526 | foreach ($relatingFiles as $fileset) { |
527 | foreach ($fileset as $file) { |
528 | $requestedAttachments = $this->getRequestedAttachments($file); |
529 | $this->workspaceDAO->updateUnitDefsAttachments($file->getId(), $requestedAttachments); |
530 | } |
531 | } |
532 | |
533 | } |
534 | |
535 | public function getBookletResourcePaths(string $bookletId): array { |
536 | $resourceList = $this->workspaceDAO->getBookletResourcePaths($bookletId); |
537 | $resourceListStructured = []; |
538 | foreach ($resourceList as $item) { |
539 | $resourceListStructured[$item['id']][$item['relationship_type']][] = "{$item['type']}/{$item['name']}"; |
540 | $path = "/ws_$this->workspaceId/{$item['type']}/{$item['name']}"; |
541 | CacheService::storeFile($path); |
542 | } |
543 | return $resourceListStructured; |
544 | } |
545 | |
546 | public function getWorkspaceHash(): string { |
547 | return hash('XXH3', serialize(self::getHashOfAllFiles($this->getWorkspacePath()))); |
548 | } |
549 | |
550 | public function setWorkspaceHash(): void { |
551 | $this->workspaceDAO->setWorkspaceHash($this->getWorkspaceHash()); |
552 | } |
553 | |
554 | private function loadFilesIntoCache(WorkspaceCache $workspaceCache, FileType $type): void { |
555 | foreach (FileType::getDependenciesOfType($type) as $dependingType) { |
556 | $files = $this->workspaceDAO->getAllFilesWhere(['type' => $dependingType]); |
557 | foreach ($files[$dependingType] as $file) { |
558 | $workspaceCache->addFile($dependingType, $file); |
559 | } |
560 | } |
561 | } |
562 | } |