diff options
author | Sam Light <samlight1994@gmail.com> | 2022-09-05 09:45:04 +0100 |
---|---|---|
committer | Sam Light <samlight1994@gmail.com> | 2022-09-05 09:45:04 +0100 |
commit | 08908a314cb75dbcf51e6221c2f2c49d3a55d17f (patch) | |
tree | 994b65cc5a3eb70c720cece5e3c4536aff72c7c1 | |
parent | cb292e5674afde1ac99de9c9d3d2dbac5f8c0aee (diff) |
Implemented majority of the player functionality
-rw-r--r-- | composer.json | 3 | ||||
-rw-r--r-- | composer.lock | 6 | ||||
-rw-r--r-- | config/scorm-player.php | 5 | ||||
-rw-r--r-- | database/migrations/scorm_player_improvements.php.stub | 53 | ||||
-rw-r--r-- | resources/views/player.blade.php | 13 | ||||
-rw-r--r-- | routes/web.php | 29 | ||||
-rw-r--r-- | src/Http/Controllers/ScormPlayerController.php | 103 | ||||
-rw-r--r-- | src/Models/Scorm.php | 12 | ||||
-rw-r--r-- | src/Models/ScormSco.php | 12 | ||||
-rw-r--r-- | src/Models/ScormScoTracking.php | 115 | ||||
-rw-r--r-- | src/ScormPlayerServiceProvider.php | 27 |
11 files changed, 371 insertions, 7 deletions
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 @@ +<?php + +return [ + 'middleware' => '', +]; 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 @@ +<?php + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::table('scorm_sco_tracking', function (Blueprint $table) { + $table->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 @@ +<html> + <head> + <link rel="stylesheet" type="text/css" href="{{ manifest('css/scorm_player.css') }}" /> + </head> + + <body> + <iframe class="scorm-player"></iframe> + <script> + window.scorm_api_data = {{ Js::from($scorm_api_data) }}; + </script> + <script src="{{ manifest('js/scorm_player.js') }}"></script> + </body> +</html> 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 @@ +<?php + +use Illuminate\Support\Facades\Route; + +//use Illuminate\Routing\Middleware\SubstituteBindings; + +use Lightscale\ScormPlayer\Http\Controllers\ScormPlayerController; + +Route::name('scorm-player.')->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 @@ +<?php + +namespace Lightscale\ScormPlayer\Http\Controllers; + +use Lightscale\ScormPlayer\Models\{ + Scorm, + ScormSco, + ScormScoTracking, +}; + +use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Auth; +use Illuminate\Http\Request; +use Illuminate\Http\Testing\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; + + +class ScormPlayerController extends Controller +{ + + public function player(Request $request, Scorm $module) + { + $sco = $request->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 @@ +<?php + +namespace Lightscale\ScormPlayer\Models; + +use Illuminate\Database\Eloquent\Factories\HasFactory; + +use Peopleaps\Scorm\Model\ScormModel; + +class Scorm extends ScormModel +{ + use HasFactory; +} diff --git a/src/Models/ScormSco.php b/src/Models/ScormSco.php new file mode 100644 index 0000000..b237f70 --- /dev/null +++ b/src/Models/ScormSco.php @@ -0,0 +1,12 @@ +<?php + +namespace Lightscale\ScormPlayer\Models; + +use Illuminate\Database\Eloquent\Factories\HasFactory; + +use Peopleaps\Scorm\Model\ScormScoModel; + +class ScormSco extends ScormScoModel +{ + use HasFactory; +} diff --git a/src/Models/ScormScoTracking.php b/src/Models/ScormScoTracking.php new file mode 100644 index 0000000..517015e --- /dev/null +++ b/src/Models/ScormScoTracking.php @@ -0,0 +1,115 @@ +<?php + +namespace Lightscale\ScormPlayer\Models; + +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; + +use Peopleaps\Scorm\Model\ScormScoTrackingModel; + +class ScormScoTracking extends ScormScoTrackingModel +{ + use HasFactory; + + protected $fillable = [ + 'uuid', + 'progression', + 'score', + 'score_raw', + 'score_min', + 'score_max', + 'score_scaled', + 'lesson_status', + 'completion_status', + 'session_time', + 'total_time_int', + 'total_time_string', + 'entry', + 'suspend_data', + 'credit', + 'exit', + 'location', + 'mode', + 'is_locked', + 'details', + 'latest_date', + ]; + + public function user() + { + return $this->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'); } } |