1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279 |
- <?php
- namespace Logipro\Bundle\SCORMBundle\Services;
-
- use Doctrine\ORM\EntityManagerInterface;
- use Logipro\Bundle\SCORMBundle\Entity\Course;
- use Logipro\Bundle\SCORMBundle\Entity\Learner;
- use Logipro\Bundle\SCORMBundle\Entity\MapCourseZipfile;
- use Logipro\Bundle\SCORMBundle\Entity\Registration;
- use Logipro\Bundle\SCORMBundle\Entity\RegistrationAttempt;
- use Logipro\Bundle\SCORMBundle\Entity\ZipFile;
- use Logipro\Bundle\SCORMBundle\Entity\Scorm2004\Scorm2004Track;
- use Logipro\Bundle\SCORMBundle\Entity\Scorm2004\Scorm2004MapTrackItem;
- use Logipro\Bundle\SCORMBundle\Entity\Scorm2004\Scorm2004AttemptProgressInformation;
- use Logipro\Bundle\SCORMBundle\Entity\Scorm2004\Scorm2004Interaction;
- use Logipro\Bundle\SCORMBundle\LearningModels\DOMSCORM12;
- use Logipro\Bundle\SCORMBundle\LearningModels\DOMSCORM2004;
- use Logipro\Bundle\SCORMBundle\Player\SCORM2004\Scorm2004PlayerLogic;
- use Logipro\Bundle\SCORMBundle\Package\PackageValidator;
- use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
- use Symfony\Component\HttpFoundation\Cookie;
- use Symfony\Component\HttpFoundation\Response;
- use Symfony\Component\HttpFoundation\Session\SessionInterface;
- use Symfony\Component\Routing\RouterInterface;
-
- /**
- * Service de communication du moteur
- */
- class ScormEngine
- {
- /**
- * Service de routing
- * Necessaire pour l'API
- * @var RouterInterface
- */
- protected $router;
-
- /**
- * Service permettant d'accéder aux paramètres du container
- * @var ParameterBagInterface
- */
- protected $parameters;
-
- protected $packageFolder;
-
- protected $contentFolder;
-
- protected $contentUrlPrefix;
-
- /**
- * la variable de session passé automatiquement par le kernel
- * du fait de sa déclaration ET de sa présence dans la déclaration de service
- * (ici services.xml) Presence de la ligne <argument type="service" id="session"/>
- *
- * @var SessionInterface
- */
- protected $session;
-
- /**
- * @var EntityManagerInterface
- */
- protected $em;
-
- /**
- * la response est créée ou modifiée pour inserer un cookie dans le header
- * lors de la lecture du paquet (ce cookie est uniquement utilisé dans un mecanisme de
- * blocage de sécurité)
- *
- * @var Response
- */
- private $response;
-
- public function __construct(RouterInterface $router, ParameterBagInterface $parameters, SessionInterface $session, EntityManagerInterface $em)
- {
- // Récupération des services
- $this->router = $router;
- $this->parameters = $parameters;
- $this->session = $session;
- $this->em = $em;
-
- // Définitions des params
- $this->packageFolder = $this->parameters->get('logipro_scorm.package_folder');
- $this->contentFolder = $this->parameters->get('logipro_scorm.content_folder');
- $this->contentUrlPrefix = $this->parameters->get('logipro_scorm.content_url_prefix');
- }
-
- // COURS
-
- /**
- * Crée un cours à partir d'un package identifié par un chemin
- *
- * @param string $courseKey
- * @param string $zipPath
- * @param bool $analyse
- * @param string $versioningRule
- *
- * @return array
- */
- public function createCourse(string $courseKey, string $zipPath, bool $analyse = false, string $versioningRule = Course::VERSIONING_RULE_DEFAULT)
- {
- // Création de la structure de la réponse
- $response = array(
- 'code' => 200,
- 'isSuccessfullyCreated' => false,
- 'error' => '',
- 'parserErrors' => array(),
- 'course' => array(
- 'courseKey' => '',
- 'maxVersion' => 0,
- 'versioningRule' => $versioningRule
- ),
- 'metadata' => array(
- 'isAlreadyExisting' => false,
- 'standard' => '',
- 'title' => '',
- 'duration' => ''
- )
- );
-
- // Controle sur la clef fournie (elle doit etre non null et non existante en BD)
- if (!empty($courseKey)) {
- $course = $this->requireCourse($courseKey);
-
- if ($course) {
- $response['code'] = 400;
- $response['error'] = 'La clef de cours existe déjà. Utiliser \'updateCourse\' pour mettre à jour un cours existant';
- return $response;
- }
-
- // ZIP
- // Test de l'existence du fichier à uploader
- if (!file_exists($zipPath)) {
- $response['code'] = 400;
- $response['error'] = 'Le chemin ne pointe pas vers un paquet valide';
- return $response;
- }
-
- // Validation du Package
- $validation = PackageValidator::validatePackage($zipPath);
-
- if (!$validation['validation']['status']) {
- $response['code'] = 400;
- $response['error'] = $validation['validation']['error'];
- return $response;
- }
-
- $response['metadata']['standard'] = $validation['standard'];
- $response['metadata']['title'] = $validation['title'];
- $response['metadata']['duration'] = $validation['duration'];
-
- // Récupération de la signature du zip
- $hash = md5_file($zipPath);
-
- // Test de l'existence de la signature
- $repository = $this->em->getRepository(ZipFile::class);
- $zipFile = $repository->findOneBy(['zipFileprint' => $hash]);
- $response['metadata']['isAlreadyExisting'] = !empty($zipFile);
-
- // On s'arrete ici si seule l'analyse du paquet est demandée
- if ($analyse) {
- return $response;
- }
-
- // si le fichier n'existe pas déjà on le crée et on le stock coté moteur
- if (empty($zipFile)) {
- $response['metadata']['isAlreadyExisting'] = false;
-
- //upload du fichier coté moteur
- $uploadDate = new \DateTime('NOW');
- $fileName = pathinfo($zipPath)['filename'];
- $filePath = $this->packageFolder . '/' . $hash;
- if (copy($zipPath, $filePath) === false) {
- // Si la copie plante, on retourne une erreur
- $response['code'] = 400;
- $response['error'] = 'Erreur lors de la copie du fichier zip';
- return $response;
- }
- $endUploadDate = new \DateTime('NOW');
-
- // Création d'un objet permettant de traiter le fichier zip
- $zipFile = new ZipFile();
- $zipFile->setZipFileName($fileName);
- $zipFile->setZipFileprint($hash);
- $zipFile->setUploadDate($uploadDate);
- $zipFile->setEndUploadDate($endUploadDate);
- $zipFile->setStandard($validation['standard']);
- $zipFile->setImportStatus(ZipFile::IMPORT_STATUS_PROCESSING);
-
- $this->em->persist($zipFile);
- $this->em->flush();
-
- // Une fois l'objet ZipFile créé, on l'extrait
- $zipFile->extractZipFile($this->packageFolder, $this->contentFolder);
- }
-
- // COURS
- // Création du cours
- $course = new Course();
- $course->setCourseKey($courseKey);
- $course->setVersionRule($versioningRule);
- $this->em->persist($course);
-
- // Métadonées du cours
- $response['course']['courseKey'] = $course->getCourseKey();
- $response['course']['maxVersion'] = $course->getMaxVersion();
-
- // Mapping Zip/Cours
- $mapping = new MapCourseZipfile();
- $mapping->setCourse($course);
- $mapping->setZipFile($zipFile);
- $mapping->setVersion($response['course']['maxVersion']);
- $this->em->persist($mapping);
-
- $this->em->flush();
-
- $response['isSuccessfullyCreated'] = true;
- }
-
- return $response;
- }
-
- /**
- * Remplace le package d'un cours
- *
- * @param string $courseKey
- * @param string $zipPath
- * @param bool $analyse
- * @param string $versioningRule
- *
- * @return array
- */
- public function updateCourse(string $courseKey, string $zipPath, bool $analyse = false, string $versioningRule = Course::VERSIONING_RULE_DEFAULT)
- {
- // Création de la structure de la réponse
- $response = array(
- 'code' => 200,
- 'isSuccessfullyUpdated' => false,
- 'error' => '',
- 'parserErrors' => array(),
- 'course' => array(
- 'courseKey' => '',
- 'maxVersion' => 0,
- 'versioningRule' => $versioningRule
- ),
- 'metadata' => array(
- 'isAlreadyExisting' => false,
- 'standard' => '',
- 'title' => '',
- 'duration' => ''
- )
- );
-
- // Controle sur la clef fournie (elle doit etre non null et non existante en BD)
- if (!empty($courseKey)) {
- $course = $this->requireCourse($courseKey);
-
- if (empty($course)) {
- $response['code'] = 400;
- $response['error'] = 'La clef de cours ne fait référence à aucun cours existant';
- return $response;
- }
-
- // ZIP
- // Test de l'existence du fichier à uploader
- if (!file_exists($zipPath)) {
- $response['code'] = 400;
- $response['error'] = 'le chemin ne pointe pas vers un paquet valide';
- return $response;
- }
-
- // Validation du Package
- $validation = PackageValidator::validatePackage($zipPath);
-
- if (!$validation['validation']['status']) {
- return $response;
- }
-
- $response['metadata']['standard'] = $validation['standard'];
- $response['metadata']['title'] = $validation['title'];
-
- // Récupération de la signature du zip
- $hash = md5_file($zipPath);
-
- // Test de l'existence de la signature
- $repository = $this->em->getRepository(ZipFile::class);
- $zipFile = $repository->findOneBy(['zipFileprint' => $hash]);
- $response['metadata']['isAlreadyExisting'] = !empty($zipFile);
-
- // On s'arrete ici si seule l'analyse du paquet est demandée
- if ($analyse) {
- return $response;
- }
-
- // si le fichier n'existe pas déjà on le crée et on le stock coté moteur
- if (empty($zipFile)) {
- $response['metadata']['isAlreadyExisting'] = false;
-
- //upload du fichier coté moteur
- $uploadDate = new \DateTime('NOW');
- $fileName = pathinfo($zipPath)['filename'];
- $filePath = $this->packageFolder . '/' . $hash;
- if (copy($zipPath, $filePath) === false) {
- // Si la copie plante, on retourne une erreur
- $response['code'] = 400;
- $response['error'] = 'Erreur lors de la copie du fichier zip';
- return $response;
- }
- $endUploadDate = new \DateTime('NOW');
-
- // Création d'un objet permettant de traiter le fichier zip
- $zipFile = new ZipFile();
- $zipFile->setZipFileName($fileName);
- $zipFile->setZipFileprint($hash);
- $zipFile->setUploadDate($uploadDate);
- $zipFile->setEndUploadDate($endUploadDate);
- $zipFile->setStandard($validation['standard']);
- $zipFile->setImportStatus(ZipFile::IMPORT_STATUS_PROCESSING);
-
- $this->em->persist($zipFile);
-
- // Une fois l'objet ZipFile créé, on l'extrait
- $zipFile->extractZipFile($this->packageFolder, $this->contentFolder);
- }
-
- // COURS
- // MaJ du cours
- $course->setVersionRule($versioningRule);
- $newMaxVersion = $course->getMaxVersion();
- $newMaxVersion++;
- $course->setMaxVersion($newMaxVersion);
- $this->em->persist($course);
-
- // Métadonées du cours
- $response['course']['courseKey'] = $course->getCourseKey();
- $response['course']['maxVersion'] = $newMaxVersion;
-
- // Mapping Zip/Cours
- $mapping = new MapCourseZipfile();
- $mapping->setCourse($course);
- $mapping->setZipFile($zipFile);
- $mapping->setVersion($newMaxVersion);
- $this->em->persist($mapping);
-
- $this->em->flush();
-
- $response['isSuccessfullyUpdated'] = true;
- }
-
- return $response;
- }
-
- /**
- * Copie un cours à partir d'un cours existant
- *
- * @param string $orginalCourseKey
- * @param string $newCourseKey
- * @param bool $analyse
- * @param string $versioningRule
- *
- * @return array
- */
- public function duplicateCourse(string $orginalCourseKey, string $newCourseKey)
- {
- // Création de la structure de la réponse
- $response = array(
- 'code' => 200,
- 'isSuccessfullyCreated' => false,
- 'error' => '',
- 'course' => array(
- 'courseKey' => '',
- 'maxVersion' => 0,
- 'versioningRule' => ''
- )
- );
-
- // Contrôle que la clef du cours existant est fournie
- if (empty($orginalCourseKey)) {
- $response['code'] = 400;
- $response['error'] = 'La clef d\'un cours existant à dupliquer est requise.';
- return $response;
- }
-
- // Contrôle que la clef du nouveau cours est fournie
- if (empty($newCourseKey)) {
- $response['code'] = 400;
- $response['error'] = 'La clef du nouveau cours dupliqué est requise.';
- return $response;
- }
-
- // Contrôle de l'existence du cours à dupliquer
- $originalCourse = $this->requireCourse($orginalCourseKey);
-
- if (empty($originalCourse)) {
- $response['code'] = 400;
- $response['error'] = 'Le cours que vous souhaitez dupliquer est inexistant.';
- return $response;
- }
-
- // Contrôle de la disponibilité de la clef pour le nouveau cours
- $newCourse = $this->requireCourse($newCourseKey);
-
- if ($newCourse) {
- $response['code'] = 400;
- $response['error'] = 'La clef du nouveau cours existe déjà. Utilser \'updateCourse\' pour mettre à jour un cours existant.';
- return $response;
- }
-
- // COURS
- $newCourse = clone $originalCourse;
- $newCourse->setCourseKey($newCourseKey);
- $newCourse->setMaxVersion(1);
- $this->em->persist($newCourse);
-
- // Récupération de l'objet Zip
- $mappingRepository = $this->em->getRepository(MapCourseZipfile::class);
- $originalMapping = $mappingRepository->findOneBy(array('course' => $originalCourse), array('mapCourseZipfileId' => 'desc'));
- $zipFile = $originalMapping->getZipFile();
-
- // Mapping Zip/Cours
- $mapping = new MapCourseZipfile();
- $mapping->setCourse($newCourse);
- $mapping->setZipFile($zipFile);
- $mapping->setVersion($newCourse->getMaxVersion());
-
- $this->em->persist($mapping);
- $this->em->flush();
-
- // Réponse
- $response['course']['courseKey'] = $newCourse->getCourseKey();
- $response['course']['maxVersion'] = $newCourse->getMaxVersion();
- $response['course']['versioningRule'] = $newCourse->getVersionRule();
- $response['isSuccessfullyCreated'] = true;
-
- return $response;
- }
-
- /**
- * Remplace la règle de versionning d'un cours
- *
- * @param string $courseKey
- * @param string $versionRule
- *
- * @return string
- */
- public function updateCourseVersionRule(string $courseKey, string $versionRule)
- {
- $course = $this->requireCourse($courseKey);
-
- if ($course) {
- $course->setVersionRule($versionRule);
-
- $this->em->persist($course);
- $this->em->flush();
-
- return $course->getCourseKey();
- }
-
- return null;
- }
-
- /**
- * Supprime une activité
- *
- * @return bool
- */
- public function deleteCourse(string $courseKey)
- {
- $course = $this->requireCourse($courseKey);
-
- if ($course) {
- $this->em->remove($course);
- $this->em->flush();
- return true;
- }
-
- return false;
- }
-
- /**
- * retourne le numéro de version de l'activité
- */
- public function getCourseCurrentVersion(string $courseKey)
- {
- $course = $this->requireCourse($courseKey);
-
- if ($course) {
- return $course->getMaxVersion();
- }
-
- return null;
- }
-
- /**
- * retourne la règle de versionnig
- */
- public function getCourseVersionRule(string $courseKey)
- {
- $course = $this->requireCourse($courseKey);
-
- if ($course) {
- return $course->getVersionRule();
- }
-
- return null;
- }
-
- public function getCourses() : array
- {
- $repository = $this->em->getRepository(Course::class);
- $courses = $repository->findAll();
-
- return $courses;
- }
-
- // LEARNER
-
- /**
- * Création d'un apprenant
- *
- * @param string $learnerKey
- * @param string $familyName
- * @param string $givenName
- * @return int
- */
- public function createLearner(string $learnerKey, string $familyName, string $givenName)
- {
- // Création de la structure de la réponse
- $response = array(
- 'code' => 400,
- 'isAlreadyExisting' => false,
- 'error' => '',
- 'learner' => array(
- 'learnerKey' => '',
- 'familyName' => '',
- 'givenName' => ''
- )
- );
-
- if (!empty($learnerKey)) {
- $learner = $this->requireLearner($learnerKey);
-
- if (empty($learner)) {
- $familyName = $familyName ?? '';
- $givenName = $givenName ?? '';
-
- $learner = new Learner();
- $learner->setLearnerKey($learnerKey);
- $learner->setFamilyName($familyName);
- $learner->setGivenName($givenName);
-
- $this->em->persist($learner);
- $this->em->flush();
- } else {
- $response['isAlreadyExisting'] = true;
- }
-
- $response['code'] = 200;
- $response['learner']['learnerKey'] = $learner->getLearnerKey();
- $response['learner']['familyName'] = $learner->getFamilyName();
- $response['learner']['givenName'] = $learner->getGivenName();
- } else {
- $response['error'] = 'La clef d\'identification est necessaire';
- }
-
- return $response;
- }
-
- /**
- * Modification d'un apprenant
- *
- * @param string $learnerKey
- * @param string $familyName
- * @param string $givenName
- * @return int
- */
- public function updateLearner(string $learnerKey, string $familyName, string $givenName)
- {
- if (!empty($learnerKey)) {
- $learner = $this->requireLearner($learnerKey);
-
- if ($learner) {
- $learner->setFamilyName($familyName);
- $learner->setGivenName($givenName);
-
- $this->em->persist($learner);
- $this->em->flush();
- }
- }
- return $learner->getLearnerKey();
- }
-
- /**
- * récupération d'un apprenant
- *
- * @param string $learnerKey
- *
- * @return Learner
- */
- public function getLearner(string $learnerKey)
- {
- $learner = $this->requireLearner($learnerKey);
-
- return $learner;
- }
-
- public function getLearners()
- {
- $repository = $this->em->getRepository(Learner::class);
- $learners = $repository->findAll();
-
- return $learners;
- }
-
- /**
- * supprime un apprenant
- *
- * @param string $learnerKey
- *
- * @return bool
- */
- public function deleteLearner(string $learnerKey)
- {
- $learner = $this->requireLearner($learnerKey);
-
- if ($learner) {
- $this->em->remove($learner);
- $this->em->flush();
- return true;
- }
-
- return false;
- }
-
- // REGISTRATION
-
- /**
- * création d'une inscription
- *
- * @param string $registrationKey
- * @param string $courseKey
- * @param string $learnerKey
- *
- * @return string
- */
- public function createRegistration(string $registrationKey, string $courseKey, string $learnerKey)
- {
- // Création de la structure de la réponse
- $response = array(
- 'code' => 400,
- 'isAlreadyExisting' => false,
- 'error' => '',
- 'registration' => array(
- 'registrationKey' => '',
- 'courseKey' => '',
- 'learnerKey' => ''
- )
- );
-
- if (!empty($registrationKey)) {
- $registration = $this->requireRegistration($registrationKey);
- $course = $this->requireCourse($courseKey);
- $learner = $this->requireLearner($learnerKey);
-
- if ($course && $learner) {
- if (empty($registration)) {
- $registration = new Registration();
- $registration->setRegistrationKey($registrationKey);
- $registration->setCourse($course);
- $registration->setLearner($learner);
-
- $this->em->persist($registration);
- $this->em->flush();
- } else {
- $response['isAlreadyExisting'] = true;
- }
- $response['code'] = 200;
- $response['registration']['registrationKey'] = $registration->getRegistrationKey();
- $response['registration']['courseKey'] = $registration->getCourse();
- $response['registration']['learnerKey'] = $registration->getLearner();
- } else {
- $response['error'] = 'Le cours et l\'apprenant doivent exister';
- }
- } else {
- $response['error'] = 'La clef d\'identification est necessaire';
- }
-
- return $response;
- }
-
- /**
- * Renvoie les variables necessaire pour l'affichage du contenu du lecteur
- * dans le cadre d'une inscription (pour un apprenant et pour un cours)
- *
- * @param string $registrationKey
- * @param string $data
- *
- * @return array
- */
- public function getPlayerVariables(string $registrationKey, string $data = null, Response $response = null) : array
- {
- $result = array();
- // Vérification de l'existance de l'inscription
- $registration = $this->requireRegistration($registrationKey);
-
- if (empty($registration)) {
- // Erreur : une inscription valide doit être passée
- return null;
- }
-
- // Récupération du cours et des infos de version
- $course = $registration->getCourse();
- $maxVersion = $course->getMaxVersion();
- $versionRule = $course->getVersionRule();
-
- // Récupération du registrationAttempt courant
- $attemptRepository = $this->em->getRepository(RegistrationAttempt::class);
- $currentAttempt = $attemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc'));
-
- $needCreation = empty($currentAttempt);
- $currentVersion = $needCreation ? null : $currentAttempt->getCourseVersion();
-
- // Vérification de l'existance d'un essai
- // et, si oui, de la présence d'une nouvelle version si la règle de versionning impose un restart
- if ($needCreation || ($versionRule == Course::VERSIONING_RULE_RESTART && $currentVersion < $maxVersion)) {
- $newAttempt = new RegistrationAttempt();
- $newAttempt->setCourseVersion($maxVersion);
- $newAttempt->setRegistration($registration);
-
- $this->em->persist($newAttempt);
- $this->em->flush();
-
- $currentAttempt = $newAttempt;
- $currentVersion = $maxVersion;
-
- if (!$needCreation) {
- // TODO Indiquer dans la réponse que l'on a effectué une modification de version pour l'apprenant
- $data = null;
- }
- }
-
- // Récupération du zip correspondant et ainsi de la version du standard utilisée
- $mapCourseZipRepository = $this->em->getRepository(MapCourseZipfile::class);
- $mapCourseZip = $mapCourseZipRepository->findOneBy(array('course' => $course, 'version' => $currentVersion));
- $zipFile = $mapCourseZip->getZipFile();
- $standard = $zipFile->getStandard();
-
- // Traitement des données en fonction du standard
- $variables = null;
- switch ($standard) {
- case DOMSCORM2004::SCORM_2004:
- $playerLogic = new Scorm2004PlayerLogic($this->router, $this->parameters, $this->em);
- $variables = $playerLogic->getScorm2004PlayerVariables($registrationKey, $zipFile, $currentAttempt, $data);
- break;
-
- case DOMSCORM12::SCORM_12:
- default:
- return null;
- }
-
- if ($variables['result'] == 'player') {
- // Construction d'un URL protégé
- $courseKey = $zipFile->getZipFileprint();
- $this->prepareContentAccess($courseKey, $response);
- $protection = $this->getBaseUrl($courseKey);
-
- $url = $variables['player']['content'];
- $url = $protection . '/' . $url;
- $variables['player']['content'] = $url;
- } else {
- $this->response = new Response();
- }
-
- return $variables;
- }
-
- /**
- * Récupération du pourcentage de complétion d'un cours
- *
- * La complétion d'un cours est soit définie par le module lui meme
- * (possibilité d'avoir une pondération de la contribution de chaque item au taux de complétion)
- *
- * Soit elle correspond au rapport du nombre d'items complétés
- * sur le nombre d'items terminaux(consultables par l'apprenant)
- *
- * @param string $registrationKey
- *
- * @return float
- */
- public function getCompletion(string $registrationKey)
- {
- // Vérification de l'existance de l'inscription
- $registration = $this->requireRegistration($registrationKey);
- if (empty($registration)) {
- return 0;
- }
-
- // Récupération du dernier essai moteur pour cette inscription
- $registrationAttemptRepository = $this->em->getRepository(RegistrationAttempt::class);
- $lastAttempt = $registrationAttemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc'));
-
- // DETECTER AVANT LE TYPE DE SCORM POUR SAVOIR QUEL STANDARD
- // CODE SCORM 2004
- // On récupère le track correspondant
- $track2004Repository = $this->em->getRepository(Scorm2004Track::class);
- $track2004 = $track2004Repository->findOneBy(array('registrationAttempt' => $lastAttempt));
- if (empty($track2004)) {
- return 0;
- }
-
- // Dans un premier temps on détermine si la complétion peut être remontée
- // en se basant sur l'état de complétion de l'ensemble des items terminaux
- $mapTrackItemRepository = $this->em->getRepository(Scorm2004MapTrackItem::class);
- $attemptItemRepository = $this->em->getRepository(Scorm2004AttemptProgressInformation::class);
-
- $mapTrackItems = $mapTrackItemRepository->findBy(array('scorm2004Track' => $track2004));
-
- if (!empty($mapTrackItems)) {
- $numCompletedItems = 0;
- $numFinalItems = 0;
- foreach ($mapTrackItems as $item) {
- // Si les items sont terminaux
- if ($item->getAvailableOrder() > -1) {
- $numFinalItems ++;
- $identifier = $item->getItemIdentifier();
- $attemptItem = $attemptItemRepository->findOneBy(array('scorm2004Track'=> $track2004, 'item_identifier' => $identifier), array('attempt_id' => 'desc'));
- if (!empty($attemptItem)) {
- $progressStatus = $attemptItem->getProgressStatus();
- $completionStatus = $attemptItem->getCompletionStatus();
- if (true == $progressStatus && true == $completionStatus) {
- $numCompletedItems ++;
- }
- }
- }
- }
-
- if ($numFinalItems != 0) {
- $completion = 100.0 * ($numCompletedItems / $numFinalItems);
- $completion = round($completion, 2);
- // Si on obtient une valeur de complétion elle est retournée
- if ($completion > 0) {
- return $completion;
- }
- }
- }
-
- // Si aucune complétion n'a été déterminée précédement
- // on controle que l'on aie pas un taux de complétion présent au niveau de l'organisation
- $organization = $track2004->getOrganizationId();
- if (empty($organization)) {
- return 0;
- }
-
- //Récupération du dernier essai SCORM sur l'organisation
- $lastOrganizationAttempt = $attemptItemRepository->findOneBy(array('scorm2004Track'=> $track2004, 'item_identifier' => $organization), array('attempt_id' => 'desc'));
- if (empty($lastOrganizationAttempt)) {
- return 0;
- }
-
- // Récupération de la valeur continue de complétion de l'essai
- $completionAmount = $lastOrganizationAttempt->getCompletionAmount();
- if ($completionAmount > 0) {
- return round(100.0 * $completionAmount, 2);
- }
-
- // Récupération de la valeur binaire de complétion de l'essai
- $progressStatus = $lastOrganizationAttempt->getProgressStatus();
- $completionStatus = $lastOrganizationAttempt->getCompletionStatus();
- if (true == $progressStatus && true == $completionStatus) {
- return 100;
- }
-
- // Pas de complétion récupérable
- return 0;
- }
-
- /**
- * Récupération du score moyen d'un cours
- *
- * @param string $registrationKey
- */
- public function getAverageScore(string $registrationKey)
- {
- // Vérification de l'existance de l'inscription
- $registration = $this->requireRegistration($registrationKey);
- if (empty($registration)) {
- return null;
- }
-
- // Récupération du dernier essai pour cette inscription
- $registrationAttemptRepository = $this->em->getRepository(RegistrationAttempt::class);
- $lastAttempt = $registrationAttemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc'));
-
- // DETECTER AVANT LE TYPE DE SCORM POUR SAVOIR QUEL STANDARD
- // CODE SCORM 2004
- // On récupère le track correspondant
- $track2004Repository = $this->em->getRepository(Scorm2004Track::class);
- $track2004 = $track2004Repository->findOneBy(array('registrationAttempt' => $lastAttempt));
- if (empty($track2004)) {
- return null;
- }
-
- $mapTrackItemRepository = $this->em->getRepository(Scorm2004MapTrackItem::class);
-
- $mapTrackItems = $mapTrackItemRepository->findBy(array('scorm2004Track' => $track2004));
-
- $averageScore = null;
-
- if (!empty($mapTrackItems)) {
- $scoreSum = 0;
- $scoreNum = 0;
-
- foreach ($mapTrackItems as $item) {
- $scoreMax = $item->getScoreMax();
- // Si pas de score max, pas de score valide
- if ($scoreMax > 0) {
- $score = $item->getScoreRaw();
- $score = $score * 100 / $scoreMax;
- $score = max(min($score, 100), 0);
-
- $scoreNum ++;
- $scoreSum += $score;
- }
- }
-
- if ($scoreNum > 0) {
- $averageScore = $scoreSum / $scoreNum;
- $averageScore = round($averageScore, 2);
- }
- }
-
- return $averageScore;
- }
-
- /**
- * Récupération des interactions d'un cours
- * Elles sont rangées par item
- *
- * @param string $registrationKey
- */
- public function getInteractions(string $registrationKey)
- {
- // Vérification de l'existance de l'inscription
- $registration = $this->requireRegistration($registrationKey);
- if (empty($registration)) {
- return null;
- }
-
- // Récupération du dernier essai pour cette inscription
- $registrationAttemptRepository = $this->em->getRepository(RegistrationAttempt::class);
- $lastAttempt = $registrationAttemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc'));
-
- // DETECTER AVANT LE TYPE DE SCORM POUR SAVOIR QUEL STANDARD
- // CODE SCORM 2004
- // On récupère le track correspondant
- $track2004Repository = $this->em->getRepository(Scorm2004Track::class);
- $track2004 = $track2004Repository->findOneBy(array('registrationAttempt' => $lastAttempt));
- if (empty($track2004)) {
- return null;
- }
-
- $mapTrackItemRepository = $this->em->getRepository(Scorm2004MapTrackItem::class);
-
- // Récupération des données pour chaque items
- $mapTrackItems = $mapTrackItemRepository->findBy(array('scorm2004Track' => $track2004));
-
- $interactions = array();
-
- if (!empty($mapTrackItems)) {
- // Récupération du DOM afin d'avoir des infos sur les items
- $domScorm2004 = $lastAttempt->getDOM($this->packageFolder);
-
- $attemptItemRepository = $this->em->getRepository(Scorm2004AttemptProgressInformation::class);
- $interactionRepository = $this->em->getRepository(Scorm2004Interaction::class);
-
- // Pour chaque item
- foreach ($mapTrackItems as $item) {
- // On récupère l'identifiant de l'item
- $identifier = $item->getItemIdentifier();
-
- // On récupère le dernier AttemptProgressInfo en cours
- $attemptItem = $attemptItemRepository->findOneBy(array('scorm2004Track'=> $track2004, 'item_identifier' => $identifier), array('attempt_id' => 'desc'));
-
- // On récupère les interactions pour cet item et cet Attempt
- $itemInteractions = $interactionRepository->findBy(array('scorm2004Track' => $track2004, 'scorm2004AttemptProgressInformation' => $attemptItem, 'itemIdentifier' => $identifier), array('interaction_id' => 'asc'));
-
- // Si il existe des interactions
- if (!empty($itemInteractions)) {
- $result = array();
-
- // On récupère le nom de l'item
- $result['title'] = $domScorm2004->getTitle($identifier);
-
- // On Récupère le Score sur le mapTrackItem
- $score = null;
- $scoreMax = $item->getScoreMax();
- // Si pas de score max, pas de score valide
- if ($scoreMax > 0) {
- $score = $item->getScoreRaw();
- $score = $score * 100 / $scoreMax;
- $score = max(min($score, 100), 0);
- }
- $result['score'] = $score;
-
- // On récupère le Lesson Status (si on peut / voir Dans AI quoi envoyé pour avoir un résultat neutre à l'affichage)
- $status = $attemptItem->getSuccessStatus();
- $result['lessonStatus'] = $status;
-
- // On ajoute les objets interactions
- $result['interactions'] = $itemInteractions;
-
- // On ajoute ce lot d'interaction aux interactions du cours
- $interactions[] = $result;
- }
- }
- }
-
- if (count($interactions) > 0) {
- return $interactions;
- }
-
- return null;
- }
-
- /**
- * supprime une inscription
- *
- * @param string $registrationKey
- *
- * @return string
- */
- public function deleteRegistration(string $registrationKey)
- {
- $registration = $this->requireRegistration($registrationKey);
-
- if ($registration) {
- $this->em->remove($registration);
- $this->em->flush();
- return true;
- }
-
- return false;
- }
-
- /**
- * supprime les données d'apprentissage d'un apprenant pour une inscription
- *
- * @param string $learnerKey
- *
- * @return bool
- */
- public function resetRegistraitonData(string $registrationKey)
- {
- $trackRepository = $this->em->getRepository(Registration::class);
-
- $registration = $this->requireRegistration($registrationKey);
-
- if ($registration) {
- // Récupération des Registrations Attempts
- $attempts = $this->getAttempts($registrationKey);
-
- // Efface les RegistrationAttempts
- // Les objets SCORM seront effacés en cascade
- foreach ($attempts as $attempt) {
- $this->em->remove($attempt);
- }
-
- // Création d'un nouveau RegistrationAttempt
- $course = $registration->getCourse();
- $attempt = new RegistrationAttempt();
- $attempt->setCourseVersion($course->getMaxVersion());
- $attempt->setRegistration($registration);
- $this->em->persist($attempt);
-
-
- $this->em->flush();
- return true;
- }
-
- return false;
- }
-
- public function getRegistrations()
- {
- $repository = $this->em->getRepository(Registration::class);
- $registrations = $repository->findAll();
-
- return $registrations;
- }
-
- // ATTEMPT
-
- /**
- * Récupération des essais
- *
- * @param string $registrationKey
- *
- * @return string
- */
- public function getAttempts(string $registrationKey)
- {
- $registration = $this->requireRegistration($registrationKey);
-
- if ($registration) {
- $attempts = $registration->getRegistrationAttempts();
- return ($attempts);
- }
-
- return null;
- }
-
- /**
- * Récupération de l'id de l'essai en cours
- *
- * @param string $registrationKey
- *
- * @return int
- */
- public function getCurrentAttempt(string $registrationKey)
- {
- $registration = $this->requireRegistration($registrationKey);
-
- if ($registration) {
- $attemptRepository = $this->em->getRepository(RegistrationAttempt::class);
-
- $currentAttempt = $attemptRepository->findCurrentAttemptByRegistration($registration);
-
- return $currentAttempt->registrationAttemptId();
- }
-
- return null;
- }
-
- /**
- * Récupération de la version de l'activité pour l'essai
- *
- * @param int $attemptId
- *
- * @return int
- */
- public function getAttemptCourseVersion(int $attemptId)
- {
- $attempt = $this->requireRegistrationAttempt($attemptId);
-
- if ($attempt) {
- return $attempt->getCourseVersion();
- }
-
- return null;
- }
-
- /**
- * Récupération des statistiques liées à un essai
- *
- * @param int $attemptId
- *
- * @return array
- */
- public function getAttemptStats(int $attemptId)
- {
- }
-
- // TOOLS
-
- /**
- * Undocumented function
- *
- * @param string $courseKey
- * @param Response $response
- * @return void
- */
- private function prepareContentAccess(string $courseKey, Response $response = null) : void
- {
- if (!$this->session->has('scormkey')) {
- $scormkey = rand();
- $this->session->set('scormkey', $scormkey);
- } else {
- $scormkey = $this->session->get('scormkey');
- }
-
- if (null === $response) {
- $this->response = new Response();
- } else {
- $this->response = $response;
- }
-
- $pathCookie = $this->contentUrlPrefix."/$scormkey/$courseKey/";
-
- $md5cookiekey = md5($scormkey);
- $cookie = new Cookie('SCORMBundle', $md5cookiekey, 0, $pathCookie);
- $this->response->headers->setCookie($cookie);
- }
-
- /**
- * il FAUT utiliser la Response de l'objet ScormEngine pour bénéficier du cookie
- * de sécurité (sans cela blocage complet de l'acces à la ressource)
- *
- * @return Response
- */
- public function getResponse() : Response
- {
- return $this->response;
- }
-
- /**
- * renvoi l'url de base permettant d'acceder à un fichier de la ressource
- * Format :
- * /content/nomdupaquet/clefautorisation
- *
- * Example :
- * /content/
- *
- * @return string
- */
- public function getBaseUrl($courseKey) : string
- {
- return $this->contentUrlPrefix.'/'.$this->session->get('scormkey').'/'.$courseKey;
- }
-
- /**
- * Retourne l'activité
- *
- * @param string $courseKey
- * @return Course
- */
- protected function requireCourse(string $courseKey)
- {
- $repository = $this->em->getRepository(Course::class);
-
- $course = $repository->findOneBy(array('courseKey' => $courseKey));
-
- return $course;
- }
-
- /**
- * Retourne l'apprenant
- *
- * @param string $learnerKey
- * @return Learner
- */
- protected function requireLearner(string $learnerKey)
- {
- $repository = $this->em->getRepository(Learner::class);
-
- $learner = $repository->findOneBy(array('learnerKey' => $learnerKey));
-
- return $learner;
- }
-
- /**
- * Retourne l'inscription
- *
- * @param string $registrationKey
- *
- * @return Registration
- */
- protected function requireRegistration(string $registrationKey)
- {
- $repository = $this->em->getRepository(Registration::class);
-
- $registration = $repository->findOneBy(array('registrationKey' => $registrationKey));
-
- return $registration;
- }
-
- /**
- * Retourne un essais relatif à une inscription
- *
- * @param int $attemptId
- *
- * @return RegistrationAttempt
- */
- protected function requireRegistrationAttempt(int $attemptId)
- {
- $repository = $this->em->getRepository(RegistrationAttempt::class);
-
- $attempt = $repository->find($attemptId);
-
- return $attempt;
- }
- }
|