From 359e2588a4962f51c0d5cbdd2823530a15defee0 Mon Sep 17 00:00:00 2001 From: devianl2 Date: Fri, 11 Feb 2022 12:05:11 +0800 Subject: Revert "Improve SCORM disk storage handler" --- src/Manager/ScormDisk.php | 74 ------------- src/Manager/ScormManager.php | 246 ++++++++++++++++++++++--------------------- 2 files changed, 128 insertions(+), 192 deletions(-) delete mode 100644 src/Manager/ScormDisk.php (limited to 'src/Manager') diff --git a/src/Manager/ScormDisk.php b/src/Manager/ScormDisk.php deleted file mode 100644 index 249f027..0000000 --- a/src/Manager/ScormDisk.php +++ /dev/null @@ -1,74 +0,0 @@ -cleanPath($path); - - $zipArchive = new ZipArchive(); - if ($zipArchive->open($file) !== true) { - return false; - } - - /** @var FilesystemAdapter $disk */ - $disk = $this->getDisk(); - - for ($i = 0; $i < $zipArchive->numFiles; ++$i) { - $zipEntryName = $zipArchive->getNameIndex($i); - $destination = $path . DIRECTORY_SEPARATOR . $this->cleanPath($zipEntryName); - if ($this->isDirectory($zipEntryName)) { - $disk->createDir($destination); - continue; - } - $disk->putStream($destination, $zipArchive->getStream($zipEntryName)); - } - - return true; - } - - /** - * @param string $directory - * @return bool - */ - public function deleteScormFolder($folderHashedName) - { - return $this->getDisk()->deleteDirectory($folderHashedName); - } - - private function isDirectory($zipEntryName) - { - return substr($zipEntryName, -1) === '/'; - } - - private function cleanPath($path) - { - return str_replace('/', DIRECTORY_SEPARATOR, $path); - } - - /** - * @return FilesystemAdapter $disk - */ - private function getDisk() - { - if (!config()->has('filesystems.disks.' . config('scorm.disk'))) { - throw new StorageNotFoundException('scorm_disk_not_define'); - } - return Storage::disk(config('scorm.disk')); - } -} diff --git a/src/Manager/ScormManager.php b/src/Manager/ScormManager.php index 56c723c..2a54706 100644 --- a/src/Manager/ScormManager.php +++ b/src/Manager/ScormManager.php @@ -3,28 +3,31 @@ namespace Peopleaps\Scorm\Manager; +use App\Models\User; use Carbon\Carbon; use DOMDocument; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Storage; +use League\Flysystem\FileNotFoundException; +use Peopleaps\Scorm\Entity\Sco; use Peopleaps\Scorm\Entity\Scorm; use Peopleaps\Scorm\Entity\ScoTracking; use Peopleaps\Scorm\Exception\InvalidScormArchiveException; +use Peopleaps\Scorm\Exception\StorageNotFoundException; use Peopleaps\Scorm\Library\ScormLib; use Peopleaps\Scorm\Model\ScormModel; use Peopleaps\Scorm\Model\ScormScoModel; use Peopleaps\Scorm\Model\ScormScoTrackingModel; -use Illuminate\Support\Str; -use Peopleaps\Scorm\Entity\Sco; +use Ramsey\Uuid\Uuid; use ZipArchive; class ScormManager { /** @var ScormLib */ private $scormLib; - /** @var ScormDisk */ - private $scormDisk; /** * Constructor. @@ -32,18 +35,18 @@ class ScormManager * @param string $filesDir * @param string $uploadDir */ - public function __construct() - { + public function __construct( + ) { $this->scormLib = new ScormLib(); - $this->scormDisk = new ScormDisk(); } - public function uploadScormArchive(UploadedFile $file) + public function uploadScormArchive(UploadedFile $file, Model $model) { // Checks if it is a valid scorm archive $scormData = null; $zip = new ZipArchive(); $openValue = $zip->open($file); + $oldModel = null; $isScormArchive = (true === $openValue) && $zip->getStream('imsmanifest.xml'); @@ -55,71 +58,57 @@ class ScormManager $scormData = $this->generateScorm($file); } + $oldModel = $model->scorm()->first(); // get old scorm data for deletion (If success to store new) + // save to db - if (is_null($scormData) || !is_array($scormData)) { - throw new InvalidScormArchiveException('invalid_scorm_data'); - } + if ($scormData && is_array($scormData)) { - $scorm = ScormModel::whereOriginFile($scormData['identifier']); - // Check if scom package already exists to drop old one. - if (!$scorm->exists()) { - $scorm = new ScormModel(); - } else { - $scorm = $scorm->first(); - $this->deleteScormData($scorm); - } + $scorm = new ScormModel(); + $scorm->version = $scormData['version']; + $scorm->hash_name = $scormData['hashName']; + $scorm->origin_file = $scormData['name']; + $scorm->origin_file_mime = $scormData['type']; + $scorm->uuid = $scormData['hashName']; + + $scorm = $model->scorm()->save($scorm); - $scorm->uuid = $scormData['uuid']; - $scorm->title = $scormData['title']; - $scorm->version = $scormData['version']; - $scorm->entry_url = $scormData['entryUrl']; - $scorm->origin_file = $scormData['identifier']; - $scorm->save(); - - if (!empty($scormData['scos']) && is_array($scormData['scos'])) { - /** @var Sco $scoData */ - foreach ($scormData['scos'] as $scoData) { - $sco = $this->saveScormScos($scorm->id, $scoData); - if ($scoData->scoChildren) { - foreach ($scoData->scoChildren as $scoChild) { - $this->saveScormScos($scorm->id, $scoChild, $sco->id); + if (!empty($scormData['scos']) && is_array($scormData['scos'])) { + foreach ($scormData['scos'] as $scoData) { + + $scoParent = null; + if (!empty($scoData->scoParent)) { + $scoParent = ScormScoModel::where('uuid', $scoData->scoParent->uuid)->first(); } + + $sco = new ScormScoModel(); + $sco->scorm_id = $scorm->id; + $sco->uuid = $scoData->uuid; + $sco->sco_parent_id = $scoParent ? $scoParent->id : null; + $sco->entry_url = $scoData->entryUrl; + $sco->identifier = $scoData->identifier; + $sco->title = $scoData->title; + $sco->visible = $scoData->visible; + $sco->sco_parameters = $scoData->parameters; + $sco->launch_data = $scoData->launchData; + $sco->max_time_allowed = $scoData->maxTimeAllowed; + $sco->time_limit_action = $scoData->timeLimitAction; + $sco->block = $scoData->block; + $sco->score_int = $scoData->scoreToPassInt; + $sco->score_decimal = $scoData->scoreToPassDecimal; + $sco->completion_threshold = $scoData->completionThreshold; + $sco->prerequisites = $scoData->prerequisites; + $sco->save(); } } + + if ($oldModel != null) { + $this->deleteScormData($oldModel); + } } return $scormData; } - /** - * Save Scorm sco and it's nested children - * @param int $scorm_id scorm id. - * @param Sco $scoData Sco data to be store. - * @param int $sco_parent_id sco parent id for children - */ - private function saveScormScos($scorm_id, $scoData, $sco_parent_id = null) - { - $sco = new ScormScoModel(); - $sco->scorm_id = $scorm_id; - $sco->uuid = $scoData->uuid; - $sco->sco_parent_id = $sco_parent_id; - $sco->entry_url = $scoData->entryUrl; - $sco->identifier = $scoData->identifier; - $sco->title = $scoData->title; - $sco->visible = $scoData->visible; - $sco->sco_parameters = $scoData->parameters; - $sco->launch_data = $scoData->launchData; - $sco->max_time_allowed = $scoData->maxTimeAllowed; - $sco->time_limit_action = $scoData->timeLimitAction; - $sco->block = $scoData->block; - $sco->score_int = $scoData->scoreToPassInt; - $sco->score_decimal = $scoData->scoreToPassDecimal; - $sco->completion_threshold = $scoData->completionThreshold; - $sco->prerequisites = $scoData->prerequisites; - $sco->save(); - return $sco; - } - private function parseScormArchive(UploadedFile $file) { $data = []; @@ -140,18 +129,8 @@ class ScormManager throw new InvalidScormArchiveException('cannot_load_imsmanifest_message'); } - $manifest = $dom->getElementsByTagName('manifest')->item(0); - if (!is_null($manifest->attributes->getNamedItem('identifier'))) { - $data['identifier'] = $manifest->attributes->getNamedItem('identifier')->nodeValue; - } else { - throw new InvalidScormArchiveException('invalid_scorm_manifest_identifier'); - } - $titles = $dom->getElementsByTagName('title'); - if ($titles->length > 0) { - $data['title'] = Str::of($titles->item(0)->textContent)->trim('/n')->trim(); - } - $scormVersionElements = $dom->getElementsByTagName('schemaversion'); + if ($scormVersionElements->length > 0) { switch ($scormVersionElements->item(0)->textContent) { case '1.2': @@ -173,43 +152,69 @@ class ScormManager if (0 >= count($scos)) { throw new InvalidScormArchiveException('no_sco_in_scorm_archive_message'); } - - $data['entryUrl'] = $scos[0]->entryUrl ?? $scos[0]->scoChildren[0]->entryUrl; $data['scos'] = $scos; return $data; } - public function deleteScorm($model) - { + public function deleteScormData($model) { // Delete after the previous item is stored if ($model) { - $this->deleteScormData($model); + + $oldScos = $model->scos()->get(); + + // Delete all tracking associate with sco + foreach ($oldScos as $oldSco) { + $oldSco->scoTrackings()->delete(); + } + + $model->scos()->delete(); // delete scos $model->delete(); // delete scorm + + // Delete folder from server + $this->deleteScormFolder($model->hash_name); } } + /** + * @param $folderHashedName + * @return bool + */ + protected function deleteScormFolder($folderHashedName) { - private function deleteScormData($model) - { - // Delete after the previous item is stored - $oldScos = $model->scos()->get(); + $response = Storage::disk('scorm')->deleteDirectory($folderHashedName); - // Delete all tracking associate with sco - foreach ($oldScos as $oldSco) { - $oldSco->scoTrackings()->delete(); - } - $model->scos()->delete(); // delete scos - // Delete folder from server - $this->deleteScormFolder($model->uuid); + return $response; } /** - * @param $folderHashedName - * @return bool + * Unzip a given ZIP file into the web resources directory. + * + * @param string $hashName name of the destination directory */ - protected function deleteScormFolder($folderHashedName) + private function unzipScormArchive(UploadedFile $file, $hashName) { - return $this->scormDisk->deleteScormFolder($folderHashedName); + $zip = new \ZipArchive(); + $zip->open($file); + + if (!config()->has('filesystems.disks.'.config('scorm.disk').'.root')) { + throw new StorageNotFoundException(); + } + + $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root'); + + if (substr($rootFolder, -1) != '/') { + // If end with xxx/ + $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root').'/'; + } + + $destinationDir = $rootFolder.$hashName; // file path + + if (!File::isDirectory($destinationDir)) { + File::makeDirectory($destinationDir, 0755, true, true); + } + + $zip->extractTo($destinationDir); + $zip->close(); } /** @@ -219,21 +224,32 @@ class ScormManager */ private function generateScorm(UploadedFile $file) { - $uuid = Str::uuid(); + $hashName = Uuid::uuid4(); + $hashFileName = $hashName.'.zip'; $scormData = $this->parseScormArchive($file); - /** - * Unzip a given ZIP file into the web resources directory. - * - * @param string $hashName name of the destination directory - */ - $this->scormDisk->unzip($file, $uuid); + $this->unzipScormArchive($file, $hashName); + + if (!config()->has('filesystems.disks.'.config('scorm.disk').'.root')) { + throw new StorageNotFoundException(); + } + + $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root'); + + if (substr($rootFolder, -1) != '/') { + // If end with xxx/ + $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root').'/'; + } + + $destinationDir = $rootFolder.$hashName; // file path + + // Move Scorm archive in the files directory + $finalFile = $file->move($destinationDir, $hashName.'.zip'); return [ - 'identifier' => $scormData['identifier'], - 'uuid' => $uuid, - 'title' => $scormData['title'], // to follow standard file data format + 'name' => $hashFileName, // to follow standard file data format + 'hashName' => $hashName, + 'type' => $finalFile->getMimeType(), 'version' => $scormData['version'], - 'entryUrl' => $scormData['entryUrl'], 'scos' => $scormData['scos'], ]; } @@ -243,8 +259,7 @@ class ScormManager * @param $scormId * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection */ - public function getScos($scormId) - { + public function getScos($scormId) { $scos = ScormScoModel::with([ 'scorm' ])->where('scorm_id', $scormId) @@ -258,8 +273,7 @@ class ScormManager * @param $scoUuid * @return null|\Illuminate\Database\Eloquent\Builder|Model */ - public function getScoByUuid($scoUuid) - { + public function getScoByUuid($scoUuid) { $sco = ScormScoModel::with([ 'scorm' ])->where('uuid', $scoUuid) @@ -268,8 +282,7 @@ class ScormManager return $sco; } - public function getUserResult($scoId, $userId) - { + public function getUserResult($scoId, $userId) { return ScormScoTrackingModel::where('sco_id', $scoId)->where('user_id', $userId)->first(); } @@ -314,7 +327,7 @@ class ScormManager 'user_id' => $userId, 'sco_id' => $sco->id ], [ - 'uuid' => Str::uuid(), + 'uuid' => Uuid::uuid4(), 'progression' => $scoTracking->getProgression(), 'score_raw' => $scoTracking->getScoreRaw(), 'score_min' => $scoTracking->getScoreMin(), @@ -362,8 +375,7 @@ class ScormManager return $scoTracking; } - public function findScoTrackingId($scoUuid, $scoTrackingUuid) - { + public function findScoTrackingId($scoUuid, $scoTrackingUuid) { return ScormScoTrackingModel::with([ 'sco' ])->whereHas('sco', function (Builder $query) use ($scoUuid) { @@ -372,8 +384,7 @@ class ScormManager ->firstOrFail(); } - public function checkUserIsCompletedScorm($scormId, $userId) - { + public function checkUserIsCompletedScorm($scormId, $userId) { $completedSco = []; $scos = ScormScoModel::where('scorm_id', $scormId)->get(); @@ -527,8 +538,7 @@ class ScormManager $bestStatus = $lessonStatus; } - if ( - empty($tracking->getCompletionStatus()) + if (empty($tracking->getCompletionStatus()) || ($completionStatus !== $tracking->getCompletionStatus() && $statusPriority[$completionStatus] > $statusPriority[$tracking->getCompletionStatus()]) ) { // This is no longer needed as completionStatus and successStatus are merged together @@ -620,7 +630,7 @@ class ScormManager $remainingTime %= 3600; $nbMinutes = (int) ($remainingTime / 60); $nbSeconds = $remainingTime % 60; - $result .= 'P' . $nbDays . 'DT' . $nbHours . 'H' . $nbMinutes . 'M' . $nbSeconds . 'S'; + $result .= 'P'.$nbDays.'DT'.$nbHours.'H'.$nbMinutes.'M'.$nbSeconds.'S'; } return $result; -- cgit v1.2.3 From 2c643200d419e1ffae72eb8fb7c392a057a0c9b9 Mon Sep 17 00:00:00 2001 From: devianl2 Date: Fri, 11 Feb 2022 12:39:14 +0800 Subject: Revert "Revert "Improve SCORM disk storage handler"" --- src/Manager/ScormDisk.php | 74 +++++++++++++ src/Manager/ScormManager.php | 246 +++++++++++++++++++++---------------------- 2 files changed, 192 insertions(+), 128 deletions(-) create mode 100644 src/Manager/ScormDisk.php (limited to 'src/Manager') diff --git a/src/Manager/ScormDisk.php b/src/Manager/ScormDisk.php new file mode 100644 index 0000000..249f027 --- /dev/null +++ b/src/Manager/ScormDisk.php @@ -0,0 +1,74 @@ +cleanPath($path); + + $zipArchive = new ZipArchive(); + if ($zipArchive->open($file) !== true) { + return false; + } + + /** @var FilesystemAdapter $disk */ + $disk = $this->getDisk(); + + for ($i = 0; $i < $zipArchive->numFiles; ++$i) { + $zipEntryName = $zipArchive->getNameIndex($i); + $destination = $path . DIRECTORY_SEPARATOR . $this->cleanPath($zipEntryName); + if ($this->isDirectory($zipEntryName)) { + $disk->createDir($destination); + continue; + } + $disk->putStream($destination, $zipArchive->getStream($zipEntryName)); + } + + return true; + } + + /** + * @param string $directory + * @return bool + */ + public function deleteScormFolder($folderHashedName) + { + return $this->getDisk()->deleteDirectory($folderHashedName); + } + + private function isDirectory($zipEntryName) + { + return substr($zipEntryName, -1) === '/'; + } + + private function cleanPath($path) + { + return str_replace('/', DIRECTORY_SEPARATOR, $path); + } + + /** + * @return FilesystemAdapter $disk + */ + private function getDisk() + { + if (!config()->has('filesystems.disks.' . config('scorm.disk'))) { + throw new StorageNotFoundException('scorm_disk_not_define'); + } + return Storage::disk(config('scorm.disk')); + } +} diff --git a/src/Manager/ScormManager.php b/src/Manager/ScormManager.php index 2a54706..56c723c 100644 --- a/src/Manager/ScormManager.php +++ b/src/Manager/ScormManager.php @@ -3,31 +3,28 @@ namespace Peopleaps\Scorm\Manager; -use App\Models\User; use Carbon\Carbon; use DOMDocument; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\UploadedFile; -use Illuminate\Support\Facades\File; -use Illuminate\Support\Facades\Storage; -use League\Flysystem\FileNotFoundException; -use Peopleaps\Scorm\Entity\Sco; use Peopleaps\Scorm\Entity\Scorm; use Peopleaps\Scorm\Entity\ScoTracking; use Peopleaps\Scorm\Exception\InvalidScormArchiveException; -use Peopleaps\Scorm\Exception\StorageNotFoundException; use Peopleaps\Scorm\Library\ScormLib; use Peopleaps\Scorm\Model\ScormModel; use Peopleaps\Scorm\Model\ScormScoModel; use Peopleaps\Scorm\Model\ScormScoTrackingModel; -use Ramsey\Uuid\Uuid; +use Illuminate\Support\Str; +use Peopleaps\Scorm\Entity\Sco; use ZipArchive; class ScormManager { /** @var ScormLib */ private $scormLib; + /** @var ScormDisk */ + private $scormDisk; /** * Constructor. @@ -35,18 +32,18 @@ class ScormManager * @param string $filesDir * @param string $uploadDir */ - public function __construct( - ) { + public function __construct() + { $this->scormLib = new ScormLib(); + $this->scormDisk = new ScormDisk(); } - public function uploadScormArchive(UploadedFile $file, Model $model) + public function uploadScormArchive(UploadedFile $file) { // Checks if it is a valid scorm archive $scormData = null; $zip = new ZipArchive(); $openValue = $zip->open($file); - $oldModel = null; $isScormArchive = (true === $openValue) && $zip->getStream('imsmanifest.xml'); @@ -58,57 +55,71 @@ class ScormManager $scormData = $this->generateScorm($file); } - $oldModel = $model->scorm()->first(); // get old scorm data for deletion (If success to store new) - // save to db - if ($scormData && is_array($scormData)) { - - $scorm = new ScormModel(); - $scorm->version = $scormData['version']; - $scorm->hash_name = $scormData['hashName']; - $scorm->origin_file = $scormData['name']; - $scorm->origin_file_mime = $scormData['type']; - $scorm->uuid = $scormData['hashName']; - - $scorm = $model->scorm()->save($scorm); + if (is_null($scormData) || !is_array($scormData)) { + throw new InvalidScormArchiveException('invalid_scorm_data'); + } - if (!empty($scormData['scos']) && is_array($scormData['scos'])) { - foreach ($scormData['scos'] as $scoData) { + $scorm = ScormModel::whereOriginFile($scormData['identifier']); + // Check if scom package already exists to drop old one. + if (!$scorm->exists()) { + $scorm = new ScormModel(); + } else { + $scorm = $scorm->first(); + $this->deleteScormData($scorm); + } - $scoParent = null; - if (!empty($scoData->scoParent)) { - $scoParent = ScormScoModel::where('uuid', $scoData->scoParent->uuid)->first(); + $scorm->uuid = $scormData['uuid']; + $scorm->title = $scormData['title']; + $scorm->version = $scormData['version']; + $scorm->entry_url = $scormData['entryUrl']; + $scorm->origin_file = $scormData['identifier']; + $scorm->save(); + + if (!empty($scormData['scos']) && is_array($scormData['scos'])) { + /** @var Sco $scoData */ + foreach ($scormData['scos'] as $scoData) { + $sco = $this->saveScormScos($scorm->id, $scoData); + if ($scoData->scoChildren) { + foreach ($scoData->scoChildren as $scoChild) { + $this->saveScormScos($scorm->id, $scoChild, $sco->id); } - - $sco = new ScormScoModel(); - $sco->scorm_id = $scorm->id; - $sco->uuid = $scoData->uuid; - $sco->sco_parent_id = $scoParent ? $scoParent->id : null; - $sco->entry_url = $scoData->entryUrl; - $sco->identifier = $scoData->identifier; - $sco->title = $scoData->title; - $sco->visible = $scoData->visible; - $sco->sco_parameters = $scoData->parameters; - $sco->launch_data = $scoData->launchData; - $sco->max_time_allowed = $scoData->maxTimeAllowed; - $sco->time_limit_action = $scoData->timeLimitAction; - $sco->block = $scoData->block; - $sco->score_int = $scoData->scoreToPassInt; - $sco->score_decimal = $scoData->scoreToPassDecimal; - $sco->completion_threshold = $scoData->completionThreshold; - $sco->prerequisites = $scoData->prerequisites; - $sco->save(); } } - - if ($oldModel != null) { - $this->deleteScormData($oldModel); - } } return $scormData; } + /** + * Save Scorm sco and it's nested children + * @param int $scorm_id scorm id. + * @param Sco $scoData Sco data to be store. + * @param int $sco_parent_id sco parent id for children + */ + private function saveScormScos($scorm_id, $scoData, $sco_parent_id = null) + { + $sco = new ScormScoModel(); + $sco->scorm_id = $scorm_id; + $sco->uuid = $scoData->uuid; + $sco->sco_parent_id = $sco_parent_id; + $sco->entry_url = $scoData->entryUrl; + $sco->identifier = $scoData->identifier; + $sco->title = $scoData->title; + $sco->visible = $scoData->visible; + $sco->sco_parameters = $scoData->parameters; + $sco->launch_data = $scoData->launchData; + $sco->max_time_allowed = $scoData->maxTimeAllowed; + $sco->time_limit_action = $scoData->timeLimitAction; + $sco->block = $scoData->block; + $sco->score_int = $scoData->scoreToPassInt; + $sco->score_decimal = $scoData->scoreToPassDecimal; + $sco->completion_threshold = $scoData->completionThreshold; + $sco->prerequisites = $scoData->prerequisites; + $sco->save(); + return $sco; + } + private function parseScormArchive(UploadedFile $file) { $data = []; @@ -129,8 +140,18 @@ class ScormManager throw new InvalidScormArchiveException('cannot_load_imsmanifest_message'); } - $scormVersionElements = $dom->getElementsByTagName('schemaversion'); + $manifest = $dom->getElementsByTagName('manifest')->item(0); + if (!is_null($manifest->attributes->getNamedItem('identifier'))) { + $data['identifier'] = $manifest->attributes->getNamedItem('identifier')->nodeValue; + } else { + throw new InvalidScormArchiveException('invalid_scorm_manifest_identifier'); + } + $titles = $dom->getElementsByTagName('title'); + if ($titles->length > 0) { + $data['title'] = Str::of($titles->item(0)->textContent)->trim('/n')->trim(); + } + $scormVersionElements = $dom->getElementsByTagName('schemaversion'); if ($scormVersionElements->length > 0) { switch ($scormVersionElements->item(0)->textContent) { case '1.2': @@ -152,69 +173,43 @@ class ScormManager if (0 >= count($scos)) { throw new InvalidScormArchiveException('no_sco_in_scorm_archive_message'); } + + $data['entryUrl'] = $scos[0]->entryUrl ?? $scos[0]->scoChildren[0]->entryUrl; $data['scos'] = $scos; return $data; } - public function deleteScormData($model) { + public function deleteScorm($model) + { // Delete after the previous item is stored if ($model) { - - $oldScos = $model->scos()->get(); - - // Delete all tracking associate with sco - foreach ($oldScos as $oldSco) { - $oldSco->scoTrackings()->delete(); - } - - $model->scos()->delete(); // delete scos + $this->deleteScormData($model); $model->delete(); // delete scorm - - // Delete folder from server - $this->deleteScormFolder($model->hash_name); } } - /** - * @param $folderHashedName - * @return bool - */ - protected function deleteScormFolder($folderHashedName) { - $response = Storage::disk('scorm')->deleteDirectory($folderHashedName); + private function deleteScormData($model) + { + // Delete after the previous item is stored + $oldScos = $model->scos()->get(); - return $response; + // Delete all tracking associate with sco + foreach ($oldScos as $oldSco) { + $oldSco->scoTrackings()->delete(); + } + $model->scos()->delete(); // delete scos + // Delete folder from server + $this->deleteScormFolder($model->uuid); } /** - * Unzip a given ZIP file into the web resources directory. - * - * @param string $hashName name of the destination directory + * @param $folderHashedName + * @return bool */ - private function unzipScormArchive(UploadedFile $file, $hashName) + protected function deleteScormFolder($folderHashedName) { - $zip = new \ZipArchive(); - $zip->open($file); - - if (!config()->has('filesystems.disks.'.config('scorm.disk').'.root')) { - throw new StorageNotFoundException(); - } - - $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root'); - - if (substr($rootFolder, -1) != '/') { - // If end with xxx/ - $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root').'/'; - } - - $destinationDir = $rootFolder.$hashName; // file path - - if (!File::isDirectory($destinationDir)) { - File::makeDirectory($destinationDir, 0755, true, true); - } - - $zip->extractTo($destinationDir); - $zip->close(); + return $this->scormDisk->deleteScormFolder($folderHashedName); } /** @@ -224,32 +219,21 @@ class ScormManager */ private function generateScorm(UploadedFile $file) { - $hashName = Uuid::uuid4(); - $hashFileName = $hashName.'.zip'; + $uuid = Str::uuid(); $scormData = $this->parseScormArchive($file); - $this->unzipScormArchive($file, $hashName); - - if (!config()->has('filesystems.disks.'.config('scorm.disk').'.root')) { - throw new StorageNotFoundException(); - } - - $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root'); - - if (substr($rootFolder, -1) != '/') { - // If end with xxx/ - $rootFolder = config('filesystems.disks.'.config('scorm.disk').'.root').'/'; - } - - $destinationDir = $rootFolder.$hashName; // file path - - // Move Scorm archive in the files directory - $finalFile = $file->move($destinationDir, $hashName.'.zip'); + /** + * Unzip a given ZIP file into the web resources directory. + * + * @param string $hashName name of the destination directory + */ + $this->scormDisk->unzip($file, $uuid); return [ - 'name' => $hashFileName, // to follow standard file data format - 'hashName' => $hashName, - 'type' => $finalFile->getMimeType(), + 'identifier' => $scormData['identifier'], + 'uuid' => $uuid, + 'title' => $scormData['title'], // to follow standard file data format 'version' => $scormData['version'], + 'entryUrl' => $scormData['entryUrl'], 'scos' => $scormData['scos'], ]; } @@ -259,7 +243,8 @@ class ScormManager * @param $scormId * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection */ - public function getScos($scormId) { + public function getScos($scormId) + { $scos = ScormScoModel::with([ 'scorm' ])->where('scorm_id', $scormId) @@ -273,7 +258,8 @@ class ScormManager * @param $scoUuid * @return null|\Illuminate\Database\Eloquent\Builder|Model */ - public function getScoByUuid($scoUuid) { + public function getScoByUuid($scoUuid) + { $sco = ScormScoModel::with([ 'scorm' ])->where('uuid', $scoUuid) @@ -282,7 +268,8 @@ class ScormManager return $sco; } - public function getUserResult($scoId, $userId) { + public function getUserResult($scoId, $userId) + { return ScormScoTrackingModel::where('sco_id', $scoId)->where('user_id', $userId)->first(); } @@ -327,7 +314,7 @@ class ScormManager 'user_id' => $userId, 'sco_id' => $sco->id ], [ - 'uuid' => Uuid::uuid4(), + 'uuid' => Str::uuid(), 'progression' => $scoTracking->getProgression(), 'score_raw' => $scoTracking->getScoreRaw(), 'score_min' => $scoTracking->getScoreMin(), @@ -375,7 +362,8 @@ class ScormManager return $scoTracking; } - public function findScoTrackingId($scoUuid, $scoTrackingUuid) { + public function findScoTrackingId($scoUuid, $scoTrackingUuid) + { return ScormScoTrackingModel::with([ 'sco' ])->whereHas('sco', function (Builder $query) use ($scoUuid) { @@ -384,7 +372,8 @@ class ScormManager ->firstOrFail(); } - public function checkUserIsCompletedScorm($scormId, $userId) { + public function checkUserIsCompletedScorm($scormId, $userId) + { $completedSco = []; $scos = ScormScoModel::where('scorm_id', $scormId)->get(); @@ -538,7 +527,8 @@ class ScormManager $bestStatus = $lessonStatus; } - if (empty($tracking->getCompletionStatus()) + if ( + empty($tracking->getCompletionStatus()) || ($completionStatus !== $tracking->getCompletionStatus() && $statusPriority[$completionStatus] > $statusPriority[$tracking->getCompletionStatus()]) ) { // This is no longer needed as completionStatus and successStatus are merged together @@ -630,7 +620,7 @@ class ScormManager $remainingTime %= 3600; $nbMinutes = (int) ($remainingTime / 60); $nbSeconds = $remainingTime % 60; - $result .= 'P'.$nbDays.'DT'.$nbHours.'H'.$nbMinutes.'M'.$nbSeconds.'S'; + $result .= 'P' . $nbDays . 'DT' . $nbHours . 'H' . $nbMinutes . 'M' . $nbSeconds . 'S'; } return $result; -- cgit v1.2.3 From 8a88eed9cab825d297f0d738efd5e0752b675f61 Mon Sep 17 00:00:00 2001 From: Devian Date: Fri, 11 Feb 2022 13:03:49 +0800 Subject: Update --- src/Manager/ScormManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Manager') diff --git a/src/Manager/ScormManager.php b/src/Manager/ScormManager.php index 56c723c..af45d9d 100644 --- a/src/Manager/ScormManager.php +++ b/src/Manager/ScormManager.php @@ -88,7 +88,7 @@ class ScormManager } } - return $scormData; + return $scorm; } /** -- cgit v1.2.3 From 5ee6d14c811e0039db90ab13fe1e9f8ede41060e Mon Sep 17 00:00:00 2001 From: Devian Date: Sun, 13 Feb 2022 20:48:01 +0800 Subject: Added comment --- src/Manager/ScormManager.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Manager') diff --git a/src/Manager/ScormManager.php b/src/Manager/ScormManager.php index af45d9d..6870935 100644 --- a/src/Manager/ScormManager.php +++ b/src/Manager/ScormManager.php @@ -60,6 +60,8 @@ class ScormManager throw new InvalidScormArchiveException('invalid_scorm_data'); } + // Magic method: https://laravel.com/docs/5.0/queries#advanced-wheres + // https://github.com/laravel/framework/blob/9.x/src/Illuminate/Database/Query/Builder.php $scorm = ScormModel::whereOriginFile($scormData['identifier']); // Check if scom package already exists to drop old one. if (!$scorm->exists()) { -- cgit v1.2.3 From a3643727ec1f398b3e8e2fc7e4160e3346b8fe99 Mon Sep 17 00:00:00 2001 From: Christopher Thomas Date: Fri, 18 Mar 2022 19:34:17 +0000 Subject: Update ScormDisk.php Added forward compatibility with flysystem API v2 while maintaining V1 compatibility. Notable changes for ScormDisk.php - - $filesystem->createDir($path); + $filesystem->createDirectory($path); - $filesystem->putStream($path, $contents); + $filesystem->writeStream($path, $contents); --- src/Manager/ScormDisk.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/Manager') diff --git a/src/Manager/ScormDisk.php b/src/Manager/ScormDisk.php index 249f027..ef82d1c 100644 --- a/src/Manager/ScormDisk.php +++ b/src/Manager/ScormDisk.php @@ -28,15 +28,22 @@ class ScormDisk /** @var FilesystemAdapter $disk */ $disk = $this->getDisk(); + $createDir = 'createDir'; + $putStream = 'putStream'; + + if (!method_exists($disk, $createDir)) { + $createDir = 'createDirectory'; + $putStream = 'writeStream'; + } for ($i = 0; $i < $zipArchive->numFiles; ++$i) { $zipEntryName = $zipArchive->getNameIndex($i); $destination = $path . DIRECTORY_SEPARATOR . $this->cleanPath($zipEntryName); if ($this->isDirectory($zipEntryName)) { - $disk->createDir($destination); + $disk->$createDir($destination); continue; } - $disk->putStream($destination, $zipArchive->getStream($zipEntryName)); + $disk->$putStream($destination, $zipArchive->getStream($zipEntryName)); } return true; -- cgit v1.2.3 From 7a5bad1198e4ffd4509b22fa255ba2e768a8fe6e Mon Sep 17 00:00:00 2001 From: devian_peoplelogy Date: Tue, 29 Mar 2022 16:19:17 +0800 Subject: Update --- src/Manager/ScormManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Manager') diff --git a/src/Manager/ScormManager.php b/src/Manager/ScormManager.php index 6870935..d8a99dd 100644 --- a/src/Manager/ScormManager.php +++ b/src/Manager/ScormManager.php @@ -424,7 +424,7 @@ class ScormManager $lessonStatus = isset($data['cmi.core.lesson_status']) ? $data['cmi.core.lesson_status'] : 'unknown'; $sessionTime = isset($data['cmi.core.session_time']) ? $data['cmi.core.session_time'] : null; $sessionTimeInHundredth = $this->convertTimeInHundredth($sessionTime); - $progression = isset($data['cmi.progress_measure']) ? floatval($data['cmi.progress_measure']) : 0; + $progression = !empty($scoreRaw) ? floatval($scoreRaw) : 0; $entry = isset($data['cmi.core.entry']) ? $data['cmi.core.entry'] : null; $exit = isset($data['cmi.core.exit']) ? $data['cmi.core.exit'] : null; $lessonLocation = isset($data['cmi.core.lesson_location']) ? $data['cmi.core.lesson_location'] : null; -- cgit v1.2.3 From 5d901a380c38335abfc9df650f478fb0386c66cc Mon Sep 17 00:00:00 2001 From: devian_peoplelogy Date: Wed, 1 Jun 2022 09:40:40 +0800 Subject: Added resetUserData() function --- src/Manager/ScormManager.php | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/Manager') diff --git a/src/Manager/ScormManager.php b/src/Manager/ScormManager.php index d8a99dd..1d40005 100644 --- a/src/Manager/ScormManager.php +++ b/src/Manager/ScormManager.php @@ -578,6 +578,14 @@ class ScormManager return $updateResult; } + public function resetUserData($scormId, $userId) { + $scos = ScormScoModel::where('scorm_id', $scormId)->get(); + + foreach ($scos as $sco) { + $scoTracking = ScormScoTrackingModel::where('sco_id', $sco->id)->where('user_id', $userId)->delete(); + } + } + private function convertTimeInHundredth($time) { if ($time != null) { -- cgit v1.2.3