From c8a8471a1c85a882f128ed172b1a9693e88bdc3c Mon Sep 17 00:00:00 2001 From: polo Date: Thu, 19 Jun 2025 17:38:47 +0200 Subject: =?UTF-8?q?t=C3=A9l=C3=A9chargement=20automatique=20d'images=20bas?= =?UTF-8?q?e64=20non=20encapsul=C3=A9es=20dans=20du=20HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.php | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/public/index.php b/public/index.php index 5f5d904..54ddb31 100644 --- a/public/index.php +++ b/public/index.php @@ -164,6 +164,45 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ } die; } +elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_base64'){ + $json = json_decode(file_get_contents('php://input'), true); + $dest = 'images/'; + + if(!is_dir('images')){ + mkdir('images', 0777, true); + } + + // détection de data:image/ et de ;base64, et capture du format dans $type + if(!isset($json['image_base64']) || !preg_match('/^data:image\/(\w+);base64,/', $json['image_base64'], $type)){ + http_response_code(400); + echo json_encode(['message' => 'Données image base64 manquantes ou invalides']); + die; + } + + $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; + $extension = strtolower($type[1]); + if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ + $extension = 'jpeg'; + } + + $image_data = base64_decode(substr($json['image_base64'], strpos($json['image_base64'], ',') + 1)); // découpe la chaine à la virgule puis convertit en binaire + if($image_data === false){ + http_response_code(400); + echo json_encode(['message' => 'Décodage base64 invalide']); + die; + } + + $local_path = $dest . 'pasted_image_' . uniqid() . '.' . $extension; + + if(imagickCleanImage($image_data, $local_path)){ + echo json_encode(['location' => $local_path]); + } + else{ + http_response_code(500); + echo json_encode(['message' => 'Erreur image non valide']); + } + die; +} ?> @@ -188,6 +227,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ // Récupérer et sauvegarder le contenu d'origine de l'article const articleContent = document.getElementById(articleId).innerHTML; document.getElementById(articleId).setAttribute('data-original-content', articleContent); + let skipPastePreProcess = false; tinymce.init({ selector: `#${articleId}`, @@ -211,7 +251,57 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ document.querySelector(`#cancel-${articleId}`).classList.remove('hidden'); document.querySelector(`#submit-${articleId}`).classList.remove('hidden'); }); - editor.on('PastePreProcess', function (e){ // déclenchement au collage AVANT insertion dans l'éditeur + editor.on('Paste', function (e){ // déclenchement AVANT PastePreProcess et quelque que soit le contenu collé + const clipboardData = (e.clipboardData || e.originalEvent.clipboardData); + if(!clipboardData){ + return; + } + const items = clipboardData.items; + let foundImage = false; + + for(let i = 0; i < items.length; i++){ + let item = items[i]; + + if(item.type.indexOf('image') !== -1){ // test type MIME contenant image + foundImage = true; + + const file = item.getAsFile(); // presse-papier => fichier lisible + const reader = new FileReader(); + + reader.onload = function (event){ // fonction exécutée lorsque reader.readAsDataURL(file) est terminée + const base64Data = event.target.result; // données de l'image + + fetch('index.php?action=upload_image_base64', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ image_base64: base64Data }) + }) + .then(response => response.json()) + .then(data => { + if(data.location){ + editor.insertContent(''); + } + }) + .catch(error => { + console.error('Erreur lors de l’upload de l’image base64 :', error); + }); + }; + reader.readAsDataURL(file); // lecture asynchrone du fichier + } + } + + if(foundImage){ + e.preventDefault(); // supprime le collage automatiue + skipPastePreProcess = true; // désactiver le PastePreProcess pour ce collage + } + }); + editor.on('PastePreProcess', function (e){ // déclenchement au collage AVANT insertion dans l'éditeur SI le contenu est du HTML + if(skipPastePreProcess){ + skipPastePreProcess = false; // réinitialiser pour la prochaine fois + return; // ignorer ce traitement + } + + console.log('PastePreProcess'); const parser = new DOMParser(); const doc = parser.parseFromString(e.content, 'text/html'); const images = doc.querySelectorAll('img'); @@ -221,9 +311,8 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ images.forEach(img => { if(img.src.startsWith('file://')){ // détection d'images non insérables console.warn('Image locale non insérable dans tinymce :', img.src); - img.outerHTML = '
' + - "Image locale non insérée (vient-elle de LibreOffice ?). Effacez cet encadré et copiez-collez l'image seule. Ensuite cliquez sur le bouton Insérer une image puis dans la nouvelle fenêtre sur Enregistrer." + - '
'; + img.outerHTML = `
+ "Image locale non insérée (vient-elle d'un document LibreOffice ?). Effacez ce message rouge et copiez-collez l'image seule.
`; } else if(img.src.startsWith('http')){ // détection d'images web const promise = fetch('index.php?action=upload_image_url', { // promesse d'un fichier téléchargeable sur le serveur -- cgit v1.2.3