Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
UpgradeTextReaderInteractionTask
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 13
1122
0.00% covered (danger)
0.00%
0 / 1
 __invoke
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
56
 extractImages
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getPciInteractions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isLegacyTextReader
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 isLegacyTextReaderWithImages
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 validatePayload
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 getItemService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTextReaderLegacyDetection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQtiService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getImageToPropertyHelper
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 upgradeItemPCIsWithImages
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 setUpgradedNamespace
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 upgradeItemPCIsAll
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
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) 2022-2023 (original work) Open Assessment Technologies SA;
19 */
20
21declare(strict_types=1);
22
23namespace oat\pciSamples\model\LegacyPciHelper\Task;
24
25use DOMDocument;
26use Exception;
27use oat\generis\model\OntologyAwareTrait;
28use oat\oatbox\extension\AbstractAction;
29use oat\oatbox\filesystem\Directory;
30use oat\oatbox\reporting\Report;
31use oat\oatbox\service\ServiceManagerAwareTrait;
32use oat\pciSamples\model\LegacyPciHelper\ImageToPropertiesHelper;
33use oat\pciSamples\model\LegacyPciHelper\TextReaderLegacyDetection;
34use oat\taoQtiItem\helpers\QtiFile;
35use oat\taoQtiItem\model\qti\interaction\CustomInteraction;
36use oat\taoQtiItem\model\qti\interaction\ImsPortableCustomInteraction;
37use oat\taoQtiItem\model\qti\interaction\PortableCustomInteraction;
38use oat\taoQtiItem\model\qti\Item;
39use oat\taoQtiItem\model\qti\Parser;
40use oat\taoQtiItem\model\qti\QtiNamespace;
41use oat\taoQtiItem\model\qti\Service as QtiService;
42use taoItems_models_classes_ItemsService;
43
44class UpgradeTextReaderInteractionTask extends AbstractAction
45{
46    use ServiceManagerAwareTrait;
47    use OntologyAwareTrait;
48
49    /** @var Directory */
50    private $itemDirectory;
51
52    public function __invoke($params): Report
53    {
54        $this->validatePayload($params);
55        $itemResource = $this->getResource($params['itemUri']);
56
57        if (!$itemResource->exists()) {
58            return Report::createError('Item resource does not exist');
59        }
60
61        $this->itemDirectory = $this->getItemService()->getItemDirectory($itemResource);
62        $itemXmlFile = $this->itemDirectory->getFile(QtiFile::FILE);
63        $parser = new Parser($itemXmlFile->read());
64        $xmlItem = $parser->load();
65
66        if (!$this->isLegacyTextReader($xmlItem)) {
67            return Report::createWarning("Item does not contain Legacy PCI Text Reader");
68        }
69
70        if ($params['skipItemsWithoutImages'] && !$this->isLegacyTextReaderWithImages($xmlItem)) {
71            return Report::createWarning("Item does not contain any Text Reader with image, SKIPPING");
72        }
73
74        try {
75            $params['skipItemsWithoutImages']
76                ? $this->upgradeItemPCIsWithImages($xmlItem)
77                : $this->upgradeItemPCIsAll($xmlItem);
78            $this->getQtiService()->saveDataItemToRdfItem($xmlItem, $itemResource);
79        } catch (Exception $e) {
80            return Report::createError(
81                sprintf(
82                    'Task failed with item %s with error: %s',
83                    $itemResource->getUri(),
84                    $e->getMessage()
85                )
86            );
87        }
88        return  Report::createSuccess(
89            sprintf(
90                "Item %s has been modified with label: %s",
91                $itemResource->getUri(),
92                $itemResource->getLabel()
93            )
94        );
95    }
96
97    private function extractImages(array $content): array
98    {
99        $images = [];
100        foreach ($content as $element) {
101            $dom = new DOMDocument();
102            // https://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly
103            $dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $element);
104            foreach ($dom->getElementsByTagName('img') as $image) {
105                $images[] = [
106                    'fileName' => $image->getAttribute('src'),
107                ];
108            }
109        }
110
111        return $images;
112    }
113
114    private function getPciInteractions(Item $xmlItem): array
115    {
116        return $xmlItem->getBody()->getElements(CustomInteraction::class);
117    }
118
119    private function isLegacyTextReader(Item $item): bool
120    {
121        foreach ($this->getPciInteractions($item) as $pciInteraction) {
122            if ($this->getTextReaderLegacyDetection()->isLegacyTextReader($pciInteraction)) {
123                return true;
124            }
125        }
126
127        return false;
128    }
129
130    private function isLegacyTextReaderWithImages(Item $item): bool
131    {
132        foreach ($this->getPciInteractions($item) as $pciInteraction) {
133            if ($this->getTextReaderLegacyDetection()->isTextReaderWithImage($pciInteraction)) {
134                return true;
135            }
136        }
137
138        return false;
139    }
140
141    /**
142     * @throws WrongTaskPayloadException
143     */
144    private function validatePayload($params): void
145    {
146        if (!isset($params['itemUri'])) {
147            throw new WrongTaskPayloadException(
148                sprintf(
149                    'Could not find a resource with that uri: %s',
150                    $params['itemUri'] ?? '<no value set>'
151                )
152            );
153        }
154
155        if (!isset($params['skipItemsWithoutImages']) || !is_bool($params['skipItemsWithoutImages'])) {
156            throw new WrongTaskPayloadException(
157                'Missing required param skipItemsWithoutImages'
158            );
159        }
160    }
161
162    private function getItemService(): taoItems_models_classes_ItemsService
163    {
164        return $this->getServiceManager()->getContainer()->get(taoItems_models_classes_ItemsService::class);
165    }
166
167    private function getTextReaderLegacyDetection(): TextReaderLegacyDetection
168    {
169        return $this->getServiceManager()->getContainer()->get(TextReaderLegacyDetection::class);
170    }
171
172    private function getQtiService(): QtiService
173    {
174        return $this->getServiceManager()->getContainer()->get(QtiService::class);
175    }
176
177    private function getImageToPropertyHelper(): ImageToPropertiesHelper
178    {
179        return $this->getServiceManager()->getContainer()->get(ImageToPropertiesHelper::class);
180    }
181
182    private function upgradeItemPCIsWithImages(Item $xmlItem): void
183    {
184        foreach ($xmlItem->getBody()->getElements(PortableCustomInteraction::class) as $pciInteraction) {
185            if ($this->getTextReaderLegacyDetection()->isTextReaderWithImage($pciInteraction)) {
186                $properties = $pciInteraction->getProperties();
187                foreach ($properties['pages'] as $page) {
188                    $images = $this->extractImages($page['content']);
189                    $properties = $this->getImageToPropertyHelper()->addImagesToProperties(
190                        $images,
191                        $properties,
192                        $this->itemDirectory
193                    );
194                }
195
196                $this->setUpgradedNamespace($pciInteraction);
197                $pciInteraction->setProperties($properties);
198            }
199        }
200    }
201
202    private function setUpgradedNamespace(CustomInteraction $pciInteraction): void
203    {
204        /* @var ImsPortableCustomInteraction $pciInteraction */
205        $pciInteraction->setNamespace(new QtiNamespace(
206            ImsPortableCustomInteraction::NS_URI,
207            ImsPortableCustomInteraction::NS_NAME
208        ));
209    }
210
211    private function upgradeItemPCIsAll(Item $xmlItem): void
212    {
213        foreach ($xmlItem->getBody()->getElements(PortableCustomInteraction::class) as $pciInteraction) {
214            if ($this->getTextReaderLegacyDetection()->isLegacyTextReader($pciInteraction)) {
215                $this->setUpgradedNamespace($pciInteraction);
216            }
217        }
218    }
219}