Skip to content
Closed
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
84 changes: 84 additions & 0 deletions Core/Assets/JS/FileUploader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
(function ($) {
// Definimos el plugin fileUploader dentro del namespace de jQuery
$.fn.fileUploader = function (options) {
// Validamos que los parámetros obligatorios estén presentes
if (!options || !options.url || !options.action || !options.chunkSize) {
console.error("Error: Los parámetros 'url', 'action' y 'chunkSize' son obligatorios.");
return this;
}

// Configuramos los ajustes del plugin, extendiendo las opciones predeterminadas con las proporcionadas por el usuario
let settings = $.extend({
url: options.url, // URL a la que se enviarán las peticiones de subida
action: options.action, // Acción que se enviará junto con los datos del chunk
chunkSize: options.chunkSize, // Tamaño del chunk en bytes
onStart: function (filename, fileSize) {}, // Callback al iniciar la subida
onProgress: function (percentage) {}, // Callback para actualizar el progreso
onComplete: function (filename) {}, // Callback al completar la subida
onError: function (error) {} // Callback en caso de error
}, options);

// Función para manejar la subida de archivos
function uploadFile(file) {
// Llamamos al callback onStart con el nombre y tamaño del archivo
settings.onStart(file.name, file.size);

// Calculamos el número total de chunks y la posición actual
let totalChunks = Math.ceil(file.size / settings.chunkSize);
let currentChunk = 0;

// Función recursiva para subir cada chunk
function uploadChunk() {
// Si hemos subido todos los chunks, llamamos al callback onComplete
if (currentChunk >= totalChunks) {
settings.onComplete(file.name);
return;
}

// Calculamos el inicio y el fin del chunk actual
let start = currentChunk * settings.chunkSize;
let end = Math.min(start + settings.chunkSize, file.size);
let chunk = file.slice(start, end); // Obtenemos el chunk del archivo
let formData = new FormData();

// Añadimos los datos necesarios al FormData
formData.append("action", settings.action);
formData.append("file", chunk);
formData.append("filename", file.name);
formData.append("totalChunks", totalChunks);
formData.append("chunkIndex", currentChunk);

// Realizamos la petición AJAX para subir el chunk
$.ajax({
url: settings.url,
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function () {
currentChunk++; // Incrementamos el índice del chunk actual
let progress = Math.round((currentChunk / totalChunks) * 100); // Calculamos el progreso
settings.onProgress(progress); // Llamamos al callback onProgress
uploadChunk(); // Subimos el siguiente chunk
},
error: function (xhr, status, error) {
settings.onError(error); // Llamamos al callback onError en caso de fallo
}
});
}

uploadChunk(); // Iniciamos la subida con el primer chunk
}

// Iteramos sobre cada elemento seleccionado y añadimos el evento change
return this.each(function () {
$(this).on("change", function (e) {
let file = e.target.files[0]; // Obtenemos el primer archivo seleccionado

if (!file) return; // Si no hay archivo, salimos de la función

uploadFile(file); // Llamamos a la función uploadFile con el archivo seleccionado
});
});
};
})(jQuery);
42 changes: 42 additions & 0 deletions Core/Controller/AdminPlugins.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ public function privateCore(&$response, $user, $permissions)
$this->uploadPluginAction();
break;

case 'upload-chunk':
$this->uploadPluginChunkAction();
break;

default:
$this->extractPluginsZipFiles();
if (FS_DEBUG) {
Expand Down Expand Up @@ -250,4 +254,42 @@ private function uploadPluginAction(): void
$this->redirect($this->url(), 3);
}
}

private function uploadPluginChunkAction()
{
$chunk = $this->request->files->get('file');
$filename = $this->request->request->get('filename');
$totalChunks = (int) $this->request->request->get('totalChunks');
$chunkIndex = (int) $this->request->request->get('chunkIndex');
$uploadDir = Tools::folder('MyFiles', 'Tmp', 'Uploads') . DIRECTORY_SEPARATOR;

if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}

$chunkPath = $uploadDir . $filename . ".part{$chunkIndex}";
move_uploaded_file($chunk->getPathname(), $chunkPath);

// Si se han subido todos los chunks, los unimos y movemos al directorio de plugins
if (count(glob($uploadDir . $filename . ".part*")) === $totalChunks) {
$finalPath = $uploadDir . $filename;
$output = fopen($finalPath, 'wb');

for ($i = 0; $i < $totalChunks; $i++) {
$chunkFile = $uploadDir . $filename . ".part{$i}";
fwrite($output, file_get_contents($chunkFile));
unlink($chunkFile);
}

fclose($output);

$pluginsPath = Tools::folder('Plugins', $filename);
rename($finalPath, $pluginsPath);
}

$this->setTemplate(false);
$this->response->setStatusCode(Response::HTTP_OK);
$this->response->setContent(json_encode(['status' => 'ok']));
$this->response->headers->set('Content-Type', 'application/json');
}
}
70 changes: 40 additions & 30 deletions Core/View/AdminPlugins.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -126,28 +126,16 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
{% if fsc.getMaxFileUpload() < 99 %}
<div class="alert alert-warning mb-0">
{{ trans('help-server-accepts-filesize', {'%size%': fsc.getMaxFileUpload()}) }}
</div>
{% endif %}
<div class="modal-body">
<div class="form-group">
<div class="form-group mb-2">
{{ trans('select-plugin-zip-file') }}<br/>
<input type="file" name="plugin[]" accept="application/zip" multiple required/>
{% if fsc.getMaxFileUpload() >= 99 %}
<small class="form-text text-muted">
{{ trans('help-server-accepts-filesize', {'%size%': fsc.getMaxFileUpload()}) }}
</small>
{% endif %}
<input type="file" name="plugin[]" id="inputPluginFile" accept="application/zip" multiple required/>
</div>
<div id="progressFileUpload"></div>
<div class="text-right mt-5">
<button type="button" class="btn btn-spin-action btn-secondary" data-dismiss="modal">
{{ trans('cancel') }}
</button>
<button type="submit" class="btn btn-spin-action btn-success">
{{ trans('continue') }}
</button>
</div>
</div>
</div>
Expand Down Expand Up @@ -188,21 +176,6 @@
$(document).ready(function () {
searchList('#querySearchPlugin', '#all-plugins .card', '.card-title');
searchList('#querySearchInstalledPlugins', '#installed-plugins .item-plugin', '.plugin-name');

// si los archivos son demasiado grandes, no se pueden subir
$("#f_add_plugins").submit(function (e) {
let size = 0;
let files = document.querySelector('input[type=file]').files;
for (let i = 0; i < files.length; i++) {
size += files[i].size;
}

if (size > {{ fsc.getMaxFileUpload() }} * 1024 * 1024) { // MB
e.preventDefault();
alert('{{ trans('plugin-file-too-big', {'%size%': fsc.getMaxFileUpload()}) }}');
animateSpinner('remove');
}
});
});

function searchList(querySelectorInput, querySelectorItem, querySelectorPluginName) {
Expand All @@ -214,6 +187,43 @@
});
}
</script>
<script src="{{ asset('Dinamic/Assets/JS/FileUploader.js') }}"></script>
<script>
$(document).ready(function () {
// restamos 1024 al chunkSize para que quepan
// los datos que se le enviar por form
// "file", "filename", "totalChunks", "chunkIndex", "action"
const chunkSize = {{ fsc.getMaxFileUpload() * 1024 * 1024 }} - 1024;
const action = 'upload-chunk';

const urlLimpia = new URL(location.href);
urlLimpia.searchParams.delete('action');
urlLimpia.searchParams.delete('multireqtoken');
const url = urlLimpia.toString();

$("#inputPluginFile").fileUploader({
url, chunkSize, action,
onStart: function (filename, fileSize) {
animateSpinner('add')
},
onProgress: function (percentage) {
$('#progressFileUpload')
.addClass('progress')
.html(`<div class="progress-bar" role="progressbar" style="width: ${percentage}%;" aria-valuenow="${percentage}" aria-valuemin="0" aria-valuemax="100">${percentage}%</div>`);
},
onComplete: function (filename) {
const percentage = 100;
$('#progressFileUpload')
.html(`<div class="progress-bar" role="progressbar" style="width: ${percentage}%;" aria-valuenow="${percentage}" aria-valuemin="0" aria-valuemax="100">Recargando la página, espere.</div>`);
location.href = url;
},
onError: function (error) {
animateSpinner('remove');
console.error("Error: ", error);
}
});
});
</script>
{% endif %}
{% endblock %}

Expand Down