chore: version inicial del codigo fuente del LMS Estafeta
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
require_once __DIR__.'/auth_guard.php';
|
||||
// Subida de paquete SCORM (.zip): descomprime en data/scormpkg y devuelve la URL de index.html
|
||||
require_once "../config.php";
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$return = new stdClass();
|
||||
|
||||
function responder($return) {
|
||||
echo json_encode($return, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
function slugScorm($text) {
|
||||
$text = trim((string)$text);
|
||||
$text = strtr($text, array(
|
||||
'á'=>'a','à'=>'a','ä'=>'a','â'=>'a','Á'=>'A','À'=>'A','Ä'=>'A','Â'=>'A',
|
||||
'é'=>'e','è'=>'e','ë'=>'e','ê'=>'e','É'=>'E','È'=>'E','Ë'=>'E','Ê'=>'E',
|
||||
'í'=>'i','ì'=>'i','ï'=>'i','î'=>'i','Í'=>'I','Ì'=>'I','Ï'=>'I','Î'=>'I',
|
||||
'ó'=>'o','ò'=>'o','ö'=>'o','ô'=>'o','Ó'=>'O','Ò'=>'O','Ö'=>'O','Ô'=>'O',
|
||||
'ú'=>'u','ù'=>'u','ü'=>'u','û'=>'u','Ú'=>'U','Ù'=>'U','Ü'=>'U','Û'=>'U',
|
||||
'ñ'=>'n','Ñ'=>'N'
|
||||
));
|
||||
$text = strtolower($text);
|
||||
$text = preg_replace('~[^a-z0-9]+~', '-', $text);
|
||||
$text = trim($text, '-');
|
||||
if ($text === '') { $text = 'scorm'; }
|
||||
return substr($text, 0, 60);
|
||||
}
|
||||
|
||||
function limpiarRuta($p) {
|
||||
$p = str_replace('\\', '/', $p);
|
||||
$absoluta = (substr($p, 0, 1) === '/');
|
||||
$parts = array();
|
||||
foreach (explode('/', $p) as $seg) {
|
||||
if ($seg === '' || $seg === '.') continue;
|
||||
if ($seg === '..') { array_pop($parts); continue; }
|
||||
$parts[] = $seg;
|
||||
}
|
||||
return ($absoluta ? '/' : '') . implode('/', $parts);
|
||||
}
|
||||
|
||||
function buscarArchivoCI($dir, $nombre) {
|
||||
$items = @scandir($dir);
|
||||
if (!$items) return null;
|
||||
foreach ($items as $it) {
|
||||
if ($it === '.' || $it === '..') continue;
|
||||
if (strcasecmp($it, $nombre) === 0 && is_file($dir . '/' . $it)) return $dir . '/' . $it;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function primerHtml($dir) {
|
||||
$items = @scandir($dir);
|
||||
if (!$items) return null;
|
||||
foreach ($items as $it) {
|
||||
if ($it === '.' || $it === '..') continue;
|
||||
$p = $dir . '/' . $it;
|
||||
if (is_file($p) && preg_match('~\.html?$~i', $it)) return $p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hrefDeManifest($manifest) {
|
||||
$doc = new DOMDocument();
|
||||
libxml_use_internal_errors(true);
|
||||
$ok = @$doc->load($manifest);
|
||||
libxml_clear_errors();
|
||||
if (!$ok) return null;
|
||||
|
||||
$defaultOrg = '';
|
||||
foreach ($doc->getElementsByTagName('organizations') as $orgs) {
|
||||
$defaultOrg = $orgs->getAttribute('default');
|
||||
break;
|
||||
}
|
||||
|
||||
$ref = '';
|
||||
foreach ($doc->getElementsByTagName('organization') as $org) {
|
||||
if ($defaultOrg !== '' && $org->getAttribute('identifier') !== $defaultOrg) continue;
|
||||
foreach ($org->getElementsByTagName('item') as $item) {
|
||||
$ir = $item->getAttribute('identifierref');
|
||||
if ($ir !== '') { $ref = $ir; break 2; }
|
||||
}
|
||||
if ($defaultOrg !== '') break;
|
||||
}
|
||||
|
||||
$resources = $doc->getElementsByTagName('resource');
|
||||
$href = '';
|
||||
if ($ref !== '') {
|
||||
foreach ($resources as $res) {
|
||||
if ($res->getAttribute('identifier') === $ref) { $href = $res->getAttribute('href'); break; }
|
||||
}
|
||||
}
|
||||
if ($href === '') {
|
||||
foreach ($resources as $res) {
|
||||
$st = $res->getAttribute('adlcp:scormtype');
|
||||
if ($st === '') { $st = $res->getAttributeNS('http://www.adlnet.org/xsd/adlcp_rootv1p2', 'scormtype'); }
|
||||
if (strtolower($st) === 'sco') { $href = $res->getAttribute('href'); break; }
|
||||
}
|
||||
}
|
||||
if ($href === '') {
|
||||
foreach ($resources as $res) {
|
||||
$h = $res->getAttribute('href');
|
||||
if ($h !== '') { $href = $h; break; }
|
||||
}
|
||||
}
|
||||
return ($href !== '') ? $href : null;
|
||||
}
|
||||
|
||||
function detectarEntrada($dest) {
|
||||
// 1) imsmanifest.xml (raíz o un nivel dentro)
|
||||
$manifests = array();
|
||||
if (is_file($dest . '/imsmanifest.xml')) $manifests[] = $dest . '/imsmanifest.xml';
|
||||
foreach (glob($dest . '/*', GLOB_ONLYDIR) as $sub) {
|
||||
if (is_file($sub . '/imsmanifest.xml')) $manifests[] = $sub . '/imsmanifest.xml';
|
||||
}
|
||||
foreach ($manifests as $manifest) {
|
||||
$href = hrefDeManifest($manifest);
|
||||
if ($href) {
|
||||
$cand = limpiarRuta(dirname($manifest) . '/' . urldecode($href));
|
||||
if (is_file($cand)) return $cand;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) nombres comunes (Storyline index_lms.html / story.html, Rise/HTML index.html, etc.)
|
||||
$nombres = array('index.html', 'index_lms.html', 'story.html', 'storyline.html', 'launch.html',
|
||||
'scorm.html', 'scormindex.html', 'default.html', 'start.html', 'index.htm');
|
||||
$dirs = array($dest);
|
||||
foreach (glob($dest . '/*', GLOB_ONLYDIR) as $sub) { $dirs[] = $sub; }
|
||||
foreach ($nombres as $n) {
|
||||
foreach ($dirs as $d) {
|
||||
$p = buscarArchivoCI($d, $n);
|
||||
if ($p) return $p;
|
||||
}
|
||||
}
|
||||
|
||||
// 3) primer .html/.htm
|
||||
foreach ($dirs as $d) {
|
||||
$p = primerHtml($d);
|
||||
if ($p) return $p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function rrmdir($dir) {
|
||||
if (!is_dir($dir)) return;
|
||||
$items = @scandir($dir);
|
||||
if (!$items) return;
|
||||
foreach ($items as $it) {
|
||||
if ($it === '.' || $it === '..') continue;
|
||||
$p = $dir . '/' . $it;
|
||||
if (is_dir($p)) { rrmdir($p); } else { @unlink($p); }
|
||||
}
|
||||
@rmdir($dir);
|
||||
}
|
||||
|
||||
if (!isset($_FILES['files']) || !is_array($_FILES['files']['tmp_name'])) {
|
||||
$return->error = '1'; $return->txt = 'No se recibió ningún archivo.';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$tmp = $_FILES['files']['tmp_name'][0];
|
||||
$errCode = isset($_FILES['files']['error'][0]) ? $_FILES['files']['error'][0] : 1;
|
||||
$orig = '';
|
||||
if (!empty($_FILES['files']['name'][0])) { $orig = $_FILES['files']['name'][0]; }
|
||||
elseif (!empty($_POST['name'])) { $orig = $_POST['name']; }
|
||||
|
||||
if ($errCode !== 0) {
|
||||
$return->error = '1'; $return->txt = 'Error al subir el archivo (código ' . $errCode . ').';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
|
||||
if ($ext !== 'zip') {
|
||||
$return->error = '1'; $return->txt = 'Solo se aceptan paquetes SCORM en formato .zip';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$fh = @fopen($tmp, 'rb');
|
||||
$magic = $fh ? fread($fh, 4) : '';
|
||||
if ($fh) { fclose($fh); }
|
||||
if (substr($magic, 0, 2) !== 'PK') {
|
||||
$return->error = '1'; $return->txt = 'El archivo no es un ZIP válido.';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$scormBase = dirname(__DIR__) . '/data/scormpkg';
|
||||
if (!is_dir($scormBase)) { @mkdir($scormBase, 0775, true); }
|
||||
if (!is_dir($scormBase) || !is_writable($scormBase)) {
|
||||
$return->error = '1'; $return->txt = 'La carpeta data/scormpkg no existe o no tiene permisos de escritura.';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$catSlug = 'general';
|
||||
if (!empty($_POST['catid'])) {
|
||||
$cid = intval($_POST['catid']);
|
||||
if ($cid > 0) {
|
||||
dbConnect();
|
||||
global $link;
|
||||
$resCat = mysqli_query($link, "select nombre from categoria where id = $cid limit 1");
|
||||
if ($resCat && ($rowCat = mysqli_fetch_row($resCat))) {
|
||||
$catSlug = slugScorm($rowCat[0]);
|
||||
}
|
||||
if ($resCat) { mysqli_free_result($resCat); }
|
||||
mysqli_close($link);
|
||||
}
|
||||
}
|
||||
|
||||
$destBase = $scormBase . '/' . $catSlug;
|
||||
if (!is_dir($destBase)) { @mkdir($destBase, 0775, true); }
|
||||
if (!is_dir($destBase) || !is_writable($destBase)) {
|
||||
$return->error = '1'; $return->txt = 'No se pudo crear la carpeta de la categoría en data/scormpkg.';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$slug = slugScorm(pathinfo($orig, PATHINFO_FILENAME));
|
||||
$folder = $slug . '_' . time();
|
||||
$dest = $destBase . '/' . $folder;
|
||||
$i = 1;
|
||||
while (file_exists($dest)) { $folder = $slug . '_' . time() . '_' . $i; $dest = $destBase . '/' . $folder; $i++; }
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($tmp) !== true) {
|
||||
$return->error = '1'; $return->txt = 'No se pudo abrir el paquete .zip';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$totalSize = 0; $numEntries = 0;
|
||||
for ($idx = 0; $idx < $zip->numFiles; $idx++) {
|
||||
$stat = $zip->statIndex($idx);
|
||||
$name = $stat['name'];
|
||||
$numEntries++;
|
||||
$totalSize += $stat['size'];
|
||||
if (preg_match('~^(/|\\\\|[A-Za-z]:)~', $name) || strpos($name, '..') !== false) {
|
||||
$zip->close();
|
||||
$return->error = '1'; $return->txt = 'El .zip contiene rutas no permitidas.';
|
||||
responder($return);
|
||||
}
|
||||
if ($numEntries > 20000 || $totalSize > 2147483648) {
|
||||
$zip->close();
|
||||
$return->error = '1'; $return->txt = 'El paquete es demasiado grande.';
|
||||
responder($return);
|
||||
}
|
||||
}
|
||||
|
||||
if (!@mkdir($dest, 0775, true)) {
|
||||
$zip->close();
|
||||
$return->error = '1'; $return->txt = 'No se pudo crear la carpeta destino.';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
if (!$zip->extractTo($dest)) {
|
||||
$zip->close();
|
||||
rrmdir($dest);
|
||||
$return->error = '1'; $return->txt = 'No se pudo descomprimir el paquete.';
|
||||
responder($return);
|
||||
}
|
||||
$zip->close();
|
||||
|
||||
$indexPath = detectarEntrada($dest);
|
||||
if (!$indexPath) {
|
||||
rrmdir($dest);
|
||||
$return->error = '1'; $return->txt = 'No se encontró un archivo de inicio (index.html / imsmanifest.xml) en el paquete SCORM.';
|
||||
responder($return);
|
||||
}
|
||||
|
||||
$baseReal = realpath($scormBase);
|
||||
$indexReal = realpath($indexPath);
|
||||
$rel = ltrim(str_replace('\\', '/', substr($indexReal, strlen($baseReal))), '/');
|
||||
|
||||
$return->error = '0';
|
||||
$return->category = $catSlug;
|
||||
$return->folder = $catSlug . '/' . $folder;
|
||||
$return->url = $CFG->wwwroot . 'data/scormpkg/' . $rel;
|
||||
responder($return);
|
||||
?>
|
||||
Reference in New Issue
Block a user