From 8cf5ac1abf9e2a6134cb82d4582aecaa99b1331a Mon Sep 17 00:00:00 2001 From: polo Date: Thu, 19 Jun 2025 18:11:20 +0200 Subject: =?UTF-8?q?upload=20image=20=C3=A9diteur:=20t=C3=A9l=C3=A9chargeme?= =?UTF-8?q?nt=20automatique=20d'images=20base64=20non=20encapsul=C3=A9es?= =?UTF-8?q?=20dans=20du=20HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/tinymce.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++--- src/controller/ajax.php | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/public/js/tinymce.js b/public/js/tinymce.js index f8d78c8..18974af 100644 --- a/public/js/tinymce.js +++ b/public/js/tinymce.js @@ -48,6 +48,51 @@ function openEditor(id, page = '') { document.querySelector(`#new-${id}`).classList.add('hidden'); // id = new-new-id_node } }); + let skipPastePreProcess = false; + 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 const parser = new DOMParser(); const doc = parser.parseFromString(e.content, 'text/html'); @@ -58,9 +103,8 @@ function openEditor(id, page = '') { 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 diff --git a/src/controller/ajax.php b/src/controller/ajax.php index 6813d45..a462921 100644 --- a/src/controller/ajax.php +++ b/src/controller/ajax.php @@ -142,7 +142,46 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ } die; } +// cas du collage d'une image (code base64) non encapsulée dans du HTML +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; +} // détection des requêtes de type XHR, y en a pas à priori /*elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){ -- cgit v1.2.3