diff options
author | Devian <devianleong@gmail.com> | 2021-04-22 17:03:46 +0800 |
---|---|---|
committer | Devian <devianleong@gmail.com> | 2021-04-22 17:03:46 +0800 |
commit | 745cf2431a71d0e6c5f08f8605839279b2f7496e (patch) | |
tree | 11e4c7a19ac9f9efc1bb253b29b1fa488c34238e /src/Library/ScormLib.php |
Initiate commit
Diffstat (limited to 'src/Library/ScormLib.php')
-rw-r--r-- | src/Library/ScormLib.php | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/Library/ScormLib.php b/src/Library/ScormLib.php new file mode 100644 index 0000000..0ca8a6b --- /dev/null +++ b/src/Library/ScormLib.php @@ -0,0 +1,253 @@ +<?php + + +namespace Peopleaps\Scorm\Library; + + +use DOMDocument; +use Peopleaps\Scorm\Entity\Sco; +use Peopleaps\Scorm\Exception\InvalidScormArchiveException; +use Ramsey\Uuid\Uuid; + +class ScormLib +{ + /** + * Looks for the organization to use. + * + * @return array of Sco + * + * @throws InvalidScormArchiveException If a default organization + * is defined and not found + */ + public function parseOrganizationsNode(DOMDocument $dom) + { + $organizationsList = $dom->getElementsByTagName('organizations'); + $resources = $dom->getElementsByTagName('resource'); + + if ($organizationsList->length > 0) { + $organizations = $organizationsList->item(0); + $organization = $organizations->firstChild; + + if (!is_null($organizations->attributes) + && !is_null($organizations->attributes->getNamedItem('default'))) { + $defaultOrganization = $organizations->attributes->getNamedItem('default')->nodeValue; + } else { + $defaultOrganization = null; + } + // No default organization is defined + if (is_null($defaultOrganization)) { + while (!is_null($organization) + && 'organization' !== $organization->nodeName) { + $organization = $organization->nextSibling; + } + + if (is_null($organization)) { + return $this->parseResourceNodes($resources); + } + } + // A default organization is defined + // Look for it + else { + while (!is_null($organization) + && ('organization' !== $organization->nodeName + || is_null($organization->attributes->getNamedItem('identifier')) + || $organization->attributes->getNamedItem('identifier')->nodeValue !== $defaultOrganization)) { + $organization = $organization->nextSibling; + } + + if (is_null($organization)) { + throw new InvalidScormArchiveException('default_organization_not_found_message'); + } + } + + return $this->parseItemNodes($organization, $resources); + } else { + throw new InvalidScormArchiveException('no_organization_found_message'); + } + } + + /** + * Creates defined structure of SCOs. + * + * @return array of Sco + * + * @throws InvalidScormArchiveException + */ + private function parseItemNodes(\DOMNode $source, \DOMNodeList $resources, Sco $parentSco = null) + { + $item = $source->firstChild; + $scos = []; + + while (!is_null($item)) { + if ('item' === $item->nodeName) { + $sco = new Sco(); + $scos[] = $sco; + $sco->setUuid(Uuid::uuid4()); + $sco->setScoParent($parentSco); + $this->findAttrParams($sco, $item, $resources); + $this->findNodeParams($sco, $item->firstChild); + + if ($sco->isBlock()) { + $sco->setScoChildren($this->parseItemNodes($item, $resources, $sco)); + } + } + $item = $item->nextSibling; + } + + return $scos; + } + + private function parseResourceNodes(\DOMNodeList $resources) + { + $scos = []; + + foreach ($resources as $resource) { + if (!is_null($resource->attributes)) { + $scormType = $resource->attributes->getNamedItemNS( + $resource->lookupNamespaceUri('adlcp'), + 'scormType' + ); + + if (!is_null($scormType) && 'sco' === $scormType->nodeValue) { + $identifier = $resource->attributes->getNamedItem('identifier'); + $href = $resource->attributes->getNamedItem('href'); + + if (is_null($identifier)) { + throw new InvalidScormArchiveException('sco_with_no_identifier_message'); + } + if (is_null($href)) { + throw new InvalidScormArchiveException('sco_resource_without_href_message'); + } + $sco = new Sco(); + $sco->setUuid(Uuid::uuid4()); + $sco->setBlock(false); + $sco->setVisible(true); + $sco->setIdentifier($identifier->nodeValue); + $sco->setTitle($identifier->nodeValue); + $sco->setEntryUrl($href->nodeValue); + $scos[] = $sco; + } + } + } + + return $scos; + } + + /** + * Initializes parameters of the SCO defined in attributes of the node. + * It also look for the associated resource if it is a SCO and not a block. + * + * @throws InvalidScormArchiveException + */ + private function findAttrParams(Sco $sco, \DOMNode $item, \DOMNodeList $resources) + { + $identifier = $item->attributes->getNamedItem('identifier'); + $isVisible = $item->attributes->getNamedItem('isvisible'); + $identifierRef = $item->attributes->getNamedItem('identifierref'); + $parameters = $item->attributes->getNamedItem('parameters'); + + // throws an Exception if identifier is undefined + if (is_null($identifier)) { + throw new InvalidScormArchiveException('sco_with_no_identifier_message'); + } + $sco->setIdentifier($identifier->nodeValue); + + // visible is true by default + if (!is_null($isVisible) && 'false' === $isVisible) { + $sco->setVisible(false); + } else { + $sco->setVisible(true); + } + + // set parameters for SCO entry resource + if (!is_null($parameters)) { + $sco->setParameters($parameters->nodeValue); + } + + // check if item is a block or a SCO. A block doesn't define identifierref + if (is_null($identifierRef)) { + $sco->setBlock(true); + } else { + $sco->setBlock(false); + // retrieve entry URL + $sco->setEntryUrl($this->findEntryUrl($identifierRef->nodeValue, $resources)); + } + } + + /** + * Initializes parameters of the SCO defined in children nodes. + */ + private function findNodeParams(Sco $sco, \DOMNode $item) + { + while (!is_null($item)) { + switch ($item->nodeName) { + case 'title': + $sco->setTitle($item->nodeValue); + break; + case 'adlcp:masteryscore': + $sco->setScoreToPassInt($item->nodeValue); + break; + case 'adlcp:maxtimeallowed': + case 'imsss:attemptAbsoluteDurationLimit': + $sco->setMaxTimeAllowed($item->nodeValue); + break; + case 'adlcp:timelimitaction': + case 'adlcp:timeLimitAction': + $action = strtolower($item->nodeValue); + + if ('exit,message' === $action + || 'exit,no message' === $action + || 'continue,message' === $action + || 'continue,no message' === $action) { + $sco->setTimeLimitAction($action); + } + break; + case 'adlcp:datafromlms': + case 'adlcp:dataFromLMS': + $sco->setLaunchData($item->nodeValue); + break; + case 'adlcp:prerequisites': + $sco->setPrerequisites($item->nodeValue); + break; + case 'imsss:minNormalizedMeasure': + $sco->setScoreToPassDecimal($item->nodeValue); + break; + case 'adlcp:completionThreshold': + if ($item->nodeValue && !is_nan($item->nodeValue)) { + $sco->setCompletionThreshold(floatval($item->nodeValue)); + } + break; + } + $item = $item->nextSibling; + } + } + + /** + * Searches for the resource with the given id and retrieve URL to its content. + * + * @return string URL to the resource associated to the SCO + * + * @throws InvalidScormArchiveException + */ + public function findEntryUrl($identifierref, \DOMNodeList $resources) + { + foreach ($resources as $resource) { + $identifier = $resource->attributes->getNamedItem('identifier'); + + if (!is_null($identifier)) { + $identifierValue = $identifier->nodeValue; + + if ($identifierValue === $identifierref) { + $href = $resource->attributes->getNamedItem('href'); + + if (is_null($href)) { + throw new InvalidScormArchiveException('sco_resource_without_href_message'); + } + + return $href->nodeValue; + } + } + } + throw new InvalidScormArchiveException('sco_without_resource_message'); + } +} |