Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
70.83% |
51 / 72 |
|
22.22% |
2 / 9 |
CRAP | |
0.00% |
0 / 1 |
ImsManifestMetadataInjector | |
70.83% |
51 / 72 |
|
22.22% |
2 / 9 |
65.39 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setMappings | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
3.58 | |||
getMappings | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addMapping | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
removeMapping | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
removeMappingByNamespace | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
clearMappings | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
inject | |
82.76% |
24 / 29 |
|
0.00% |
0 / 1 |
11.62 | |||
createMetadataElement | |
91.67% |
22 / 24 |
|
0.00% |
0 / 1 |
12.08 |
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 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); |
19 | * |
20 | */ |
21 | |
22 | namespace oat\taoQtiItem\model\qti\metadata\imsManifest; |
23 | |
24 | use DOMDocument; |
25 | use DOMElement; |
26 | use oat\taoQtiItem\model\qti\metadata\imsManifest\classificationMetadata\ClassificationMetadataValue; |
27 | use oat\taoQtiItem\model\qti\metadata\imsManifest\classificationMetadata\ClassificationValue; |
28 | use oat\taoQtiItem\model\qti\metadata\MetadataInjectionException; |
29 | use oat\taoQtiItem\model\qti\metadata\MetadataInjector; |
30 | use oat\taoQtiItem\model\qti\metadata\MetadataValue; |
31 | use InvalidArgumentException; |
32 | |
33 | /** |
34 | * A MetadataExtractor implementation. |
35 | * |
36 | * This implementation simply iterate through nodes and create an array of MetadataSimpleInstance object |
37 | * |
38 | * @author Antoine Robin <antoine.robin@vesperiagroup.com> |
39 | * @author Jérôme Bogaerts <jerome@taotesting.com> |
40 | */ |
41 | class ImsManifestMetadataInjector implements MetadataInjector |
42 | { |
43 | /** |
44 | * An array of IMSManifesMapping object. |
45 | * |
46 | * @var ImsManifestMapping[] |
47 | */ |
48 | private $mappings; |
49 | |
50 | /** |
51 | * Create a new ImsManifestMetadataInjector object. |
52 | * |
53 | * @param ImsManifestMapping[] $mappings (optional) An array of ImsManifestMapping objects. |
54 | */ |
55 | public function __construct(array $mappings = []) |
56 | { |
57 | $this->setMappings($mappings); |
58 | } |
59 | |
60 | /** |
61 | * Set the ImsManifestMapping objects of this injector. |
62 | * |
63 | * @param ImsManifestMapping[] $mappings An array of ImsManifestMapping objects. |
64 | * @throws InvalidArgumentException If $mappings contains objects/values different from ImsManifestMapping. |
65 | */ |
66 | protected function setMappings(array $mappings = []) |
67 | { |
68 | foreach ($mappings as $mapping) { |
69 | if (!$mapping instanceof ImsManifestMapping) { |
70 | $msg = "The mappings argument must be an array composed of ImsManifestMapping objects"; |
71 | throw new InvalidArgumentException($msg); |
72 | } |
73 | } |
74 | |
75 | $this->mappings = $mappings; |
76 | } |
77 | |
78 | /** |
79 | * Get the registered ImsManifestMapping objects. If no mapping |
80 | * already registered, this method returns an empty array. |
81 | * |
82 | * @return ImsManifestMapping[] An array of ImsManifestMapping. |
83 | */ |
84 | public function getMappings() |
85 | { |
86 | return $this->mappings; |
87 | } |
88 | |
89 | /** |
90 | * Add an XML mapping to this Manifest Extractor. |
91 | * |
92 | * If a mapping with an already registered XML namespace is given as |
93 | * a $mapping, it is simply ignored. |
94 | * |
95 | * @param ImsManifestMapping $mapping An XML mapping. |
96 | */ |
97 | public function addMapping(ImsManifestMapping $mapping) |
98 | { |
99 | $mappings = $this->getMappings(); |
100 | |
101 | $ns = $mapping->getNamespace(); |
102 | |
103 | if (isset($mappings[$ns]) === false) { |
104 | $mappings[$ns] = $mapping; |
105 | } |
106 | |
107 | $this->setMappings($mappings); |
108 | } |
109 | |
110 | /** |
111 | * Remove an already registered ImsManifestMapping. |
112 | * |
113 | * If $mapping cannot be found as a previously registered mapping, nothing happens. |
114 | * |
115 | * @param ImsManifestMapping $mapping An ImsManifestMapping object. |
116 | */ |
117 | public function removeMapping(ImsManifestMapping $mapping) |
118 | { |
119 | $mappings = $this->getMappings(); |
120 | |
121 | if (($key = array_search($mapping, $mappings, true)) !== false) { |
122 | unset($mappings[$key]); |
123 | } |
124 | } |
125 | |
126 | /** |
127 | * Remove a previously registered ImsManifestMapping by its namespace. |
128 | * |
129 | * If no previously registered ImsManifestMapping object can be found |
130 | * for the given $namespace, nothing happens. |
131 | * |
132 | * @param string $namespace An XML namespace. |
133 | */ |
134 | public function removeMappingByNamespace($namespace) |
135 | { |
136 | $mappings = $this->getMappings(); |
137 | |
138 | if (isset($mappings[$namespace]) === true) { |
139 | unset($mappings[$namespace]); |
140 | } |
141 | } |
142 | |
143 | /** |
144 | * Clear all the previously registered ImsManifestMapping objects |
145 | * from this injector. |
146 | */ |
147 | public function clearMappings() |
148 | { |
149 | $this->setMappings(); |
150 | } |
151 | |
152 | /** |
153 | * Inject some MetadataValue objects into the $target DOMElement object. |
154 | * |
155 | * The injection will take care of serializing the MetadataValue objects into the correct sections of the |
156 | * the IMS Manifest File, by looking at previously registered IMSManifestMapping objects. |
157 | * |
158 | * @throws MetadataInjectionException If $target is not a DOMDocument object or something goes wrong during the |
159 | * injection process. |
160 | */ |
161 | public function inject($target, array $values) |
162 | { |
163 | /** @var $target DOMDocument */ |
164 | if (! $target instanceof DOMDocument) { |
165 | throw new MetadataInjectionException(__('The target must be an instance of DOMDocument')); |
166 | } |
167 | |
168 | $map = []; |
169 | |
170 | // Inject the mapping in the root node |
171 | foreach ($this->getMappings() as $mapping) { |
172 | /** @var $root DOMElement */ |
173 | $root = $target->getElementsByTagName('manifest')->item(0); |
174 | $root->setAttribute('xmlns:' . $mapping->getPrefix(), $mapping->getNamespace()); |
175 | $root->setAttribute( |
176 | 'xsi:schemaLocation', |
177 | $root->getAttribute('xsi:schemaLocation') . ' ' . $mapping->getNamespace( |
178 | ) . ' ' . $mapping->getSchemaLocation() |
179 | ); |
180 | |
181 | $map[$mapping->getNamespace()] = $mapping->getPrefix(); |
182 | } |
183 | |
184 | // Get all resource nodes |
185 | $resources = $target->getElementsByTagName('resource'); |
186 | |
187 | // Iterate through values to inject them in the DOMElement |
188 | foreach ($values as $identifier => $metadataValues) { |
189 | $metadataNode = null; |
190 | |
191 | // Search the node that has the given identifier |
192 | /** @var $resource DOMElement */ |
193 | foreach ($resources as $resource) { |
194 | if ($resource->getAttribute('identifier') === $identifier) { |
195 | // If metadata already exists we take it |
196 | if ($resource->getElementsByTagName('metadata')->length !== 0) { |
197 | $metadataNode = $resource->getElementsByTagName('metadata')->item(0); |
198 | } else { |
199 | $metadataNode = $target->createElement('metadata'); |
200 | } |
201 | |
202 | if ($resource->getElementsByTagName('file')->length !== 0) { |
203 | $fileNode = $resource->getElementsByTagName('file')->item(0); |
204 | $resource->insertBefore($metadataNode, $fileNode); |
205 | } else { |
206 | $resource->appendChild($metadataNode); |
207 | } |
208 | break; |
209 | } |
210 | } |
211 | |
212 | if (is_null($metadataNode) || empty($map)) { |
213 | continue; |
214 | } |
215 | |
216 | // Add the metadata values into the right path |
217 | /** @var $metadata MetaDataValue */ |
218 | foreach ($metadataValues as $metadata) { |
219 | $this->createMetadataElement($metadata, $metadataNode, $map, $target); |
220 | } |
221 | } |
222 | } |
223 | |
224 | /** |
225 | * Add an element based on MetadataValue object to DomDocument |
226 | * |
227 | * @param MetadataValue $metadata |
228 | * @param DOMElement $metadataNode |
229 | * @param $map |
230 | * @param DOMDocument $imsManifest |
231 | */ |
232 | protected function createMetadataElement( |
233 | MetadataValue $metadata, |
234 | DOMElement $metadataNode, |
235 | $map, |
236 | DOMDocument $imsManifest |
237 | ) { |
238 | $path = $metadata->getPath(); |
239 | $path = array_reverse($path); |
240 | |
241 | $uniqNodes = []; |
242 | if ($metadata instanceof ClassificationValue) { |
243 | $uniqNodes = ['taxonPath', 'source']; |
244 | } |
245 | |
246 | $oldChildNode = null; |
247 | foreach ($path as $index => $element) { |
248 | $name = substr($element, (strpos($element, '#') + 1)); |
249 | $base = substr($element, 0, (strpos($element, '#'))); |
250 | |
251 | if ( |
252 | in_array($name, $uniqNodes) |
253 | || is_null($oldChildNode) |
254 | || $metadataNode->getElementsByTagName($map[$base] . ':' . $name)->length === 0 |
255 | ) { |
256 | $node = $imsManifest->createElement($map[$base] . ':' . $name); |
257 | } else { |
258 | $node = $metadataNode->getElementsByTagName($map[$base] . ':' . $name)->item(0); |
259 | } |
260 | |
261 | if ($name == 'string' || $name == 'langstring') { |
262 | $node->setAttribute('xml:lang', $metadata->getLanguage()); |
263 | } |
264 | |
265 | if (isset($oldChildNode)) { |
266 | $node->appendChild($oldChildNode); |
267 | if ($name == 'taxonPath' && $metadata instanceof ClassificationMetadataValue) { |
268 | foreach ($metadata->getEntries() as $entry) { |
269 | $this->createMetadataElement($entry, $node, $map, $imsManifest); |
270 | } |
271 | } |
272 | } else { |
273 | $node->nodeValue = htmlspecialchars($metadata->getValue()); |
274 | } |
275 | $oldChildNode = $node; |
276 | } |
277 | |
278 | $metadataNode->appendChild($oldChildNode); |
279 | } |
280 | } |