Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 403
0.00% covered (danger)
0.00%
0 / 43
CRAP
0.00% covered (danger)
0.00%
0 / 1
taoQtiTest_models_classes_QtiTestCompiler
0.00% covered (danger)
0.00%
0 / 403
0.00% covered (danger)
0.00%
0 / 43
8742
0.00% covered (danger)
0.00%
0 / 1
 getPublicDirectory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPublicDirectory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPrivateDirectory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPrivateDirectory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRenderingEngine
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setRenderingEngine
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMarkupPostRenderer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMarkupPostRenderer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCssScoper
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCssScoper
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExtraPath
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setExtraPath
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 initCompilation
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
2
 compile
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
20
 prepareXmlStorageExceptionReport
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 getContainer
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 compactTest
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 compileItems
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
30
 legacyCompileItem
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 compileJsonItem
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 explodeRubricBlocks
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 updateTestDefinition
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 copyPrivateResources
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 buildServiceCall
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 compileRubricBlocks
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 1
110
 copyPublicResources
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 copyRemoteResources
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
110
 compileTest
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 compileTestMetadata
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 compileAdaptive
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 1
132
 compileMeta
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 compileIndex
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 compileAssessmentItemRefHrefIndex
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getPublicMimeTypes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildHrefIndexPath
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 addCompilationInfo
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getCompilatonInfo
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTaoQtiTestExtension
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 buildCompilationInfo
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 setClientContainer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 useClientTestRunner
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCssScoping
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 useCssScoping
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
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) 2013-2017 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22use oat\taoQtiTest\models\runner\RunnerService;
23use qtism\runtime\rendering\markup\xhtml\XhtmlRenderingEngine;
24use qtism\data\storage\xml\XmlStorageException;
25use qtism\runtime\rendering\markup\MarkupPostRenderer;
26use qtism\runtime\rendering\css\CssScoper;
27use qtism\data\QtiComponentIterator;
28use qtism\data\storage\xml\XmlDocument;
29use qtism\data\storage\xml\XmlCompactDocument;
30use qtism\data\AssessmentTest;
31use qtism\data\ExtendedAssessmentSection;
32use qtism\data\ExtendedAssessmentItemRef;
33use qtism\data\AssessmentItemRef;
34use qtism\data\content\RubricBlock;
35use qtism\data\content\StylesheetCollection;
36use qtism\common\utils\Url;
37use oat\taoQtiItem\model\qti\Service;
38use oat\oatbox\filesystem\Directory;
39use oat\taoQtiTest\models\TestCategoryRulesService;
40use oat\taoQtiTest\models\QtiTestCompilerIndex;
41use oat\taoQtiTest\models\cat\CatService;
42use oat\taoQtiTest\models\CompilationDataService;
43use oat\taoQtiItem\model\QtiJsonItemCompiler;
44use oat\taoDelivery\model\container\delivery\DeliveryContainerRegistry;
45use oat\taoDelivery\model\container\delivery\ContainerProvider;
46use oat\tao\model\metadata\compiler\ResourceJsonMetadataCompiler;
47
48/**
49 * A Test Compiler implementation that compiles a QTI Test and related QTI Items.
50 *
51 * @author Jérôme Bogaerts <jerome@taotesting.com>
52 * @package taoQtiTest
53
54 */
55class taoQtiTest_models_classes_QtiTestCompiler extends taoTests_models_classes_TestCompiler implements
56    ContainerProvider
57{
58    public const ADAPTIVE_SECTION_MAP_FILENAME = 'adaptive-section-map.json';
59
60    public const ADAPTIVE_PLACEHOLDER_CATEGORY = 'x-tao-qti-adaptive-placeholder';
61
62    public const COMPILATION_INFO_FILENAME = 'compilation-info.json';
63    /**
64     * The list of mime types of files that are accepted to be put
65     * into the public compilation directory.
66     *
67     * @var array
68     */
69    private static $publicMimeTypes = ['text/css',
70                                               'image/png',
71                                               'image/jpeg',
72                                               'image/gif',
73                                               'text/html',
74                                               'application/x-shockwave-flash',
75                                               'video/x-flv',
76                                               'image/bmp',
77                                               'image/svg+xml',
78                                               'audio/mpeg',
79                                               'audio/ogg',
80                                               'video/quicktime',
81                                               'video/webm',
82                                               'video/ogg',
83                                               'application/pdf',
84                                               'application/x-font-woff',
85                                               'application/vnd.ms-fontobject',
86                                               'application/x-font-ttf',
87                                               'image/svg+xml',
88                                               'image/svg+xml'];
89
90    /**
91     * The public compilation directory.
92     *
93     * @var tao_models_classes_service_StorageDirectory
94     */
95    private $publicDirectory = null;
96
97    /**
98     * The private compilation directory.
99     *
100     * @var tao_models_classes_service_StorageDirectory
101     */
102    private $privateDirectory = null;
103
104    /**
105     * The rendering engine that will be used to create rubric block templates.
106     *
107     * @var XhtmlRenderingEngine
108     */
109    private $renderingEngine = null;
110
111    /**
112     * The Post renderer to be used in template oriented rendering.
113     *
114     * @var MarkupPostRenderer
115     */
116    private $markupPostRenderer = null;
117
118    /**
119     * The CSS Scoper will scope CSS files to their related rubric block.
120     *
121     * @var CssScoper
122     */
123    private $cssScoper = null;
124
125    /**
126     * An additional path to be used when test definitions are located in sub-directories.
127     *
128     * @var string
129     */
130    private $extraPath;
131
132    private $compilationInfo = [];
133
134    /**
135     * Whenever or not rubric block css should be scoped
136     * @var boolean
137     */
138    private $settingCssScope = true;
139
140    /**
141     * Whenever or not the new client test runner should be used
142     * @var boolean
143     */
144    private $settingClientContainer = true;
145
146    /**
147     * Get the public compilation directory.
148     *
149     * @return tao_models_classes_service_StorageDirectory
150     */
151    protected function getPublicDirectory()
152    {
153        return $this->publicDirectory;
154    }
155
156    /**
157     * Set the public compilation directory.
158     *
159     * @param tao_models_classes_service_StorageDirectory $directory
160     */
161    protected function setPublicDirectory(tao_models_classes_service_StorageDirectory $directory)
162    {
163        $this->publicDirectory = $directory;
164    }
165
166    /**
167     * Get the private compilation directory.
168     *
169     * @return tao_models_classes_service_StorageDirectory
170     */
171    protected function getPrivateDirectory()
172    {
173        return $this->privateDirectory;
174    }
175
176    /**
177     * Set the private compilation directory.
178     *
179     * @param tao_models_classes_service_StorageDirectory $directory
180     */
181    protected function setPrivateDirectory(tao_models_classes_service_StorageDirectory $directory)
182    {
183        $this->privateDirectory = $directory;
184    }
185
186    /**
187     * Get the rendering engine that will be used to render rubric block templates.
188     *
189     * @return XhtmlRenderingEngine
190     */
191    protected function getRenderingEngine()
192    {
193        return $this->renderingEngine;
194    }
195
196    /**
197     * Set the rendering engine that will be used to render rubric block templates.
198     *
199     * @param XhtmlRenderingEngine $renderingEngine
200     */
201    protected function setRenderingEngine(XhtmlRenderingEngine $renderingEngine)
202    {
203        $this->renderingEngine = $renderingEngine;
204    }
205
206    /**
207     * Get the markup post renderer to be used after template oriented rendering.
208     *
209     * @return MarkupPostRenderer
210     */
211    protected function getMarkupPostRenderer()
212    {
213        return $this->markupPostRenderer;
214    }
215
216    /**
217     * Set the markup post renderer to be used after template oriented rendering.
218     *
219     * @param MarkupPostRenderer $markupPostRenderer
220     */
221    protected function setMarkupPostRenderer(MarkupPostRenderer $markupPostRenderer)
222    {
223        $this->markupPostRenderer = $markupPostRenderer;
224    }
225
226    /**
227     * Get the CSS Scoper tool that will scope CSS files to their related rubric block.
228     *
229     * @return CssScoper
230     */
231    protected function getCssScoper()
232    {
233        return $this->cssScoper;
234    }
235
236    /**
237     * Set the CSS Scoper tool that will scope CSS files to their related rubric block.
238     *
239     * @param CssScoper $cssScoper
240     */
241    protected function setCssScoper(CssScoper $cssScoper)
242    {
243        $this->cssScoper = $cssScoper;
244    }
245
246    /**
247     * Get the extra path to be used when test definition is located
248     * in sub-directories.
249     *
250     * @return string
251     */
252    protected function getExtraPath()
253    {
254        return $this->extraPath;
255    }
256
257    /**
258     * Set the extra path to be used when test definition is lovated in sub-directories.
259     *
260     * @param string $extraPath
261     */
262    protected function setExtraPath($extraPath)
263    {
264        $this->extraPath = $extraPath;
265    }
266
267    /**
268     * Initialize the compilation by:
269     *
270     * * 1. Spawning public and private compilation directoryies.
271     * * 2. Instantiating appropriate rendering engine and CSS utilities.
272     *
273     * for the next compilation process.
274     */
275    protected function initCompilation()
276    {
277        $ds = DIRECTORY_SEPARATOR;
278
279        // Initialize public and private compilation directories.
280        $this->setPrivateDirectory($this->spawnPrivateDirectory());
281        $this->setPublicDirectory($this->spawnPublicDirectory());
282
283        // Extra path.
284        $testService = taoQtiTest_models_classes_QtiTestService::singleton();
285        $testDefinitionDir = dirname($testService->getRelTestPath($this->getResource()));
286        $this->setExtraPath($testDefinitionDir);
287
288        // Initialize rendering engine.
289        $renderingEngine = new XhtmlRenderingEngine();
290        $renderingEngine->setStylesheetPolicy(XhtmlRenderingEngine::STYLESHEET_SEPARATE);
291        $renderingEngine->setXmlBasePolicy(XhtmlRenderingEngine::XMLBASE_PROCESS);
292        $renderingEngine->setFeedbackShowHidePolicy(XhtmlRenderingEngine::TEMPLATE_ORIENTED);
293        $renderingEngine->setViewPolicy(XhtmlRenderingEngine::TEMPLATE_ORIENTED);
294        $renderingEngine->setPrintedVariablePolicy(XhtmlRenderingEngine::TEMPLATE_ORIENTED);
295        $renderingEngine->setStateName(taoQtiTest_models_classes_QtiTestService::TEST_RENDERING_STATE_NAME);
296        $renderingEngine->setRootBase(
297            taoQtiTest_models_classes_QtiTestService::TEST_PLACEHOLDER_BASE_URI . rtrim($this->getExtraPath(), $ds)
298        );
299        $renderingEngine->setViewsName(taoQtiTest_models_classes_QtiTestService::TEST_VIEWS_NAME);
300        $this->setRenderingEngine($renderingEngine);
301
302        // Initialize CSS Scoper.
303        $this->setCssScoper(new CssScoper());
304
305        // Initialize Post Markup Renderer.
306        $this->setMarkupPostRenderer(new MarkupPostRenderer(true, true, true));
307
308        // Initialize the index that will contains info about items
309        $this->setContext(new QtiTestCompilerIndex());
310    }
311
312    /**
313     * Compile a QTI Test and the related QTI Items.
314     *
315     * The compilation process occurs as follows:
316     *
317     * * 1. The resources composing the test are copied into the private compilation directory.
318     * * 2. The test definition is packed (test and items put together in a single definition).
319     * * 3. The items composing the test are compiled.
320     * * 4. The rubric blocks are rendered into PHP templates.
321     * * 5. The test definition is compiled into PHP source code for maximum performance.
322     * * 6. The resources composing the test that have to be accessed at delivery time are compied into the public
323     *      compilation directory.
324     * * 7. The Service Call definition enabling TAO to run the compiled test is built.
325     *
326     * @return tao_models_classes_service_ServiceCall A ServiceCall object that represent the way to call the newly
327     *                                                compiled test.
328     * @throws taoQtiTest_models_classes_QtiTestCompilationFailedException If an error occurs during the compilation.
329     */
330    public function compile()
331    {
332
333        $report = new common_report_Report(common_report_Report::TYPE_INFO);
334
335        try {
336            // 0. Initialize compilation (compilation directories, renderers, ...).
337            $this->initCompilation();
338
339            // 1. Copy the resources composing the test into the private complilation directory.
340            $this->copyPrivateResources();
341
342            // 2. Compact the test definition itself.
343            $compiledDoc = $this->compactTest();
344
345            // 3. Compile the items of the test.
346            $itemReport = $this->compileItems($compiledDoc);
347            $report->add($itemReport);
348            if ($itemReport->getType() != common_report_Report::TYPE_SUCCESS) {
349                common_Logger::e($report->getMessage(), $report->getErrors());
350                $msg = 'Failed item compilation.';
351                $code = taoQtiTest_models_classes_QtiTestCompilationFailedException::ITEM_COMPILATION;
352                throw new taoQtiTest_models_classes_QtiTestCompilationFailedException(
353                    $msg,
354                    $this->getResource(),
355                    $code
356                );
357            }
358
359            // 4. Explode the rubric blocks in the test into rubric block refs.
360            $this->explodeRubricBlocks($compiledDoc);
361
362            // 5. Update test definition with additional runtime info.
363            $assessmentTest = $compiledDoc->getDocumentComponent();
364            //$this->updateTestDefinition($assessmentTest);
365
366            // 6. Compile rubricBlocks and serialize on disk.
367            $this->compileRubricBlocks($assessmentTest);
368
369            // 7. Copy the needed files into the public directory.
370            $this->copyPublicResources();
371
372            // 8. Compile adaptive components of the test.
373            $this->compileAdaptive($assessmentTest);
374
375            // 9. Compile the test definition into PHP source code and put it
376            // into the private directory.
377            $this->compileTest($assessmentTest);
378
379            // 9.1. Compile test meta data into JSON file.
380            $this->compileTestMetadata($this->getResource());
381
382            // 10. Compile the test meta data into PHP array source code and put it
383            // into the private directory.
384            $this->compileMeta($assessmentTest);
385
386            // 11. Compile the test index in JSON content and put it into the private directory.
387            $this->compileIndex();
388
389            // 12. Build the service call.
390            $serviceCall = $this->buildServiceCall();
391
392            // 13. Record some compilation info.
393            $this->buildCompilationInfo();
394
395            common_Logger::t("QTI Test successfully compiled.");
396
397            $report->setType(common_report_Report::TYPE_SUCCESS);
398            $report->setMessage(__('QTI Test "%s" successfully published.', $this->getResource()->getLabel()));
399            $report->setData($serviceCall);
400        } catch (XmlStorageException $e) {
401            $report = $this->prepareXmlStorageExceptionReport($e, $report);
402        } catch (Exception $e) {
403            common_Logger::e($e->getMessage());
404            // All exception that were not catched in the compilation steps
405            // above have a last chance here.
406            $report->setType(common_report_Report::TYPE_ERROR);
407            $report->setMessage(__('QTI Test "%s" publishing failed.', $this->getResource()->getLabel()));
408        }
409
410        // Reset time outs to initial value.
411        helpers_TimeOutHelper::reset();
412
413        return $report;
414    }
415
416    /**
417     * @param XmlStorageException $e
418     * @param common_report_Report $report
419     * @return common_report_Report
420     * @throws common_exception_Error
421     */
422    private function prepareXmlStorageExceptionReport(XmlStorageException $e, common_report_Report $report)
423    {
424        $details[] = $e->getMessage();
425        $subReport = new common_report_Report(
426            common_report_Report::TYPE_ERROR,
427            __('The QTI Test XML or one of its dependencies is malformed or empty.')
428        );
429        $itemReport = new common_report_Report(common_report_Report::TYPE_ERROR, $e->getMessage());
430        while (($previous = $e->getPrevious()) != null) {
431            $details[] = $previous->getMessage();
432            $e = $e->getPrevious();
433        }
434        if (method_exists($e, 'getErrors')) {
435            /** @var LibXMLError $error */
436            foreach ($e->getErrors() as $error) {
437                $itemReport->add(new common_report_Report(common_report_Report::TYPE_ERROR, $error->message));
438            }
439        } else {
440            $itemReport->add(new common_report_Report(common_report_Report::TYPE_ERROR, $e->getMessage()));
441        }
442
443        $subReport->add($itemReport);
444
445        common_Logger::e(implode("\n", $details));
446        $report->add($subReport);
447
448        $report->setType(common_report_Report::TYPE_ERROR);
449        $report->setMessage(__('QTI Test "%s" publishing failed.', $this->getResource()->getLabel()));
450        return $report;
451    }
452
453    /**
454     * {@inheritDoc}
455     * @see \oat\taoDelivery\model\container\delivery\ContainerProvider::getContainer()
456     */
457    public function getContainer()
458    {
459        $registry = DeliveryContainerRegistry::getRegistry();
460        $registry->setServiceLocator($this->getServiceLocator());
461        if ($this->useClientTestRunner()) {
462            // client container
463            $container = $registry->getDeliveryContainer('qtiTest', [
464                'source' => $this->getResource()->getUri(),
465                'private' => $this->getPrivateDirectory()->getId(),
466                'public' => $this->getPublicDirectory()->getId()
467            ]);
468        } else {
469            $serviceCall = $this->buildServiceCall();
470            $container = $registry->getDeliveryContainer('service', $serviceCall);
471        }
472        return $container;
473    }
474
475    /**
476     * Compact the test and items in a single QTI-XML Compact Document.
477     *
478     * @return XmlCompactDocument.
479     */
480    protected function compactTest()
481    {
482        $testService = taoQtiTest_models_classes_QtiTestService::singleton();
483        $test = $this->getResource();
484
485        common_Logger::t('Compacting QTI test ' . $test->getLabel() . '...');
486
487        $resolver = new taoQtiTest_helpers_ItemResolver(Service::singleton());
488        $originalDoc = $testService->getDoc($test);
489
490        $compiledDoc = XmlCompactDocument::createFromXmlAssessmentTestDocument($originalDoc, $resolver, $resolver);
491        common_Logger::t("QTI Test XML transformed in a compact version.");
492
493        return $compiledDoc;
494    }
495
496    /**
497     * Compile the items referended by $compactDoc.
498     *
499     * @param XmlCompactDocument $compactDoc An XmlCompactDocument object referencing the items of the test.
500     * @throws taoQtiTest_models_classes_QtiTestCompilationFailedException If the test does not refer to at least
501     *                                                                     one item.
502     * @return common_report_Report
503     */
504    protected function compileItems(XmlCompactDocument $compactDoc)
505    {
506        $report = new common_report_Report(common_report_Report::TYPE_SUCCESS, __('Items Compilation'));
507        $iterator = new QtiComponentIterator($compactDoc->getDocumentComponent(), ['assessmentItemRef']);
508        $itemCount = 0;
509        foreach ($iterator as $assessmentItemRef) {
510            // Each item could take some time to be compiled, making the request to timeout.
511            helpers_TimeOutHelper::setTimeOutLimit(helpers_TimeOutHelper::SHORT);
512            $subReport = $this->useClientTestRunner()
513                ? $this->compileJsonItem($assessmentItemRef)
514                : $this->legacyCompileItem($assessmentItemRef);
515            $report->add($subReport);
516            if ($subReport->getType() != common_report_Report::TYPE_SUCCESS) {
517                $report->setType(common_report_Report::TYPE_ERROR);
518            }
519            // Count the item even if it fails to avoid false "no item" error.
520            $itemCount++;
521            common_Logger::t(
522                "QTI Item successfully compiled and registered as a service call in the QTI Test Definition."
523            );
524        }
525
526        if ($itemCount === 0) {
527            $report->setType(common_report_Report::TYPE_ERROR);
528            $report->setMessage(__("A QTI Test must contain at least one QTI Item to be compiled. None found."));
529        }
530        return $report;
531    }
532
533    /**
534     *
535     * @param AssessmentItemRef $assessmentItemRef
536     * @return common_report_Report
537     */
538    protected function legacyCompileItem(AssessmentItemRef &$assessmentItemRef)
539    {
540        $item = new core_kernel_classes_Resource($assessmentItemRef->getHref());
541        $report = $this->subCompile($item);
542        if ($report->getType() == common_report_Report::TYPE_SUCCESS) {
543            $itemService = $report->getdata();
544            $inputValues = tao_models_classes_service_ServiceCallHelper::getInputValues($itemService, []);
545            $assessmentItemRef->setHref(
546                $inputValues['itemUri'] . '|' . $inputValues['itemPath'] . '|' . $inputValues['itemDataPath']
547            );
548
549            // Ask for item ref information compilation for fast later usage.
550            $this->compileAssessmentItemRefHrefIndex($assessmentItemRef);
551        }
552        return $report;
553    }
554
555    /**
556     *
557     * @param AssessmentItemRef $item
558     * @return common_report_Report
559     */
560    protected function compileJsonItem(AssessmentItemRef &$assessmentItemRef)
561    {
562        $jsonCompiler = new QtiJsonItemCompiler(
563            new core_kernel_classes_Resource($assessmentItemRef->getHref()),
564            $this->getStorage()
565        );
566        $jsonCompiler->setServiceLocator($this->getServiceLocator());
567        $jsonCompiler->setContext($this->getContext());
568        $report = $jsonCompiler->compileJson();
569        if ($report->getType() == common_report_Report::TYPE_SUCCESS) {
570            // store $itemUri, $publicDirId, $privateDirId in a string
571            $assessmentItemRef->setHref(implode('|', $report->getdata()));
572            $this->compileAssessmentItemRefHrefIndex($assessmentItemRef);
573        }
574        return $report;
575    }
576
577    /**
578     * Explode the rubric blocks of the test definition into separate QTI-XML files and
579     * remove the compact XML document from the file system (useless for
580     * the rest of the compilation process).
581     *
582     * @param XmlCompactDocument $compiledDoc
583     */
584    protected function explodeRubricBlocks(XmlCompactDocument $compiledDoc)
585    {
586        common_Logger::t("Exploding QTI rubricBlocks...");
587
588        $privateDir = $this->getPrivateDirectory();
589        $explodedRubricBlocks = $compiledDoc->explodeRubricBlocks();
590
591        foreach ($explodedRubricBlocks as $href => $rubricBlock) {
592            $doc = new XmlDocument();
593            $doc->setDocumentComponent($rubricBlock);
594
595            $data = $doc->saveToString();
596            $privateDir->write($href, $data);
597        }
598    }
599
600    /**
601     * Update the test definition with additional data, such as TAO specific
602     * rules and variables.
603     *
604     * @param AssessmentTest $assessmentTest
605     */
606    protected function updateTestDefinition(AssessmentTest $assessmentTest)
607    {
608        // Call TestCategoryRulesService to generate additional rules if enabled.
609        $config = $this->getTaoQtiTestExtension()->getConfig('TestCompiler');
610        if (
611            isset($config['enable-category-rules-generation'])
612            && $config['enable-category-rules-generation'] === true
613        ) {
614            common_Logger::t('Automatic Category Rules Generation will occur...');
615            $testCategoryRulesService = $this->getServiceLocator()->get(TestCategoryRulesService::SERVICE_ID);
616            $testCategoryRulesService->apply($assessmentTest);
617        }
618    }
619
620    /**
621     * Copy the resources (e.g. images) of the test to the private compilation directory.
622     */
623    protected function copyPrivateResources()
624    {
625        $testService = taoQtiTest_models_classes_QtiTestService::singleton();
626        $testDefinitionDir = $testService->getQtiTestDir($this->getResource());
627
628        $privateDir = $this->getPrivateDirectory();
629        $iterator = $testDefinitionDir->getFlyIterator(Directory::ITERATOR_RECURSIVE | Directory::ITERATOR_FILE);
630        foreach ($iterator as $object) {
631            $relPath = $testDefinitionDir->getRelPath($object);
632            $privateDir->getFile($relPath)->write($object->readStream());
633        }
634    }
635
636    /**
637     * Build the Service Call definition that makes TAO able to run the compiled test
638     * later on at delivery time.
639     *
640     * @return tao_models_classes_service_ServiceCall
641     */
642    protected function buildServiceCall()
643    {
644        $service = new tao_models_classes_service_ServiceCall(
645            new core_kernel_classes_Resource(RunnerService::INSTANCE_TEST_RUNNER_SERVICE)
646        );
647        $param = new tao_models_classes_service_ConstantParameter(
648            // Test Definition URI passed to the QtiTestRunner service.
649            new core_kernel_classes_Resource(
650                taoQtiTest_models_classes_QtiTestService::INSTANCE_FORMAL_PARAM_TEST_DEFINITION
651            ),
652            $this->getResource()
653        );
654        $service->addInParameter($param);
655
656        $param = new tao_models_classes_service_ConstantParameter(
657            // Test Compilation URI passed to the QtiTestRunner service.
658            new core_kernel_classes_Resource(
659                taoQtiTest_models_classes_QtiTestService::INSTANCE_FORMAL_PARAM_TEST_COMPILATION
660            ),
661            $this->getPrivateDirectory()->getId() . '|' . $this->getPublicDirectory()->getId()
662        );
663        $service->addInParameter($param);
664
665        return $service;
666    }
667
668    /**
669     * Compile the RubricBlocRefs' contents into a separate rubric block PHP template.
670     *
671     * @param AssessmentTest $assessmentTest The AssessmentTest object you want to compile the rubrickBlocks.
672     */
673    protected function compileRubricBlocks(AssessmentTest $assessmentTest)
674    {
675        common_Logger::t("Compiling QTI rubricBlocks...");
676
677        $rubricBlockRefs = $assessmentTest->getComponentsByClassName('rubricBlockRef');
678        $testService = taoQtiTest_models_classes_QtiTestService::singleton();
679        $sourceDir = $testService->getQtiTestDir($this->getResource());
680
681        foreach ($rubricBlockRefs as $rubricRef) {
682            $rubricRefHref = $rubricRef->getHref();
683            $cssScoper = $this->getCssScoper();
684            $renderingEngine = $this->getRenderingEngine();
685            $markupPostRenderer = $this->getMarkupPostRenderer();
686            $publicCompiledDocDir = $this->getPublicDirectory();
687            $privateCompiledDocDir = $this->getPrivateDirectory();
688
689            // -- loading...
690            common_Logger::t("Loading rubricBlock '" . $rubricRefHref . "'...");
691
692            $rubricDoc = new XmlDocument();
693            $rubricDoc->loadFromString($this->getPrivateDirectory()->read($rubricRefHref));
694
695            common_Logger::t("rubricBlock '" . $rubricRefHref . "' successfully loaded.");
696
697            // -- rendering...
698            common_Logger::t("Rendering rubricBlock '" . $rubricRefHref . "'...");
699
700            $pathinfo = pathinfo($rubricRefHref);
701            $renderingFile = $pathinfo['filename'] . '.php';
702
703            $rubric = $rubricDoc->getDocumentComponent();
704            $rubricStylesheets = $rubric->getStylesheets();
705            $stylesheets = new StylesheetCollection();
706            // In any case, include the base QTI Stylesheet.
707            $stylesheets->merge($rubricStylesheets);
708            $rubric->setStylesheets($stylesheets);
709
710            // -- If the rubricBlock has no id, give it a auto-generated one in order
711            // to be sure that CSS rescoping procedure works fine (it needs at least an id
712            // to target its scoping).
713            if ($rubric->hasId() === false) {
714                // Prepend 'tao' to the generated id because the CSS
715                // ident token must begin by -|[a-zA-Z]
716                $rubric->setId('tao' . uniqid());
717            }
718
719            // -- Copy eventual remote resources of the rubricBlock.
720            $this->copyRemoteResources($rubric);
721
722            $domRendering = $renderingEngine->render($rubric);
723            $mainStringRendering = $markupPostRenderer->render($domRendering);
724
725            // Prepend stylesheets rendering to the main rendering.
726            $styleRendering = $renderingEngine->getStylesheets();
727            $mainStringRendering = $styleRendering->ownerDocument->saveXML($styleRendering) . $mainStringRendering;
728
729            if ($this->useCssScoping()) {
730                foreach ($stylesheets as $rubricStylesheet) {
731                    $relPath = trim($this->getExtraPath(), '/');
732                    $relPath = (empty($relPath) ? '' : $relPath . DIRECTORY_SEPARATOR)
733                        . $rubricStylesheet->getHref();
734                    $sourceFile = $sourceDir->getFile($relPath);
735
736                    if (!$publicCompiledDocDir->has($relPath)) {
737                        try {
738                            $data = $sourceFile->read();
739                            $tmpDir = \tao_helpers_File::createTempDir();
740                            $tmpFile = $tmpDir . 'tmp.css';
741                            file_put_contents($tmpFile, $data);
742                            $scopedCss = $cssScoper->render($tmpFile, $rubric->getId());
743                            unlink($tmpFile);
744                            rmdir($tmpDir);
745                            $publicCompiledDocDir->write($relPath, $scopedCss);
746                        } catch (\InvalidArgumentException $e) {
747                            common_Logger::e('Unable to copy file into public directory: ' . $relPath);
748                        }
749                    }
750                }
751            }
752
753            // -- Replace the artificial 'tao://qti-directory' base path with a runtime call to the delivery time base
754            // path.
755            $mainStringRendering = str_replace(
756                taoQtiTest_models_classes_QtiTestService::TEST_PLACEHOLDER_BASE_URI,
757                '<?php echo $' . taoQtiTest_models_classes_QtiTestService::TEST_BASE_PATH_NAME . '; ?>',
758                $mainStringRendering
759            );
760            if (!$privateCompiledDocDir->has($renderingFile)) {
761                try {
762                    $privateCompiledDocDir->write($renderingFile, $mainStringRendering);
763                    common_Logger::t("rubricBlockRef '" . $rubricRefHref . "' successfully rendered.");
764                } catch (\InvalidArgumentException $e) {
765                    common_Logger::e('Unable to copy file into public directory: ' . $renderingFile);
766                }
767            }
768
769            // -- Clean up old rubric block and reference the new rubric block template.
770            $privateCompiledDocDir->delete($rubricRefHref);
771
772            $rubricRef->setHref('./' . $pathinfo['filename'] . '.php');
773        }
774    }
775
776    /**
777     * Copy the test resources (e.g. images) that will be availabe at delivery time
778     * in the public compilation directory.
779     *
780     */
781    protected function copyPublicResources()
782    {
783        $testService = taoQtiTest_models_classes_QtiTestService::singleton();
784        $testDefinitionDir = $testService->getQtiTestDir($this->getResource());
785
786        $publicCompiledDocDir = $this->getPublicDirectory();
787        $iterator = $testDefinitionDir->getFlyIterator(Directory::ITERATOR_RECURSIVE | Directory::ITERATOR_FILE);
788        foreach ($iterator as $file) {
789            /** @var \oat\oatbox\filesystem\File $file */
790            $mime = $file->getMimeType();
791            $pathinfo = pathinfo($file->getBasename());
792
793            if (in_array($mime, self::getPublicMimeTypes()) === true && $pathinfo['extension'] !== 'php') {
794                $publicPathFile = $testDefinitionDir->getRelPath($file);
795                common_Logger::d('Public ' . $file->getPrefix() . '(' . $mime . ') to ' . $publicPathFile);
796                $publicCompiledDocDir->getFile($publicPathFile)->write($file->readStream());
797            }
798        }
799    }
800
801    /**
802     * Copy all remote resource (absolute URLs to another host) contained in a rubricBlock into a dedicated directory.
803     * Remote resources can be refereced by the following QTI classes/attributes:
804     *
805     * * a:href
806     * * object:data
807     * * img:src
808     *
809     * @param AssessmentTest $assessmentTest An AssessmentTest object.
810     * @throws taoQtiTest_models_classes_QtiTestCompilationFailedException If a remote resource cannot be retrieved.
811     */
812    protected function copyRemoteResources(RubricBlock $rubricBlock)
813    {
814        $ds = DIRECTORY_SEPARATOR;
815        $tmpDir = tao_helpers_File::createTempDir();
816        $destPath = trim($this->getExtraPath(), $ds) . $ds
817            . taoQtiTest_models_classes_QtiTestService::TEST_REMOTE_FOLDER . $ds;
818
819        // Search for all class-attributes in QTI-XML that might reference a remote file.
820        $search = $rubricBlock->getComponentsByClassName(['a', 'object', 'img']);
821        foreach ($search as $component) {
822            switch ($component->getQtiClassName()) {
823                case 'object':
824                    $url = $component->getData();
825                    break;
826
827                case 'img':
828                    $url = $component->getSrc();
829                    break;
830            }
831
832            if (isset($url) && !preg_match('@^' . ROOT_URL . '@', $url) && !Url::isRelative($url)) {
833                $tmpFile = taoItems_helpers_Deployment::retrieveFile($url, $tmpDir);
834                if ($tmpFile !== false) {
835                    $pathinfo = pathinfo($tmpFile);
836                    $handle = fopen($tmpFile, 'r');
837                    $this->getPublicDirectory()->writeStream($destPath . $pathinfo['basename'], $handle);
838                    fclose($handle);
839                    unlink($tmpFile);
840                    $newUrl =  taoQtiTest_models_classes_QtiTestService::TEST_REMOTE_FOLDER . '/'
841                        . $pathinfo['basename'];
842
843                    switch ($component->getQtiClassName()) {
844                        case 'object':
845                            $component->setData($newUrl);
846                            break;
847
848                        case 'img':
849                            $component->setSrc($newUrl);
850                            break;
851                    }
852                } else {
853                    $msg = "The remote resource referenced by '${url}' could not be retrieved.";
854                    throw new taoQtiTest_models_classes_QtiTestCompilationFailedException(
855                        $msg,
856                        $this->getResource(),
857                        taoQtiTest_models_classes_QtiTestCompilationFailedException::REMOTE_RESOURCE
858                    );
859                }
860            }
861        }
862    }
863
864    /**
865     * Compile the given $test into PHP source code for maximum performance. The file will be stored
866     * into PRIVATE_DIRECTORY/compact-test.php.
867     *
868     * @param AssessmentTest $test
869     */
870    protected function compileTest(AssessmentTest $test)
871    {
872        common_Logger::t("Compiling QTI test definition...");
873
874        $this->getServiceLocator()->get(CompilationDataService::SERVICE_ID)->writeCompilationData(
875            $this->getPrivateDirectory(),
876            taoQtiTest_models_classes_QtiTestService::TEST_COMPILED_FILENAME,
877            $test
878        );
879
880        common_Logger::d("QTI-PHP Test Compilation file saved to stream.");
881    }
882
883    /**
884     * @param core_kernel_classes_Resource $resource
885     * @throws FileNotFoundException
886     * @throws common_Exception
887     */
888    protected function compileTestMetadata(core_kernel_classes_Resource $resource)
889    {
890        /** @var ResourceJsonMetadataCompiler $jsonMetadataCompiler */
891        $jsonMetadataCompiler = $this->getServiceLocator()->get(ResourceJsonMetadataCompiler::SERVICE_ID);
892        $metadataJson = $jsonMetadataCompiler->compile($resource);
893
894        $this->getPrivateDirectory()->write(
895            taoQtiTest_models_classes_QtiTestService::TEST_COMPILED_METADATA_FILENAME,
896            json_encode($metadataJson)
897        );
898    }
899
900    /**
901     * Compile Adaptive Test Information.
902     *
903     * This method compiles all information required at runtime in terms of Adaptive Testing.
904     *
905     * @param \qtism\data\AssessmentTest $test
906     */
907    protected function compileAdaptive(AssessmentTest $test)
908    {
909        $catService = $this->getServiceLocator()->get(CatService::SERVICE_ID);
910        $compilationDataService = $this->getServiceLocator()->get(CompilationDataService::SERVICE_ID);
911        $catSectionMap = [];
912
913        $trail = [];
914        foreach ($test->getTestParts() as $testPart) {
915            foreach ($testPart->getAssessmentSections() as $assessmentSection) {
916                array_push($trail, $assessmentSection);
917            }
918        }
919
920        $traversed = [];
921
922        while (count($trail) > 0) {
923            $current = array_pop($trail);
924
925            if (in_array($current, $traversed, true) === false) {
926                // 1st pass.
927                array_push($trail, $current);
928
929                foreach ($current->getSectionParts() as $sectionPart) {
930                    if ($sectionPart instanceof ExtendedAssessmentSection) {
931                        array_push($trail, $sectionPart);
932                    }
933                }
934
935                array_push($traversed, $current);
936            } else {
937                // 2nd pass.
938                $sectionParts = $current->getSectionParts();
939                $sectionIdentifier = $current->getIdentifier();
940
941                $catInfo = $catService->getAdaptiveAssessmentSectionInfo(
942                    $test,
943                    $this->getPrivateDirectory(),
944                    $this->getExtraPath(),
945                    $sectionIdentifier
946                );
947
948                if ($catInfo !== false) {
949                    // QTI Adaptive Section detected.
950                    \common_Logger::d(
951                        "QTI Adaptive Section with identifier '" . $current->getIdentifier() . "' found."
952                    );
953
954                    // Deal with AssessmentSection Compiling.
955                    $compilationDataService->writeCompilationData(
956                        $this->getPrivateDirectory(),
957                        "adaptive-assessment-section-${sectionIdentifier}",
958                        $current
959                    );
960
961                    foreach ($sectionParts->getKeys() as $sectionPartIdentifier) {
962                        $sectionPart =  $sectionParts[$sectionPartIdentifier];
963
964                        if ($sectionPart instanceof ExtendedAssessmentItemRef) {
965                            $sectionPartHref = $sectionPart->getHref();
966
967                            // Deal with AssessmentItemRef Compiling.
968                            $compilationDataService->writeCompilationData(
969                                $this->getPrivateDirectory(),
970                                "adaptive-assessment-item-ref-${sectionPartIdentifier}",
971                                $sectionPart
972                            );
973
974                            unset($sectionParts[$sectionPartIdentifier]);
975                        }
976                    }
977
978                    if (count($sectionParts) === 0) {
979                        $placeholderIdentifier = "adaptive-placeholder-${sectionIdentifier}";
980                        // Make the placeholder's href something predictable for later use...
981                        $placeholderHref = "x-tao-qti-adaptive://section/${sectionIdentifier}";
982
983                        $placeholder = new ExtendedAssessmentItemRef($placeholderIdentifier, $placeholderHref);
984
985                        // Tag the item ref in order to make it recognizable as an adaptive placeholder.
986                        $placeholder->getCategories()[] = self::ADAPTIVE_PLACEHOLDER_CATEGORY;
987                        $sectionParts[] = $placeholder;
988
989                        \common_Logger::d(
990                            "Adaptive AssessmentItemRef Placeholder '${placeholderIdentifier}' injected in "
991                                . "AssessmentSection '${sectionIdentifier}'."
992                        );
993
994                        // Ask for section setup to the CAT Engine.
995                        $section = $catService
996                            ->getEngine($catInfo['adaptiveEngineRef'])
997                            ->setupSection($catInfo['adaptiveSectionIdentifier']);
998                        $catSectionMap[$catInfo['qtiSectionIdentifier']] = [
999                            'section' => $section,
1000                            'endpoint' => $catInfo['adaptiveEngineRef'],
1001                        ];
1002                    }
1003                }
1004            }
1005        }
1006
1007        // Write Adaptive Section Map for runtime usage.
1008        $this->getPrivateDirectory()->write(self::ADAPTIVE_SECTION_MAP_FILENAME, json_encode($catSectionMap));
1009    }
1010
1011    /**
1012     * Compile the $test meta-data into PHP source code for maximum performance. The file is
1013     * stored into PRIVATE_DIRECTORY/test-meta.php.
1014     *
1015     * @param AssessmentTest $test
1016     */
1017    protected function compileMeta(AssessmentTest $test)
1018    {
1019        common_Logger::t("Compiling test metadata...");
1020        $compiledDocDir = $this->getPrivateDirectory();
1021
1022        /** @var CompilationDataService $compilationDataService */
1023        $compilationDataService = $this->getServiceLocator()->get(CompilationDataService::SERVICE_ID);
1024        $compilationDataService->writeCompilationMetadata($compiledDocDir, $test);
1025    }
1026
1027    /**
1028     * Compile the test index into JSON file to improve performance of the map build.
1029     * The file is stored into PRIVATE_DIRECTORY/test-index.json.
1030     */
1031    protected function compileIndex()
1032    {
1033        $compiledDocDir = $this->getPrivateDirectory();
1034
1035        /** @var $index QtiTestCompilerIndex */
1036        $index = $this->getContext();
1037        if ($index) {
1038            $compiledDocDir->write(taoQtiTest_models_classes_QtiTestService::TEST_COMPILED_INDEX, $index->serialize());
1039        }
1040    }
1041
1042    /**
1043     * Compile AssessmentItemRef Href Indexes
1044     *
1045     * This method indexes the value of $assessmentItemRef->href by $assessmentItemRef->identifier for later
1046     * usage at delivery time (for fast access).
1047     *
1048     * @param \qtism\data\AssessmentItemRef $assessmentItemRef
1049     */
1050    protected function compileAssessmentItemRefHrefIndex(AssessmentItemRef $assessmentItemRef)
1051    {
1052        $compiledDocDir = $this->getPrivateDirectory();
1053        $compiledDocDir->getFile(self::buildHrefIndexPath($assessmentItemRef->getIdentifier()))
1054            ->write($assessmentItemRef->getHref());
1055    }
1056
1057    /**
1058     * Get the list of mime types of files that are accepted to be put
1059     * into the public compilation directory.
1060     *
1061     * @return array
1062     */
1063    protected static function getPublicMimeTypes()
1064    {
1065        return self::$publicMimeTypes;
1066    }
1067
1068    /**
1069     * Build Href Index Path
1070     *
1071     * Builds the Href Index Path from given $identifier.
1072     *
1073     * @param string $identifier
1074     * @return string
1075     */
1076    public static function buildHrefIndexPath($identifier)
1077    {
1078        return taoQtiTest_models_classes_QtiTestService::TEST_COMPILED_HREF_INDEX_FILE_PREFIX . md5($identifier)
1079            . taoQtiTest_models_classes_QtiTestService::TEST_COMPILED_HREF_INDEX_FILE_EXTENSION;
1080    }
1081
1082    protected function addCompilationInfo($key, $info)
1083    {
1084        if (is_scalar($info)) {
1085            $this->compilationInfo[$key] = $info;
1086        }
1087    }
1088
1089    public function getCompilatonInfo()
1090    {
1091        return $this->compilationInfo;
1092    }
1093
1094    protected function getTaoQtiTestExtension()
1095    {
1096        return $this
1097            ->getServiceLocator()
1098            ->get(\common_ext_ExtensionsManager::SERVICE_ID)
1099            ->getExtensionById('taoQtiTest');
1100    }
1101
1102    protected function buildCompilationInfo()
1103    {
1104        $this->addCompilationInfo('tao-version', TAO_VERSION);
1105        $this->addCompilationInfo('testqti-version', $this->getTaoQtiTestExtension()->getVersion());
1106        $this->addCompilationInfo(
1107            'compilation-data-service-implementation',
1108            get_class($this->getServiceLocator()->get(CompilationDataService::SERVICE_ID))
1109        );
1110
1111        $this->getPrivateDirectory()->write(
1112            self::COMPILATION_INFO_FILENAME,
1113            json_encode($this->getCompilatonInfo())
1114        );
1115    }
1116
1117    /**
1118     * Set whenever or not the compiler should use client test container
1119     * @param boolean $boolean
1120     */
1121    public function setClientContainer($boolean)
1122    {
1123        $this->settingClientContainer = !!$boolean;
1124    }
1125
1126    /**
1127     * Whenever or not we use the Client Test runner
1128     * @return boolean
1129     */
1130    protected function useClientTestRunner()
1131    {
1132        return $this->settingClientContainer;
1133    }
1134
1135    /**
1136     * Set whenever or not the compiler should scope rubric block css
1137     * @param boolean $boolean
1138     */
1139    public function setCssScoping($boolean)
1140    {
1141        $this->settingCssScope = !!$boolean;
1142    }
1143
1144    /**
1145     * Whenever or not we scope rubric block css
1146     * @return boolean
1147     */
1148    protected function useCssScoping()
1149    {
1150        return $this->settingCssScope;
1151    }
1152}