Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.63% |
1 / 158 |
|
0.00% |
0 / 26 |
CRAP | |
0.00% |
0 / 1 |
MediaSource | |
0.63% |
1 / 158 |
|
0.00% |
0 / 26 |
3022.93 | |
0.00% |
0 / 1 |
enableAccessControl | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getLanguage | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getRootClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
add | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
delete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDirectories | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getDirectory | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFileInfo | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
20 | |||
getFileStream | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
download | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
getBaseName | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
forceMimeType | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getOrCreatePath | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
getArrayFromJson | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getServiceLocator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRootClassUri | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getLang | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getMediaService | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getFileManagement | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
removeSchemaFromUriOrLink | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getProcessedFileStream | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getPreparer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFileSourceUnserializer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
searchDirectories | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
56 | |||
getPermissionsMapper | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
__destruct | |
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
5.67 |
1 | <?php |
2 | |
3 | /** |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; under version 2 |
7 | * of the License (non-upgradable). |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * |
18 | * Copyright (c) 2014-2020 (original work) Open Assessment Technologies SA; |
19 | */ |
20 | |
21 | namespace oat\taoMediaManager\model; |
22 | |
23 | use oat\generis\model\OntologyAwareTrait; |
24 | use oat\oatbox\Configurable; |
25 | use oat\oatbox\log\LoggerAwareTrait; |
26 | use oat\oatbox\service\ServiceManager; |
27 | use oat\tao\model\accessControl\AccessControlEnablerInterface; |
28 | use oat\tao\model\media\MediaManagement; |
29 | use oat\tao\model\media\mediaSource\DirectorySearchQuery; |
30 | use oat\tao\model\media\ProcessedFileStreamAware; |
31 | use oat\taoMediaManager\model\export\service\MediaResourcePreparerInterface; |
32 | use oat\taoMediaManager\model\mapper\MediaSourcePermissionsMapper; |
33 | use oat\taoMediaManager\model\fileManagement\FileManagement; |
34 | use oat\taoMediaManager\model\fileManagement\FileSourceUnserializer; |
35 | use Psr\Http\Message\StreamInterface; |
36 | use tao_helpers_Uri; |
37 | use tao_models_classes_FileNotFoundException; |
38 | |
39 | use function GuzzleHttp\Psr7\stream_for; |
40 | |
41 | class MediaSource extends Configurable implements |
42 | MediaManagement, |
43 | ProcessedFileStreamAware, |
44 | AccessControlEnablerInterface |
45 | { |
46 | use LoggerAwareTrait; |
47 | use OntologyAwareTrait; |
48 | |
49 | public const SCHEME_NAME = 'taomedia://mediamanager/'; |
50 | |
51 | /** @var MediaService */ |
52 | protected $mediaService; |
53 | |
54 | /** @var FileManagement */ |
55 | protected $fileManagementService; |
56 | |
57 | /** @var MediaSourcePermissionsMapper */ |
58 | private $permissionsMapper; |
59 | |
60 | /** @var string[] */ |
61 | private $tmpFiles = []; |
62 | |
63 | public function enableAccessControl(): AccessControlEnablerInterface |
64 | { |
65 | $this->getPermissionsMapper()->enableAccessControl(); |
66 | |
67 | return $this; |
68 | } |
69 | |
70 | /** |
71 | * Returns the language URI to be used |
72 | * @return string |
73 | */ |
74 | protected function getLanguage() |
75 | { |
76 | return $this->hasOption('lang') |
77 | ? $this->getOption('lang') |
78 | : ''; |
79 | } |
80 | |
81 | public function getRootClass() |
82 | { |
83 | return $this->getClass($this->getRootClassUri()); |
84 | } |
85 | |
86 | /** |
87 | * (non-PHPdoc) |
88 | * |
89 | * @see \oat\tao\model\media\MediaManagement::add |
90 | */ |
91 | public function add($source, $fileName, $parent, $mimetype = null) |
92 | { |
93 | if (!file_exists($source)) { |
94 | throw new tao_models_classes_FileNotFoundException($source); |
95 | } |
96 | |
97 | $clazz = $this->getOrCreatePath($parent); |
98 | |
99 | $service = $this->getMediaService(); |
100 | $instanceUri = $service->createMediaInstance($source, $clazz->getUri(), $this->getLang(), $fileName, $mimetype); |
101 | |
102 | return $this->getFileInfo($instanceUri); |
103 | } |
104 | |
105 | /** |
106 | * (non-PHPdoc) |
107 | * |
108 | * @see \oat\tao\model\media\MediaManagement::delete |
109 | */ |
110 | public function delete($link) |
111 | { |
112 | return $this->getMediaService()->deleteResource($this->getResource(tao_helpers_Uri::decode($link))); |
113 | } |
114 | |
115 | public function getDirectories(DirectorySearchQuery $params): array |
116 | { |
117 | return $this->searchDirectories( |
118 | $params->getParentLink(), |
119 | $params->getFilter(), |
120 | $params->getDepth(), |
121 | $params->getChildrenLimit(), |
122 | $params->getChildrenOffset() |
123 | ); |
124 | } |
125 | |
126 | /** |
127 | * @inheritDoc |
128 | */ |
129 | public function getDirectory($parentLink = '', $acceptableMime = [], $depth = 1) |
130 | { |
131 | return $this->searchDirectories($parentLink, $acceptableMime, $depth, 0, 0); |
132 | } |
133 | |
134 | /** |
135 | * (non-PHPdoc) |
136 | * |
137 | * @see \oat\tao\model\media\MediaBrowser::getFileInfo |
138 | */ |
139 | public function getFileInfo($link) |
140 | { |
141 | // get the media link from the resource |
142 | $resource = $this->getResource(tao_helpers_Uri::decode($this->removeSchemaFromUriOrLink($link))); |
143 | $properties = [ |
144 | $this->getProperty(TaoMediaOntology::PROPERTY_LINK), |
145 | $this->getProperty(TaoMediaOntology::PROPERTY_MIME_TYPE), |
146 | $this->getProperty(TaoMediaOntology::PROPERTY_ALT_TEXT) |
147 | ]; |
148 | |
149 | $propertiesValues = $resource->getPropertiesValues($properties); |
150 | |
151 | $fileLink = $propertiesValues[TaoMediaOntology::PROPERTY_LINK][0] ?? null; |
152 | $mime = $propertiesValues[TaoMediaOntology::PROPERTY_MIME_TYPE][0] ?? null; |
153 | $fileLink = $fileLink instanceof \core_kernel_classes_Resource ? $fileLink->getUri() : (string)$fileLink; |
154 | $fileLink = $this->getFileSourceUnserializer()->unserialize($fileLink); |
155 | |
156 | if (!isset($mime, $fileLink)) { |
157 | throw new tao_models_classes_FileNotFoundException($link); |
158 | } |
159 | |
160 | // add the alt text to file array |
161 | $altArray = $propertiesValues[TaoMediaOntology::PROPERTY_ALT_TEXT] ?? null; |
162 | $alt = $resource->getLabel(); |
163 | if (count($altArray) > 0) { |
164 | $alt = (string)$altArray[0]; |
165 | } |
166 | |
167 | return $this->getPermissionsMapper()->map( |
168 | [ |
169 | 'name' => $resource->getLabel(), |
170 | 'uri' => self::SCHEME_NAME . tao_helpers_Uri::encode($link), |
171 | 'mime' => (string)$mime, |
172 | 'size' => $this->getFileManagement()->getFileSize($fileLink), |
173 | 'alt' => $alt, |
174 | 'link' => $fileLink |
175 | ], |
176 | $resource->getUri() |
177 | ); |
178 | } |
179 | |
180 | /** |
181 | * @param string $link |
182 | * @return \Psr\Http\Message\StreamInterface |
183 | * @throws \core_kernel_persistence_Exception |
184 | * @throws tao_models_classes_FileNotFoundException |
185 | */ |
186 | public function getFileStream($link) |
187 | { |
188 | $resource = $this->getResource(tao_helpers_Uri::decode($link)); |
189 | $fileLink = $resource->getOnePropertyValue( |
190 | $this->getProperty(TaoMediaOntology::PROPERTY_LINK) |
191 | ); |
192 | |
193 | if (is_null($fileLink)) { |
194 | throw new tao_models_classes_FileNotFoundException($link); |
195 | } |
196 | |
197 | $fileLink = $fileLink instanceof \core_kernel_classes_Resource ? $fileLink->getUri() : (string)$fileLink; |
198 | $fileLink = $this->getFileSourceUnserializer()->unserialize($fileLink); |
199 | |
200 | return $this->getFileManagement()->getFileStream($fileLink); |
201 | } |
202 | |
203 | /** |
204 | * (non-PHPdoc) |
205 | * |
206 | * @see \oat\tao\model\media\MediaBrowser::download |
207 | * @deprecated |
208 | */ |
209 | public function download($link) |
210 | { |
211 | $this->logInfo('Deprecated, creates tmpfiles'); |
212 | $stream = $this->getFileStream($link); |
213 | $filename = tempnam(sys_get_temp_dir(), 'media'); |
214 | $fh = fopen($filename, 'w'); |
215 | while (!$stream->eof()) { |
216 | fwrite($fh, $stream->read(1048576)); |
217 | } |
218 | fclose($fh); |
219 | |
220 | $this->tmpFiles[] = $filename; |
221 | |
222 | return $filename; |
223 | } |
224 | |
225 | /** |
226 | * @param string $link |
227 | * @return string |
228 | * @throws \core_kernel_persistence_Exception |
229 | * @throws tao_models_classes_FileNotFoundException |
230 | */ |
231 | public function getBaseName($link) |
232 | { |
233 | $stream = $this->getFileStream($link); |
234 | $filename = $stream->getMetadata('uri'); |
235 | |
236 | if ($filename === 'php://temp') { |
237 | // We are currently retrieving a remote resource (e.g. on Amazon S3). |
238 | $fileinfo = $this->getFileInfo($link); |
239 | $filename = $fileinfo['link']; |
240 | } |
241 | |
242 | return basename($filename); |
243 | } |
244 | |
245 | /** |
246 | * Force the mime-type of a resource |
247 | * |
248 | * @param string $link |
249 | * @param string $mimeType |
250 | * @return boolean |
251 | */ |
252 | public function forceMimeType($link, $mimeType) |
253 | { |
254 | $resource = $this->getResource(tao_helpers_Uri::decode($link)); |
255 | return $resource->editPropertyValues( |
256 | $this->getProperty(TaoMediaOntology::PROPERTY_MIME_TYPE), |
257 | $mimeType |
258 | ); |
259 | } |
260 | |
261 | /** |
262 | * |
263 | * @param string $path |
264 | * @return \core_kernel_classes_Class |
265 | */ |
266 | private function getOrCreatePath($path) |
267 | { |
268 | $rootClass = $this->getRootClass(); |
269 | |
270 | if ($path === '') { |
271 | return $rootClass; |
272 | } |
273 | |
274 | // If the path is a class URI, returns the existing class. |
275 | $class = $this->getClass(tao_helpers_Uri::decode($path)); |
276 | if ($class->isSubClassOf($rootClass) || $class->equals($rootClass) || $class->exists()) { |
277 | return $class; |
278 | } |
279 | |
280 | // If the given path is a json-encoded array, creates the full path from root class. |
281 | $labels = $this->getArrayFromJson($path); |
282 | if ($labels) { |
283 | return $rootClass->createSubClassPathByLabel($labels); |
284 | } |
285 | |
286 | // Retrieve or create a direct subclass of the root class. |
287 | return $rootClass->retrieveOrCreateSubClassByLabel($path); |
288 | } |
289 | |
290 | /** |
291 | * Tries to find a json-encoded array in the given string. |
292 | * |
293 | * If string is actually a json string and a json-encoded array, returns the array. |
294 | * Else, returns false. |
295 | * |
296 | * @param string $string |
297 | * @return array|bool |
298 | */ |
299 | private function getArrayFromJson($string) |
300 | { |
301 | $decoded = json_decode($string); |
302 | |
303 | return $decoded !== null && is_array($decoded) |
304 | ? $decoded |
305 | : false; |
306 | } |
307 | |
308 | /** |
309 | * Get the service Locator |
310 | * |
311 | * @return ServiceManager |
312 | */ |
313 | protected function getServiceLocator() |
314 | { |
315 | return ServiceManager::getServiceManager(); |
316 | } |
317 | |
318 | protected function getRootClassUri() |
319 | { |
320 | return $this->hasOption('rootClass') |
321 | ? $this->getOption('rootClass') |
322 | : MediaService::singleton()->getRootClass(); |
323 | } |
324 | |
325 | protected function getLang() |
326 | { |
327 | return $this->hasOption('lang') ? $this->getOption('lang') : ''; |
328 | } |
329 | |
330 | /** |
331 | * @return MediaService |
332 | */ |
333 | protected function getMediaService() |
334 | { |
335 | if (!$this->mediaService) { |
336 | $this->mediaService = MediaService::singleton(); |
337 | } |
338 | return $this->mediaService; |
339 | } |
340 | |
341 | /** |
342 | * @return FileManagement |
343 | */ |
344 | protected function getFileManagement() |
345 | { |
346 | if (!$this->fileManagementService) { |
347 | $this->fileManagementService = $this->getServiceLocator()->get(FileManagement::SERVICE_ID); |
348 | } |
349 | return $this->fileManagementService; |
350 | } |
351 | |
352 | private function removeSchemaFromUriOrLink(string $uriOrLink): string |
353 | { |
354 | return str_replace(self::SCHEME_NAME, '', $uriOrLink); |
355 | } |
356 | |
357 | public function getProcessedFileStream(string $link): StreamInterface |
358 | { |
359 | return stream_for( |
360 | $this->getPreparer()->prepare( |
361 | $this->getResource(tao_helpers_Uri::decode($link)), |
362 | $this->getFileStream($link) |
363 | ) |
364 | ); |
365 | } |
366 | |
367 | private function getPreparer(): MediaResourcePreparerInterface |
368 | { |
369 | return $this->getServiceLocator()->get(MediaResourcePreparerInterface::SERVICE_ID); |
370 | } |
371 | |
372 | private function getFileSourceUnserializer(): FileSourceUnserializer |
373 | { |
374 | return $this->getServiceLocator()->get(FileSourceUnserializer::class); |
375 | } |
376 | |
377 | private function searchDirectories( |
378 | string $parentLink = '', |
379 | array $acceptableMime = [], |
380 | int $depth = 1, |
381 | int $childrenLimit = 0, |
382 | int $childrenOffset = 0 |
383 | ): array { |
384 | |
385 | $class = $this->getClass($parentLink == '' ? $this->getRootClassUri() : tao_helpers_Uri::decode($parentLink)); |
386 | |
387 | $data = $this->getPermissionsMapper()->map( |
388 | [ |
389 | 'path' => self::SCHEME_NAME . tao_helpers_Uri::encode($class->getUri()), |
390 | 'label' => $class->getLabel(), |
391 | 'childrenLimit' => $childrenLimit, |
392 | ], |
393 | $class->getUri() |
394 | ); |
395 | |
396 | if ($depth > 0) { |
397 | $children = []; |
398 | foreach ($class->getSubClasses() as $subclass) { |
399 | $children[] = $this->searchDirectories( |
400 | $subclass->getUri(), |
401 | $acceptableMime, |
402 | $depth - 1, |
403 | $childrenLimit, |
404 | $childrenOffset |
405 | ); |
406 | } |
407 | |
408 | $filter = []; |
409 | |
410 | if (!empty($acceptableMime)) { |
411 | $filter = array_merge($filter, [TaoMediaOntology::PROPERTY_MIME_TYPE => $acceptableMime]); |
412 | } |
413 | |
414 | $options = array_filter([ |
415 | 'limit' => $childrenLimit, |
416 | 'offset' => $childrenOffset, |
417 | ]); |
418 | |
419 | foreach ($class->searchInstances($filter, $options) as $instance) { |
420 | try { |
421 | $children[] = $this->getFileInfo($instance->getUri()); |
422 | } catch (tao_models_classes_FileNotFoundException $e) { |
423 | $this->logEmergency( |
424 | sprintf( |
425 | 'Encountered issues "%s" while fetching details for %s', |
426 | $e->getMessage(), |
427 | $instance->getUri() |
428 | ) |
429 | ); |
430 | } |
431 | } |
432 | $data['children'] = $children; |
433 | $data['total'] = $class->countInstances($filter); |
434 | } else { |
435 | $data['parent'] = $parentLink; |
436 | } |
437 | |
438 | return $data; |
439 | } |
440 | |
441 | private function getPermissionsMapper(): MediaSourcePermissionsMapper |
442 | { |
443 | if (!$this->permissionsMapper) { |
444 | $this->permissionsMapper = $this->getServiceLocator()->get(MediaSourcePermissionsMapper::class); |
445 | } |
446 | |
447 | return $this->permissionsMapper; |
448 | } |
449 | |
450 | public function __destruct() |
451 | { |
452 | foreach ($this->tmpFiles as $tmpFile) { |
453 | if (is_writable($tmpFile)) { |
454 | unlink($tmpFile); |
455 | } |
456 | } |
457 | } |
458 | } |