diff options
-rw-r--r-- | public/index.php | 69 |
1 files changed, 48 insertions, 21 deletions
diff --git a/public/index.php b/public/index.php index c423162..5f5d904 100644 --- a/public/index.php +++ b/public/index.php | |||
@@ -1,19 +1,21 @@ | |||
1 | <?php | 1 | <?php |
2 | function imagickCleanImage(string $image_data, string $local_path): bool // "string" parce que file_get_contents... | 2 | function imagickCleanImage(string $image_data, string $local_path, string $format = 'jpeg'): bool // "string" parce que file_get_contents... |
3 | { | 3 | { |
4 | try{ | 4 | try{ |
5 | $imagick = new Imagick(); | 5 | $imagick = new Imagick(); |
6 | $imagick->readImageBlob($image_data); | 6 | $imagick->readImageBlob($image_data); |
7 | $imagick->stripImage(); // nettoyage métadonnées | 7 | $imagick->stripImage(); // nettoyage métadonnées |
8 | $imagick->setImageFormat('jpeg'); | 8 | $imagick->setImageFormat($format); |
9 | $imagick->setImageCompression(Imagick::COMPRESSION_JPEG); | 9 | if($format === 'jpeg'){ |
10 | $imagick->setImageCompressionQuality(85); // optionnel | 10 | $imagick->setImageCompression(Imagick::COMPRESSION_JPEG); |
11 | $imagick->setImageCompressionQuality(85); // optionnel | ||
12 | } | ||
11 | $imagick->writeImage($local_path); // enregistrement | 13 | $imagick->writeImage($local_path); // enregistrement |
12 | $imagick->clear(); | 14 | $imagick->clear(); |
13 | $imagick->destroy(); | 15 | $imagick->destroy(); |
14 | return true; | 16 | return true; |
15 | } | 17 | } |
16 | catch (Exception $e) { | 18 | catch(Exception $e){ |
17 | return false; | 19 | return false; |
18 | } | 20 | } |
19 | } | 21 | } |
@@ -23,7 +25,7 @@ function curlDownloadImage(string $url, $maxRetries = 3, $timeout = 10): string| | |||
23 | $attempt = 0; | 25 | $attempt = 0; |
24 | $imageData = false; | 26 | $imageData = false; |
25 | 27 | ||
26 | while($attempt < $maxRetries) { | 28 | while($attempt < $maxRetries){ |
27 | $ch = curl_init($url); // instance de CurlHandle | 29 | $ch = curl_init($url); // instance de CurlHandle |
28 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | 30 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
29 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | 31 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); |
@@ -34,7 +36,7 @@ function curlDownloadImage(string $url, $maxRetries = 3, $timeout = 10): string| | |||
34 | 36 | ||
35 | $imageData = curl_exec($ch); | 37 | $imageData = curl_exec($ch); |
36 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); | 38 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
37 | $curlError = curl_error($ch); | 39 | //$curlError = curl_error($ch); |
38 | 40 | ||
39 | curl_close($ch); | 41 | curl_close($ch); |
40 | 42 | ||
@@ -49,6 +51,12 @@ function curlDownloadImage(string $url, $maxRetries = 3, $timeout = 10): string| | |||
49 | return false; // échec après trois tentatives | 51 | return false; // échec après trois tentatives |
50 | } | 52 | } |
51 | 53 | ||
54 | function sanitizeFileName(string $filename): string { | ||
55 | $filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $filename); // ne garder que les lettres, chiffres, tirets et underscores | ||
56 | $filename = preg_replace('/_+/', '_', $filename); // doublons d'underscores | ||
57 | return trim($filename, '_'); | ||
58 | } | ||
59 | |||
52 | if(isset($_GET['action']) && $_GET['action'] == 'editor_submit'){ | 60 | if(isset($_GET['action']) && $_GET['action'] == 'editor_submit'){ |
53 | // récupération des données | 61 | // récupération des données |
54 | $data = file_get_contents('php://input'); | 62 | $data = file_get_contents('php://input'); |
@@ -95,10 +103,16 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image'){ | |||
95 | mkdir($dest, 0777, true); | 103 | mkdir($dest, 0777, true); |
96 | } | 104 | } |
97 | 105 | ||
98 | $file_path = 'images/' . uniqid() . '.jpg'; | 106 | $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; |
99 | if(imagickCleanImage(file_get_contents($file['tmp_name']), $file_path)){ // recréer l’image pour la nettoyer | 107 | $name = sanitizeFileName(pathinfo($file['name'], PATHINFO_FILENAME)); |
100 | $image_url = str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']); | 108 | $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); |
101 | echo json_encode(['location' => $image_url . $file_path]); // renvoyer l'URL de l'image téléchargée | 109 | if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ |
110 | $extension = 'jpeg'; | ||
111 | } | ||
112 | $file_path = $dest . $name . '_' . uniqid() . '.' . $extension; | ||
113 | |||
114 | if(imagickCleanImage(file_get_contents($file['tmp_name']), $file_path, $extension)){ // recréer l’image pour la nettoyer | ||
115 | echo json_encode(['location' => $file_path]); // renvoyer l'URL de l'image téléchargée | ||
102 | } | 116 | } |
103 | else{ | 117 | else{ |
104 | http_response_code(500); | 118 | http_response_code(500); |
@@ -116,6 +130,11 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ | |||
116 | 130 | ||
117 | if(isset($json['image_url'])){ | 131 | if(isset($json['image_url'])){ |
118 | $image_data = curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents | 132 | $image_data = curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents |
133 | $dest = 'images/'; | ||
134 | |||
135 | if(!is_dir($dest)) { // Vérifier si le répertoire existe, sinon le créer | ||
136 | mkdir($dest, 0777, true); | ||
137 | } | ||
119 | 138 | ||
120 | if($image_data === false){ | 139 | if($image_data === false){ |
121 | http_response_code(400); | 140 | http_response_code(400); |
@@ -123,8 +142,16 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ | |||
123 | die; | 142 | die; |
124 | } | 143 | } |
125 | 144 | ||
126 | $local_path = 'images/' . uniqid() . '.jpg'; | 145 | $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; |
127 | if(imagickCleanImage($image_data, $local_path)){ // recréer l’image pour la nettoyer | 146 | $url_path = parse_url($json['image_url'], PHP_URL_PATH); |
147 | $name = sanitizeFileName(pathinfo($url_path, PATHINFO_FILENAME)); | ||
148 | $extension = strtolower(pathinfo($url_path, PATHINFO_EXTENSION)); | ||
149 | if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ | ||
150 | $extension = 'jpeg'; | ||
151 | } | ||
152 | $local_path = $dest . $name . '_' . uniqid() . '.' . $extension; | ||
153 | |||
154 | if(imagickCleanImage($image_data, $local_path, $extension)){ // recréer l’image pour la nettoyer | ||
128 | echo json_encode(['location' => $local_path]); // nouvelle adresse | 155 | echo json_encode(['location' => $local_path]); // nouvelle adresse |
129 | } | 156 | } |
130 | else{ | 157 | else{ |
@@ -173,7 +200,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ | |||
173 | menubar: false, | 200 | menubar: false, |
174 | toolbar_mode: 'wrap', | 201 | toolbar_mode: 'wrap', |
175 | statusbar: false, | 202 | statusbar: false, |
176 | paste_data_images: true, | 203 | //paste_data_images: true, |
177 | setup: function (editor) { | 204 | setup: function (editor) { |
178 | editor.on('init', function () { | 205 | editor.on('init', function () { |
179 | editors[articleId] = editor; | 206 | editors[articleId] = editor; |
@@ -185,9 +212,9 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ | |||
185 | document.querySelector(`#submit-${articleId}`).classList.remove('hidden'); | 212 | document.querySelector(`#submit-${articleId}`).classList.remove('hidden'); |
186 | }); | 213 | }); |
187 | editor.on('PastePreProcess', function (e){ // déclenchement au collage AVANT insertion dans l'éditeur | 214 | editor.on('PastePreProcess', function (e){ // déclenchement au collage AVANT insertion dans l'éditeur |
188 | let parser = new DOMParser(); | 215 | const parser = new DOMParser(); |
189 | let doc = parser.parseFromString(e.content, 'text/html'); | 216 | const doc = parser.parseFromString(e.content, 'text/html'); |
190 | let images = doc.querySelectorAll('img'); | 217 | const images = doc.querySelectorAll('img'); |
191 | 218 | ||
192 | let downloads_in_progress = []; | 219 | let downloads_in_progress = []; |
193 | 220 | ||
@@ -195,11 +222,11 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ | |||
195 | if(img.src.startsWith('file://')){ // détection d'images non insérables | 222 | if(img.src.startsWith('file://')){ // détection d'images non insérables |
196 | console.warn('Image locale non insérable dans tinymce :', img.src); | 223 | console.warn('Image locale non insérable dans tinymce :', img.src); |
197 | img.outerHTML = '<div style="border:1px solid red; padding:10px; margin:5px 0; background-color:#ffe6e6; color:#a94442; font-size:14px;">' + | 224 | img.outerHTML = '<div style="border:1px solid red; padding:10px; margin:5px 0; background-color:#ffe6e6; color:#a94442; font-size:14px;">' + |
198 | "Image locale non insérée. Pour insérer une image depuis LibreOffice, copiez l'image seule et recoller." + | 225 | "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." + |
199 | '</div>'; | 226 | '</div>'; |
200 | } | 227 | } |
201 | else if(img.src.startsWith('http')){ // détection d'images web | 228 | else if(img.src.startsWith('http')){ // détection d'images web |
202 | let promise = fetch('index.php?action=upload_image_url', { // promesse d'un fichier téléchargeable sur le serveur | 229 | const promise = fetch('index.php?action=upload_image_url', { // promesse d'un fichier téléchargeable sur le serveur |
203 | method: 'POST', | 230 | method: 'POST', |
204 | headers: { 'Content-Type': 'application/json' }, | 231 | headers: { 'Content-Type': 'application/json' }, |
205 | body: JSON.stringify({ image_url: img.src }) | 232 | body: JSON.stringify({ image_url: img.src }) |
@@ -211,7 +238,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ | |||
211 | } | 238 | } |
212 | }) | 239 | }) |
213 | .catch(error => { | 240 | .catch(error => { |
214 | console.error('Erreur lors de l’upload de l’image :', error); | 241 | console.error('Erreur lors de l’upload de l’image distante:', error); |
215 | }); | 242 | }); |
216 | 243 | ||
217 | downloads_in_progress.push(promise); | 244 | downloads_in_progress.push(promise); |
@@ -232,7 +259,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ | |||
232 | } | 259 | } |
233 | }); | 260 | }); |
234 | }, | 261 | }, |
235 | // upload d'image | 262 | // upload d'image natif de tinymce avec le bouton "Insérer une image" |
236 | images_upload_handler: (blobInfo, progress) => new Promise((resolve, reject) => { | 263 | images_upload_handler: (blobInfo, progress) => new Promise((resolve, reject) => { |
237 | const formData = new FormData(); | 264 | const formData = new FormData(); |
238 | formData.append("file", blobInfo.blob()); | 265 | formData.append("file", blobInfo.blob()); |