| 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 | } |