Skip to content

Commit

Permalink
feature: Add xsd files, updated namespaces, new qti3 validator and be…
Browse files Browse the repository at this point in the history
…tter check for qti names
  • Loading branch information
Karol-Stelmaczonek committed Dec 19, 2024
1 parent a0d7975 commit 8e4d0cf
Show file tree
Hide file tree
Showing 13 changed files with 136,681 additions and 101 deletions.
6 changes: 1 addition & 5 deletions model/Export/Qti3Package/Exporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,12 @@ protected function itemContentPostProcessing($content): string
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXML($content);

$content = $this->transformationService->cleanNamespaces($content);

$dom->loadXML($content);

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

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

//QTI3 namespace
$newRoot->setAttribute('xmlns', self::QTI_SCHEMA_NAMESPACE);
Expand Down
58 changes: 58 additions & 0 deletions model/Export/Qti3Package/Qti3SdValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?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 SimpleXMLElement;

class Qti3SdValidator
{
private const LOCAL_XSD_PATH = __DIR__ . '/../../qti/data/qtiv3p0/imsqti_asiv3p0_v1p0.xsd';
const SCHEMA = 'http://www.w3.org/2001/XMLSchema';

private ?array $qtiElementNames = null;

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

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

private function loadQtiElementNames(): void
{
$xml = new SimpleXMLElement(file_get_contents(self::LOCAL_XSD_PATH));
$xml->registerXPathNamespace('xs', self::SCHEMA);

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

$this->qtiElementNames = [];
foreach ($elements as $element) {
$name = (string)$element['name'];
if (str_starts_with($name, 'qti-')) {
$this->qtiElementNames[] = $name;
}
}
}
}
71 changes: 24 additions & 47 deletions model/Export/Qti3Package/TransformationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,22 @@

class TransformationService
{
private const QTI_ELEMENT_PREFIXES = [
'response',
'outcome',
'item',
'choice',
'simple',
'default',
'value'
];
private Qti3SdValidator $validator;

public function transformAttributes(DOMElement $sourceElement, DOMElement $targetElement): void
public function __construct()
{
if (!$sourceElement->hasAttributes()) {
return;
}

foreach ($sourceElement->attributes as $attribute) {
if (
!str_starts_with($attribute->nodeName, 'xmlns') &&
$attribute->nodeName !== 'xsi:schemaLocation'
) {
$attrName = $this->camelToHyphen($attribute->nodeName);
if (!empty($attrName)) {
$targetElement->setAttribute($attrName, $attribute->value);
}
}
}
$this->validator = new Qti3SdValidator();
}

public function transformChildren(DOMElement $oldElement, DOMElement $newParent, DOMDocument $newDom): void
{
foreach ($oldElement->childNodes as $child) {
if ($child instanceof DOMElement) {
$newName = $this->isQtiElement($child->nodeName) ? $this->getElementName($child) : $child->nodeName;
$newName = $this->createQtiElementName($child->nodeName);

if (!$this->validator->isQtiElementName($newName)) {
$newName = $child->nodeName;
}

$newElement = $newDom->createElement($newName);

Expand All @@ -79,32 +61,27 @@ public function transformChildren(DOMElement $oldElement, DOMElement $newParent,
}
}


public function getElementName(DOMElement $element): string
{
return sprintf('qti-%s', $this->camelToHyphen($element->nodeName));
}

public function cleanNamespaces(string $content): string
public function transformAttributes(DOMElement $sourceElement, DOMElement $targetElement): void
{
$content = preg_replace('/\s*xmlns[^=]*="[^"]*"\s*/i', '', $content);
$content = preg_replace('/<([a-z0-9]+):([^>\s]+)(\s+[^>]*)?>/i', '<$2$3>', $content);
$content = preg_replace('/<\/[a-z0-9]+:([^>]+)>/i', '</$1>', $content);
return preg_replace('/\s{2,}/', ' ', $content);
}
if (!$sourceElement->hasAttributes()) {
return;
}

private function isQtiElement(string $nodeName): bool
{
foreach (self::QTI_ELEMENT_PREFIXES as $prefix) {
if (str_starts_with($nodeName, $prefix)) {
return true;
}
if (str_ends_with($nodeName, ucfirst($prefix))) {
return true;
foreach ($sourceElement->attributes as $attribute) {
if (!str_starts_with($attribute->nodeName, 'xmlns')
&& $attribute->nodeName !== 'xsi:schemaLocation'
) {
$attrName = $this->camelToHyphen($attribute->nodeName);
if (!empty($attrName)) {
$targetElement->setAttribute($attrName, $attribute->value);
}
}
}
}

return false;
public function createQtiElementName(string $nodeName): string
{
return sprintf('qti-%s', $this->camelToHyphen($nodeName));
}

private function camelToHyphen(string $string): string
Expand Down
Loading

0 comments on commit 8e4d0cf

Please sign in to comment.