Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feture/aut 3978 export items as qti3 package #2650

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
114 changes: 114 additions & 0 deletions model/Export/Qti3Package/Exporter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiItem\model\Export\Qti3Package;

use common_ext_ExtensionsManager;
use DOMDocument;
use DOMException;
use oat\taoQtiItem\model\Export\QTIPackedItemExporter;
use tao_helpers_Display;
use taoItems_models_classes_TemplateRenderer as TemplateRenderer;

class Exporter extends QTIPackedItemExporter
{
private const QTI_SCHEMA_NAMESPACE = 'http://www.imsglobal.org/xsd/imsqtiasi_v3p0';
private const XML_SCHEMA_INSTANCE = 'http://www.w3.org/2001/XMLSchema-instance';
private const XSI_SCHEMA_LOCATION = 'http://www.imsglobal.org/xsd/imsqtiasi_v3p0';
// phpcs:ignore Generic.Files.LineLength.TooLong
private const XSI_SCHEMA_LOCATION_XSD = 'https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd';

private ?TransformationService $transformationService = null;

/**
* @throws \common_ext_ExtensionException
* @throws \Exception
Karol-Stelmaczonek marked this conversation as resolved.
Show resolved Hide resolved
*/
protected function renderManifest(array $options, array $qtiItemData): DOMDocument
{
$tpl = common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiItem')->getDir()
. 'model/qti/templates/imsmanifestQti30.tpl.php';

$templateRenderer = new TemplateRenderer(
$tpl,
[
'qtiItems' => [$qtiItemData],
'manifestIdentifier' => 'MANIFEST-' .
tao_helpers_Display::textCleaner(uniqid('tao', true), '-')
]
);

$newManifest = new DOMDocument('1.0', TAO_DEFAULT_ENCODING);
$newManifest->loadXML($templateRenderer->render());

return $newManifest;
}

public function getQTIVersion(): string
{
return '3p0';
}


/**
* @throws DOMException
*/
protected function itemContentPostProcessing($content): string
Karol-Stelmaczonek marked this conversation as resolved.
Show resolved Hide resolved
{
$transformationService = $this->getTransformationService();
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXML($content);

$newDom = new DOMDocument('1.0', 'UTF-8');
$newDom->preserveWhiteSpace = false;
$newDom->formatOutput = true;

$oldRoot = $dom->documentElement;
$newRoot = $newDom->createElement($transformationService->createQtiElementName($oldRoot->nodeName));

//QTI3 namespace
$newRoot->setAttribute('xmlns', self::QTI_SCHEMA_NAMESPACE);
$newRoot->setAttribute('xmlns:xsi', self::XML_SCHEMA_INSTANCE);
$newRoot->setAttribute(
'xsi:schemaLocation',
sprintf('%s %s', self::XSI_SCHEMA_LOCATION, self::XSI_SCHEMA_LOCATION_XSD)
);

$transformationService->transformAttributes($oldRoot, $newRoot);

$newDom->appendChild($newRoot);

$transformationService->transformChildren($oldRoot, $newRoot, $newDom);

return $newDom->saveXML();
}

public function setTransformationService(TransformationService $service): void
{
$this->transformationService = $service;
}

public function getTransformationService(): TransformationService
{
return $this->transformationService;
}
}
45 changes: 45 additions & 0 deletions model/Export/Qti3Package/ExporterFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiItem\model\Export\Qti3Package;

use core_kernel_classes_Resource;
use DOMDocument;
use ZipArchive;

class ExporterFactory
{
private TransformationService $transformationService;

public function __construct(TransformationService $transformationService)
{
$this->transformationService = $transformationService;
}

public function create(core_kernel_classes_Resource $item, ZipArchive $zip, ?DOMDocument $manifest = null): Exporter
{
$exporter = new Exporter($item, $zip, $manifest);
$exporter->setTransformationService($this->transformationService);

return $exporter;
}
}
33 changes: 33 additions & 0 deletions model/Export/Qti3Package/Form.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiItem\model\Export\Qti3Package;

use oat\taoQtiItem\model\Export\ExportForm;

class Form extends ExportForm
{
protected function getFormGroupName(): string
{
return __('Export QTI 3.0 Package');
}
}
49 changes: 49 additions & 0 deletions model/Export/Qti3Package/Handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiItem\model\Export\Qti3Package;

use core_kernel_classes_Resource;
use DOMDocument;
use oat\taoQtiItem\model\Export\QtiPackageExportHandler;
use tao_helpers_form_Form;
use ZipArchive;

class Handler extends QtiPackageExportHandler
{
public function getLabel(): string
{
return __('QTI Package 3.0');
}

protected function createExporter($item, ZipArchive $zipArchive, DOMDocument $manifest = null): Exporter
{
/** @var ExporterFactory $factory */
$factory = $this->getServiceManager()->getContainer()->get(ExporterFactory::class);
return $factory->create($item, $zipArchive, $manifest);
}

public function getExportForm(core_kernel_classes_Resource $resource): tao_helpers_form_Form
{
return (new Form($this->getFormData($resource)))->getForm();
}
}
82 changes: 82 additions & 0 deletions model/Export/Qti3Package/Qti3XsdValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA.
*/

declare(strict_types=1);

namespace oat\taoQtiItem\model\Export\Qti3Package;

use oat\taoQtiItem\model\ValidationService;

class Qti3XsdValidator
{
private const QTI3_NAMESPACE = 'http://www.imsglobal.org/xsd/imsqtiasi_v3p0';

private ?array $qtiElementNames = null;
private ValidationService $validationService;

public function __construct(ValidationService $validationService = null)
{
$this->validationService = $validationService ?? new ValidationService();
}

public function isQtiElementName(string $elementName): bool
{
if ($this->qtiElementNames === null) {
$this->qtiElementNames = $this->loadQtiElementNames();
}

return in_array($elementName, $this->qtiElementNames, true);
}

public function loadQtiElementNames(): array
{
$schemaFiles = $this->validationService->getContentValidationSchema(self::QTI3_NAMESPACE);

if (empty($schemaFiles)) {
throw new \RuntimeException('QTI3 XSD schema files not found');
}

$mainSchema = $schemaFiles[0]; // Use the first schema file

if (!file_exists($mainSchema)) {
throw new \RuntimeException(
sprintf(
'QTI3 XSD schema file not found at path: %s',
$mainSchema
)
);
}

$xml = new \SimpleXMLElement(file_get_contents($mainSchema));
$xml->registerXPathNamespace('xs', 'http://www.w3.org/2001/XMLSchema');

$elements = $xml->xpath('//xs:element[@name]');

$qtiElementNames = [];
foreach ($elements as $element) {
$name = (string)$element['name'];
if (str_starts_with($name, 'qti-')) {
$qtiElementNames[] = $name;
}
}

return $qtiElementNames;
}
}
Loading
Loading