Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 83 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 1 |
AssetManager | |
0.00% |
0 / 83 |
|
0.00% |
0 / 14 |
1260 | |
0.00% |
0 / 1 |
loadAssetHandler | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getItemContent | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setItemContent | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getSource | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
setSource | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
importAuxiliaryFiles | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
20 | |||
importDependencyFiles | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
30 | |||
importFile | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
copyDependencyFiles | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
30 | |||
copyFilesToItemDir | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
finalize | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
getAbsolutePath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRelativePath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
importAsset | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 |
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) 2016 (original work) Open Assessment Technologies SA; |
19 | * |
20 | */ |
21 | |
22 | namespace oat\taoQtiItem\model\qti\asset; |
23 | |
24 | use helpers_File; |
25 | use oat\tao\model\import\InvalidSourcePathException; |
26 | use oat\taoQtiItem\model\qti\asset\handler\AssetHandler; |
27 | use oat\taoQtiItem\model\qti\asset\handler\MediaAssetHandler; |
28 | use oat\taoQtiItem\model\qti\Resource as QtiResource; |
29 | |
30 | class AssetManager |
31 | { |
32 | /** |
33 | * @var array of AssetHandler |
34 | */ |
35 | protected $assetHandlers; |
36 | |
37 | /** |
38 | * Content of the item |
39 | * @var array |
40 | */ |
41 | protected $itemContent = ''; |
42 | |
43 | /** |
44 | * Location of extracted zip package |
45 | * @var string |
46 | */ |
47 | protected $source; |
48 | |
49 | /** |
50 | * Load an asset handler associated |
51 | * |
52 | * @param $assetHandler |
53 | * @return $this |
54 | * @throws AssetManagerException |
55 | */ |
56 | public function loadAssetHandler($assetHandler) |
57 | { |
58 | if (!$assetHandler instanceof AssetHandler) { |
59 | throw new AssetManagerException( |
60 | 'Asset handler "' . get_class($assetHandler) . '" is not supported by AssetManager' |
61 | ); |
62 | } |
63 | $this->assetHandlers[] = $assetHandler; |
64 | return $this; |
65 | } |
66 | |
67 | /** |
68 | * Get item content |
69 | * |
70 | * @return string |
71 | */ |
72 | public function getItemContent() |
73 | { |
74 | return $this->itemContent; |
75 | } |
76 | |
77 | /** |
78 | * Set item content |
79 | * |
80 | * @param $itemContent |
81 | * @return $this |
82 | */ |
83 | public function setItemContent($itemContent) |
84 | { |
85 | $this->itemContent = $itemContent; |
86 | return $this; |
87 | } |
88 | |
89 | /** |
90 | * Get source |
91 | * |
92 | * @return mixed |
93 | * @throws AssetManagerException |
94 | */ |
95 | public function getSource() |
96 | { |
97 | if (!$this->source) { |
98 | throw new AssetManagerException( |
99 | 'No source folder set to assetManager when loading auxiliary files & dependencies.' |
100 | ); |
101 | } |
102 | return $this->source; |
103 | } |
104 | |
105 | /** |
106 | * Set source |
107 | * |
108 | * @param $source |
109 | * @return $this |
110 | */ |
111 | public function setSource($source) |
112 | { |
113 | $this->source = $source; |
114 | return $this; |
115 | } |
116 | |
117 | /** |
118 | * Import auxiliaryFile e.q. css, js... |
119 | * |
120 | * @param QtiResource $qtiItemResource |
121 | * @return $this |
122 | * @throws AssetManagerException |
123 | * @throws InvalidSourcePathException |
124 | */ |
125 | public function importAuxiliaryFiles(QtiResource $qtiItemResource) |
126 | { |
127 | $qtiFile = $this->getSource() . helpers_File::urlToPath($qtiItemResource->getFile()); |
128 | foreach ($qtiItemResource->getAuxiliaryFiles() as $auxiliaryFile) { |
129 | $absolutePath = $this->getAbsolutePath($auxiliaryFile); |
130 | $relativePath = $this->getRelativePath($qtiFile, $absolutePath); |
131 | |
132 | if (!helpers_File::isAbsoluteFileInsideDirectory($absolutePath, $this->getSource())) { |
133 | throw new InvalidSourcePathException(dirname($qtiFile), $auxiliaryFile); |
134 | } |
135 | |
136 | try { |
137 | $this->importAsset($absolutePath, $relativePath); |
138 | } catch (\common_Exception $e) { |
139 | throw new AssetManagerException( |
140 | 'Error occurs during auxiliary assets handling for item: ' . $qtiItemResource->getIdentifier() |
141 | . ', assetFile: ' . $relativePath, |
142 | 0, |
143 | $e |
144 | ); |
145 | } |
146 | } |
147 | return $this; |
148 | } |
149 | |
150 | /** |
151 | * Import dependencies files |
152 | * |
153 | * @param QtiResource $qtiItemResource |
154 | * @param $dependencies |
155 | * @return $this |
156 | * @throws AssetManagerException |
157 | */ |
158 | public function importDependencyFiles(QtiResource $qtiItemResource, $dependencies) |
159 | { |
160 | $qtiFile = $this->getSource() . helpers_File::urlToPath($qtiItemResource->getFile()); |
161 | foreach ($qtiItemResource->getDependencies() as $dependenciesFile) { |
162 | if (!isset($dependencies[$dependenciesFile])) { |
163 | continue; |
164 | } |
165 | |
166 | $dependencyResource = $dependencies[$dependenciesFile]; |
167 | try { |
168 | $this->importFile($qtiFile, $dependencyResource->getFile()); |
169 | foreach ($dependencyResource->getAuxiliaryFiles() as $auxiliaryFile) { |
170 | /* |
171 | * Load resources to the items folder |
172 | * it creates duplications to make items independent (for safe deletion and edition) |
173 | */ |
174 | $this->importFile($qtiFile, $auxiliaryFile); |
175 | } |
176 | } catch (AssetManagerException $e) { |
177 | throw new AssetManagerException( |
178 | $e->getMessage() . ', item ' . $qtiItemResource->getIdentifier(), |
179 | $e->getCode(), |
180 | $e->getPrevious() |
181 | ); |
182 | } |
183 | } |
184 | return $this; |
185 | } |
186 | |
187 | /** |
188 | * @param $qtiFile |
189 | * @param $file |
190 | * @throws AssetManagerException |
191 | */ |
192 | private function importFile($qtiFile, $file) |
193 | { |
194 | $absolutePath = $this->getAbsolutePath($file); |
195 | $relativePath = $this->getRelativePath($qtiFile, $absolutePath); |
196 | try { |
197 | $this->importAsset($absolutePath, $relativePath); |
198 | } catch (\common_Exception $e) { |
199 | throw new AssetManagerException( |
200 | 'Error occurs during dependency assets handling: assetFile: ' . $relativePath, |
201 | 0, |
202 | $e |
203 | ); |
204 | } |
205 | } |
206 | |
207 | /** |
208 | * We have to copy all the files used by item that stored not in the item folder |
209 | * and used by direct links in the item |
210 | * |
211 | * @param QtiResource $qtiItemResource |
212 | * @param $dependencies |
213 | * @throws AssetManagerException |
214 | * @example see QtiPackageExportTest::testExportQtiPackageWithDependencies |
215 | */ |
216 | public function copyDependencyFiles(QtiResource $qtiItemResource, $dependencies) |
217 | { |
218 | $qtiFile = $this->getSource() . helpers_File::urlToPath($qtiItemResource->getFile()); |
219 | foreach ($qtiItemResource->getDependencies() as $dependenciesFile) { |
220 | if (!isset($dependencies[$dependenciesFile])) { |
221 | continue; |
222 | } |
223 | /** @var QtiResource $dependencyResource */ |
224 | $dependencyResource = $dependencies[$dependenciesFile]; |
225 | |
226 | $this->copyFilesToItemDir(dirname($qtiFile), $dependencyResource); |
227 | /** @var array $dependencies recursive dependencies */ |
228 | $secondLevelDependencies = $dependencyResource->getDependencies(); |
229 | if ($secondLevelDependencies && count($secondLevelDependencies)) { |
230 | $this->copyDependencyFiles($qtiItemResource, $secondLevelDependencies); |
231 | } |
232 | } |
233 | } |
234 | |
235 | /** |
236 | * @param string $itemDir |
237 | * @param QtiResource $dependencyResource |
238 | * @throws AssetManagerException |
239 | */ |
240 | protected function copyFilesToItemDir($itemDir, QtiResource $dependencyResource) |
241 | { |
242 | // copy auxiliary files to the items folder |
243 | foreach ($dependencyResource->getAuxiliaryFiles() as $auxiliaryFile) { |
244 | $auxiliaryPath = $this->getAbsolutePath($auxiliaryFile); |
245 | $dest = $itemDir . '/' . $auxiliaryFile; |
246 | if ( |
247 | !helpers_File::isFileInsideDirectory($auxiliaryFile, $itemDir) |
248 | && !helpers_File::copy($auxiliaryPath, $dest) |
249 | ) { |
250 | throw new AssetManagerException('File ' . $auxiliaryPath . ' was not copied to the ' . $itemDir); |
251 | } |
252 | } |
253 | } |
254 | |
255 | /** |
256 | * Finalize asset handling |
257 | */ |
258 | public function finalize() |
259 | { |
260 | foreach ($this->assetHandlers as $handler) { |
261 | $handler->finalize(); |
262 | } |
263 | } |
264 | |
265 | /** |
266 | * Return the location of file as absolute path |
267 | * |
268 | * @param $file |
269 | * @return string |
270 | * @throws AssetManagerException |
271 | */ |
272 | protected function getAbsolutePath($file) |
273 | { |
274 | return $this->getSource() . str_replace('/', DIRECTORY_SEPARATOR, $file); |
275 | } |
276 | |
277 | /** |
278 | * Return the location of file as relative path in package |
279 | * |
280 | * @param $qtiFile |
281 | * @param $absolutePath |
282 | * @return mixed |
283 | */ |
284 | protected function getRelativePath($qtiFile, $absolutePath) |
285 | { |
286 | return str_replace(DIRECTORY_SEPARATOR, '/', helpers_File::getRelPath($qtiFile, $absolutePath)); |
287 | } |
288 | |
289 | /** |
290 | * Loop each assetHandlers populated by loadAssetHandler() |
291 | * The first applicable return info about file handling processing |
292 | * and break chain. |
293 | * |
294 | * @param $absolutePath |
295 | * @param $relativePath |
296 | * @throws AssetManagerException |
297 | */ |
298 | protected function importAsset($absolutePath, $relativePath) |
299 | { |
300 | /** @var AssetHandler $assetHandler */ |
301 | foreach ($this->assetHandlers as $assetHandler) { |
302 | if ($assetHandler->isApplicable($relativePath)) { |
303 | $info = $assetHandler->handle($absolutePath, $relativePath); |
304 | |
305 | if ($relativePath != ltrim($info['uri'], '/')) { |
306 | $this->setItemContent(str_replace($relativePath, $info['uri'], $this->getItemContent())); |
307 | } |
308 | |
309 | //Break chain of asset handler, first applicable is taken |
310 | return; |
311 | } |
312 | } |
313 | throw new AssetManagerException( |
314 | 'Unable to import auxiliary & dependency files. No asset handler applicable to file : ' . $relativePath |
315 | ); |
316 | } |
317 | } |