Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/create zip #51

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions resources/js/serviceworker.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ self.addEventListener("activate", (e) => {
});

self.addEventListener("message", async (e) => {
console.log("message", e.data);
const url = e.data;
const urlTrimmed = url.replace(/([^\w ]|_)/g, "");
const request = await fetch(url);
Expand Down Expand Up @@ -74,13 +73,10 @@ self.addEventListener("message", async (e) => {

// Fetching content using Service Worker
self.addEventListener("fetch", async (e) => {
console.log("fetch", e.request.url, e.request.url.includes(FOLDER_PREFIX));
if (e.request.url.includes(FOLDER_PREFIX)) {
const id = e.request.url.split(FOLDER_PREFIX)[1].split("/").join("");
const zip = resolvers[id];

console.log("zip", resolvers, id);

// find zip ID
if (zip) {
const uri = e.request.url
Expand Down
160 changes: 35 additions & 125 deletions resources/views/player-serverless.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,120 +29,13 @@
display: block;
}
</style>

<!-- <script type="text/javascript">
const settings = @json($data);
const token = settings.token;
const cmi = settings.player.cmi;

if (settings.version === 'scorm_12') {
scorm12();
} else if (settings.version === 'scorm_2004') {
scorm2004();
}

function scorm12() {
window.API = new Scorm12API(settings.player);
window.API.loadFromJSON(cmi);

window.API.on('LMSSetValue.cmi.*', function(CMIElement, value) {
const data = {
cmi: {
[CMIElement]: value
}
}

post(data);
});

// window.API.on('LMSGetValue.cmi.*', function(CMIElement) {
// get(CMIElement)
// .then(res => res.json())
// .then(res => {
// window.API.LMSSetValue(CMIElement, res)
// })
// });

window.API.on('LMSCommit', function() {
const data = {
cmi: window.API.cmi
}

post(data);
});
}

function scorm2004() {
window.API_1484_11 = new Scorm2004API(settings.player);
window.API_1484_11.loadFromJSON(cmi);

window.API_1484_11.on('SetValue.cmi.*', function(CMIElement, value) {
const data = {
cmi: {
[CMIElement]: value
}
}

post(data);
});

// window.API_1484_11.on('GetValue.cmi.*', function(CMIElement) {
// get(CMIElement)
// .then(res => res.json())
// .then(res => {
// window.API_1484_11.SetValue(CMIElement, res)
// });
// });

window.API_1484_11.on('Commit', function() {
const data = {
cmi: window.API_1484_11.cmi
}

post(data);
});
}

function post(data) {
if (!token) {
return;
}

fetch(settings.lmsUrl + '/' + settings.uuid, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
body: JSON.stringify(data)
});
}

function get(key) {
if (!token) {
return;
}

return fetch(settings.lmsUrl + '/' + settings.scorm_id + '/' + key, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
}
});
}
</script> -->
</head>

<body>
<div id="loading">loading</div>
<script type="module">
function scorm12(settings) {
window.API = new Scorm12API(settings);
//window.API.loadFromJSON(cmi);

window.API.on(
"LMSSetValue.cmi.*",
function (CMIElement, value) {
Expand Down Expand Up @@ -212,6 +105,9 @@ function aicc(settings) {
}

function post(data) {
// TODO: Implement your BACKEND endpoint for set data:
// this is authenticated endpoint Route::post('/{uuid}', [ScormTrackController::class, 'set']);

console.log(
"TODO: Implement your BACKEND endpoint for set data:",
data
Expand All @@ -222,6 +118,8 @@ function post(data) {
}

function get(key) {
// TODO: Implement your BACKEND endpoint for get key:
// this is authenticated endpoint Route::get('/{scoId}/{key}', [ScormTrackController::class, 'get']);
console.log(
"TODO: Implement your BACKEND endpoint for get key:",
key
Expand All @@ -234,9 +132,8 @@ function get(key) {
navigator.serviceWorker.addEventListener("message", (event) => {
const scormObj = event.data.scormObj;
// Those Settings should be fetched from the backend
const settings = @json($data);

console.log("settings", settings);
const settings = window.ScormSettings.data;

if (scormObj.version === "2004") {
scorm2004(settings);
Expand All @@ -246,20 +143,14 @@ function get(key) {
scorm12(settings);
}

const scoEl = document.getElementById("scos");
const iframeEl = document.getElementById("iframe_el");

scoEl.innerHTML == "";

loading(false);

const iframe = document.createElement("iframe");
iframe.src = `${scormObj.PREFIX}/${settings.entry_url}`;
iframeEl.innerHTML = "";
iframeEl.appendChild(iframe);



});

function register(url = "serviceworker.js") {
Expand All @@ -284,26 +175,45 @@ function loading(isLoading = true) {
: "none";
}

function loadScormSCO(uuid, registration, retry = true) {
fetch(`/api/scorm/show/${uuid}`)
.then((res) => res.json())
.then(async (settings) => {
window.ScormSettings = settings;
const zipUrl = settings.data.entry_url_zip;
// 4. check if zip file exists
const exists = await fetch(zipUrl, { method: "HEAD" });
if (exists.ok) {
// 4a. send zip url to service worker
registration.active.postMessage(zipUrl);
} else {
// 4b. create zip file
const exists = await fetch(
`/api/scorm/zip/${uuid}`
);
if (exists.ok) {
// 5b. zip created, recall function
retry &&
loadScormSCO(uuid, registration, false);
}
}
});
}

function init() {
// 1. show loading
loading();
// prettier-ignore
// 2. register service worker
register("/api/scorm/service-worker").then((reg) => {
navigator.serviceWorker.ready.then((registration) => {
//console.log("ready", registration);
loading(false);
});
}).then(() => {
navigator.serviceWorker.ready.then((registration) => {
registration.active.postMessage("{{ $data['entry_url_zip'] }}");
loadScormSCO("{{$data['uuid']}}", registration);
loading(true);
});
});
});
}

init();
</script>
<div id="scos"></div>
<div id="iframe_el"></div>
<!-- <iframe src={{ $data['entry_url_absolute'] }}></iframe> -->
</body>
</html>
2 changes: 0 additions & 2 deletions src/EscolaLmsScormServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use EscolaLms\Scorm\Services\Contracts\ScormTrackServiceContract;
use EscolaLms\Scorm\Services\ScormQueryService;
use EscolaLms\Scorm\Services\ScormService;
use EscolaLms\Scorm\Commands\CopyServiceWorkerJSCommand;
use EscolaLms\Scorm\Services\ScormTrackService;
use Illuminate\Support\ServiceProvider;

Expand Down Expand Up @@ -38,7 +37,6 @@ public function boot()

public function register(): void
{
$this->commands([CopyServiceWorkerJSCommand::class]);

$this->app->register(AuthServiceProvider::class);
}
Expand Down
9 changes: 9 additions & 0 deletions src/Http/Controllers/ScormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,13 @@ public function delete(ScormDeleteRequest $request, ScormModel $scormModel): Jso

return $this->sendSuccess('Scorm Package deleted successfully');
}

public function createOrGetZip(string $uuid, Request $request): JsonResponse
{
$data = $this->scormService->createOrGetZipByUuid(
$uuid,
);

return $this->sendResponse($data, 'Scorm object fetched successfully');
}
}
49 changes: 45 additions & 4 deletions src/Services/ScormService.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,30 @@ private function generateScorm(UploadedFile $file): array
];
}

/**
* Create zip file for scorm if not exists,
* Legacy method for service worker
* @param string $uuid
* @return bool
*/
public function createOrGetZipByUuid(string $uuid): bool
{
try {
// @phpstan-ignore-next-line
$scorm = ScormScoModel::with(['scorm'])
->where('uuid', $uuid)
->first()
->scorm;
if ($scorm) {
$this->zipScorm($scorm->id);
return true;
}
} catch (\Exception $e) {
return false;
}
return false;
}

/**
* Get SCO list
* @param $scormId
Expand Down Expand Up @@ -321,7 +345,7 @@ public function zipScorm(int $id): string
$scormPath = 'scorm' . DIRECTORY_SEPARATOR . $scorm->version . DIRECTORY_SEPARATOR . $scorm->hash_name;
$scormFilePath = $scormPath . DIRECTORY_SEPARATOR . $scorm->hash_name . '.zip';

if (Storage::disk('local')->exists($scormFilePath)) {
if (Storage::disk((config('scorm.disk')))->exists($scormFilePath)) {
return $scormFilePath;
}

Expand All @@ -344,33 +368,50 @@ public function uploadServiceWorkerToBucket(): bool

public function zipScormFiles(ScormModel $scorm): string
{
$isLocal = config('scorm.disk') === 'local';
$scormDisk = Storage::disk(config('scorm.disk'));
$scormLocal = Storage::disk('local');
$scormPath = 'scorm' . DIRECTORY_SEPARATOR . $scorm->version . DIRECTORY_SEPARATOR . $scorm->hash_name;
$scormFilePath = $scormPath . DIRECTORY_SEPARATOR . $scorm->hash_name . '.zip';
$files = array_filter($scormDisk->allFiles($scormPath), fn($item) => $item !== $scormFilePath);

if (!Storage::disk('local')->exists('scorm/exports')) {
Storage::disk('local')->makeDirectory('scorm/exports');
if (!Storage::disk(config('scorm.disk'))->exists('scorm/exports')) {
Storage::disk(config('scorm.disk'))->makeDirectory('scorm/exports');
}

$zip = new \ZipArchive();
$zipFilePath = 'scorm/exports/' . uniqid(rand(), true) . $scorm->hash_name . '.zip';
// zip file must be in local disk always
$zipFile = Storage::disk('local')->path($zipFilePath);

if (!$zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
throw new \Exception("Zip file could not be created: " . $zip->getStatusString());
}


foreach ($files as $file) {
$prefix = 'scorm/' . $scorm->version . DIRECTORY_SEPARATOR . $scorm->uuid . DIRECTORY_SEPARATOR;

// crazy bug
$file = $file[0] == 's' ? $file : 's' . $file;
$dir = str_replace($prefix, "", $file);
if (!$zip->addFile($scormDisk->path($file), $dir)) {

$localFile = $scormLocal->put($file, $scormDisk->get($file));

if (!$zip->addFile($scormLocal->path($file), $dir)) {
throw new \Exception("File [`{$file}`] could not be added to the zip file: " . $zip->getStatusString());
}
}

$zip->close();

if (!$isLocal) {
Storage::disk(config('scorm.disk'))->put($zipFilePath, file_get_contents($zipFile));
if (!Storage::disk(config('scorm.disk'))->exists($scormFilePath)) {
Storage::disk(config('scorm.disk'))->copy($zipFilePath, $scormFilePath);
}
}

return $zipFilePath;
}

Expand Down
1 change: 1 addition & 0 deletions src/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
Route::get('/play/{uuid}', [ScormController::class, "showView"]);
Route::get('/service-worker', [ScormController::class, "showViewServiceWorker"]);
Route::get('/show/{uuid}', [ScormController::class, "showJson"]);
Route::get('/zip/{uuid}', [ScormController::class, "createOrGetZip"]);

Route::group(['prefix' => '/track', 'middleware' => ['auth:api', SubstituteBindings::class]], function () {
Route::post('/{uuid}', [ScormTrackController::class, 'set']);
Expand Down
Loading