From 08908a314cb75dbcf51e6221c2f2c49d3a55d17f Mon Sep 17 00:00:00 2001 From: Sam Light Date: Mon, 5 Sep 2022 09:45:04 +0100 Subject: Implemented majority of the player functionality --- composer.json | 3 + composer.lock | 6 +- config/scorm-player.php | 5 + .../migrations/scorm_player_improvements.php.stub | 53 ++++++++++ resources/views/player.blade.php | 13 +++ routes/web.php | 29 ++++++ src/Http/Controllers/ScormPlayerController.php | 103 ++++++++++++++++++ src/Models/Scorm.php | 12 +++ src/Models/ScormSco.php | 12 +++ src/Models/ScormScoTracking.php | 115 +++++++++++++++++++++ src/ScormPlayerServiceProvider.php | 27 ++++- 11 files changed, 371 insertions(+), 7 deletions(-) create mode 100644 config/scorm-player.php create mode 100644 database/migrations/scorm_player_improvements.php.stub create mode 100644 resources/views/player.blade.php create mode 100644 routes/web.php create mode 100644 src/Http/Controllers/ScormPlayerController.php create mode 100644 src/Models/Scorm.php create mode 100644 src/Models/ScormSco.php create mode 100644 src/Models/ScormScoTracking.php diff --git a/composer.json b/composer.json index b01c72d..484426e 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,9 @@ "laravel": { "providers": [ "Lightscale\\ScormPlayer\\ScormPlayerServiceProvider" + ], + "dont-discover": [ + "devianl2/laravel-scorm" ] } } diff --git a/composer.lock b/composer.lock index 57467d7..ee15c34 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "828ec0e609f83cd705bc45738334f2ed", + "content-hash": "4a4f1c1d08af50453de8e49ac552c9d0", "packages": [ { "name": "devianl2/laravel-scorm", @@ -842,7 +842,9 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": [], + "platform": { + "php": "^8.0" + }, "platform-dev": [], "plugin-api-version": "2.3.0" } diff --git a/config/scorm-player.php b/config/scorm-player.php new file mode 100644 index 0000000..74f89fa --- /dev/null +++ b/config/scorm-player.php @@ -0,0 +1,5 @@ + '', +]; diff --git a/database/migrations/scorm_player_improvements.php.stub b/database/migrations/scorm_player_improvements.php.stub new file mode 100644 index 0000000..4ee5d03 --- /dev/null +++ b/database/migrations/scorm_player_improvements.php.stub @@ -0,0 +1,53 @@ +renameColumn('exit_mode', 'exit'); + $table->renameColumn('lesson_location', 'location'); + $table->renameColumn('lesson_mode', 'mode'); + $table->string('session_time')->change(); + }); + + Schema::table('scorm', function (Blueprint $table) { + $table->unique('uuid'); + }); + + Schema::table('scorm_sco', function (Blueprint $table) { + $table->unique('uuid'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('scorm_sco_tracking', function (Blueprint $table) { + $table->renameColumn('exit', 'exit_mode'); + $table->renameColumn('location', 'lesson_location'); + $table->renameColumn('mode', 'lesson_mode'); + }); + + Schema::table('scorm', function (Blueprint $table) { + $table->dropUnique('scorm_uuid_unique'); + }); + + Schema::table('scorm_sco', function (Blueprint $table) { + $table->dropUnique('scorm_sco_uuid_unique'); + }); + } +}; diff --git a/resources/views/player.blade.php b/resources/views/player.blade.php new file mode 100644 index 0000000..a2baee5 --- /dev/null +++ b/resources/views/player.blade.php @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..b86d710 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,29 @@ +prefix('elearning')->middleware([ + 'web', +])->group(function() { + + $group = function() { + Route::get('/scorm/{sco}', 'scormLoad')->name('scorm.load'); + Route::post('/scorm/{tracking}', 'scormCommit')->name('scorm.commit'); + + Route::get('{module:uuid}', 'player')->name('player'); + Route::get('files/{uuid}/{path}', 'serveModule')->name('serve')->where('path', '.*'); + }; + + $route = Route::controller(ScormPlayerController::class); + $middleware = config('scorm.middleware'); + + if(!empty($middleware)) { + $route->middleware($middleware); + } + + $route->group($group); +}); diff --git a/src/Http/Controllers/ScormPlayerController.php b/src/Http/Controllers/ScormPlayerController.php new file mode 100644 index 0000000..711be60 --- /dev/null +++ b/src/Http/Controllers/ScormPlayerController.php @@ -0,0 +1,103 @@ +query('sco'); + $sco = $module->scos()->findOrFail($sco); + + $route_data = ['sco' => $sco]; + $scorm_api_data = [ + 'routes' => [ + 'load' => route('scorm-player.scorm.load', $route_data), + ], + ]; + + return view('scorm-player::player', compact( + 'scorm_api_data' + )); + } + + public function serveModule(string $uuid, string $path) + { + $path = Storage::disk('scorm-local')->path("{$uuid}/{$path}"); + $mime = MimeType::from($path); + + try { + return response()->file($path, [ + 'content-type' => $mime, + ]); + } + catch(FileNotFoundException $e) { + abort(404); + } + } + + public function scormLoad(ScormSco $sco) + { + $user = Auth::user(); + + $tracking = ScormScoTracking::where([ + 'sco_id' => $sco->id, + 'user_id' => $user->id, + ])->first(); + + if(!$tracking) { + $tracking = new ScormScoTracking([ + 'uuid' => Str::uuid(), + 'progression' => 0, + ]); + $tracking->user()->associate($user); + $tracking->sco_id = $sco->id; + $tracking->save(); + } + + $commit_url = route('scorm-player.scorm.commit', [ + 'tracking' => $tracking->id, + ]); + + $scorm_entry = route('scorm-player.serve', [ + 'uuid' => $sco->scorm->uuid, + 'path' => $sco->entry_url, + ]); + + return [ + 'tracking_id' => $tracking->id, + 'tracking' => $tracking->getCMIData(), + 'entry_url' => $scorm_entry, + 'commit_url' => $commit_url, + ]; + } + + public function scormCommit(Request $request, ScormScoTracking $tracking) + { + $data = $request->all(); + + $tracking->setCMIData($data); + + return [ + 'result' => true, + ]; + } + +} diff --git a/src/Models/Scorm.php b/src/Models/Scorm.php new file mode 100644 index 0000000..7218ff6 --- /dev/null +++ b/src/Models/Scorm.php @@ -0,0 +1,12 @@ +belongsTo(config('scorm.user_class')); + } + + public function commentsFromLearner() : Attribute + { + return Attribute::make( + get: fn() => [] + ); + } + + protected function commentsFromLms() : Attribute + { + return Attribute::make( + get: fn() => [] + ); + } + + protected function score() : Attribute + { + return Attribute::make( + get: fn() => [ + 'min' => $this->score_min, + 'max' => $this->score_max, + 'raw' => $this->score_raw, + 'scaled' => $this->score_scaled, + ], + set: fn(array $data) => [ + 'score_min' => $data['min'] ?? null, + 'score_max' => $data['max'] ?? null, + 'score_raw' => $data['raw'] ?? null, + 'score_scaled' => $data['scaled'] ?? null, + ] + ); + } + + protected function learnerId() : Attribute + { + return Attribute::make( + get: fn() => $this->user_id + ); + } + + protected function learnerName() : Attribute + { + return Attribute::make( + get: fn() => $this->user->name + ); + } + + public function getCMIData() : array + { + $data = collect([ + 'score', + 'entry', + 'credit', + 'exit', + 'mode', + 'location', + 'suspend_data', + 'completion_status', + //'comments_from_lms', + //'comments_from_learner', + 'learner_id', + 'learner_name', + ])->mapWithKeys(fn($k) => [$k => $this->{$k}])->all(); + + return ['cmi' => $data]; + } + + public function setCMIData(array $data) + { + $cmi = $data['cmi'] ?? []; + $this->fill($cmi); + $this->save(); + } +} diff --git a/src/ScormPlayerServiceProvider.php b/src/ScormPlayerServiceProvider.php index dc269be..c1f3369 100644 --- a/src/ScormPlayerServiceProvider.php +++ b/src/ScormPlayerServiceProvider.php @@ -2,23 +2,40 @@ namespace Lightscale\ScormPlayer; -use Illuminate\Support\ServiceProvider; +use Peopleaps\Scorm\ScormServiceProvider; -class ScormPlayerServiceProvider extends ServiceProvider +class ScormPlayerServiceProvider extends ScormServiceProvider { public function register() { - \Log::debug('player regist'); + parent::register(); + + $this->mergeConfigFrom( + __DIR__ . '/../config/scorm-player.php', 'scorm' + ); + } + + protected function offerPublishing() + { + parent::offerPublishing(); + + $this->publishes([ + __DIR__ . '/../database/migrations/scorm_player_improvements.php.stub' => + $this->getMigrationFileName('scorm_player_improvements.php'), + ], 'migrations'); + } public function boot() { + parent::boot(); + // Load routes - //$this->loadRoutesFrom(__DIR__.'/../routes/web.php'); + $this->loadRoutesFrom(__DIR__ . '/../routes/web.php'); // Load views - //$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); + $this->loadViewsFrom(__DIR__.'/../resources/views', 'scorm-player'); } } -- cgit v1.2.3