Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
StyleService
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 9
1260
0.00% covered (danger)
0.00%
0 / 1
 isQtiItem
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getItemContent
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 setItemContent
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getBodyStyles
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 addBodyStyles
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
56
 removeBodyStyles
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
 getClassBodyStyles
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 addClassBodyStyles
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 removeClassBodyStyles
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
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 (under the project TAO-PRODUCT);
19 *
20 */
21
22namespace oat\taoQtiItem\model\style;
23
24use tao_models_classes_Service;
25use core_kernel_classes_Class;
26use core_kernel_classes_Resource;
27use oat\taoQtiItem\model\ItemModel;
28use taoItems_models_classes_ItemsService;
29use SimpleXMLElement;
30use oat\taoQtiItem\model\qti\Service;
31
32class StyleService extends tao_models_classes_Service
33{
34    /**
35     * The regex pattern of valid style names
36     */
37    public const STYLE_NAME_PATTERN = '([a-zA-Z0-9_-]*)';
38
39    /**
40     * Check if the resource in argument is a valid qti item
41     * @param core_kernel_classes_Resource $itemResource
42     * @return boolean
43     */
44    private function isQtiItem(core_kernel_classes_Resource $itemResource)
45    {
46        $itemService = taoItems_models_classes_ItemsService::singleton();
47        return $itemService->hasItemModel($itemResource, [ItemModel::MODEL_URI]);
48    }
49
50    /**
51     * Get the item content for a qti item resource
52     *
53     * @param core_kernel_classes_Resource $itemResource
54     * @param string $langCode
55     * @return SimpleXMLElement
56     */
57    private function getItemContent(core_kernel_classes_Resource $itemResource, $langCode = '')
58    {
59        if ($this->isQtiItem($itemResource)) {
60            $itemContent  = Service::singleton()
61                ->getDataItemByRdfItem($itemResource, $langCode)
62                ->toXML();
63            if (!empty($itemContent)) {
64                return simplexml_load_string($itemContent);
65            }
66        }
67
68        return null;
69    }
70
71    /**
72     * Set the item content for a qti item resource
73     *
74     * @param SimpleXMLElement $content
75     * @param core_kernel_classes_Resource $itemResource
76     * @return bool
77     */
78    private function setItemContent(SimpleXMLElement $content, core_kernel_classes_Resource $itemResource)
79    {
80        if ($this->isQtiItem($itemResource)) {
81            return Service::singleton()->saveXmlItemToRdfItem($content->asXML(), $itemResource);
82        }
83
84        return false;
85    }
86
87    /**
88     * Get the array of body style classes set to the itemBody of a qti item
89     *
90     * @param core_kernel_classes_Resource $itemResource
91     * @param string $langCode
92     * @return array
93     * @throws \common_Exception
94     */
95    public function getBodyStyles(core_kernel_classes_Resource $itemResource, $langCode = '')
96    {
97        $itemContent = $this->getItemContent($itemResource, $langCode);
98        if (is_null($itemContent)) {
99            throw new \common_Exception('cannot find valid qti item content');
100        } else {
101            $classAttr = (string) $itemContent->itemBody['class'];
102            preg_match_all('/x-tao-style-' . self::STYLE_NAME_PATTERN . '/', $classAttr, $matches);
103            if (isset($matches[1])) {
104                return $matches[1];
105            }
106        }
107    }
108
109    /**
110     * Add an array of body style classes to the itemBody of a qti item
111     *
112     * @param core_kernel_classes_Resource $itemResource
113     * @param string $langCode
114     * @return boolean
115     * @throws \common_Exception
116     */
117    public function addBodyStyles($styleNames, core_kernel_classes_Resource $itemResource, $langCode = '')
118    {
119        $itemContent = $this->getItemContent($itemResource, $langCode);
120        if (!is_null($itemContent) && !empty($styleNames)) {
121            $classAttr = (string) $itemContent->itemBody['class'];
122            foreach ($styleNames as $styleName) {
123                if (!empty($styleName) && preg_match(self::STYLE_NAME_PATTERN, $styleName)) {
124                    $newClass = 'x-tao-style-' . $styleName;
125                    if (strpos($classAttr, $styleName) === false) {
126                        $classAttr .= ' ' . $newClass;
127                    }
128                } else {
129                    throw new \common_Exception('invalid style name');
130                }
131            }
132            $itemContent->itemBody['class'] = trim($classAttr);
133            return $this->setItemContent($itemContent, $itemResource);
134        }
135        return false;
136    }
137
138    /**
139     * Remove an array of body style classes set to the itemBody of a qti item
140     *
141     * @param array $styleNames
142     * @param core_kernel_classes_Resource $itemResource
143     * @param string $langCode
144     * @throws \common_Exception
145     */
146    public function removeBodyStyles($styleNames, core_kernel_classes_Resource $itemResource, $langCode = '')
147    {
148        $itemContent = $this->getItemContent($itemResource, $langCode);
149        if (!is_null($itemContent) && !empty($styleNames)) {
150            $classAttr = (string) $itemContent->itemBody['class'];
151            foreach ($styleNames as $styleName) {
152                if (!empty($styleName) && preg_match(self::STYLE_NAME_PATTERN, $styleName)) {
153                    $styleName = 'x-tao-style-' . $styleName;
154                    $classAttr = preg_replace('/(?:^|\\s)' . $styleName . '(?:\\s|$)/', ' ', $classAttr);
155                } else {
156                    throw new \common_Exception('invalid style name');
157                }
158            }
159            $itemContent->itemBody['class'] = trim($classAttr);
160            return $this->setItemContent($itemContent, $itemResource);
161        }
162        return false;
163    }
164
165    /**
166     * Get an array that give the style usage within an tao item subclasses.
167     * It only takes into account qti item with a no-empty content
168     *
169     * @param core_kernel_classes_Class $itemClass
170     * @return array
171     */
172    public function getClassBodyStyles(core_kernel_classes_Class $itemClass)
173    {
174        $usages = [];
175        $union = [];
176        $intersect = [];
177        $items = $itemClass->getInstances(true);
178        $i = 0;
179        foreach ($items as $item) {
180            if ($this->isQtiItem($item)) {
181                try {
182                    $styles = $this->getBodyStyles($item);
183                    $usages[$item->getUri()] = $styles;
184                    if ($i) {
185                        $intersect = array_intersect($styles, $intersect);
186                    } else {
187                        $intersect = $styles;
188                    }
189                    $union = array_unique(array_merge($styles, $union));
190                    $i++;
191                } catch (\common_Exception $e) {
192                }
193            }
194        }
195        return [
196            'all' => $union,
197            'checked' => $intersect,
198            'indeterminate' => array_values(array_diff($union, $intersect))
199        ];
200    }
201
202    /**
203     * Add an array of body style classes to the itemBody of all qti items in given class
204     *
205     * @param array $styleNames
206     * @param core_kernel_classes_Class $itemClass
207     * @param boolean $recursive
208     */
209    public function addClassBodyStyles($styleNames, core_kernel_classes_Class $itemClass, $recursive = true)
210    {
211        $updatedItems = [];
212        $items = $itemClass->getInstances($recursive);
213        foreach ($items as $item) {
214            if ($this->isQtiItem($item)) {
215                if ($this->addBodyStyles($styleNames, $item)) {
216                    $updatedItems[] = $item;
217                }
218            }
219        }
220        return $updatedItems;
221    }
222
223    /**
224     * Remove  an array of body style classes from the itemBody of all qti items in given class
225     *
226     * @param array $styleNames
227     * @param core_kernel_classes_Class $itemClass
228     * @param boolean $recursive
229     */
230    public function removeClassBodyStyles($styleNames, core_kernel_classes_Class $itemClass, $recursive = true)
231    {
232        $updatedItems = [];
233        $items = $itemClass->getInstances($recursive);
234        foreach ($items as $item) {
235            if ($this->isQtiItem($item)) {
236                if ($this->removeBodyStyles($styleNames, $item)) {
237                    $updatedItems[] = $item;
238                }
239            }
240        }
241        return $updatedItems;
242    }
243}