From 44b9a5b07915d133fe6de29ae9355048220b17d0 Mon Sep 17 00:00:00 2001 From: Anh Thao PHAM Date: Mon, 14 Jul 2014 10:04:32 +0200 Subject: [PATCH] Adds SCORM 2004 resource --- Controller/ScormController.php | 156 ++ Entity/Scorm2004Resource.php | 50 + Entity/Scorm2004Sco.php | 213 +++ Entity/Scorm2004ScoTracking.php | 280 ++++ Form/{Scorm12Type.php => ScormType.php} | 4 +- ...n.php => InvalidScormArchiveException.php} | 2 +- Listener/Scorm12Listener.php | 39 +- Listener/Scorm2004Listener.php | 682 +++++++++ .../Version20140708115102.php | 124 ++ Migrations/ibm_db2/Version20140708115102.php | 134 ++ Migrations/mysqli/Version20140708115102.php | 124 ++ Migrations/oci8/Version20140708115102.php | 218 +++ Migrations/pdo_ibm/Version20140708115102.php | 134 ++ .../pdo_mysql/Version20140708115102.php | 124 ++ Migrations/pdo_oci/Version20140708115102.php | 218 +++ .../pdo_pgsql/Version20140708115102.php | 134 ++ .../pdo_sqlite/Version20140708115102.php | 92 ++ .../pdo_sqlsrv/Version20140708115102.php | 135 ++ Migrations/sqlsrv/Version20140708115102.php | 135 ++ Repository/Scorm2004ResourceRepository.php | 31 + Resources/config/config.yml | 7 +- Resources/public/js/scorm_12_api.js | 338 +++-- Resources/public/js/scorm_2004_api.js | 1278 ++++++++++++++++- Resources/translations/resource.en.yml | 7 +- Resources/translations/resource.fr.yml | 7 +- Resources/views/scorm12MenuSco.html.twig | 2 +- Resources/views/scorm2004.html.twig | 43 + Resources/views/scorm2004MenuSco.html.twig | 142 ++ 28 files changed, 4615 insertions(+), 238 deletions(-) create mode 100644 Entity/Scorm2004Resource.php create mode 100644 Entity/Scorm2004Sco.php create mode 100644 Entity/Scorm2004ScoTracking.php rename Form/{Scorm12Type.php => ScormType.php} (94%) rename Listener/Exception/{InvalidScorm12ArchiveException.php => InvalidScormArchiveException.php} (88%) create mode 100644 Listener/Scorm2004Listener.php create mode 100644 Migrations/drizzle_pdo_mysql/Version20140708115102.php create mode 100644 Migrations/ibm_db2/Version20140708115102.php create mode 100644 Migrations/mysqli/Version20140708115102.php create mode 100644 Migrations/oci8/Version20140708115102.php create mode 100644 Migrations/pdo_ibm/Version20140708115102.php create mode 100644 Migrations/pdo_mysql/Version20140708115102.php create mode 100644 Migrations/pdo_oci/Version20140708115102.php create mode 100644 Migrations/pdo_pgsql/Version20140708115102.php create mode 100644 Migrations/pdo_sqlite/Version20140708115102.php create mode 100644 Migrations/pdo_sqlsrv/Version20140708115102.php create mode 100644 Migrations/sqlsrv/Version20140708115102.php create mode 100644 Repository/Scorm2004ResourceRepository.php create mode 100644 Resources/views/scorm2004.html.twig create mode 100644 Resources/views/scorm2004MenuSco.html.twig diff --git a/Controller/ScormController.php b/Controller/ScormController.php index 6626432..f331647 100644 --- a/Controller/ScormController.php +++ b/Controller/ScormController.php @@ -17,6 +17,9 @@ use Claroline\ScormBundle\Entity\Scorm12Resource; use Claroline\ScormBundle\Entity\Scorm12Sco; use Claroline\ScormBundle\Entity\Scorm12ScoTracking; +use Claroline\ScormBundle\Entity\Scorm2004Resource; +use Claroline\ScormBundle\Entity\Scorm2004Sco; +use Claroline\ScormBundle\Entity\Scorm2004ScoTracking; use Claroline\ScormBundle\Event\Log\LogScorm12ResultEvent; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -51,6 +54,7 @@ public function __construct( $this->om = $om; $this->securityContext = $securityContext; $this->scorm12ScoTrackingRepo = $om->getRepository('ClarolineScormBundle:Scorm12ScoTracking'); + $this->scorm2004ScoTrackingRepo = $om->getRepository('ClarolineScormBundle:Scorm2004ScoTracking'); $this->userRepo = $om->getRepository('ClarolineCoreBundle:User'); } @@ -361,6 +365,141 @@ private function logScorm12ScoResult( $this->eventDispatcher->dispatch('log', $event); } + /** + * @EXT\Route( + * "/render/scorm/2004/{scormId}", + * name = "claro_render_scorm_2004_resource" + * ) + * @EXT\Method("GET") + * @EXT\ParamConverter( + * "scorm", + * class="ClarolineScormBundle:Scorm2004Resource", + * options={"id" = "scormId", "strictId" = true} + * ) + * @EXT\Template("ClarolineScormBundle::scorm2004.html.twig") + * + * @param Scorm2004Resource $scorm + * + * @return Response + */ + public function renderScorm2004ResourceAction(Scorm2004Resource $scorm) + { + $this->checkScorm2004ResourceAccess('OPEN', $scorm); + $user = $this->securityContext->getToken()->getUser(); + + $scos = $scorm->getScos(); + $rootScos = array(); + + $checkTracking = true; + $createTracking = false; + + foreach ($scos as $sco) { + if (is_null($sco->getScoParent())) { + $rootScos[] = $sco; + } + + if ($checkTracking) { + $scoTracking = $this->scorm2004ScoTrackingRepo->findOneBy( + array('user' => $user->getId(), 'sco' => $sco->getId()) + ); + $checkTracking = false; + + if (is_null($scoTracking)) { + $createTracking = true; + } + } + + if ($createTracking) { + $scoTracking = new Scorm2004ScoTracking(); + $scoTracking->setUser($user); + $scoTracking->setSco($sco); + $scoTracking->setScoreRaw(-1); + $scoTracking->setScoreMax(-1); + $scoTracking->setScoreMin(-1); + $scoTracking->setLessonStatus('not attempted'); + $scoTracking->setSuspendData(''); + $scoTracking->setEntry('ab-initio'); + $scoTracking->setLessonLocation(''); + $scoTracking->setCredit('no-credit'); + $scoTracking->setTotalTime(0); + $scoTracking->setSessionTime(0); + $scoTracking->setLessonMode('normal'); + $scoTracking->setExitMode(''); + $scoTracking->setBestLessonStatus('not attempted'); + + $this->om->persist($scoTracking); + } + } + + if ($createTracking) { + $this->om->flush(); + } + + return array( + 'resource' => $scorm, + '_resource' => $scorm, + 'scos' => $rootScos, + 'workspace' => $scorm->getResourceNode()->getWorkspace() + ); + } + + /** + * @EXT\Route( + * "/scorm/2004/render/sco/{scoId}", + * name = "claro_render_scorm_2004_sco" + * ) + * @EXT\Method("GET") + * @EXT\ParamConverter( + * "scorm2004Sco", + * class="ClarolineScormBundle:Scorm2004Sco", + * options={"id" = "scoId", "strictId" = true} + * ) + * @EXT\Template("ClarolineScormBundle::scorm2004MenuSco.html.twig") + * + * @param Scorm2004Sco $scorm2004Sco + * + * @return Response + */ + public function renderScorm2004ScoAction(Scorm2004Sco $scorm2004Sco) + { + $user = $this->securityContext->getToken()->getUser(); + $scorm = $scorm2004Sco->getScormResource(); + $this->checkScorm2004ResourceAccess('OPEN', $scorm); + + $scos = $scorm->getScos(); + $entryUrl = $scorm2004Sco->getEntryUrl(); + + if (is_string($entryUrl) && preg_match('/^http/', $entryUrl)) { + $scormPath = $entryUrl . $scorm2004Sco->getParameters(); + } else { + $scormPath = 'uploads/scormresources/' + . $scorm->getHashName() + . DIRECTORY_SEPARATOR + . $scorm2004Sco->getEntryUrl() + . $scorm2004Sco->getParameters(); + } + $rootScos = array(); + + foreach ($scos as $sco) { + if (is_null($sco->getScoParent())) { + $rootScos[] = $sco; + } + } + $scoTracking = $this->scorm2004ScoTrackingRepo->findOneBy( + array('user' => $user->getId(), 'sco' => $scorm2004Sco->getId()) + ); + + return array( + 'resource' => $scorm, + '_resource' => $scorm, + 'currentSco' => $scorm2004Sco, + 'scos' => $rootScos, + 'scoTracking' => $scoTracking, + 'scormUrl' => $scormPath, + 'workspace' => $scorm->getResourceNode()->getWorkspace() + ); + } + /** * Convert time (HHHH:MM:SS.hh) to integer (hundredth of second) * @@ -401,4 +540,21 @@ private function checkAccess($permission, Scorm12Resource $resource) throw new AccessDeniedException($collection->getErrorsForDisplay()); } } + + /** + * Checks if the current user has the right to perform an action on a Scorm2004Resource. + * + * @param string $permission + * @param Scorm2004Resource $resource + * + * @throws AccessDeniedException + */ + private function checkScorm2004ResourceAccess($permission, Scorm2004Resource $resource) + { + $collection = new ResourceCollection(array($resource->getResourceNode())); + + if (!$this->securityContext->isGranted($permission, $collection)) { + throw new AccessDeniedException($collection->getErrorsForDisplay()); + } + } } \ No newline at end of file diff --git a/Entity/Scorm2004Resource.php b/Entity/Scorm2004Resource.php new file mode 100644 index 0000000..8624fc7 --- /dev/null +++ b/Entity/Scorm2004Resource.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Claroline\ScormBundle\Entity; + +use Claroline\CoreBundle\Entity\Resource\AbstractResource; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity(repositoryClass="Claroline\ScormBundle\Repository\Scorm2004ResourceRepository") + * @ORM\Table(name="claro_scorm_2004_resource") + */ +class Scorm2004Resource extends AbstractResource +{ + /** + * @ORM\Column(name="hash_name", length=50) + */ + protected $hashName; + + /** + * @ORM\OneToMany( + * targetEntity="Claroline\ScormBundle\Entity\Scorm2004Sco", + * mappedBy="scormResource" + * ) + */ + protected $scos; + + public function getHashName() + { + return $this->hashName; + } + + public function setHashName($hashName) + { + $this->hashName = $hashName; + } + + public function getScos() + { + return $this->scos; + } +} diff --git a/Entity/Scorm2004Sco.php b/Entity/Scorm2004Sco.php new file mode 100644 index 0000000..91406c1 --- /dev/null +++ b/Entity/Scorm2004Sco.php @@ -0,0 +1,213 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Claroline\ScormBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + * @ORM\Table(name="claro_scorm_2004_sco") + */ +class Scorm2004Sco +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\ManyToOne( + * targetEntity="Claroline\ScormBundle\Entity\Scorm2004Resource", + * inversedBy="scos" + * ) + * @ORM\JoinColumn(name="scorm_resource_id", onDelete="CASCADE", nullable=false) + */ + protected $scormResource; + + /** + * @ORM\ManyToOne( + * targetEntity="Claroline\ScormBundle\Entity\Scorm2004Sco", + * inversedBy="scoChildren" + * ) + * @ORM\JoinColumn(name="sco_parent_id", onDelete="CASCADE", nullable=true) + */ + protected $scoParent; + + /** + * @ORM\OneToMany( + * targetEntity="Claroline\ScormBundle\Entity\Scorm2004Sco", + * mappedBy="scoParent" + * ) + */ + protected $scoChildren; + + /** + * @ORM\Column(name="entry_url", nullable=true) + */ + protected $entryUrl; + + /** + * @ORM\Column(name="scorm_identifier", nullable=false) + */ + protected $identifier; + + /** + * @ORM\Column(nullable=false, length=200) + */ + protected $title; + + /** + * @ORM\Column(type="boolean", nullable=false) + */ + protected $visible; + + /** + * @ORM\Column(nullable=true, length=1000) + */ + protected $parameters; + + /** + * @ORM\Column(name="time_limit_action", nullable=true) + */ + protected $timeLimitAction; + + /** + * @ORM\Column(name="launch_data", nullable=true, length=4000) + */ + protected $launchData; + + /** + * @ORM\Column(name="is_block", type="boolean", nullable=false) + */ + protected $isBlock; + + public function getId() + { + return $this->id; + } + + public function getScormResource() + { + return $this->scormResource; + } + public function getScoParent() + { + return $this->scoParent; + } + + public function getScoChildren() + { + return $this->scoChildren; + } + + public function getEntryUrl() + { + return $this->entryUrl; + } + + public function getIdentifier() + { + return $this->identifier; + } + + public function getTitle() + { + return $this->title; + } + + public function isVisible() + { + return $this->visible; + } + + public function getParameters() + { + return $this->parameters; + } + + public function getTimeLimitAction() + { + return $this->timeLimitAction; + } + + public function getLaunchData() + { + return $this->launchData; + } + + public function getIsBlock() + { + return $this->isBlock; + } + + public function setId($id) + { + $this->id = $id; + } + + public function setScormResource($scormResource) + { + $this->scormResource = $scormResource; + } + + public function setScoParent($scoParent) + { + $this->scoParent = $scoParent; + } + + public function setScoChildren($scoChildren) + { + $this->scoChildren = $scoChildren; + } + + public function setEntryUrl($entryUrl) + { + $this->entryUrl = $entryUrl; + } + + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function setVisible($visible) + { + $this->visible = $visible; + } + + public function setParameters($parameters) + { + $this->parameters = $parameters; + } + + public function setTimeLimitAction($timeLimitAction) + { + $this->timeLimitAction = $timeLimitAction; + } + + public function setLaunchData($launchData) + { + $this->launchData = $launchData; + } + + public function setIsBlock($isBlock) + { + $this->isBlock = $isBlock; + } +} diff --git a/Entity/Scorm2004ScoTracking.php b/Entity/Scorm2004ScoTracking.php new file mode 100644 index 0000000..148e00f --- /dev/null +++ b/Entity/Scorm2004ScoTracking.php @@ -0,0 +1,280 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Claroline\ScormBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + * @ORM\Table(name="claro_scorm_2004_sco_tracking") + */ +class Scorm2004ScoTracking +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\ManyToOne(targetEntity="Claroline\CoreBundle\Entity\User") + * @ORM\JoinColumn(name="user_id", onDelete="CASCADE", nullable=false) + */ + protected $user; + + /** + * @ORM\ManyToOne(targetEntity="Claroline\ScormBundle\Entity\Scorm2004Sco") + * @ORM\JoinColumn(name="scorm_id", onDelete="CASCADE", nullable=false) + */ + protected $sco; + + /** + * @ORM\Column(name="score_raw", type="integer", nullable=true) + */ + protected $scoreRaw; + + /** + * @ORM\Column(name="score_min", type="integer", nullable=true) + */ + protected $scoreMin; + + /** + * @ORM\Column(name="score_max", type="integer", nullable=true) + */ + protected $scoreMax; + + /** + * @ORM\Column(name="lesson_status", nullable=true) + */ + protected $lessonStatus; + + /** + * @ORM\Column(name="session_time", type="integer", nullable=true) + */ + protected $sessionTime; + + /** + * @ORM\Column(name="total_time", type="integer", nullable=true) + */ + protected $totalTime; + + /** + * @ORM\Column(nullable=true) + */ + protected $entry; + + /** + * @ORM\Column(name="suspend_data", nullable=true, length=4096) + */ + protected $suspendData; + + /** + * @ORM\Column(nullable=true) + */ + protected $credit; + + /** + * @ORM\Column(name="exit_mode", nullable=true) + */ + protected $exitMode; + + /** + * @ORM\Column(name="lesson_location", nullable=true) + */ + protected $lessonLocation; + + /** + * @ORM\Column(name="lesson_mode", nullable=true) + */ + protected $lessonMode; + + /** + * @ORM\Column(name="best_score_raw", type="integer", nullable=true) + */ + protected $bestScoreRaw; + + /** + * @ORM\Column(name="best_lesson_status", nullable=true) + */ + protected $bestLessonStatus; + + public function getId() + { + return $this->id; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getUser() + { + return $this->user; + } + + public function setUser($user) + { + $this->user = $user; + } + + public function getSco() + { + return $this->sco; + } + + public function setSco($sco) + { + $this->sco = $sco; + } + + public function getScoreRaw() + { + return $this->scoreRaw; + } + + public function setScoreRaw($scoreRaw) + { + $this->scoreRaw = $scoreRaw; + } + + public function getScoreMin() + { + return $this->scoreMin; + } + + public function setScoreMin($scoreMin) + { + $this->scoreMin = $scoreMin; + } + + public function getScoreMax() + { + return $this->scoreMax; + } + + public function setScoreMax($scoreMax) + { + $this->scoreMax = $scoreMax; + } + + public function getLessonStatus() + { + return $this->lessonStatus; + } + + public function setLessonStatus($lessonStatus) + { + $this->lessonStatus = $lessonStatus; + } + + public function getSessionTime() + { + return $this->sessionTime; + } + + public function setSessionTime($sessionTime) + { + $this->sessionTime = $sessionTime; + } + + public function getTotalTime() + { + return $this->totalTime; + } + + public function setTotalTime($totalTime) + { + $this->totalTime = $totalTime; + } + + public function getEntry() + { + return $this->entry; + } + + public function setEntry($entry) + { + $this->entry = $entry; + } + + public function getSuspendData() + { + return $this->suspendData; + } + + public function setSuspendData($suspendData) + { + $this->suspendData = $suspendData; + } + + public function getCredit() + { + return $this->credit; + } + + public function setCredit($credit) + { + $this->credit = $credit; + } + + public function getExitMode() + { + return $this->exitMode; + } + + public function setExitMode($exitMode) + { + $this->exitMode = $exitMode; + } + + public function getLessonLocation() + { + return $this->lessonLocation; + } + + public function setLessonLocation($lessonLocation) + { + $this->lessonLocation = $lessonLocation; + } + + public function getLessonMode() + { + return $this->lessonMode; + } + + public function setLessonMode($lessonMode) + { + $this->lessonMode = $lessonMode; + } + + public function getBestScoreRaw() + { + return $this->bestScoreRaw; + } + + public function getBestLessonStatus() + { + return $this->bestLessonStatus; + } + + public function setBestScoreRaw($bestScoreRaw) + { + $this->bestScoreRaw = $bestScoreRaw; + } + + public function setBestLessonStatus($bestLessonStatus) + { + $this->bestLessonStatus = $bestLessonStatus; + } +} diff --git a/Form/Scorm12Type.php b/Form/ScormType.php similarity index 94% rename from Form/Scorm12Type.php rename to Form/ScormType.php index f068d57..a70995b 100644 --- a/Form/Scorm12Type.php +++ b/Form/ScormType.php @@ -18,7 +18,7 @@ use Symfony\Component\OptionsResolver\OptionsResolverInterface; -class Scorm12Type extends AbstractType +class ScormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { @@ -46,7 +46,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) public function getName() { - return 'scorm_12_form'; + return 'scorm_form'; } public function setDefaultOptions(OptionsResolverInterface $resolver) diff --git a/Listener/Exception/InvalidScorm12ArchiveException.php b/Listener/Exception/InvalidScormArchiveException.php similarity index 88% rename from Listener/Exception/InvalidScorm12ArchiveException.php rename to Listener/Exception/InvalidScormArchiveException.php index 728af8a..b8fce19 100644 --- a/Listener/Exception/InvalidScorm12ArchiveException.php +++ b/Listener/Exception/InvalidScormArchiveException.php @@ -14,6 +14,6 @@ /** * Class for exceptions thrown when no selection has been made with the ResourceQueryBuilder. */ -class InvalidScorm12ArchiveException extends \Exception +class InvalidScormArchiveException extends \Exception { } diff --git a/Listener/Scorm12Listener.php b/Listener/Scorm12Listener.php index 2c1d012..614e45f 100644 --- a/Listener/Scorm12Listener.php +++ b/Listener/Scorm12Listener.php @@ -21,8 +21,8 @@ use Claroline\CoreBundle\Persistence\ObjectManager; use Claroline\ScormBundle\Entity\Scorm12Resource; use Claroline\ScormBundle\Entity\Scorm12Sco; -use Claroline\ScormBundle\Form\Scorm12Type; -use Claroline\ScormBundle\Listener\Exception\InvalidScorm12ArchiveException; +use Claroline\ScormBundle\Form\ScormType; +use Claroline\ScormBundle\Listener\Exception\InvalidScormArchiveException; use JMS\DiExtraBundle\Annotation as DI; use Symfony\Bundle\TwigBundle\TwigEngine; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -106,7 +106,7 @@ public function __construct( public function onCreateForm(CreateFormResourceEvent $event) { $form = $this->formFactory->create( - new Scorm12Type(), + new ScormType(), new Scorm12Resource() ); $content = $this->templating->render( @@ -128,7 +128,7 @@ public function onCreateForm(CreateFormResourceEvent $event) public function onCreate(CreateResourceEvent $event) { $form = $this->formFactory->create( - new Scorm12Type(), + new ScormType(), new Scorm12Resource() ); $form->handleRequest($this->request); @@ -151,7 +151,7 @@ public function onCreate(CreateResourceEvent $event) $this->om->persist($scormResource); $this->persistScos($scormResource, $scos); } else { - throw new InvalidScorm12ArchiveException('no_sco_in_scorm_archive_message'); + throw new InvalidScormArchiveException('no_sco_in_scorm_archive_message'); } $this->unzipScormArchive($tmpFile, $hashName); // Move Scorm archive in the files directory @@ -163,7 +163,7 @@ public function onCreate(CreateResourceEvent $event) return; } } - } catch (InvalidScorm12ArchiveException $e) { + } catch (InvalidScormArchiveException $e) { $msg = $e->getMessage(); $errorMsg = $this->translator->trans( $msg, @@ -287,7 +287,7 @@ private function isScormArchive(UploadedFile $file) && $zip->getStream("imsmanifest.xml"); if (!$isScormArchive) { - throw new InvalidScorm12ArchiveException('invalid_scorm_archive_message'); + throw new InvalidScormArchiveException('invalid_scorm_archive_message'); } return true; @@ -334,7 +334,6 @@ private function deleteFiles($dirPath) * Parses imsmanifest.xml file of a Scorm archive and * creates Scos defined in it. * - * @param Scorm12Resource $scormResource * @param UploadedFile $file * * @return array of Scorm resources @@ -354,7 +353,7 @@ private function generateScosFromScormArchive(UploadedFile $file) if (!$dom->loadXML($contents)) { - throw new InvalidScorm12ArchiveException('cannot_load_imsmanifest_message'); + throw new InvalidScormArchiveException('cannot_load_imsmanifest_message'); } $scormVersionElements = $dom->getElementsByTagName('schemaversion'); @@ -362,7 +361,7 @@ private function generateScosFromScormArchive(UploadedFile $file) if ($scormVersionElements->length > 0 && $scormVersionElements->item(0)->textContent !== '1.2') { - throw new InvalidScorm12ArchiveException('invalid_scorm_version_12_message'); + throw new InvalidScormArchiveException('invalid_scorm_version_12_message'); } $scos = $this->parseOrganizationsNode($dom); @@ -399,7 +398,7 @@ private function persistScos( * * @param \DOMDocument $dom * @return array of Scorm12Sco - * @throws InvalidScorm12ArchiveException If a default organization + * @throws InvalidScormArchiveException If a default organization * is defined and not found */ private function parseOrganizationsNode(\DOMDocument $dom) @@ -446,7 +445,7 @@ private function parseOrganizationsNode(\DOMDocument $dom) if (is_null($organization)) { - throw new InvalidScorm12ArchiveException('default_organization_not_found_message'); + throw new InvalidScormArchiveException('default_organization_not_found_message'); } } @@ -460,7 +459,7 @@ private function parseOrganizationsNode(\DOMDocument $dom) * @param \DOMNode $source * @param \DOMNodeList $resources * @return array of Scorm12Sco - * @throws InvalidScorm12ArchiveException + * @throws InvalidScormArchiveException */ private function parseItemNodes( \DOMNode $source, @@ -508,11 +507,11 @@ private function parseResourceNodes(\DOMNodeList $resources) if (is_null($identifier)) { - throw new InvalidScorm12ArchiveException('sco_with_no_identifier_message'); + throw new InvalidScormArchiveException('sco_with_no_identifier_message'); } if (is_null($href)) { - throw new InvalidScorm12ArchiveException('sco_resource_without_href_message'); + throw new InvalidScormArchiveException('sco_resource_without_href_message'); } $sco = new Scorm12Sco(); $sco->setIsBlock(false); @@ -535,7 +534,7 @@ private function parseResourceNodes(\DOMNodeList $resources) * @param Scorm12Sco $sco * @param \DOMNode $item * @param \DOMNodeList $resources - * @throws InvalidScorm12ArchiveException + * @throws InvalidScormArchiveException */ private function findAttrParams( Scorm12Sco $sco, @@ -550,7 +549,7 @@ private function findAttrParams( // throws an Exception if identifier is undefined if (is_null($identifier)) { - throw new InvalidScorm12ArchiveException('sco_with_no_identifier_message'); + throw new InvalidScormArchiveException('sco_with_no_identifier_message'); } $sco->setIdentifier($identifier->nodeValue); @@ -626,7 +625,7 @@ private function findNodeParams(Scorm12Sco $sco, \DOMNode $item) * @param string $identifierref id of the resource associated to the SCO * @param \DOMNodeList $resources * @return string URL to the resource associated to the SCO - * @throws InvalidScorm12ArchiveException + * @throws InvalidScormArchiveException */ public function findEntryUrl($identifierref, \DOMNodeList $resources) { @@ -641,14 +640,14 @@ public function findEntryUrl($identifierref, \DOMNodeList $resources) if (is_null($href)) { - throw new InvalidScorm12ArchiveException('sco_resource_without_href_message'); + throw new InvalidScormArchiveException('sco_resource_without_href_message'); } return $href->nodeValue; } } } - throw new InvalidScorm12ArchiveException('sco_without_resource_message'); + throw new InvalidScormArchiveException('sco_without_resource_message'); } /** diff --git a/Listener/Scorm2004Listener.php b/Listener/Scorm2004Listener.php new file mode 100644 index 0000000..adcd7b5 --- /dev/null +++ b/Listener/Scorm2004Listener.php @@ -0,0 +1,682 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Claroline\ScormBundle\Listener; + +use Claroline\CoreBundle\Event\CopyResourceEvent; +use Claroline\CoreBundle\Event\CreateFormResourceEvent; +use Claroline\CoreBundle\Event\CreateResourceEvent; +use Claroline\CoreBundle\Event\OpenResourceEvent; +use Claroline\CoreBundle\Event\DeleteResourceEvent; +use Claroline\CoreBundle\Event\DownloadResourceEvent; +use Claroline\CoreBundle\Listener\NoHttpRequestException; +use Claroline\CoreBundle\Persistence\ObjectManager; +use Claroline\ScormBundle\Entity\Scorm2004Resource; +use Claroline\ScormBundle\Entity\Scorm2004Sco; +use Claroline\ScormBundle\Form\ScormType; +use Claroline\ScormBundle\Listener\Exception\InvalidScormArchiveException; +use JMS\DiExtraBundle\Annotation as DI; +use Symfony\Bundle\TwigBundle\TwigEngine; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormFactory; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Translation\Translator; + +/** + * @DI\Service + */ +class Scorm2004Listener +{ + private $container; + // path to the Scorm archive file + private $filePath; + private $formFactory; + private $httpKernel; + private $om; + private $request; + private $router; + private $scormResourceRepo; + // path to the Scorm unzipped files + private $scormResourcesPath; + private $scorm2004ScoTrackingRepo; + private $securityContext; + private $templating; + private $translator; + + /** + * @DI\InjectParams({ + * "container" = @DI\Inject("service_container"), + * "formFactory" = @DI\Inject("form.factory"), + * "httpKernel" = @DI\Inject("http_kernel"), + * "om" = @DI\Inject("claroline.persistence.object_manager"), + * "requestStack" = @DI\Inject("request_stack"), + * "router" = @DI\Inject("router"), + * "securityContext" = @DI\Inject("security.context"), + * "templating" = @DI\Inject("templating"), + * "translator" = @DI\Inject("translator") + * }) + */ + public function __construct( + ContainerInterface $container, + FormFactory $formFactory, + HttpKernelInterface $httpKernel, + ObjectManager $om, + RequestStack $requestStack, + UrlGeneratorInterface $router, + SecurityContextInterface $securityContext, + TwigEngine $templating, + Translator $translator + ) + { + $this->container = $container; + $this->filePath = $this->container + ->getParameter('claroline.param.files_directory') . DIRECTORY_SEPARATOR; + $this->formFactory = $formFactory; + $this->httpKernel = $httpKernel; + $this->om = $om; + $this->request = $requestStack->getCurrentRequest(); + $this->router = $router; + $this->scormResourceRepo = $om->getRepository('ClarolineScormBundle:Scorm2004Resource'); + $this->scormResourcesPath = $this->container + ->getParameter('kernel.root_dir') . '/../web/uploads/scormresources/'; + $this->scorm2004ScoTrackingRepo = $om->getRepository('ClarolineScormBundle:Scorm2004ScoTracking'); + $this->securityContext = $securityContext; + $this->templating = $templating; + $this->translator = $translator; + } + + /** + * @DI\Observe("create_form_claroline_scorm_2004") + * + * @param CreateFormResourceEvent $event + */ + public function onCreateForm(CreateFormResourceEvent $event) + { + $form = $this->formFactory->create( + new ScormType(), + new Scorm2004Resource() + ); + $content = $this->templating->render( + 'ClarolineCoreBundle:Resource:createForm.html.twig', + array( + 'form' => $form->createView(), + 'resourceType' => 'claroline_scorm_2004' + ) + ); + $event->setResponseContent($content); + $event->stopPropagation(); + } + + /** + * @DI\Observe("create_claroline_scorm_2004") + * + * @param CreateResourceEvent $event + */ + public function onCreate(CreateResourceEvent $event) + { + $form = $this->formFactory->create( + new ScormType(), + new Scorm2004Resource() + ); + $form->handleRequest($this->request); + + try { + if ($form->isValid()) { + $tmpFile = $form->get('file')->getData(); + + if ($this->isScormArchive($tmpFile)) { + $scormResource = new Scorm2004Resource(); + $scormResource->setName($form['name']->getData()); + $fileName = $tmpFile->getClientOriginalName(); + $extension = pathinfo($fileName, PATHINFO_EXTENSION); + $hashName = $this->container->get('claroline.utilities.misc') + ->generateGuid() . "." . $extension; + $scormResource->setHashName($hashName); + $scos = $this->generateScosFromScormArchive($tmpFile); + + if (count($scos) > 0) { + $this->om->persist($scormResource); + $this->persistScos($scormResource, $scos); + } else { + throw new InvalidScormArchiveException('no_sco_in_scorm_archive_message'); + } + $this->unzipScormArchive($tmpFile, $hashName); + // Move Scorm archive in the files directory + $tmpFile->move($this->filePath, $hashName); + + $event->setResources(array($scormResource)); + $event->stopPropagation(); + + return; + } + } + } catch (InvalidScormArchiveException $e) { + $msg = $e->getMessage(); + $errorMsg = $this->translator->trans( + $msg, + array(), + 'resource' + ); + $form->addError(new FormError($errorMsg)); + } + $content = $this->templating->render( + 'ClarolineCoreBundle:Resource:createForm.html.twig', + array( + 'form' => $form->createView(), + 'resourceType' => $event->getResourceType() + ) + ); + $event->setErrorFormContent($content); + $event->stopPropagation(); + } + + /** + * @DI\Observe("open_claroline_scorm_2004") + * + * @param OpenResourceEvent $event + */ + public function onOpen(OpenResourceEvent $event) + { + if (!$this->request) { + throw new NoHttpRequestException(); + } + $scorm = $event->getResource(); + $params['_controller'] = 'ClarolineScormBundle:Scorm:renderScorm2004Resource'; + $params['scormId'] = $scorm->getId(); + + $subRequest = $this->request->duplicate( + array(), + null, + $params + ); + $response = $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + + $event->setResponse($response); + $event->stopPropagation(); + } + + /** + * @DI\Observe("delete_claroline_scorm_2004") + * + * @param DeleteResourceEvent $event + */ + public function onDelete(DeleteResourceEvent $event) + { + $hashName = $event->getResource()->getHashName(); + $scormArchiveFile = $this->filePath . $hashName; + $scormResourcesPath = $this->scormResourcesPath . $hashName; + + $nbScorm = (int)($this->scormResourceRepo->getNbScormWithHashName($hashName)); + + if ($nbScorm === 1) { + + if (file_exists($scormArchiveFile)) { + $event->setFiles(array($scormArchiveFile)); + } + if (file_exists($scormResourcesPath)) { + $this->deleteFiles($scormResourcesPath); + } + } + $this->om->remove($event->getResource()); + $event->stopPropagation(); + } + + /** + * @DI\Observe("copy_claroline_scorm_2004") + * + * @param CopyResourceEvent $event + */ + public function onCopy(CopyResourceEvent $event) + { + $resource = $event->getResource(); + $copy = new Scorm2004Resource(); + $copy->setHashName($resource->getHashName()); + $copy->setName($resource->getName()); + $this->om->persist($copy); + + $scos = $resource->getScos(); + + foreach ($scos as $sco) { + + if (is_null($sco->getScoParent())) { + $this->copySco($sco, $copy); + } + } + + $event->setCopy($copy); + $event->stopPropagation(); + } + + /** + * @DI\Observe("download_claroline_scorm_2004") + * + * @param DownloadResourceEvent $event + */ + public function onDownload(DownloadResourceEvent $event) + { + $event->setItem($this->filePath . $event->getResource()->getHashName()); + $event->stopPropagation(); + } + + /** + * Checks if a UploadedFile is a zip archive that contains a + * imsmanifest.xml file in its root directory. + * + * @param UploadedFile $file + * + * @return boolean. + */ + private function isScormArchive(UploadedFile $file) + { + $zip = new \ZipArchive(); + $isScormArchive = $file->getClientMimeType() === 'application/zip' + && $zip->open($file) + && $zip->getStream("imsmanifest.xml"); + + if (!$isScormArchive) { + throw new InvalidScormArchiveException('invalid_scorm_archive_message'); + } + + return true; + } + + /** + * Unzip a given ZIP file into the web resources directory + * + * @param UploadedFile $file + * @param $hashName name of the destination directory + */ + private function unzipScormArchive(UploadedFile $file, $hashName) + { + $zip = new \ZipArchive(); + $zip->open($file); + $destinationDir = $this->scormResourcesPath . $hashName; + + if (!file_exists($destinationDir)) { + mkdir($destinationDir, 0777, true); + } + $zip->extractTo($destinationDir); + $zip->close(); + } + + /** + * Deletes recursively a directory and its content. + * + * @param $dir The path to the directory to delete. + */ + private function deleteFiles($dirPath) + { + foreach (glob($dirPath . '/*') as $content) { + + if (is_dir($content)) { + $this->deleteFiles($content); + } else { + unlink($content); + } + } + rmdir($dirPath); + } + + /** + * Parses imsmanifest.xml file of a Scorm archive and + * creates Scos defined in it. + * + * @param UploadedFile $file + * + * @return array of Scorm resources + */ + private function generateScosFromScormArchive(UploadedFile $file) + { + $contents = ''; + $zip = new \ZipArchive(); + + $zip->open($file); + $stream = $zip->getStream("imsmanifest.xml"); + + while (!feof($stream)) { + $contents .= fread($stream, 2); + } + $dom = new \DOMDocument(); + + if (!$dom->loadXML($contents)) { + + throw new InvalidScormArchiveException('cannot_load_imsmanifest_message'); + } + + $scormVersionElements = $dom->getElementsByTagName('schemaversion'); + + if ($scormVersionElements->length > 0 + && $scormVersionElements->item(0)->textContent !== 'CAM 1.3' + && $scormVersionElements->item(0)->textContent !== '2004 3rd Edition' + && $scormVersionElements->item(0)->textContent !== '2004 4th Edition') { + + throw new InvalidScormArchiveException('invalid_scorm_version_2004_message'); + } + + $scos = $this->parseOrganizationsNode($dom); + + return $scos; + } + + /** + * Associates SCORM resource to SCOs and persists them. + * As array $scos can also contain an array of scos + * this method is call recursively when an element is an array. + * + * @param Scorm2004Resource $scormResource + * @param array $scos Array of Scorm2004Sco + */ + private function persistScos( + Scorm2004Resource $scormResource, + array $scos + ) + { + foreach ($scos as $sco) { + + if (is_array($sco)) { + $this->persistScos($scormResource, $sco); + } else { + $sco->setScormResource($scormResource); + $this->om->persist($sco); + } + } + } + + /** + * Looks for the organization to use + * + * @param \DOMDocument $dom + * @return array of Scorm2004Sco + * @throws InvalidScormArchiveException If a default organization + * is defined and not found + */ + private 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->nodeName !== 'organization') { + + $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->nodeName !== 'organization' + || 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); + } + } + + /** + * Creates defined structure of SCOs + * + * @param \DOMNode $source + * @param \DOMNodeList $resources + * @return array of Scorm2004Sco + * @throws InvalidScormArchiveException + */ + private function parseItemNodes( + \DOMNode $source, + \DOMNodeList $resources, + Scorm2004Sco $parentSco = null + ) + { + $item = $source->firstChild; + $scos = array(); + + while (!is_null($item)) { + + if ($item->nodeName === 'item') { + $sco = new Scorm2004Sco(); + $scos[] = $sco; + $sco->setScoParent($parentSco); + $this->findAttrParams($sco, $item, $resources); + $this->findNodeParams($sco, $item->firstChild); + + if ($sco->getIsBlock()) { + $scos[] = $this->parseItemNodes($item, $resources, $sco); + } + } + $item = $item->nextSibling; + } + + return $scos; + } + + private function parseResourceNodes(\DOMNodeList $resources) + { + $scos = array(); + + foreach ($resources as $resource) { + + if (!is_null($resource->attributes)) { + $scormType = $resource->attributes->getNamedItemNS( + $resource->lookupNamespaceUri('adlcp'), + 'scormType' + ); + + if (!is_null($scormType) && $scormType->nodeValue === 'sco') { + $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 Scorm2004Sco(); + $sco->setIsBlock(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. + * + * @param Scorm2004Sco $sco + * @param \DOMNode $item + * @param \DOMNodeList $resources + * @throws InvalidScormArchiveException + */ + private function findAttrParams( + Scorm2004Sco $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) && $isVisible === 'false') { + $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->setIsBlock(true); + } else { + $sco->setIsBlock(false); + // retrieve entry URL + $sco->setEntryUrl( + $this->findEntryUrl($identifierRef->nodeValue, $resources) + ); + } + } + + /** + * Initializes parameters of the SCO defined in children nodes + * + * @param Scorm2004Sco $sco + * @param \DOMNode $item + */ + private function findNodeParams(Scorm2004Sco $sco, \DOMNode $item) + { + while (!is_null($item)) { + + switch ($item->nodeName) { + case 'title': + $sco->setTitle($item->nodeValue); + break; + case 'adlcp:timeLimitAction': + $action = strtolower($item->nodeValue); + + if ($action === 'exit,message' + || $action === 'exit,no message' + || $action === 'continue,message' + || $action === 'continue,no message') { + + $sco->setTimeLimitAction($action); + } + break; + case 'adlcp:dataFromLMS': + $sco->setLaunchData($item->nodeValue); + break; + case 'adlcp:completionThreshold': + break; + case 'imsss:attemptAbsoluteDurationLimit': + break; + case 'imsss:minNormalizedMeasure': + break; + } + $item = $item->nextSibling; + } + } + + /** + * Searches for the resource with the given id and retrieve URL to its content. + * + * @param string $identifierref id of the resource associated to the SCO + * @param \DOMNodeList $resources + * @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'); + } + + /** + * Copy given sco and its children + * + * @param Scorm2004Sco $sco + * @param Scorm2004Resource $resource + * @param Scorm2004Sco $scoParent + */ + private function copySco( + Scorm2004Sco $sco, + Scorm2004Resource $resource, + Scorm2004Sco $scoParent = null + ) + { + $scoCopy = new Scorm2004Sco(); + $scoCopy->setScormResource($resource); + $scoCopy->setScoParent($scoParent); + $scoCopy->setEntryUrl($sco->getEntryUrl()); + $scoCopy->setIdentifier($sco->getIdentifier()); + $scoCopy->setIsBlock($sco->getIsBlock()); + $scoCopy->setLaunchData($sco->getLaunchData()); + $scoCopy->setParameters($sco->getParameters()); + $scoCopy->setTimeLimitAction($sco->getTimeLimitAction()); + $scoCopy->setTitle($sco->getTitle()); + $scoCopy->setVisible($sco->isVisible()); + $this->om->persist($scoCopy); + + foreach ($sco->getScoChildren() as $scoChild) { + $this->copySco($scoChild, $resource, $scoCopy); + } + } +} diff --git a/Migrations/drizzle_pdo_mysql/Version20140708115102.php b/Migrations/drizzle_pdo_mysql/Version20140708115102.php new file mode 100644 index 0000000..2656d43 --- /dev/null +++ b/Migrations/drizzle_pdo_mysql/Version20140708115102.php @@ -0,0 +1,124 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INT AUTO_INCREMENT NOT NULL, + scorm_resource_id INT NOT NULL, + sco_parent_id INT DEFAULT NULL, + entry_url VARCHAR(255) DEFAULT NULL, + scorm_identifier VARCHAR(255) NOT NULL, + title VARCHAR(200) NOT NULL, + visible BOOLEAN NOT NULL, + parameters VARCHAR(1000) DEFAULT NULL, + time_limit_action VARCHAR(255) DEFAULT NULL, + launch_data VARCHAR(4000) DEFAULT NULL, + is_block BOOLEAN NOT NULL, + PRIMARY KEY(id), + INDEX IDX_E88F1DDD167AFF3D (scorm_resource_id), + INDEX IDX_E88F1DDD48C689D5 (sco_parent_id) + ) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INT AUTO_INCREMENT NOT NULL, + user_id INT NOT NULL, + scorm_id INT NOT NULL, + score_raw INT DEFAULT NULL, + score_min INT DEFAULT NULL, + score_max INT DEFAULT NULL, + lesson_status VARCHAR(255) DEFAULT NULL, + session_time INT DEFAULT NULL, + total_time INT DEFAULT NULL, + entry VARCHAR(255) DEFAULT NULL, + suspend_data TEXT DEFAULT NULL, + credit VARCHAR(255) DEFAULT NULL, + exit_mode VARCHAR(255) DEFAULT NULL, + lesson_location VARCHAR(255) DEFAULT NULL, + lesson_mode VARCHAR(255) DEFAULT NULL, + best_score_raw INT DEFAULT NULL, + best_lesson_status VARCHAR(255) DEFAULT NULL, + PRIMARY KEY(id), + INDEX IDX_3A61CA76ED395 (user_id), + INDEX IDX_3A61CD75F22BE (scorm_id) + ) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INT AUTO_INCREMENT NOT NULL, + hash_name VARCHAR(50) NOT NULL, + resourceNode_id INT DEFAULT NULL, + PRIMARY KEY(id), + UNIQUE INDEX UNIQ_D16AB015B87FAB32 (resourceNode_id) + ) + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP FOREIGN KEY FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/ibm_db2/Version20140708115102.php b/Migrations/ibm_db2/Version20140708115102.php new file mode 100644 index 0000000..6d3de86 --- /dev/null +++ b/Migrations/ibm_db2/Version20140708115102.php @@ -0,0 +1,134 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + scorm_resource_id INTEGER NOT NULL, + sco_parent_id INTEGER DEFAULT NULL, + entry_url VARCHAR(255) DEFAULT NULL, + scorm_identifier VARCHAR(255) NOT NULL, + title VARCHAR(200) NOT NULL, + visible SMALLINT NOT NULL, + parameters VARCHAR(1000) DEFAULT NULL, + time_limit_action VARCHAR(255) DEFAULT NULL, + launch_data VARCHAR(4000) DEFAULT NULL, + is_block SMALLINT NOT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + user_id INTEGER NOT NULL, + scorm_id INTEGER NOT NULL, + score_raw INTEGER DEFAULT NULL, + score_min INTEGER DEFAULT NULL, + score_max INTEGER DEFAULT NULL, + lesson_status VARCHAR(255) DEFAULT NULL, + session_time INTEGER DEFAULT NULL, + total_time INTEGER DEFAULT NULL, + entry VARCHAR(255) DEFAULT NULL, + suspend_data CLOB(1M) DEFAULT NULL, + credit VARCHAR(255) DEFAULT NULL, + exit_mode VARCHAR(255) DEFAULT NULL, + lesson_location VARCHAR(255) DEFAULT NULL, + lesson_mode VARCHAR(255) DEFAULT NULL, + best_score_raw INTEGER DEFAULT NULL, + best_lesson_status VARCHAR(255) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + hash_name VARCHAR(50) NOT NULL, + resourceNode_id INTEGER DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP FOREIGN KEY FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/mysqli/Version20140708115102.php b/Migrations/mysqli/Version20140708115102.php new file mode 100644 index 0000000..ec264b5 --- /dev/null +++ b/Migrations/mysqli/Version20140708115102.php @@ -0,0 +1,124 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INT AUTO_INCREMENT NOT NULL, + scorm_resource_id INT NOT NULL, + sco_parent_id INT DEFAULT NULL, + entry_url VARCHAR(255) DEFAULT NULL, + scorm_identifier VARCHAR(255) NOT NULL, + title VARCHAR(200) NOT NULL, + visible TINYINT(1) NOT NULL, + parameters VARCHAR(1000) DEFAULT NULL, + time_limit_action VARCHAR(255) DEFAULT NULL, + launch_data VARCHAR(4000) DEFAULT NULL, + is_block TINYINT(1) NOT NULL, + INDEX IDX_E88F1DDD167AFF3D (scorm_resource_id), + INDEX IDX_E88F1DDD48C689D5 (sco_parent_id), + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INT AUTO_INCREMENT NOT NULL, + user_id INT NOT NULL, + scorm_id INT NOT NULL, + score_raw INT DEFAULT NULL, + score_min INT DEFAULT NULL, + score_max INT DEFAULT NULL, + lesson_status VARCHAR(255) DEFAULT NULL, + session_time INT DEFAULT NULL, + total_time INT DEFAULT NULL, + entry VARCHAR(255) DEFAULT NULL, + suspend_data VARCHAR(4096) DEFAULT NULL, + credit VARCHAR(255) DEFAULT NULL, + exit_mode VARCHAR(255) DEFAULT NULL, + lesson_location VARCHAR(255) DEFAULT NULL, + lesson_mode VARCHAR(255) DEFAULT NULL, + best_score_raw INT DEFAULT NULL, + best_lesson_status VARCHAR(255) DEFAULT NULL, + INDEX IDX_3A61CA76ED395 (user_id), + INDEX IDX_3A61CD75F22BE (scorm_id), + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INT AUTO_INCREMENT NOT NULL, + hash_name VARCHAR(50) NOT NULL, + resourceNode_id INT DEFAULT NULL, + UNIQUE INDEX UNIQ_D16AB015B87FAB32 (resourceNode_id), + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP FOREIGN KEY FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/oci8/Version20140708115102.php b/Migrations/oci8/Version20140708115102.php new file mode 100644 index 0000000..508f92a --- /dev/null +++ b/Migrations/oci8/Version20140708115102.php @@ -0,0 +1,218 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id NUMBER(10) NOT NULL, + scorm_resource_id NUMBER(10) NOT NULL, + sco_parent_id NUMBER(10) DEFAULT NULL, + entry_url VARCHAR2(255) DEFAULT NULL, + scorm_identifier VARCHAR2(255) NOT NULL, + title VARCHAR2(200) NOT NULL, + visible NUMBER(1) NOT NULL, + parameters VARCHAR2(1000) DEFAULT NULL, + time_limit_action VARCHAR2(255) DEFAULT NULL, + launch_data VARCHAR2(4000) DEFAULT NULL, + is_block NUMBER(1) NOT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + DECLARE constraints_Count NUMBER; BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count + FROM USER_CONSTRAINTS + WHERE TABLE_NAME = 'CLARO_SCORM_2004_SCO' + AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 + OR constraints_Count = '' THEN EXECUTE IMMEDIATE 'ALTER TABLE CLARO_SCORM_2004_SCO ADD CONSTRAINT CLARO_SCORM_2004_SCO_AI_PK PRIMARY KEY (ID)'; END IF; END; + "); + $this->addSql(" + CREATE SEQUENCE CLARO_SCORM_2004_SCO_ID_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 + "); + $this->addSql(" + CREATE TRIGGER CLARO_SCORM_2004_SCO_AI_PK BEFORE INSERT ON CLARO_SCORM_2004_SCO FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN + SELECT CLARO_SCORM_2004_SCO_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; IF ( + : NEW.ID IS NULL + OR : NEW.ID = 0 + ) THEN + SELECT CLARO_SCORM_2004_SCO_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = 'CLARO_SCORM_2004_SCO_ID_SEQ'; + SELECT : NEW.ID INTO last_InsertID + FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP + SELECT CLARO_SCORM_2004_SCO_ID_SEQ.NEXTVAL INTO last_Sequence + FROM DUAL; END LOOP; END IF; END; + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id NUMBER(10) NOT NULL, + user_id NUMBER(10) NOT NULL, + scorm_id NUMBER(10) NOT NULL, + score_raw NUMBER(10) DEFAULT NULL, + score_min NUMBER(10) DEFAULT NULL, + score_max NUMBER(10) DEFAULT NULL, + lesson_status VARCHAR2(255) DEFAULT NULL, + session_time NUMBER(10) DEFAULT NULL, + total_time NUMBER(10) DEFAULT NULL, + entry VARCHAR2(255) DEFAULT NULL, + suspend_data CLOB DEFAULT NULL, + credit VARCHAR2(255) DEFAULT NULL, + exit_mode VARCHAR2(255) DEFAULT NULL, + lesson_location VARCHAR2(255) DEFAULT NULL, + lesson_mode VARCHAR2(255) DEFAULT NULL, + best_score_raw NUMBER(10) DEFAULT NULL, + best_lesson_status VARCHAR2(255) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + DECLARE constraints_Count NUMBER; BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count + FROM USER_CONSTRAINTS + WHERE TABLE_NAME = 'CLARO_SCORM_2004_SCO_TRACKING' + AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 + OR constraints_Count = '' THEN EXECUTE IMMEDIATE 'ALTER TABLE CLARO_SCORM_2004_SCO_TRACKING ADD CONSTRAINT CLARO_SCORM_2004_SCO_TRACKING_AI_PK PRIMARY KEY (ID)'; END IF; END; + "); + $this->addSql(" + CREATE SEQUENCE CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 + "); + $this->addSql(" + CREATE TRIGGER CLARO_SCORM_2004_SCO_TRACKING_AI_PK BEFORE INSERT ON CLARO_SCORM_2004_SCO_TRACKING FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN + SELECT CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; IF ( + : NEW.ID IS NULL + OR : NEW.ID = 0 + ) THEN + SELECT CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = 'CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ'; + SELECT : NEW.ID INTO last_InsertID + FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP + SELECT CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ.NEXTVAL INTO last_Sequence + FROM DUAL; END LOOP; END IF; END; + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id NUMBER(10) NOT NULL, + hash_name VARCHAR2(50) NOT NULL, + resourceNode_id NUMBER(10) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + DECLARE constraints_Count NUMBER; BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count + FROM USER_CONSTRAINTS + WHERE TABLE_NAME = 'CLARO_SCORM_2004_RESOURCE' + AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 + OR constraints_Count = '' THEN EXECUTE IMMEDIATE 'ALTER TABLE CLARO_SCORM_2004_RESOURCE ADD CONSTRAINT CLARO_SCORM_2004_RESOURCE_AI_PK PRIMARY KEY (ID)'; END IF; END; + "); + $this->addSql(" + CREATE SEQUENCE CLARO_SCORM_2004_RESOURCE_ID_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 + "); + $this->addSql(" + CREATE TRIGGER CLARO_SCORM_2004_RESOURCE_AI_PK BEFORE INSERT ON CLARO_SCORM_2004_RESOURCE FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN + SELECT CLARO_SCORM_2004_RESOURCE_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; IF ( + : NEW.ID IS NULL + OR : NEW.ID = 0 + ) THEN + SELECT CLARO_SCORM_2004_RESOURCE_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = 'CLARO_SCORM_2004_RESOURCE_ID_SEQ'; + SELECT : NEW.ID INTO last_InsertID + FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP + SELECT CLARO_SCORM_2004_RESOURCE_ID_SEQ.NEXTVAL INTO last_Sequence + FROM DUAL; END LOOP; END IF; END; + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP CONSTRAINT FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/pdo_ibm/Version20140708115102.php b/Migrations/pdo_ibm/Version20140708115102.php new file mode 100644 index 0000000..2da7dfb --- /dev/null +++ b/Migrations/pdo_ibm/Version20140708115102.php @@ -0,0 +1,134 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + scorm_resource_id INTEGER NOT NULL, + sco_parent_id INTEGER DEFAULT NULL, + entry_url VARCHAR(255) DEFAULT NULL, + scorm_identifier VARCHAR(255) NOT NULL, + title VARCHAR(200) NOT NULL, + visible SMALLINT NOT NULL, + parameters VARCHAR(1000) DEFAULT NULL, + time_limit_action VARCHAR(255) DEFAULT NULL, + launch_data VARCHAR(4000) DEFAULT NULL, + is_block SMALLINT NOT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + user_id INTEGER NOT NULL, + scorm_id INTEGER NOT NULL, + score_raw INTEGER DEFAULT NULL, + score_min INTEGER DEFAULT NULL, + score_max INTEGER DEFAULT NULL, + lesson_status VARCHAR(255) DEFAULT NULL, + session_time INTEGER DEFAULT NULL, + total_time INTEGER DEFAULT NULL, + entry VARCHAR(255) DEFAULT NULL, + suspend_data CLOB(1M) DEFAULT NULL, + credit VARCHAR(255) DEFAULT NULL, + exit_mode VARCHAR(255) DEFAULT NULL, + lesson_location VARCHAR(255) DEFAULT NULL, + lesson_mode VARCHAR(255) DEFAULT NULL, + best_score_raw INTEGER DEFAULT NULL, + best_lesson_status VARCHAR(255) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + hash_name VARCHAR(50) NOT NULL, + resourceNode_id INTEGER DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP FOREIGN KEY FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/pdo_mysql/Version20140708115102.php b/Migrations/pdo_mysql/Version20140708115102.php new file mode 100644 index 0000000..b828820 --- /dev/null +++ b/Migrations/pdo_mysql/Version20140708115102.php @@ -0,0 +1,124 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INT AUTO_INCREMENT NOT NULL, + scorm_resource_id INT NOT NULL, + sco_parent_id INT DEFAULT NULL, + entry_url VARCHAR(255) DEFAULT NULL, + scorm_identifier VARCHAR(255) NOT NULL, + title VARCHAR(200) NOT NULL, + visible TINYINT(1) NOT NULL, + parameters VARCHAR(1000) DEFAULT NULL, + time_limit_action VARCHAR(255) DEFAULT NULL, + launch_data VARCHAR(4000) DEFAULT NULL, + is_block TINYINT(1) NOT NULL, + INDEX IDX_E88F1DDD167AFF3D (scorm_resource_id), + INDEX IDX_E88F1DDD48C689D5 (sco_parent_id), + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INT AUTO_INCREMENT NOT NULL, + user_id INT NOT NULL, + scorm_id INT NOT NULL, + score_raw INT DEFAULT NULL, + score_min INT DEFAULT NULL, + score_max INT DEFAULT NULL, + lesson_status VARCHAR(255) DEFAULT NULL, + session_time INT DEFAULT NULL, + total_time INT DEFAULT NULL, + entry VARCHAR(255) DEFAULT NULL, + suspend_data VARCHAR(4096) DEFAULT NULL, + credit VARCHAR(255) DEFAULT NULL, + exit_mode VARCHAR(255) DEFAULT NULL, + lesson_location VARCHAR(255) DEFAULT NULL, + lesson_mode VARCHAR(255) DEFAULT NULL, + best_score_raw INT DEFAULT NULL, + best_lesson_status VARCHAR(255) DEFAULT NULL, + INDEX IDX_3A61CA76ED395 (user_id), + INDEX IDX_3A61CD75F22BE (scorm_id), + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INT AUTO_INCREMENT NOT NULL, + hash_name VARCHAR(50) NOT NULL, + resourceNode_id INT DEFAULT NULL, + UNIQUE INDEX UNIQ_D16AB015B87FAB32 (resourceNode_id), + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP FOREIGN KEY FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP FOREIGN KEY FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/pdo_oci/Version20140708115102.php b/Migrations/pdo_oci/Version20140708115102.php new file mode 100644 index 0000000..1cc4d1d --- /dev/null +++ b/Migrations/pdo_oci/Version20140708115102.php @@ -0,0 +1,218 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id NUMBER(10) NOT NULL, + scorm_resource_id NUMBER(10) NOT NULL, + sco_parent_id NUMBER(10) DEFAULT NULL, + entry_url VARCHAR2(255) DEFAULT NULL, + scorm_identifier VARCHAR2(255) NOT NULL, + title VARCHAR2(200) NOT NULL, + visible NUMBER(1) NOT NULL, + parameters VARCHAR2(1000) DEFAULT NULL, + time_limit_action VARCHAR2(255) DEFAULT NULL, + launch_data VARCHAR2(4000) DEFAULT NULL, + is_block NUMBER(1) NOT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + DECLARE constraints_Count NUMBER; BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count + FROM USER_CONSTRAINTS + WHERE TABLE_NAME = 'CLARO_SCORM_2004_SCO' + AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 + OR constraints_Count = '' THEN EXECUTE IMMEDIATE 'ALTER TABLE CLARO_SCORM_2004_SCO ADD CONSTRAINT CLARO_SCORM_2004_SCO_AI_PK PRIMARY KEY (ID)'; END IF; END; + "); + $this->addSql(" + CREATE SEQUENCE CLARO_SCORM_2004_SCO_ID_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 + "); + $this->addSql(" + CREATE TRIGGER CLARO_SCORM_2004_SCO_AI_PK BEFORE INSERT ON CLARO_SCORM_2004_SCO FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN + SELECT CLARO_SCORM_2004_SCO_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; IF ( + : NEW.ID IS NULL + OR : NEW.ID = 0 + ) THEN + SELECT CLARO_SCORM_2004_SCO_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = 'CLARO_SCORM_2004_SCO_ID_SEQ'; + SELECT : NEW.ID INTO last_InsertID + FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP + SELECT CLARO_SCORM_2004_SCO_ID_SEQ.NEXTVAL INTO last_Sequence + FROM DUAL; END LOOP; END IF; END; + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id NUMBER(10) NOT NULL, + user_id NUMBER(10) NOT NULL, + scorm_id NUMBER(10) NOT NULL, + score_raw NUMBER(10) DEFAULT NULL, + score_min NUMBER(10) DEFAULT NULL, + score_max NUMBER(10) DEFAULT NULL, + lesson_status VARCHAR2(255) DEFAULT NULL, + session_time NUMBER(10) DEFAULT NULL, + total_time NUMBER(10) DEFAULT NULL, + entry VARCHAR2(255) DEFAULT NULL, + suspend_data CLOB DEFAULT NULL, + credit VARCHAR2(255) DEFAULT NULL, + exit_mode VARCHAR2(255) DEFAULT NULL, + lesson_location VARCHAR2(255) DEFAULT NULL, + lesson_mode VARCHAR2(255) DEFAULT NULL, + best_score_raw NUMBER(10) DEFAULT NULL, + best_lesson_status VARCHAR2(255) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + DECLARE constraints_Count NUMBER; BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count + FROM USER_CONSTRAINTS + WHERE TABLE_NAME = 'CLARO_SCORM_2004_SCO_TRACKING' + AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 + OR constraints_Count = '' THEN EXECUTE IMMEDIATE 'ALTER TABLE CLARO_SCORM_2004_SCO_TRACKING ADD CONSTRAINT CLARO_SCORM_2004_SCO_TRACKING_AI_PK PRIMARY KEY (ID)'; END IF; END; + "); + $this->addSql(" + CREATE SEQUENCE CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 + "); + $this->addSql(" + CREATE TRIGGER CLARO_SCORM_2004_SCO_TRACKING_AI_PK BEFORE INSERT ON CLARO_SCORM_2004_SCO_TRACKING FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN + SELECT CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; IF ( + : NEW.ID IS NULL + OR : NEW.ID = 0 + ) THEN + SELECT CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = 'CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ'; + SELECT : NEW.ID INTO last_InsertID + FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP + SELECT CLARO_SCORM_2004_SCO_TRACKING_ID_SEQ.NEXTVAL INTO last_Sequence + FROM DUAL; END LOOP; END IF; END; + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id NUMBER(10) NOT NULL, + hash_name VARCHAR2(50) NOT NULL, + resourceNode_id NUMBER(10) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + DECLARE constraints_Count NUMBER; BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count + FROM USER_CONSTRAINTS + WHERE TABLE_NAME = 'CLARO_SCORM_2004_RESOURCE' + AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 + OR constraints_Count = '' THEN EXECUTE IMMEDIATE 'ALTER TABLE CLARO_SCORM_2004_RESOURCE ADD CONSTRAINT CLARO_SCORM_2004_RESOURCE_AI_PK PRIMARY KEY (ID)'; END IF; END; + "); + $this->addSql(" + CREATE SEQUENCE CLARO_SCORM_2004_RESOURCE_ID_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 + "); + $this->addSql(" + CREATE TRIGGER CLARO_SCORM_2004_RESOURCE_AI_PK BEFORE INSERT ON CLARO_SCORM_2004_RESOURCE FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN + SELECT CLARO_SCORM_2004_RESOURCE_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; IF ( + : NEW.ID IS NULL + OR : NEW.ID = 0 + ) THEN + SELECT CLARO_SCORM_2004_RESOURCE_ID_SEQ.NEXTVAL INTO : NEW.ID + FROM DUAL; ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = 'CLARO_SCORM_2004_RESOURCE_ID_SEQ'; + SELECT : NEW.ID INTO last_InsertID + FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP + SELECT CLARO_SCORM_2004_RESOURCE_ID_SEQ.NEXTVAL INTO last_Sequence + FROM DUAL; END LOOP; END IF; END; + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP CONSTRAINT FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/pdo_pgsql/Version20140708115102.php b/Migrations/pdo_pgsql/Version20140708115102.php new file mode 100644 index 0000000..3455ca9 --- /dev/null +++ b/Migrations/pdo_pgsql/Version20140708115102.php @@ -0,0 +1,134 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id SERIAL NOT NULL, + scorm_resource_id INT NOT NULL, + sco_parent_id INT DEFAULT NULL, + entry_url VARCHAR(255) DEFAULT NULL, + scorm_identifier VARCHAR(255) NOT NULL, + title VARCHAR(200) NOT NULL, + visible BOOLEAN NOT NULL, + parameters VARCHAR(1000) DEFAULT NULL, + time_limit_action VARCHAR(255) DEFAULT NULL, + launch_data VARCHAR(4000) DEFAULT NULL, + is_block BOOLEAN NOT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id SERIAL NOT NULL, + user_id INT NOT NULL, + scorm_id INT NOT NULL, + score_raw INT DEFAULT NULL, + score_min INT DEFAULT NULL, + score_max INT DEFAULT NULL, + lesson_status VARCHAR(255) DEFAULT NULL, + session_time INT DEFAULT NULL, + total_time INT DEFAULT NULL, + entry VARCHAR(255) DEFAULT NULL, + suspend_data VARCHAR(4096) DEFAULT NULL, + credit VARCHAR(255) DEFAULT NULL, + exit_mode VARCHAR(255) DEFAULT NULL, + lesson_location VARCHAR(255) DEFAULT NULL, + lesson_mode VARCHAR(255) DEFAULT NULL, + best_score_raw INT DEFAULT NULL, + best_lesson_status VARCHAR(255) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id SERIAL NOT NULL, + hash_name VARCHAR(50) NOT NULL, + resourceNode_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP CONSTRAINT FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/pdo_sqlite/Version20140708115102.php b/Migrations/pdo_sqlite/Version20140708115102.php new file mode 100644 index 0000000..c6ba3c9 --- /dev/null +++ b/Migrations/pdo_sqlite/Version20140708115102.php @@ -0,0 +1,92 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INTEGER NOT NULL, + scorm_resource_id INTEGER NOT NULL, + sco_parent_id INTEGER DEFAULT NULL, + entry_url VARCHAR(255) DEFAULT NULL, + scorm_identifier VARCHAR(255) NOT NULL, + title VARCHAR(200) NOT NULL, + visible BOOLEAN NOT NULL, + parameters VARCHAR(1000) DEFAULT NULL, + time_limit_action VARCHAR(255) DEFAULT NULL, + launch_data VARCHAR(4000) DEFAULT NULL, + is_block BOOLEAN NOT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + scorm_id INTEGER NOT NULL, + score_raw INTEGER DEFAULT NULL, + score_min INTEGER DEFAULT NULL, + score_max INTEGER DEFAULT NULL, + lesson_status VARCHAR(255) DEFAULT NULL, + session_time INTEGER DEFAULT NULL, + total_time INTEGER DEFAULT NULL, + entry VARCHAR(255) DEFAULT NULL, + suspend_data CLOB DEFAULT NULL, + credit VARCHAR(255) DEFAULT NULL, + exit_mode VARCHAR(255) DEFAULT NULL, + lesson_location VARCHAR(255) DEFAULT NULL, + lesson_mode VARCHAR(255) DEFAULT NULL, + best_score_raw INTEGER DEFAULT NULL, + best_lesson_status VARCHAR(255) DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INTEGER NOT NULL, + hash_name VARCHAR(50) NOT NULL, + resourceNode_id INTEGER DEFAULT NULL, + PRIMARY KEY(id) + ) + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/pdo_sqlsrv/Version20140708115102.php b/Migrations/pdo_sqlsrv/Version20140708115102.php new file mode 100644 index 0000000..1a115e4 --- /dev/null +++ b/Migrations/pdo_sqlsrv/Version20140708115102.php @@ -0,0 +1,135 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INT IDENTITY NOT NULL, + scorm_resource_id INT NOT NULL, + sco_parent_id INT, + entry_url NVARCHAR(255), + scorm_identifier NVARCHAR(255) NOT NULL, + title NVARCHAR(200) NOT NULL, + visible BIT NOT NULL, + parameters NVARCHAR(1000), + time_limit_action NVARCHAR(255), + launch_data NVARCHAR(4000), + is_block BIT NOT NULL, + PRIMARY KEY (id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INT IDENTITY NOT NULL, + user_id INT NOT NULL, + scorm_id INT NOT NULL, + score_raw INT, + score_min INT, + score_max INT, + lesson_status NVARCHAR(255), + session_time INT, + total_time INT, + entry NVARCHAR(255), + suspend_data VARCHAR(MAX), + credit NVARCHAR(255), + exit_mode NVARCHAR(255), + lesson_location NVARCHAR(255), + lesson_mode NVARCHAR(255), + best_score_raw INT, + best_lesson_status NVARCHAR(255), + PRIMARY KEY (id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INT IDENTITY NOT NULL, + hash_name NVARCHAR(50) NOT NULL, + resourceNode_id INT, + PRIMARY KEY (id) + ) + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + WHERE resourceNode_id IS NOT NULL + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP CONSTRAINT FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Migrations/sqlsrv/Version20140708115102.php b/Migrations/sqlsrv/Version20140708115102.php new file mode 100644 index 0000000..53f1e07 --- /dev/null +++ b/Migrations/sqlsrv/Version20140708115102.php @@ -0,0 +1,135 @@ +addSql(" + CREATE TABLE claro_scorm_2004_sco ( + id INT IDENTITY NOT NULL, + scorm_resource_id INT NOT NULL, + sco_parent_id INT, + entry_url NVARCHAR(255), + scorm_identifier NVARCHAR(255) NOT NULL, + title NVARCHAR(200) NOT NULL, + visible BIT NOT NULL, + parameters NVARCHAR(1000), + time_limit_action NVARCHAR(255), + launch_data NVARCHAR(4000), + is_block BIT NOT NULL, + PRIMARY KEY (id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD167AFF3D ON claro_scorm_2004_sco (scorm_resource_id) + "); + $this->addSql(" + CREATE INDEX IDX_E88F1DDD48C689D5 ON claro_scorm_2004_sco (sco_parent_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_sco_tracking ( + id INT IDENTITY NOT NULL, + user_id INT NOT NULL, + scorm_id INT NOT NULL, + score_raw INT, + score_min INT, + score_max INT, + lesson_status NVARCHAR(255), + session_time INT, + total_time INT, + entry NVARCHAR(255), + suspend_data VARCHAR(MAX), + credit NVARCHAR(255), + exit_mode NVARCHAR(255), + lesson_location NVARCHAR(255), + lesson_mode NVARCHAR(255), + best_score_raw INT, + best_lesson_status NVARCHAR(255), + PRIMARY KEY (id) + ) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CA76ED395 ON claro_scorm_2004_sco_tracking (user_id) + "); + $this->addSql(" + CREATE INDEX IDX_3A61CD75F22BE ON claro_scorm_2004_sco_tracking (scorm_id) + "); + $this->addSql(" + CREATE TABLE claro_scorm_2004_resource ( + id INT IDENTITY NOT NULL, + hash_name NVARCHAR(50) NOT NULL, + resourceNode_id INT, + PRIMARY KEY (id) + ) + "); + $this->addSql(" + CREATE UNIQUE INDEX UNIQ_D16AB015B87FAB32 ON claro_scorm_2004_resource (resourceNode_id) + WHERE resourceNode_id IS NOT NULL + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD167AFF3D FOREIGN KEY (scorm_resource_id) + REFERENCES claro_scorm_2004_resource (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + ADD CONSTRAINT FK_E88F1DDD48C689D5 FOREIGN KEY (sco_parent_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CA76ED395 FOREIGN KEY (user_id) + REFERENCES claro_user (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + ADD CONSTRAINT FK_3A61CD75F22BE FOREIGN KEY (scorm_id) + REFERENCES claro_scorm_2004_sco (id) + ON DELETE CASCADE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_resource + ADD CONSTRAINT FK_D16AB015B87FAB32 FOREIGN KEY (resourceNode_id) + REFERENCES claro_resource_node (id) + ON DELETE CASCADE + "); + } + + public function down(Schema $schema) + { + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD48C689D5 + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco_tracking + DROP CONSTRAINT FK_3A61CD75F22BE + "); + $this->addSql(" + ALTER TABLE claro_scorm_2004_sco + DROP CONSTRAINT FK_E88F1DDD167AFF3D + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_sco_tracking + "); + $this->addSql(" + DROP TABLE claro_scorm_2004_resource + "); + } +} \ No newline at end of file diff --git a/Repository/Scorm2004ResourceRepository.php b/Repository/Scorm2004ResourceRepository.php new file mode 100644 index 0000000..e96271a --- /dev/null +++ b/Repository/Scorm2004ResourceRepository.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Claroline\ScormBundle\Repository; + +use Doctrine\ORM\EntityRepository; + +class Scorm2004ResourceRepository extends EntityRepository +{ + public function getNbScormWithHashName($hashName) + { + $dql = ' + SELECT COUNT(s.id) + FROM Claroline\ScormBundle\Entity\Scorm2004Resource s + WHERE s.hashName = :hashName + '; + + $query = $this->_em->createQuery($dql); + $query->setParameter('hashName', $hashName); + + return $query->getSingleScalarResult(); + } +} diff --git a/Resources/config/config.yml b/Resources/config/config.yml index 67cf560..41cd39e 100644 --- a/Resources/config/config.yml +++ b/Resources/config/config.yml @@ -6,4 +6,9 @@ plugin: name: claroline_scorm_12 icon: scorm_12_icon.png activity_rules: - - action: resource-scorm_12-sco_result \ No newline at end of file + - action: resource-scorm_12-sco_result + - class: Claroline\ScormBundle\Entity\Scorm2004Resource + name: claroline_scorm_2004 + icon: scorm_2004_icon.png + activity_rules: + - action: resource-scorm_2004-sco_result \ No newline at end of file diff --git a/Resources/public/js/scorm_12_api.js b/Resources/public/js/scorm_12_api.js index e6be6d1..70ae5a3 100644 --- a/Resources/public/js/scorm_12_api.js +++ b/Resources/public/js/scorm_12_api.js @@ -135,38 +135,37 @@ function LMSGetValue(arg) if (apiInitialized) { switch (arg) { - - case 'cmi.core._children' : - case 'cmi.core.student_id' : - case 'cmi.core.student_name' : - case 'cmi.core.lesson_location' : - case 'cmi.core.credit' : - case 'cmi.core.lesson_status' : - case 'cmi.core.entry' : - case 'cmi.core.score._children' : - case 'cmi.core.score.raw' : - case 'cmi.core.score.min' : - case 'cmi.core.score.max' : - case 'cmi.core.total_time' : - case 'cmi.suspend_data' : - case 'cmi.launch_data' : - case 'cmi.core.lesson_mode' : - case 'cmi.student_data._children' : - case 'cmi.student_data.mastery_score' : - case 'cmi.student_data.max_time_allowed' : - case 'cmi.student_data.time_limit_action' : - apiLastError = '0'; - - return scoData[arg]; - case 'cmi.core.exit' : - case 'cmi.core.session_time' : - apiLastError = '404'; // write only - - return ''; - default : - apiLastError = '401'; - - return ''; + case 'cmi.core._children' : + case 'cmi.core.student_id' : + case 'cmi.core.student_name' : + case 'cmi.core.lesson_location' : + case 'cmi.core.credit' : + case 'cmi.core.lesson_status' : + case 'cmi.core.entry' : + case 'cmi.core.score._children' : + case 'cmi.core.score.raw' : + case 'cmi.core.score.min' : + case 'cmi.core.score.max' : + case 'cmi.core.total_time' : + case 'cmi.suspend_data' : + case 'cmi.launch_data' : + case 'cmi.core.lesson_mode' : + case 'cmi.student_data._children' : + case 'cmi.student_data.mastery_score' : + case 'cmi.student_data.max_time_allowed' : + case 'cmi.student_data.time_limit_action' : + apiLastError = '0'; + + return scoData[arg]; + case 'cmi.core.exit' : + case 'cmi.core.session_time' : + apiLastError = '404'; // write only + + return ''; + default : + apiLastError = '401'; + + return ''; } } else { // not initialized error @@ -185,119 +184,118 @@ function LMSSetValue(argName, argValue) if (apiInitialized) { switch (argName) { - - case 'cmi.core._children' : - case 'cmi.core.score._children' : - case 'cmi.student_data._children' : - apiLastError = '402'; // invalid set value, element is a keyword - - return 'false'; - case 'cmi.core.student_id' : - case 'cmi.core.student_name' : - case 'cmi.core.credit' : - case 'cmi.core.entry' : - case 'cmi.core.total_time' : - case 'cmi.launch_data' : - case 'cmi.core.lesson_mode' : - case 'cmi.student_data.mastery_score' : - case 'cmi.student_data.max_time_allowed' : - case 'cmi.student_data.time_limit_action' : - apiLastError = '403'; // read only - - return 'false'; - case 'cmi.core.lesson_location' : - - if (argValue.length > 255) { - apiLastError = '405'; + case 'cmi.core._children' : + case 'cmi.core.score._children' : + case 'cmi.student_data._children' : + apiLastError = '402'; // invalid set value, element is a keyword return 'false'; - } - scoData[argName] = argValue; - apiLastError = '0'; - - return 'true'; - case 'cmi.core.lesson_status' : - var upperCaseLessonStatus = argValue.toUpperCase(); - - if (upperCaseLessonStatus !== 'PASSED' && - upperCaseLessonStatus !== 'FAILED' && - upperCaseLessonStatus !== 'COMPLETED' && - upperCaseLessonStatus !== 'INCOMPLETE' && - upperCaseLessonStatus !== 'BROWSED' && - upperCaseLessonStatus !== 'NOT ATTEMPTED') { - - apiLastError = '405'; + case 'cmi.core.student_id' : + case 'cmi.core.student_name' : + case 'cmi.core.credit' : + case 'cmi.core.entry' : + case 'cmi.core.total_time' : + case 'cmi.launch_data' : + case 'cmi.core.lesson_mode' : + case 'cmi.student_data.mastery_score' : + case 'cmi.student_data.max_time_allowed' : + case 'cmi.student_data.time_limit_action' : + apiLastError = '403'; // read only return 'false'; - } - scoData[argName] = argValue; - apiLastError = '0'; - - return 'true'; - case 'cmi.core.score.raw' : - case 'cmi.core.score.min' : - case 'cmi.core.score.max' : + case 'cmi.core.lesson_location' : + + if (argValue.length > 255) { + apiLastError = '405'; + + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + case 'cmi.core.lesson_status' : + var upperCaseLessonStatus = argValue.toUpperCase(); + + if (upperCaseLessonStatus !== 'PASSED' && + upperCaseLessonStatus !== 'FAILED' && + upperCaseLessonStatus !== 'COMPLETED' && + upperCaseLessonStatus !== 'INCOMPLETE' && + upperCaseLessonStatus !== 'BROWSED' && + upperCaseLessonStatus !== 'NOT ATTEMPTED') { + + apiLastError = '405'; + + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + case 'cmi.core.score.raw' : + case 'cmi.core.score.min' : + case 'cmi.core.score.max' : + + if (isNaN(parseInt(argValue)) || (argValue < 0) || (argValue > 100)) { + apiLastError = '405'; + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + case 'cmi.core.exit' : + var upperCaseExit = argValue.toUpperCase(); + + if (upperCaseExit !== 'TIME-OUT' && + upperCaseExit !== 'SUSPEND' && + upperCaseExit !== 'LOGOUT' && + upperCaseExit !== '') { + + apiLastError = '405'; + + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + case 'cmi.core.session_time' : + // regex to check format + // hhhh:mm:ss.ss + var re = /^[0-9]{2,4}:[0-9]{2}:[0-9]{2}(.[0-9]{1,2})?$/; + + if (!re.test(argValue)) { + apiLastError = '405'; + + return 'false'; + } + // check that minute and second are 0 <= x < 60 + var timeArray = argValue.split(':'); + + if (timeArray[1] < 0 || timeArray[1] >= 60 || timeArray[2] < 0 || timeArray[2] >= 60) { + apiLastError = '405'; + + return 'false'; + } + + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + case 'cmi.suspend_data' : + if (argValue.length > 4096) { + apiLastError = '405'; + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + default : + apiLastError = '401'; - if (isNaN(parseInt(argValue)) || (argValue < 0) || (argValue > 100)) { - apiLastError = '405'; return 'false'; - } - scoData[argName] = argValue; - apiLastError = '0'; - - return 'true'; - case 'cmi.core.exit' : - var upperCaseExit = argValue.toUpperCase(); - - if (upperCaseExit !== 'TIME-OUT' && - upperCaseExit !== 'SUSPEND' && - upperCaseExit !== 'LOGOUT' && - upperCaseExit !== '') { - - apiLastError = '405'; - - return 'false'; - } - scoData[argName] = argValue; - apiLastError = '0'; - - return 'true'; - case 'cmi.core.session_time' : - // regex to check format - // hhhh:mm:ss.ss - var re = /^[0-9]{2,4}:[0-9]{2}:[0-9]{2}(.[0-9]{1,2})?$/; - - if (!re.test(argValue)) { - apiLastError = '405'; - - return 'false'; - } - // check that minute and second are 0 <= x < 60 - var timeArray = argValue.split(':'); - - if (timeArray[1] < 0 || timeArray[1] >= 60 || timeArray[2] < 0 || timeArray[2] >= 60) { - apiLastError = '405'; - - return 'false'; - } - - scoData[argName] = argValue; - apiLastError = '0'; - - return 'true'; - case 'cmi.suspend_data' : - if (argValue.length > 4096) { - apiLastError = '405'; - return 'false'; - } - scoData[argName] = argValue; - apiLastError = '0'; - - return 'true'; - default : - apiLastError = '401'; - - return 'false'; } } else { // not initialized error @@ -323,23 +321,22 @@ function LMSGetErrorString(errorCode) console.log('*** LMSGetErrorString:: [' + errorCode + '] ***'); switch (errorCode) { + case '0' : + case '101' : + case '201' : + case '202' : + case '203' : + case '301' : + case '401' : + case '402' : + case '403' : + case '404' : + case '405' : + + return errorString[errorCode]; + default : - case '0' : - case '101' : - case '201' : - case '202' : - case '203' : - case '301' : - case '401' : - case '402' : - case '403' : - case '404' : - case '405' : - - return errorString[errorCode]; - default : - - return 'Unknown Error'; + return 'Unknown Error'; } } @@ -355,23 +352,22 @@ function LMSGetDiagnostic(errorCode) console.log('*** LMSGetDiagnostic:: [' + index + '] ***'); switch (index) { + case '0' : + case '101' : + case '201' : + case '202' : + case '203' : + case '301' : + case '401' : + case '402' : + case '403' : + case '404' : + case '405' : + + return errorString[index]; + default : - case '0' : - case '101' : - case '201' : - case '202' : - case '203' : - case '301' : - case '401' : - case '402' : - case '403' : - case '404' : - case '405' : - - return errorString[index]; - default : - - return 'Unknown Error'; + return 'Unknown Error'; } } diff --git a/Resources/public/js/scorm_2004_api.js b/Resources/public/js/scorm_2004_api.js index 00aad92..c23a190 100644 --- a/Resources/public/js/scorm_2004_api.js +++ b/Resources/public/js/scorm_2004_api.js @@ -1,3 +1,93 @@ +var scormDataElement = document.getElementById('twig-scorm-data'); +var scoData = []; + +scoData['cmi.comments_from_learner'] = []; +scoData['cmi.comments_from_learner._children'] = 'comment,location,timestamp'; +//scoData['cmi.comments_from_learner._count']; +//scoData['cmi.comments_from_learner.n.comment']; +//scoData['cmi.comments_from_learner.n.location']; +//scoData['cmi.comments_from_learner.n.timestamp']; + +scoData['cmi.comments_from_lms'] = []; +scoData['cmi.comments_from_lms._children'] = 'comment,location,timestamp'; +//scoData['cmi.comments_from_lms._count']; +//scoData['cmi.comments_from_lms.n.comment']; +//scoData['cmi.comments_from_lms.n.location']; +//scoData['cmi.comments_from_lms.n.timestamp']; + +scoData['cmi.completion_status']; +scoData['cmi.completion_threshold']; +scoData['cmi.credit'] = 'credit'; +scoData['cmi.entry']; +scoData['cmi.exit']; + +scoData['cmi.interactions'] = []; +scoData['cmi.interactions._children'] = 'id,type,objectives,timestamp,correct_responses,weighting,learner_response,result,latency,description'; +//scoData['cmi.interactions._count']; +//scoData['cmi.interactions.n.id']; +//scoData['cmi.interactions.n.type']; +// +//scoData['cmi.interactions.n.objectives'] = []; +//scoData['cmi.interactions.n.objectives._count']; +//scoData['cmi.interactions.n.objectives.n.id']; +// +//scoData['cmi.interactions.n.timestamp']; +// +//scoData['cmi.interactions.n.correct_responses'] = []; +//scoData['cmi.interactions.n.correct_responses._count']; +//scoData['cmi.interactions.n.correct_responses.n.pattern']; +// +//scoData['cmi.interactions.n.weighting']; +//scoData['cmi.interactions.n.learner_response']; +//scoData['cmi.interactions.n.result']; +//scoData['cmi.interactions.n.latency']; +//scoData['cmi.interactions.n.description']; + +scoData['cmi.launch_data'] = scormDataElement.getAttribute('launch-data'); +scoData['cmi.learner_id'] = scormDataElement.getAttribute('learner-id'); +scoData['cmi.learner_name'] = scormDataElement.getAttribute('learner-name'); +scoData['cmi.learner_preference'] = []; +scoData['cmi.learner_preference._children'] = 'audio_level,language,delivery_speed,audio_captioning'; +//scoData['cmi.learner_preference.audio_level']; +//scoData['cmi.learner_preference.language']; +//scoData['cmi.learner_preference.delivery_speed']; +//scoData['cmi.learner_preference.audio_captioning']; + +scoData['cmi.location'] = ''; +scoData['cmi.max_time_allowed']; +scoData['cmi.mode'] = 'normal'; + +scoData['cmi.objectives'] = []; +scoData['cmi.objectives._children'] = 'id,score,success_status,completion_status,progress_measure,description'; +//scoData['cmi.objectives._count']; +//scoData['cmi.objectives.n.id']; +//scoData['cmi.objectives.n.score'] = []; +//scoData['cmi.objectives.n.score._children'] = 'scaled,raw,min,max'; +//scoData['cmi.objectives.n.score.scaled']; +//scoData['cmi.objectives.n.score.raw']; +//scoData['cmi.objectives.n.score.min']; +//scoData['cmi.objectives.n.score.max']; +//scoData['cmi.objectives.n.success_status']; +//scoData['cmi.objectives.n.completion_status']; +//scoData['cmi.objectives.n.progress_measure']; +//scoData['cmi.objectives.n.description']; + +scoData['cmi.progress_measure']; +scoData['cmi.scaled_passing_score']; + +scoData['cmi.score'] = []; +scoData['cmi.score._children'] = 'scaled,raw,min,max'; +//scoData['cmi.score.scaled']; +//scoData['cmi.score.raw']; +//scoData['cmi.score.max']; +//scoData['cmi.score.min']; + +scoData['cmi.session_time']; +scoData['cmi.success_status']; +scoData['cmi.suspend_data']; +scoData['cmi.time_limit_action']; +scoData['cmi.total_time']; + var errorString = []; errorString['0'] = 'No error'; errorString['101'] = 'General Exception'; @@ -26,12 +116,38 @@ errorString['406'] = 'Data Model Element Type Mismatch'; errorString['407'] = 'Data Model Element Value Out Of Range'; errorString['408'] = 'Data Model Dependency Not Established'; +var scoId = scormDataElement.getAttribute('sco-id'); +var apiInitialized = false; +var apiTerminated = false; +var apiLastError = '0'; +var pattern = '^[-]?[0-9]+([.][0-9]{1,7})?$'; +var regex = new RegExp(pattern); + function Initialize(arg) { 'use strict'; console.log('*** Initialize ***'); - + + if (arg !== '') { + // General Argument Error + apiLastError = '201'; + + return 'false'; + } else if (apiInitialized) { + // Already Initialized + apiLastError = '103'; + + return 'false'; + } else if (apiTerminated) { + // Content Instance Terminated + apiLastError = '104'; + + return 'false'; + } + apiLastError = '0'; + apiInitialized = true; + return 'true'; } @@ -41,7 +157,36 @@ function Terminate(arg) console.log('*** Terminate ***'); - return true; + if (arg !== '') { + // General Argument Error + apiLastError = '201'; + + return 'false'; + } else if (!apiInitialized) { + // Termination Before Initialization + apiLastError = '112'; + + return 'false'; + } else if (apiTerminated) { + // Termination After Termination + apiLastError = '113'; + + return 'false'; + } + apiLastError = '0'; + apiTerminated = true; + apiInitialized = false; + // Set value for 'cmi.entry' depending on 'cmi.exit' + if (scoData['cmi.exit'].toUpperCase() === 'SUSPEND' || + scoData['cmi.exit'].toUpperCase() === 'LOGOUT') { + + scoData['cmi.entry'] = 'resume'; + } else { + scoData['cmi.entry'] = ''; + } +// commitResult('log'); + + return 'true'; } function GetValue(arg) @@ -50,7 +195,389 @@ function GetValue(arg) console.log('*** GetValue:: ' + arg + ' ***'); - return ''; + if (!apiInitialized) { + // Retrieve Data Before Initialization + apiLastError = '122'; + + return ''; + } else if (apiTerminated) { + // Retrieve Data After Termination + apiLastError = '123'; + + return ''; + } + + switch (arg) { + case 'cmi.comments_from_learner': + case 'cmi.comments_from_lms': + case 'cmi.interactions': + case 'cmi.learner_preference': + case 'cmi.objectives': + case 'cmi.score': + // General Get Failure + apiLastError = '301'; + + return ''; + + case 'cmi.exit': + case 'cmi.session_time': + // Data Model Element Is Write Only + apiLastError = '405'; + + return ''; + + case 'cmi.comments_from_learner._count': + apiLastError = '0'; + + return scoData['cmi.comments_from_learner'].length; + + case 'cmi.comments_from_lms._count': + apiLastError = '0'; + + return scoData['cmi.comments_from_lms'].length; + + case 'cmi.interactions._count': + apiLastError = '0'; + + return scoData['cmi.interactions'].length; + + case 'cmi.objectives._count': + apiLastError = '0'; + + return scoData['cmi.objectives'].length; + + case 'cmi.comments_from_learner._children': + case 'cmi.comments_from_lms._children': + case 'cmi.completion_threshold': + case 'cmi.credit': + case 'cmi.entry': + case 'cmi.interactions._children': + case 'cmi.launch_data': + case 'cmi.learner_id': + case 'cmi.learner_name': + case 'cmi.learner_preference._children': + case 'cmi.location': + case 'cmi.max_time_allowed': + case 'cmi.mode': + case 'cmi.objectives._children': + case 'cmi.scaled_passing_score': + case 'cmi.score._children': + case 'cmi.time_limit_action': + case 'cmi.total_time': + apiLastError = '0'; + + return scoData[arg]; + + case 'cmi.completion_status': + case 'cmi.learner_preference.audio_level': + case 'cmi.learner_preference.language': + case 'cmi.learner_preference.delivery_speed': + case 'cmi.learner_preference.audio_captioning': + case 'cmi.progress_measure': + case 'cmi.score.scaled': + case 'cmi.score.raw': + case 'cmi.score.max': + case 'cmi.score.min': + case 'cmi.success_status': + case 'cmi.suspend_data': + + if (scoData[arg] === undefined) { + // Data Model Element Value Not Initialized + apiLastError = '403'; + + return ''; + } + apiLastError = '0'; + + return scoData[arg]; + + default: + var splitted = arg.split('.'); + + if (splitted[0] === 'cmi' && splitted[2].trim()) { + + var cmiIndex = 'cmi.' + splitted[1]; + var nIndex = parseInt(splitted[2]); + + switch (splitted[1]) { + case 'comments_from_learner' : + case 'comments_from_lms' : + + if (scoData[cmiIndex].length > nIndex) { + + switch (splitted[3]) { + case 'comment': + case 'location': + case 'timestamp': + + if (scoData[cmiIndex][nIndex][splitted[3]] !== undefined) { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex][splitted[3]]; + } else { + // Data Model Element Value Not Initialized + apiLastError = '403'; + + return ''; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + + switch (splitted[3]) { + case 'comment': + case 'location': + case 'timestamp': + // Data Model Element Value Not Initialized + apiLastError = '403'; + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + } + + return ''; + } + break; + + case 'interactions' : + + if (scoData[cmiIndex].length > nIndex) { + + switch (splitted[3]) { + case 'id': + case 'type': + case 'timestamp': + case 'weighting': + case 'learner_response': + case 'result': + case 'latency': + case 'description': + + if (scoData[cmiIndex][nIndex][splitted[3]] !== undefined) { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex][splitted[3]]; + } else { + // Data Model Element Value Not Initialized + apiLastError = '403'; + + return ''; + } + break; + + case 'objectives': + + if (splitted[4] === '_count') { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex]['objectives'].length; + } else if (splitted[4].trim() && scoData[cmiIndex][nIndex]['objectives'].length > splitted[4]) { + var objectiveIndex = parseInt(splitted[4]); + + if (splitted[5] === 'id') { + + if (scoData[cmiIndex][nIndex]['objectives'][objectiveIndex]['id'] !== undefined) { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex]['objectives'][objectiveIndex]['id']; + } else { + // Data Model Element Value Not Initialized + apiLastError = '403'; + + return ''; + } + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + if (splitted[5] === 'id') { + // Data Model Element Value Not Initialized + apiLastError = '403'; + } else { + // Undefined Data Model Element + apiLastError = '401'; + } + + return ''; + } + break; + + case 'correct_responses': + + if (splitted[4] === '_count') { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex]['correct_responses'].length; + } else if (splitted[4].trim() && scoData[cmiIndex][nIndex]['correct_responses'].length > splitted[4]) { + var responseIndex = parseInt(splitted[4]); + + if (splitted[5] === 'pattern') { + + if (scoData[cmiIndex][nIndex]['correct_responses'][responseIndex]['pattern'] !== undefined) { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex]['correct_responses'][responseIndex]['pattern']; + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + if (splitted[5] === 'pattern') { + // Data Model Element Value Not Initialized + apiLastError = '403'; + } else { + // Undefined Data Model Element + apiLastError = '401'; + } + + return ''; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + break; + + case 'objectives' : + + if (scoData[cmiIndex].length > nIndex) { + + switch (splitted[3]) { + case 'id': + case 'success_status': + case 'completion_status': + case 'progress_measure': + case 'description': + + if (scoData[cmiIndex][nIndex][splitted[3]] !== undefined) { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex][splitted[3]]; + } else { + // Data Model Element Value Not Initialized + apiLastError = '403'; + + return ''; + } + break; + + case 'score': + + switch (splitted[4]) { + case '_children': + case 'scaled': + case 'raw': + case 'min': + case 'max': + var scoreIndex = 'score.' + splitted[4]; + + if (scoData[cmiIndex][nIndex][scoreIndex] !== undefined) { + apiLastError = '0'; + + return scoData[cmiIndex][nIndex][scoreIndex]; + } else { + // Data Model Element Value Not Initialized + apiLastError = '403'; + + return ''; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + + switch (splitted[3]) { + case 'id': + case 'success_status': + case 'completion_status': + case 'progress_measure': + case 'description': + // Data Model Element Value Not Initialized + apiLastError = '403'; + break; + + case 'score': + + switch (splitted[4]) { + case '_children': + case 'scaled': + case 'raw': + case 'min': + case 'max': + // Data Model Element Value Not Initialized + apiLastError = '403'; + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + } + + return ''; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } } function SetValue(argName, argValue) @@ -59,16 +586,620 @@ function SetValue(argName, argValue) console.log('*** SetValue:: [' + argName + '] = ' + argValue + ' ***'); - return 'true'; + if (!apiInitialized) { + // Store Data Before Initialization + apiLastError = '132'; + + return ''; + } else if (apiTerminated) { + // Store Data After Termination + apiLastError = '133'; + + return ''; + } + + var argStringValue; + var argFloatValue; + + switch (argName) { + case 'cmi.comments_from_learner': + case 'cmi.comments_from_lms': + case 'cmi.interactions': + case 'cmi.learner_preference': + case 'cmi.objectives': + case 'cmi.score': + // General Get Failure + apiLastError = '301'; + + return 'false'; + + case 'cmi.comments_from_learner._children': + case 'cmi.comments_from_learner._count': + case 'cmi.comments_from_lms._children': + case 'cmi.comments_from_lms._count': + case 'cmi.completion_threshold': + case 'cmi.credit': + case 'cmi.entry': + case 'cmi.interactions._children': + case 'cmi.interactions._count': + case 'cmi.launch_data': + case 'cmi.learner_id': + case 'cmi.learner_name': + case 'cmi.learner_preference._children': + case 'cmi.max_time_allowed': + case 'cmi.mode': + case 'cmi.objectives._children': + case 'cmi.objectives._count': + case 'cmi.scaled_passing_score': + case 'cmi.score._children': + case 'cmi.time_limit_action': + case 'cmi.total_time': + // Data Model Element Is Read Only + apiLastError = '404'; + + return 'false'; + + case 'cmi.completion_status': + var upperCaseLessonStatus = argValue.toUpperCase(); + + if (upperCaseLessonStatus !== 'COMPLETED' && + upperCaseLessonStatus !== 'INCOMPLETE' && + upperCaseLessonStatus !== 'NOT_ATTEMPTED' && + upperCaseLessonStatus !== 'UNKNOWN') { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + + case 'cmi.exit': + var upperCaseExit = argValue.toUpperCase(); + + if (upperCaseExit !== 'TIME-OUT' && + upperCaseExit !== 'SUSPEND' && + upperCaseExit !== 'LOGOUT' && + upperCaseExit !== 'NORMAL' && + upperCaseExit !== '') { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + + case 'cmi.learner_preference.audio_level': + case 'cmi.learner_preference.delivery_speed': + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + argFloatValue = parseFloat(argValue); + + if (argFloatValue >= 0) { + scoData[argName] = argFloatValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Value Out Of Range + apiLastError = '407'; + + return 'false'; + } + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'cmi.learner_preference.audio_captioning': + if (argValue !== '-1' && + argValue !== '0' && + argValue !== '1') { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + + case 'cmi.progress_measure': + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + argFloatValue = parseFloat(argValue); + + if (argFloatValue >= 0 && argFloatValue <= 1) { + scoData[argName] = argFloatValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Value Out Of Range + apiLastError = '407'; + + return 'false'; + } + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'cmi.score.scaled': + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + argFloatValue = parseFloat(argValue); + + if (argFloatValue >= -1 && argFloatValue <= 1) { + scoData[argName] = argFloatValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Value Out Of Range + apiLastError = '407'; + + return 'false'; + } + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'cmi.score.raw': + case 'cmi.score.max': + case 'cmi.score.min': + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + scoData[argName] = parseFloat(argValue); + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'cmi.success_status': + var upperCaseSuccessStatus = argValue.toUpperCase(); + + if (upperCaseSuccessStatus !== 'PASSED' && + upperCaseSuccessStatus !== 'FAILED' && + upperCaseSuccessStatus !== 'UNKNOWN') { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + + case 'cmi.learner_preference.language': + case 'cmi.location': + case 'cmi.session_time': + case 'cmi.suspend_data': + scoData[argName] = argValue; + apiLastError = '0'; + + return 'true'; + + default: + var splitted = arg.split('.'); + + if (splitted[0] === 'cmi' && splitted[2].trim()) { + var cmiIndex = 'cmi.' + splitted[1]; + var nIndex = parseInt(splitted[2]); + + switch (splitted[1]) { + case 'comments_from_learner' : + case 'comments_from_lms' : + + switch (splitted[3]) { + case 'comment': + case 'location': + case 'timestamp': + + if (scoData[cmiIndex][nIndex] === undefined) { + scoData[cmiIndex][nIndex] = []; + } + scoData[cmiIndex][nIndex][splitted[3]] = argValue; + apiLastError = '0'; + + return 'true'; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + break; + + case 'interactions' : + + switch (splitted[3]) { + case 'type': + var upperCaseType = argValue.toUpperCase(); + + if (upperCaseType !== 'TRUE-FALSE' && + upperCaseType !== 'CHOICE' && + upperCaseType !== 'FILL-IN' && + upperCaseType !== 'LONG-FILL-IN' && + upperCaseType !== 'LIKERT' && + upperCaseType !== 'MATCHING' && + upperCaseType !== 'PERFORMANCE' && + upperCaseType !== 'SEQUENCING' && + upperCaseType !== 'NUMERIC' && + upperCaseType !== 'OTHER') { + + if (scoData[cmiIndex][nIndex] === undefined) { + scoData[cmiIndex][nIndex] = []; + } + scoData[cmiIndex][nIndex]['type'] = argValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'weighting': + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + + if (scoData[cmiIndex][nIndex] === undefined) { + scoData[cmiIndex][nIndex] = []; + } + scoData[cmiIndex][nIndex]['weighting'] = parseFloat(argValue); + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'result': + var upperCaseResult = argValue.toUpperCase(); + + if (upperCaseResult !== 'CORRECT' && + upperCaseResult !== 'INCORRECT' && + upperCaseResult !== 'UNANTICIPATED' && + upperCaseResult !== 'NEUTRAL') { + + if (scoData[cmiIndex][nIndex] === undefined) { + scoData[cmiIndex][nIndex] = []; + } + scoData[cmiIndex][nIndex]['result'] = argValue; + apiLastError = '0'; + + return 'true'; + } else if (regex.test('' + argValue)) { + + if (scoData[cmiIndex][nIndex] === undefined) { + scoData[cmiIndex][nIndex] = []; + } + scoData[cmiIndex][nIndex]['weighting'] = parseFloat(argValue); + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'id': + case 'timestamp': + case 'learner_response': + case 'latency': + case 'description': + + if (scoData[cmiIndex][nIndex] === undefined) { + scoData[cmiIndex][nIndex] = []; + } + scoData[cmiIndex][nIndex][splitted[3]] = argValue; + apiLastError = '0'; + + return 'true'; + + case 'objectives': + + if (splitted[4] === '_count') { + // Data Model Element Is Read Only + apiLastError = '404'; + + return 'false'; + } else { + + if (splitted[4].trim()) { + var objectiveIndex = parseInt(splitted[4]); + + if (splitted[5] === 'id') { + + if (scoData[cmiIndex][nIndex]['objectives'] === undefined) { + scoData[cmiIndex][nIndex]['objectives'] = []; + } + + if (scoData[cmiIndex][nIndex]['objectives'][objectiveIndex] === undefined) { + scoData[cmiIndex][nIndex]['objectives'][objectiveIndex] = []; + } + scoData[cmiIndex][nIndex]['objectives'][objectiveIndex]['id'] = argValue; + apiLastError = '0'; + + return 'true'; + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + } + break; + + case 'correct_responses': + + if (splitted[4] === '_count') { + // Data Model Element Is Read Only + apiLastError = '404'; + + return 'false'; + } else { + + if (splitted[4].trim()) { + var responseIndex = parseInt(splitted[4]); + + if (splitted[5] === 'pattern') { + + if (scoData[cmiIndex][nIndex]['correct_responses'] === undefined) { + scoData[cmiIndex][nIndex]['correct_responses'] = []; + } + + if (scoData[cmiIndex][nIndex]['correct_responses'][responseIndex] === undefined) { + scoData[cmiIndex][nIndex]['correct_responses'][responseIndex] = []; + } + scoData[cmiIndex][nIndex]['correct_responses'][responseIndex]['pattern'] = argValue; + apiLastError = '0'; + + return 'true'; + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + break; + + case 'objectives' : + + switch (splitted[3]) { + case 'success_status': + var upperCaseObjSuccessStatus = argValue.toUpperCase(); + + if (upperCaseObjSuccessStatus !== 'PASSED' && + upperCaseObjSuccessStatus !== 'FAILED' && + upperCaseObjSuccessStatus !== 'UNKNOWN') { + + scoData[cmiIndex][nIndex]['success_status'] = argValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'completion_status': + var upperCaseObjCompletionStatus = argValue.toUpperCase(); + + if (upperCaseObjCompletionStatus !== 'COMPLETED' && + upperCaseObjCompletionStatus !== 'INCOMPLETE' && + upperCaseObjCompletionStatus !== 'NOT_ATTEMPTED' && + upperCaseObjCompletionStatus !== 'UNKNOWN') { + + scoData[cmiIndex][nIndex]['completion_status'] = argValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'progress_measure': + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + argFloatValue = parseFloat(argValue); + + if (argFloatValue >= 0 && argFloatValue <= 1) { + scoData[cmiIndex][nIndex]['progress_measure'] = argFloatValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Value Out Of Range + apiLastError = '407'; + + return 'false'; + } + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'id': + case 'description': + scoData[cmiIndex][nIndex][splitted[3]] = argValue; + apiLastError = '0'; + + return 'true'; + + case 'score': + + switch (splitted[4]) { + case '_children': + // Data Model Element Is Read Only + apiLastError = '404'; + + return 'false'; + + case 'scaled': + + if (scoData[cmiIndex][nIndex]['score._children'] === undefined) { + scoData[cmiIndex][nIndex]['score._children'] = 'scaled,raw,min,max'; + } + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + argFloatValue = parseFloat(argValue); + + if (argFloatValue >= -1 && argFloatValue <= 1) { + scoData[cmiIndex][nIndex]['score.scaled'] = argFloatValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Value Out Of Range + apiLastError = '407'; + + return 'false'; + } + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + case 'raw': + case 'min': + case 'max': + + if (scoData[cmiIndex][nIndex]['score._children'] === undefined) { + scoData[cmiIndex][nIndex]['score._children'] = 'scaled,raw,min,max'; + } + argStringValue = '' + argValue; + + if (regex.test(argStringValue)) { + argFloatValue = parseFloat(argValue); + scoData[cmiIndex][nIndex]['score.' + splitted[4]] = argFloatValue; + apiLastError = '0'; + + return 'true'; + } else { + // Data Model Element Type Mismatch + apiLastError = '406'; + + return 'false'; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return 'false'; + } + break; + + default: + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } else { + // Undefined Data Model Element + apiLastError = '401'; + + return ''; + } + } } function GetLastError() { 'use strict'; - console.log('*** GetLastError:: '); + console.log('*** GetLastError:: ' + apiLastError + ' ***'); - return ''; + return apiLastError; } function GetErrorString(errorCode) @@ -76,8 +1207,40 @@ function GetErrorString(errorCode) 'use strict'; console.log('*** GetErrorString:: [' + errorCode + '] ***'); + + switch (errorCode) { + case '0': + case '101': + case '102': + case '103': + case '104': + case '111': + case '112': + case '113': + case '122': + case '123': + case '132': + case '133': + case '142': + case '143': + case '201': + case '301': + case '351': + case '391': + case '401': + case '402': + case '403': + case '404': + case '405': + case '406': + case '407': + case '408': + + return errorString[errorCode]; + default : - return 'Unknown Error'; + return ''; + } } function GetDiagnostic(errorCode) @@ -89,26 +1252,40 @@ function GetDiagnostic(errorCode) if (index === '') { index = apiLastError; } - console.log('*** LMSGetDiagnostic:: [' + index + '] ***'); + console.log('*** GetDiagnostic:: [' + index + '] ***'); switch (index) { + case '0' : + case '101' : + case '102' : + case '103' : + case '104' : + case '111' : + case '112' : + case '113' : + case '122' : + case '123' : + case '132' : + case '133' : + case '142' : + case '143' : + case '201' : + case '301' : + case '351' : + case '391' : + case '401' : + case '402' : + case '403' : + case '404' : + case '405' : + case '406' : + case '407' : + case '408' : + + return errorString[index]; + default : - case '0' : - case '101' : - case '201' : - case '202' : - case '203' : - case '301' : - case '401' : - case '402' : - case '403' : - case '404' : - case '405' : - - return errorString[index]; - default : - - return 'Unknown Error'; + return ''; } } @@ -116,24 +1293,26 @@ function Commit(arg) { 'use strict'; - console.log('*** LMSCommit ***'); - - if (apiInitialized) { - if (arg !== '') { - apiLastError = '201'; + console.log('*** Commit ***'); + + if (arg !== '') { + // General Argument Error + apiLastError = '201'; - return 'false'; - } else { - apiLastError = '0'; - commitResult('persist'); + return 'false'; + } else if (!apiInitialized) { + // Commit Before Initialization + apiLastError = '142'; - return 'true'; - } - } else { - apiLastError = '301'; + return 'false'; + } else if (apiTerminated) { + // Commit After Termination + apiLastError = '143'; return 'false'; } +// apiLastError = '0'; +// commitResult('persist'); } function APIClass() { @@ -150,4 +1329,27 @@ function APIClass() } var API_1484_11 = new APIClass(); -var api_1484_11 = new APIClass(); \ No newline at end of file +var api_1484_11 = new APIClass(); + +//var chameau = ['crabe', 'huitre']; +//var tab = 'cmi.interactions. 001 .objectives.1._count'.split('.'); +// +//console.log(typeof tab[2]); +// +//if (tab[2].trim() && chameau.length > tab[2]) { +// var index = parseInt(tab[2]); +// console.log('chameaux2=' + chameau[index]); +//} + +var real2 = '66555.6'; +//var real2 = parseFloat(real); + +console.log('real:' + typeof real2 + '--> ' + real2); + +var param = '' + real2; + +if (regex.test(param)) { + console.log('yet'); +} else { + console.log(param); +} \ No newline at end of file diff --git a/Resources/translations/resource.en.yml b/Resources/translations/resource.en.yml index eebd23f..ffad8af 100644 --- a/Resources/translations/resource.en.yml +++ b/Resources/translations/resource.en.yml @@ -1,13 +1,16 @@ cannot_load_imsmanifest_message: 'Cannot load "imsmanifest.xml" file' claroline_scorm_12: 'Scorm (1.2)' claroline_scorm_12_resource: 'Scorm (1.2)' +claroline_scorm_2004: 'Scorm (2004)' +claroline_scorm_2004_resource: 'Scorm (2004)' default_organization_not_found_message: 'Default organization defined in "imsmanifest.xml" cannot be found.' invalid_scorm_archive_message: 'The file must be a ZIP archive with an "imsmanifest.xml" file in the root directory.' invalid_scorm_version_12_message: 'Scorm version defined in "imsmanifest.xml" must be "1.2".' +invalid_scorm_version_2004_message: 'Scorm version defined in "imsmanifest.xml" must be "CAM 1.3" or "2004 3rd Edition" or "2004 4th Edition".' no_sco_in_scorm_archive_message: 'No SCO defined in "imsmanifest.xml"' quit: Quit sco_resource_without_href_message: 'A SCO defined in "imsmanifest.xml" is attached to an empty resource' sco_with_no_identifier_message: 'A SCO defined in "imsmanifest.xml" has no identifier' sco_without_resource_message: 'A SCO defined in "imsmanifest.xml" isn''t attached to a resource' -scorm_12_form_file: File -scorm_12_form_name: Name \ No newline at end of file +scorm_form_file: File +scorm_form_name: Name \ No newline at end of file diff --git a/Resources/translations/resource.fr.yml b/Resources/translations/resource.fr.yml index 6b755a4..c75929d 100644 --- a/Resources/translations/resource.fr.yml +++ b/Resources/translations/resource.fr.yml @@ -1,13 +1,16 @@ cannot_load_imsmanifest_message: 'Impossible de charger le fichier "imsmanifest.xml"' claroline_scorm_12: 'Scorm (1.2)' claroline_scorm_12_resource: 'Scorm (1.2)' +claroline_scorm_2004: 'Scorm (2004)' +claroline_scorm_2004_resource: 'Scorm (2004)' default_organization_not_found_message: 'L''organisation par défaut définie dans le fichier "imsmanifest.xml" de l''archive est introuvable.' invalid_scorm_archive_message: 'Le fichier doit être une archive ZIP contenant le fichier "imsmanifest.xml" à la racine.' invalid_scorm_version_12_message: 'La version de Scorm définie dans le fichier "imsmanifest.xml" de l''archive doit être à "1.2".' +invalid_scorm_version_2004_message: 'La version de Scorm définie dans le fichier "imsmanifest.xml" de l''archive doit être à "CAM 1.3" ou "2004 3rd Edition" ou "2004 4th Edition".' no_sco_in_scorm_archive_message: 'Aucun SCO n''est défini dans le fichier "imsmanifest.xml" de l''archive' quit: Quitter sco_resource_without_href_message: 'Un SCO défini dans le fichier "imsmanifest.xml" de l''archive est associé à une ressource vide' sco_with_no_identifier_message: 'Un SCO défini dans le fichier "imsmanifest.xml" de l''archive n''a pas d''identifiant' sco_without_resource_message: 'Un SCO défini dans le fichier "imsmanifest.xml" de l''archive n''est associé à aucune ressource' -scorm_12_form_file: Fichier -scorm_12_form_name: Nom \ No newline at end of file +scorm_form_file: Fichier +scorm_form_name: Nom \ No newline at end of file diff --git a/Resources/views/scorm12MenuSco.html.twig b/Resources/views/scorm12MenuSco.html.twig index 8e4a3a0..53fbf81 100644 --- a/Resources/views/scorm12MenuSco.html.twig +++ b/Resources/views/scorm12MenuSco.html.twig @@ -78,7 +78,7 @@
diff --git a/Resources/views/scorm2004.html.twig b/Resources/views/scorm2004.html.twig new file mode 100644 index 0000000..f57f4cf --- /dev/null +++ b/Resources/views/scorm2004.html.twig @@ -0,0 +1,43 @@ +{% macro renderMenu(scos) %} +
    + {% for sco in scos %} + + {% if sco.getIsBlock() %} + {% set scoChildren = sco.getScoChildren() %} +
  • {{ sco.getTitle() }}
  • + + {% if scoChildren is not null %} + {{ _self.renderMenu(scoChildren) }} + {% endif %} + + {% else %} +
  • + + {{ sco.getTitle() }} + +
  • + {% endif %} + {% endfor %} +
+{% endmacro %} + +{% from _self import renderMenu %} + +{% set layout = "ClarolineCoreBundle:Workspace:layout.html.twig" %} + +{% if isDesktop() %} + {% set layout = "ClarolineCoreBundle:Desktop:layout.html.twig" %} +{% endif %} + +{% extends layout %} + +{% block section_content %} +
+

{{ resource.getResourceNode().getName() }}

+
+
+
+ {{ renderMenu(scos) }} +
+
+{% endblock %} diff --git a/Resources/views/scorm2004MenuSco.html.twig b/Resources/views/scorm2004MenuSco.html.twig new file mode 100644 index 0000000..4eb3470 --- /dev/null +++ b/Resources/views/scorm2004MenuSco.html.twig @@ -0,0 +1,142 @@ +{% macro renderMenu(scos) %} +
    + {% for sco in scos %} + + {% if sco.getIsBlock() %} + {% set scoChildren = sco.getScoChildren() %} +
  • {{ sco.getTitle() }}
  • + + {% if scoChildren is not null %} + {{ _self.renderMenu(scoChildren) }} + {% endif %} + + {% else %} +
  • + + {{ sco.getTitle() }} + +
  • + {% endif %} + {% endfor %} +
+{% endmacro %} + +{% from _self import renderMenu %} + +{% set layout = "ClarolineCoreBundle:Workspace:layout.html.twig" %} + +{% if isDesktop() %} + {% set layout = "ClarolineCoreBundle:Desktop:layout.html.twig" %} +{% endif %} + +{% extends layout %} + +{% block javascripts %} + + {{ parent() }} + +{% endblock %} + +{% block section_content %} +
+

{{ currentSco.getTitle() }}

+
+
+
+ + + +
+
+ +
+
+
+
+ +
+
+
+
+ +{% endblock %}