Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
QtiItemAssetXmlReplacer
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
2 / 2
10
100.00% covered (success)
100.00%
1 / 1
 replaceAssetNodeValue
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 replaceEncodedNodes
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
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) 2020 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22declare(strict_types=1);
23
24namespace oat\taoQtiItem\model\compile\QtiAssetCompiler;
25
26use DOMDocument;
27use DOMXPath;
28use DOMElement;
29use oat\oatbox\config\ConfigurationService;
30use oat\taoQtiItem\model\pack\QtiAssetPacker\PackedAsset;
31
32class QtiItemAssetXmlReplacer extends ConfigurationService
33{
34    /**
35     * xpath query to find nodes which may contain html encoded content
36     */
37    private const HTML_CONTENT_NODES_QUERY = "//*[local-name()='entry']|//*[local-name()='property']";
38
39    /**
40     * @param DOMDocument $packedAssets
41     * @param PackedAsset[] $packedAssets
42     * @return PackedAsset[]
43     */
44    public function replaceAssetNodeValue(DOMDocument $domDocument, array $packedAssets): array
45    {
46        $xpath = new DOMXPath($domDocument);
47        $attributeNodes = $xpath->query('//@*');
48
49        /** @var DOMElement $node */
50        foreach ($attributeNodes as $node) {
51            if (isset($packedAssets[$node->value])) {
52                $node->value = $packedAssets[$node->value]->getReplacedBy();
53            }
54        }
55
56        $fileHrefNodes =  $xpath->query("//*[local-name()='fileHref']");
57
58        /** @var DOMElement $fileHrefNodes */
59        foreach ($fileHrefNodes as $node) {
60            if (isset($packedAssets[$node->nodeValue])) {
61                $node->nodeValue = $packedAssets[$node->nodeValue]->getReplacedBy();
62            }
63        }
64
65        $this->replaceEncodedNodes($xpath, $packedAssets);
66
67        return $packedAssets;
68    }
69
70    /**
71     * Replace assets in html encoded properties (such as content of text reader interaction)
72     * @param $xpath
73     * @param array $packedAssets
74     */
75    private function replaceEncodedNodes(DOMXPath $xpath, array $packedAssets): void
76    {
77        $replacementList = array_filter($packedAssets, function ($asset) {
78            return $asset instanceof PackedAsset;
79        });
80
81        /** @var PackedAsset $asset */
82        foreach ($replacementList as $key => $asset) {
83            $replacementList[$key] = $asset->getReplacedBy();
84        }
85
86        $attributeNodes = $xpath->query(self::HTML_CONTENT_NODES_QUERY) ?: [];
87        foreach ($attributeNodes as $node) {
88            if ($node->nodeValue) {
89                $node->nodeValue = strtr(htmlentities($node->nodeValue, ENT_XML1), $replacementList);
90            }
91        }
92    }
93}